Posts

About

Flutter Stateful Widgets

April 19, 2020

Stateless Widgets

Stateless widgets in Flutter are pretty straightforward. They take in some properties defined by a parent widget. Then they define a build function that builds the view based on the properties. They are only rebuilt when the properties are changed. Stateless widgets usually look like the following:

class BoldLabel extends StatelessWidget {
  const BoldLabel({
    Key key,
     String text,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Text(
      text,
      style: TextStyle(
        fontWeight: FontWeight.bold,
      ),
    );
  }
}

Stateful Widgets

Not every widget can be stateless, otherwise your app would be completely static. Anytime some type of dynamic behaviour is needed, a Stateful Widget should be used. Like Stateless Widgets, Stateful Widgets can take in properties and are rebuilt when they change. Stateful Widgets define a createState function to create a State object. The state object defines the build function as well as any state. The widget is also rebuilt any time that state is changed using setState. A typical Stateful Widget will look something like the following:

class Counter extends StatefulWidget {
  const Counter({Key key}) : super(key: key);

  
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('current count is: ${count}'),
        FlatButton(
          child: Text('Increment'),
          onPressed: () => {
            setState(() {
              count = count + 1;
            })
          },
        )
      ],
    );
  }
}

Passing Props To Stateful Widgets

It is not really obvious how to use properties defined by the Stateful Widget inside the State. My first instinct was to pass them along.

class Counter extends StatefulWidget {
  const Counter({Key key, String label}) : super(key: key);

  
  _CounterState createState() => _CounterState(label);
}

class _CounterState extends State<Counter> {
  final String label;
  int count = 0;

  _CounterState(this.label);

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('${label}: ${count}'),
        FlatButton(
          child: Text('Increment'),
          onPressed: () => {
            setState(() {
              count = count + 1;
            })
          },
        )
      ],
    );
  }
}

Not only is that tedious to do, it leads to some serious problems. If the label property is changed by the parent widget, the Counter will not show the new label. This is because createState is only called when the widget is initially created. It will not be recreated when properties changes.

This is why the State has a read only widget property to get the widget. This can be used to access any properties on the widget without having to pass them along. It will also properly react to any changes to the properties!

class Counter extends StatefulWidget {
  const Counter({Key key, String label}) : super(key: key);

  
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('${widget.label}: ${count}'),
        FlatButton(
          child: Text('Increment'),
          onPressed: () => {
            setState(() {
              count = count + 1;
            })
          },
        )
      ],
    );
  }
}

Conclusion

If you need access to a Stateful Widget’s properties in its State, then use widget to get them. Otherwise it requires more code and more headaches.


Written by Jacob Oakes
I am a software architect who enjoys learning new things, clean code, and automated tests.