274

I'm wondering what the recommended way of passing data to a stateful widget, while creating it, is.

The two styles I've seen are:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState(_server);
}

class _ServerInfoState extends State<ServerInfo> {
  Server _server;

  _ServerInfoState(Server server) {
    this._server = server;
  }
}

This method keeps a value both in ServerInfo and _ServerInfoState, which seems a bit wasteful.

The other method is to use widget._server:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState();
}

class _ServerInfoState extends State<ServerInfo> {
  @override
    Widget build(BuildContext context) {
      widget._server = "10"; // Do something we the server value
      return null;
    }
}

This seems a bit backwards as the state is no longer stored in _ServerInfoSate but instead in the widget.

Is there a best practice for this?

Mojachiee
  • 2,859
  • 4
  • 14
  • 9
  • 6
    The constructor can be reduced to `ServerInfo(this._server);` – Günter Zöchbauer Jun 12 '18 at 13:58
  • This questioned have been asked earlier: https://stackoverflow.com/questions/50428708/is-there-a-easy-way-to-pass-statefultwidget-class-variable-value-to-state-class – Blasanka Mar 11 '19 at 19:24
  • 1
    Does this answer your question? [Passing data to StatefulWidget and accessing it in it's state in Flutter](https://stackoverflow.com/questions/50287995/passing-data-to-statefulwidget-and-accessing-it-in-its-state-in-flutter) – moonvader Mar 01 '20 at 11:46
  • This answer is added one month before this one: https://stackoverflow.com/questions/50428708/pass-statefulwidget-data-to-the-state-class-without-using-constructor – Blasanka Mar 11 '20 at 10:33
  • Does this answer your question? [Pass StatefulWidget data to the State class without using constructor](https://stackoverflow.com/questions/50428708/pass-statefulwidget-data-to-the-state-class-without-using-constructor) – Blasanka Mar 11 '20 at 10:34

8 Answers8

506

Don't pass parameters to State using it's constructor. You should only access the parameters using this.widget.myField.

Not only editing the constructor requires a lot of manual work ; it doesn't bring anything. There's no reason to duplicate all the fields of Widget.

EDIT :

Here's an example:

class ServerIpText extends StatefulWidget {
  final String serverIP;

  const ServerIpText ({ Key? key, this.serverIP }): super(key: key);

  @override
  _ServerIpTextState createState() => _ServerIpTextState();
}

class _ServerIpTextState extends State<ServerIpText> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.serverIP);
  }
}

class AnotherClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ServerIpText(serverIP: "127.0.0.1")
    );
  }
}
Jack'
  • 1,722
  • 1
  • 19
  • 27
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 53
    A further comment, anything you pass to a State object through the constructor won't ever get updated! – Jonah Williams Jun 14 '18 at 04:10
  • 12
    And here I am and don't understand the comment. "Don't pass parameters to State using it's constructor". So how do I pass parameters to the State? – KhoPhi Aug 03 '18 at 20:58
  • 14
    @Rexford `State` already as access to all the properties of `Stateful` by using the `widget` field. – Rémi Rousselet Aug 03 '18 at 21:00
  • 11
    @RémiRousselet What if I want to use foo to pre-fill a textfield, and still allow the user to edit it. Should I also add another foo property in the state? – Said Saifi Sep 03 '18 at 06:29
  • It worked for me, Thanks. But initially, my values were not updating and I still don't know why. Please, where can I read more on this? And why won't any object get updated through the constructor when passing data to a stateful widgets@JonahWilliams – fritz-playmaker Oct 06 '18 at 00:36
  • 3
    @user6638204 You can create another foo property in the state, and override `void initState()` on state to set initial value. Check this [thread](https://groups.google.com/d/msg/flutter-dev/zRnFQU3iZZs/ThAfrMfyBwAJ) option C as example. – Joseph Cheng Oct 30 '18 at 16:17
  • @user6638204 I think you should do it, but also you don't need to call setState(( ) {}) because you don't need to rebuild the container(your page widget) of the textField widget , the textfield is rebuilding itself while typing – Mariano Jan 09 '19 at 06:55
  • It did not work for me. The widget first renders for initial default state of foo, and then, on top of it, renders for one that passed in parameter. – polina-c Jul 02 '19 at 08:45
  • @RémiRousselet I know it's a bit offtopic but still: why do all samples and tutorials use optional parameters while Dart may have required parameters as well - just without brackets `{` and `}`. Normally, if I add parameters I intend to use them so I don't want to wonder if they were passed or not, most of the time they are mandatory. – Kirill Karmazin Aug 23 '19 at 12:37
  • @KirillKarmazin it's just a good practice for readability. Soon Dart will have named required parameters. – Rémi Rousselet Aug 23 '19 at 12:56
  • @RémiRousselet got it, thank you. By the way, Dart already has `@required` annotation for optional named parameters, guess I'll use it for now – Kirill Karmazin Aug 24 '19 at 20:50
  • please help with this https://stackoverflow.com/questions/58218265/discussion-about-complex-ui-with-widgets – Amit Oct 03 '19 at 11:35
  • 4
    On other hand, keeping all the objects in widget instead of state seems weird. – Andrey Gordeev May 20 '20 at 06:38
  • 1
    @JonahWilliams, if you want to change variable you can use it in **initState()** instead of using in **build** function, – Akbar Pulatov Oct 08 '20 at 05:09
  • I want to add create the constructor with a function pointer so the child can programatically set the boottomnavbar selection which belongs to the parent. I think this is the proper way to do this. – Bhikkhu Subhuti Aug 15 '21 at 14:09
65

Best way is don't pass parameters to State class using it's constructor. You can easily access in State class using widget.myField.

For Example

class UserData extends StatefulWidget {
  final String clientName;
  final int clientID;
  const UserData(this.clientName,this.clientID);

  @override
  UserDataState createState() => UserDataState();
}

class UserDataState extends State<UserData> {
  @override
  Widget build(BuildContext context) {
    // Here you direct access using widget
    return Text(widget.clientName); 
  }
}

Pass your data when you Navigate screen :

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));
Sanjayrajsinh
  • 15,014
  • 7
  • 73
  • 78
19

Another answer, building on @RémiRousselet's anwser and for @user6638204's question, if you want to pass initial values and still be able to update them in the state later:

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

  @override
  _MyStatefulState createState() => _MyStatefulState(foo: this.foo);
}

class _MyStatefulState extends State<MyStateful> {
  String foo;

  _MyStatefulState({this.foo});

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}
  • 10
    We can directly use initState to do something like foo = widget.foo, no passing to constructor is required – Aqib Jun 28 '19 at 04:40
  • How to pass argument to this ? – Steev James Aug 17 '19 at 14:32
  • 1
    @SteevJames the widget `MyStateful` has one optional named parameter (property) you can create this widget by calling `MyStateful(foo: "my string",)` – Kirill Karmazin Aug 24 '19 at 21:35
  • @Aqib the `initState` does not solve a problem in the following scenario: for example, you created your Statefull widget with empty parameters and you're waiting for your data to load. When the data is loaded you want to update your Statefull widget with the fresh data and in this case when you call MyStatefull(newData) it's `initState()` won't be called! In this case `didUpdateWidget(MyStatefull oldWidget)` will be called and you would need to compare your data from argument `oldWidget.getData()` with `widget.data` and if it's not the same - call `setState()` to rebuild the widget. – Kirill Karmazin Aug 24 '19 at 21:53
  • @Aqib ... but this is more theoretical problem, since in the first place you wouldn't want to use Statful widget that depends only on the parameters, it's better to use simple Stateless widget instead. Secondly you better not show the widget at all when your data is not ready. – Kirill Karmazin Aug 24 '19 at 21:56
  • 1
    @kirill-karmazin can you elaborate more on the Stateless widget solution? what would you use instead? Is it a best practice from the Flutter team? Thank you – camillo777 Nov 12 '19 at 11:38
  • All these answers seem kinda wasteful - here, `foo` is kept in the widget after being used in the state creation, never to be used again. – hacker1024 Aug 29 '20 at 10:19
