39

Is it possible to create a class that extends a class extending StatelessWidget or StatefulWidget.

For example:

class MyButton extends StatelessWidget {
final String label;
Button({this.label});
@override
Widget build(BuildContext context) {
    return ButtonExample("label");}
}

then

class SubmitButton extends MyButton
{
   String label;
   SubmitButton({Key key, this.label}) : super(label: label);

// then somehow extend the parent build and change only the color
// or in case of StatefulWidget change a functionality
}

I tried to search for examples online but I had not success.

M20
  • 1,032
  • 2
  • 15
  • 34
  • 7
    That's usually not a good idea. Rather use composition instead of inheritance. – Günter Zöchbauer Jul 23 '18 at 10:30
  • 2
    @GünterZöchbauer Okay, this is probably why I did not find examples doing this. Please post your comment as an answer. – M20 Jul 23 '18 at 11:16
  • 1
    Check out `TextFormField` for an example of how to extend (yes even the flutter devs extend widgets other than StatelessWidget occasionally). It CAN have value to extend. Just don't overuse it. – DarkNeuron Jan 28 '21 at 15:51

5 Answers5

54

In Flutter composition is preferred over inheritance.
Widgets are not supposed to be extended, this is why there are no examples or tutorials how to do it.

Flutter has a strong focus on composition and the included widget library contains a lot of smaller widgets that do one thing well, that allow to compose them into custom widgets in many different ways.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 12
    Then let's say I want to extend the capabilities of the `BottomNavBar` by changing the `onTap` method implementation but keep all the style-related stuff the same. How should I make it into a generic widget that I can use everywhere easily without taking every single property of `BottomNavBar` one by one? – M. Azyoksul Jun 13 '21 at 14:54
  • 1
    make a widget with all styles etc and onTap add as parameter – Kozubi Feb 17 '22 at 05:44
  • 5
    Would've been nice to get a code example on how to use composition; this isn't an answer so much as a comment that the approach is wrong. – dKen May 04 '22 at 09:24
36

As stated by Gunter, flutter uses composition over inheritance.

Official source : flutter faq

Rather than having each widget provide a large number of parameters, Flutter embraces composition. Widgets are built out of smaller widgets that you can reuse and combine in novel ways to make custom widgets. For example, rather than subclassing a generic button widget, RaisedButton combines a Material widget with a GestureDetector widget. The Material widget provides the visual design and the GestureDetector widget provides the interaction design.

This means that instead of extending a Widget, you should create a smaller one and then reuse it.

A practical example would be a base button :

class MyButton extends StatelessWidget {
  final Color color;

  MyButton({this.color = Colors.grey, Key key}): super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Text("My Button"),
    );
  }
}

Then reused using composition to create a more specific type of button :

class OutlineButton extends StatelessWidget {
  final Color color;

  OutlineButton({this.color = Colors.grey, Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        border: Border.all(
          color: color,
          width: 2.0,
          style: BorderStyle.solid,
        ),
      ),
      child: MyButton(
        color: color,
      ),
    );
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 7
    What if I have customTextField that defines a global style and then an emailTextField that uses the customTextField for style but needs some of attributes of TextFormField like keyboardType? Without having to explicitly pass this attribute in customTextField? – Otman Bouchari May 09 '20 at 13:28
15

If you strongly need to extend a widget that already extends StatefulWidget you can do something like this:

class WidgetFoo extends StatefulWidget {
  final String varFromFoo = 'foo';
  @override
  State<StatefulWidget> createState() => WidgetFooState<WidgetFoo>();
}

// Don't make this class name private (beginning with _) to allow its usage in other modules.
class WidgetFooState <T extends StatefulWidget> extends State<T> {
  String varFromFooState = 'foo state';
  @override
  Widget build(BuildContext context) {
    return Text(getText());
  }

  String getText() {
    return 'WidgetFoo';
  }
}

class WidgetBar extends WidgetFoo {
  @override
  State<StatefulWidget> createState() => _WidgetBarState<WidgetBar>();
}

class _WidgetBarState extends WidgetFooState<WidgetBar> {
  @override
  String getText() {
    return 'WidgetBar, ${varFromFooState}, ${widget.varFromFoo}';
  }
}

If you instantiate the WidgetBar it will render the WidgetBar, foo state, foo text, using variables from ancestors.

This is not the best way to develop on Flutter but still, that's a direct answer to your question. The extension of stateless widgets is similar. You just add methods that return some default values and that can be overridden in an inherited class. That's the classics of OOP.

Alexander Pravdin
  • 4,982
  • 3
  • 27
  • 30
  • This should perhaps be the answer to this question. This pattern can be super helpful if all your screens are containing Scaffold, Appbar and you dont wish to write that boiler plate code again and again. Can also contain details about how you show your progress bar as well – Jayshil Dave Mar 10 '23 at 17:23
1

You can use mixins. Check dart docs or flutter docs on how to use them. Using mixins, you can add properties/functions to existing class. You would have already seen an example in animation controllers.

1

In my opinion, one of the valid use-cases for extending a StatefulWidget and the corresponding State is the handling of Streams. I prefere to have a base class that handles the re-subscription instead of repeating the same code over and over again.

abstract class DataRepositoryConsumer extends StatefulWidget {
  const DataRepositoryConsumer({Key? key, required this.dataRepository}) : super(key: key);

  final DataRepository dataRepository;
}

abstract class DataRepositoryConsumerState<T extends DataRepositoryConsumer> extends State<T> {
  @override
  void initState() {
    subscribeToStreams();
    super.initState();
  }

  @override
  void didUpdateWidget(covariant T oldWidget) {
    if (widget.dataRepository != oldWidget.dataRepository) {
      subscribeToStreams();
    }
    super.didUpdateWidget(oldWidget);
  }

  // method used to get the streams from the dataRepository and store them in the state
  void subscribeToDataStreams();
}
artkoenig
  • 7,117
  • 2
  • 40
  • 61