Is it possible to use polymorphism with an abstraction layer for different widgets in Flutter?

1,250

Make your AbstractWidget extend or implement Widget. However, I must agree with Rémi Rousselet. An abstract class should not know anything about its children (that's why it is an abstraction, anyway). I would do, instead:

class Foo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var widgetList = new List<Widget>();
    for (var item in items) {
      X content = fetchContentFromAPI();
      widgetList.add(abstractWidgetWith(content: content, id: item.id));
    }
    return Column(children: widgetList);
  }

  Widget abstractWidgetWith({@required int id, @required X content}) {
    switch (id) {
      case 1:
        return Implementation1(content);
      default:
        return Implementation2(content);
    }
  }
}

abstract class AbstractWidget {
  final X content;
  AbstractWidget(this.content);
}

class Implementation1 extends StatelessWidget implements AbstractWidget {
  final X content;

  Implementation1(this.content);

  @override
  Widget build(BuildContext context) {
    // Display content in some type of way
  }
}

class Implementation2 extends StatelessWidget implements AbstractWidget {
  final X content;

  Implementation2(this.content);

  @override
  Widget build(BuildContext context) {
    // Display content in some type of way
  }
}

I just want to make an addition about something you said:

It's just that the extensive switch case goes against the design patterns and principles I've been taught.

The idea here is to always look for an abstraction instead of repeating condition structures. Notice repeating emphasizing. If the condition structure is used more than once, abstraction is mostly a better option. If this is not the case, you are probably overkilling the problem by creating abstraction.

Again, notice repeating emphasizing. You tend to need an abstraction when you have lots of condition structures, but I'll end up using ONE condition in the end. In other words, you can't get rid of the condition structures, you are just going to use it less.

In the context of this question, seems like you followed all the rules. This looks like a clean code to me.

Share:
1,250
RDNotorious
Author by

RDNotorious

Updated on December 06, 2022

Comments

  • RDNotorious
    RDNotorious over 1 year

    I have a set of around 8 widgets that all accept a single parameter of type X and display the content of type X in a different way. What I am trying to create is an abstraction layer that defines the structure of such a widget. Besides the structure, the abstraction layer would define a factory method to decide which implementation gets used based on an ID. The different implementations are all widgets that extend either Stateless- or StatefulWidget.

    The abstraction layer would look like the following:

    abstract class AbstractWidget {
    final X content;
    
    factory AbstractWidget({@required int id, @required X content}) {
      switch (id) {
        case 1:
          return Implementation1(content);
          break;
        default: return Implementation2(content);
       }
      }
     }
    

    An implementation would look like the following:

    class Implementation1 extends StatelessWidget implements AbstractWidget {
      final X content;
    
      Implementation1(this.content);
    
      @override
      Widget build(BuildContext context) {
        // Display content in some type of way
      }
    }
    

    So what I'm trying to achieve is the following:

    var widgetList = new List<Widget>();
    for (var item in items) {
      X content = fetchContentFromAPI();
      widgetList.add(AbstractWidget(content: content, id: item.id));
    }
    return Column(children: widgetList);
    

    This would not work because the AbstractWidget isn't technically a Widget-type, even though it can only return instances of either Stateless- or StatefulWidgets. If anyone knows a better way of implementing my structure, it would help me a lot!

    • Rémi Rousselet
      Rémi Rousselet almost 5 years
      Can't you just make a function that does the switch case, instead of using a factory constructor?
    • RDNotorious
      RDNotorious almost 5 years
      That's what I used initially but I wanted to make use of an abstraction so that in the future new implementations would have to follow the same protocol before they could get added to the list.
    • Rémi Rousselet
      Rémi Rousselet almost 5 years
      But that function can be used as your abstraction. If you always use that function instead of the AbstractWidget constructor, the behavior is about the same.
    • Rémi Rousselet
      Rémi Rousselet almost 5 years
      In any case, this is generally a bad idea to use advanced inheritance with widgets. Functions or composition – that's it.
    • RDNotorious
      RDNotorious almost 5 years
      It's just that the extensive switch case goes against the design patterns and principles I've been taught. This was a start to try and create a more extensible version of my code by defining a class structure for all new implementations. I don't see how the function you mentiond would define any rules to the widgets that get added to the list
    • RDNotorious
      RDNotorious almost 5 years
      Nevermind, I see now how the function would define the same functionality as the class by requiring certain parameters. However I'm still wondering if there is a better way of implementing my use case without the extensive switch case.
    • Rémi Rousselet
      Rémi Rousselet almost 5 years
      There's no difference between making a switch case inside a factory and inside a function besides altering or not the base class. Since the base class slot of widgets is already taken, a function is the only real alternative.
  • Rémi Rousselet
    Rémi Rousselet almost 5 years
    That won't do. Dart classes can't extend more than one class. And AbstractWidget can't be represented as a mixin either because of the factory constructor.
  • Hugo Passos
    Hugo Passos almost 5 years
    You don't have to extend more than one class. Implementations would continue as they are: class Implementation1 extends StatelessWidget implements AbstractWidget.
  • Rémi Rousselet
    Rémi Rousselet almost 5 years
    But then it's not an abstract class, it is an interface. You can't define a method inside AbstractWidget that all subclasses will use.
  • Hugo Passos
    Hugo Passos almost 5 years
    If you implement AbstractClass, it'll be considered an interface. If you extend AbstractClass, it'll be considered an abstract class. This won't change by the time you make AbstractClassextend or implement Widget. Since OP is implementing AbstractClass, all subclasses are going to use its methods.
  • Hugo Passos
    Hugo Passos almost 5 years
    I don't know what led you to this confusion, but you can paste OP's code, make AbstractWidget extend or implement Widget and check for yourself. It works :)
  • RDNotorious
    RDNotorious almost 5 years
    I've decided to go for @HugoPassos code snippet that's based on both of your replies. Thanks for the help!
  • Hugo Passos
    Hugo Passos almost 5 years
    Notorius, please take a look at the addition I made at the end of the answer.