5

I've read in couple places that extending a Flutter widget is an anti-pattern. Is that true?

I've used widget subclassing to cut down on nesting by subclassing the widget I'm removing and put its widgets in its constructor, like so

class Foo extends FormBuilder {
    Foo() : super (
        // bunch of widgets here
    );
}

Extending a stateless widget seems more popular, but it adds a few more lines of code and a widget to the tree, which isn't my preference:

class Foo extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return FormBuilder(
       // bunch of widgets here
    );
}

I've read returning a widget from a function is an antipattern because it breaks rendering optimization. Does my first approach likewise have hidden side effects? I.e., is it really an antipattern?

buttonsrtoys
  • 2,359
  • 3
  • 32
  • 52
  • 1
    I think it has to do with the fact that, if you want to render something, its preferable to nest mutiple `build()` methods rather than calling the `build()` function of another Widget. I don't think that it has been tested to the extent of being proven bad per say. But if those that are in charge of the code [do not recommend it](https://stackoverflow.com/a/51477727/12456169), I don't would recommend it too. – wafL Aug 20 '20 at 11:45

2 Answers2

3

Extending stateful widgets can lead to problems as their state is typed to the superclass and you can not extend their state as most state classes are kept private. A lot of the lookup methods like BuildContext.findAncestorStateOfType() will potentially fail.

Extending stateless widgets should work in most cases but is not recommended, as you have already discovered.

In general with the whole reactive and widget nature of Flutter the principal of composition over inheritance is a good pattern to follow.

You compose widgets into new widgets into new widgets into new widgets into new widgets... You get the point.

Besides that, you save 2 lines of code that mostly get auto generated but you rob yourself of all the simple helpers in VSCode/IntelliJ like "Wrap widget with padding". It is a lot harder to wrap the extended FormBuilder with a padding on all usages in your app. If you compose it is simple, just wrap it inside Foo. Same goes for all other widgets that you use for theming, colors, font styles etc. - padding is just an example.

kuhnroyal
  • 7,188
  • 1
  • 34
  • 47
2

Flutter is more of composition rather than Inheritance. But Inheritance using StatelessWidget will always be useful when parent widgets need to be reused in the child.

Eg:

class FormBuilder extends StatelessWidget {
  Widget getWidget() {
    return Text('Child Text');
  }

  @override
  Widget build(BuildContext context) {
    return Text('FormBuilder Text');
  }
}

class Foo extends FormBuilder {
  Foo() : super();

  @override
  Widget build(BuildContext context) {
    return getWidget();
  }
}

So, Widget which will call Foo() Widget that Widget Element tree will be

-- Foo

-- Container

If its normal Composition then Element tree would be for this

class Foo extends StatelessWidget {
      Foo() : super();
    
      @override
      Widget build(BuildContext context) {
        return FormBuilder();
      }
    }

-- Foo

-- FormBuilder

-- Text

There is not any official doc that says its bad pattern but flutter designed by taking Composition in mind. So, personally I never observed any such performance or lagging issue with inheritance, so I would suggest Inheritance is not a bad choice to use it.

Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147