17

For passing initial values (without passing anything to the constructor)

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

  @override
  _MyStatefulState createState() => _MyStatefulState();
}

class _MyStatefulState extends State<MyStateful> {
  @override
  void initState(){
    super.initState();
    // you can use this.widget.foo here
  }

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}
Daksh Shah
  • 2,997
  • 6
  • 37
  • 71
5

Flutter's stateful widgets API is kinda awkward: storing data in Widget in order to access it in build() method which resides in State object If you don't want to use some of bigger state management options (Provider, BLoC), use flutter_hooks (https://pub.dev/packages/flutter_hooks) - it is a nicer and cleaner substitute for SatefullWidgets:

class Counter extends HookWidget {
  final int _initialCount;

  Counter(this._initialCount = 0);
  
  @override
  Widget build(BuildContext context) {
    final counter = useState(_initialCount);

    return GestureDetector(
      // automatically triggers a rebuild of Counter widget
      onTap: () => counter.value++,
      child: Text(counter.value.toString()),
    );
  }
}
Maxim Saplin
  • 4,115
  • 38
  • 29
3

The best practice is to define the stateful widget class as immutable which means defining all dependencies (arrival parameter) as final parameters. and getting access to them by widget.<fieldName> in the state class. In case you want to change their values like reassigning you should define the same typed properties in your state class and re-assign them in the initState function. it is highly recommended not to define any not-final property in your stateful widget class and make it a mutable class. something like this pattern:

class SomePage extends StatefulWidget{
  final String? value;
  SomePage({this.value});

  @override
  State<SomePage> createState() => _SomePageState();
}

class _SomePageState extends State<SomePage> {
  String? _value;

  @override
  void initState(){
    super.initState();
    setState(() {
      _value = widget.value;
    });
  }

 @override
 Widget build(BuildContext context) {
    return Text(_value);
 }
}
  • It is not correct to use `setState()` within `initState()`. `setState()` should only be used when you update the state later: https://stackoverflow.com/questions/53363774/importance-of-calling-setstate-inside-initstate – Peter Bruins Mar 14 '23 at 10:27
1

@Rémi Rousselet, @Sanjayrajsinh, @Daksh Shah is also better. but I am also defined this is in from starting point.that which parameter is which value

   import 'package:flutter/material.dart';
    
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      String name = "Flutter Demo";
      String description = "This is Demo Application";
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MainActivity(
            appName: name,
            appDescription: description,
          ),
        );
      }
    }
    
    class MainActivity extends StatefulWidget {
      MainActivity({Key key, this.appName, this.appDescription}) : super(key: key);
      var appName;
      var appDescription;
    
      @override
      _MainActivityState createState() => _MainActivityState();
    }
    
    class _MainActivityState extends State<MainActivity> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.appName),
          ),
          body: Scaffold(
            body: Center(
              child: Text(widget.appDescription),
            ),
          ),
        );
      }
    }
Shirsh Shukla
  • 5,491
  • 3
  • 31
  • 44
-1

To pass data to stateful widget, first of all, create two pages. Now from the first page open the second page and pass the data.

class PageTwo extends StatefulWidget {
  final String title;
  final String name;

  PageTwo ({ this.title, this.name });

  @override
  PageTwoState createState() => PageTwoState();
}

class PageTwoStateState extends State<PageTwo> {
  @override
  Widget build(BuildContext context) {
      return Text(
         widget.title,
         style: TextStyle(
               fontSize: 18, fontWeight: FontWeight.w700),
               ),
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialButton(
          text: "Open PageTwo",
          onPressed: () {
                var destination = ServicePage(
                   title: '<Page Title>',
                   provider: '<Page Name>',
                );
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => destination));
                        },);
  }
}
Fakhriddin Abdullaev
  • 4,169
  • 2
  • 35
  • 37