“This is the third day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

preface

We all know that programming relies on abstraction, improves reusability, and is open to extension and closed to modification. The most important tools for implementing the Five SOLID principles are abstraction and inheritance. Polymorphism is one approach. Below, a simple demo introduces common best practices for FLUTTER development.

For chestnut 🌰

/// It is not recommended to avoid processing logic in a common layer
class TestWidget extends StatefulWidget {
  const TestWidget({Key? key}) : super(key: key);

  @override
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {
  dynamic param;
  Widget childWidget = Container();

  ///Provides for external calls
  void update(dynamic value) {
    setState(() {
      param = value;
      _updateBody();
    });
  }
  
  void _updateBody(){
    if(param == null){
      childWidget = Container();
      return;
    }
    switch(param.runtimeType){
      case A:
        childWidget = AWidget(a: param as A,);
        break;
      case B:
        childWidget = BWidget(b: param as B,);
        break;
      default:
        childWidget = Container();
        break; }}@override
  Widget build(BuildContext context) {
    returnContainer( child: childWidget, ); }}Copy the code

The above writing method is not recommended. Dependency inversion should be carried out, and the variable part should be put into the upper layer to ensure the bottom layer is clean, as follows:

Method 1: PassPass parameter constructorPerform inversion of control

typedef ChildBuilder = Widget Function(dynamic param);
class TestWidget extends StatefulWidget {
  final ChildBuilder builder;

  const TestWidget({Key? key, required this.builder}) : super(key: key);

  @override
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {
  dynamic param;

  void update(dynamic value) {
    setState(() {
      param = value;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    returnContainer( child: widget.builder(param), ); }}///External use of TestWidgets
Widget _builder(dynamic param){
  if (param == null) {
    return Container();
  }
  switch (param.runtimeType) {
    case A:
      return AWidget(
        a: param as A,
      );
    case B:
      return BWidget(
        b: param as B,
      );
    default:
      returnContainer(); }}return TestWidget(builder: _builder);
Copy the code

Method 2: Passinheritance + The genericdecouple

/// Change TestWidget to an abstract class and specify generics
abstract class TestWidget<T> extends StatefulWidget {
  const TestWidget({Key? key}) : super(key: key);

  Widget childBuilder(T param);

  @override
  TestWidgetState<T> createState() => TestWidgetState<T>();
}

class TestWidgetState<T> extends State<TestWidget> {
  T? param;
  Widget childWidget = Container();

  void update(T value) {
    setState(() {
      param = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    returnContainer( child: widget.childBuilder(param), ); }}/// Examples of A
class ATestWidget extends TestWidget<A> {
  const ATestWidget({Key? key}) : super(key: key);

  @override
  Widget childBuilder(A param) {
    returnAWidget(a: param); }}/// Example B
class BTestWidget extends TestWidget<B> {
  const BTestWidget({Key? key}) : super(key: key);

  @override
  Widget childBuilder(B param) {
    returnBWidget(b: param); }}Copy the code