1

I am building a Stateful Widget in Flutter, and as such, there is a requirement for all arguments passed in the constructor to be final (since Stateful widgets are marked with the @immutable annotation).

Thing is, I want to have two different constructors for my widget and to exclude some of the members of the Stateful widget, depending on the constructor used. I have to stress, that I do not want these arguments to be optional, but mandatory.

For example,

class MyWidget extends StatefulWidget {
     
  MyWidget.first({this.firstArgument}};

  MyWidget.second({this.secondArgument});

  final int firstArgument;
  final String secondArgument;

  @override
  MyWidget createState() => MyWidgetState();
}

When I write this, I get a compiler error, telling me that:

All final variables must be initialized, but 'firstArgument' isn't.

The same goes for the second member variable.

How can I overcome this?

I can't move firstArgument and secondArgument to the state of MyWidget, since I want them to be initialized in the constructor(s) and also because they should not be changed. I can't mark them as not final since then I will get a compiler warning and also break the Stateful widget paradigm.

Is there a different approach I should use?

tomerpacific
  • 4,704
  • 13
  • 34
  • 52

2 Answers2

3

Thing is, I want to have two different constructors for my widget and to exclude some of the members of the Stateful widget, depending on the constructor used. I have to stress, that I do not want these arguments to be optional, but mandatory.

If you don't want them to be optional, you need to mark them as required:

  MyWidget.first({required this.firstArgument}};

  MyWidget.second({required this.secondArgument});

(If you don't have null-safety enabled, you will instead need to use the weaker @required annotation from package:meta.)

My understanding is that you want firstArgument and secondArgument to be required for MyWidget.first and MyWidget.second respectively but that they are not intended to be required together (that is, only one should be set).

You could fix this by explicitly initializing both values in the constructors:

  MyWidget.first({required this.firstArgument}} : secondArgument = null;
  MyWidget.second({required this.secondArgument}): firstArgument = null;

If you have null-safety enabled, you also would need to make your members nullable:

  final int? firstArgument;
  final String? secondArgument;
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • @jamesdiin - thanks for the detailed answer. Could you briefly explain or point to a place that does so the constructor syntax you provided and it's meaning? – tomerpacific Mar 20 '21 at 14:21
0

Maybe factory constructors would help?

    class MyWidget extends StatefulWidget {
         
      MyWidget._({this.firstArgument, this.secondArgument}};
    
      factory MyWidget.first({@required int first})=>MyWidget._(firstArgument: first, );
      factory MyWidget.second({@required String second})=>MyWidget._(secondArgument: second, );
      final int firstArgument;
      final String secondArgument;
    
      @override
      MyWidget createState() => MyWidgetState();
    }

This way, you'll only be able to build this widget using these constructors (since the class constructor is private) and when you call MyWidget.first the value for secondArgument for the widget will be null, and the same applies when you use MyWidget.second with firstArgument

Luis Utrera
  • 942
  • 6
  • 15
  • You also could use [redirecting constructors](https://dart.dev/guides/language/language-tour#redirecting-constructors) instead of `factory` constructors. – jamesdlin Mar 18 '21 at 23:12