Is it possible to use polymorphism with an abstraction layer for different widgets in Flutter?
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.
RDNotorious
Updated on December 06, 2022Comments
-
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 almost 5 yearsCan't you just make a function that does the switch case, instead of using a factory constructor?
-
RDNotorious almost 5 yearsThat'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 almost 5 yearsBut 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 almost 5 yearsIn any case, this is generally a bad idea to use advanced inheritance with widgets. Functions or composition – that's it.
-
RDNotorious almost 5 yearsIt'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 almost 5 yearsNevermind, 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 almost 5 yearsThere'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 almost 5 yearsThat 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 almost 5 yearsYou don't have to extend more than one class. Implementations would continue as they are:
class Implementation1 extends StatelessWidget implements AbstractWidget
. -
Rémi Rousselet almost 5 yearsBut 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 almost 5 yearsIf you implement
AbstractClass
, it'll be considered an interface. If you extendAbstractClass
, it'll be considered an abstract class. This won't change by the time you makeAbstractClass
extend or implementWidget
. Since OP is implementingAbstractClass
, all subclasses are going to use its methods. -
Hugo Passos almost 5 yearsI don't know what led you to this confusion, but you can paste OP's code, make
AbstractWidget
extend or implementWidget
and check for yourself. It works :) -
RDNotorious almost 5 yearsI've decided to go for @HugoPassos code snippet that's based on both of your replies. Thanks for the help!
-
Hugo Passos almost 5 yearsNotorius, please take a look at the addition I made at the end of the answer.