Flutter - Using StatefulBuilder Widget Examples

This tutorial shows you how to use the StatefulBuilder widget in Flutter.

One thing you can do to improve the performance of a Flutter application is by preventing unnecessary widget builds. That can have a significant effect especially if you can eliminate unnecessary rebuilding of expensive widgets. In this tutorial, I am going to explain how to remove unnecessary builds by using StatefulBuilder.

Using StatefulBuilder

For example, we have a widget whose children are a Switch widget and another widget. The Switch widget requires an argument named value which determines whether it's on or off. Usually, the value passed to the argument is stored in a state variable. When the switch is pressed, the state variable is updated and the build method of the State class is invoked. That means the widgets returned by the build method may be rebuilt.

To make it easy to notice that a widget is rebuilt, I added the print statements in the build methods.

  class _MyWidgetState extends State<MyWidget> {

    bool _isOn = false;

    @override
    Widget build(BuildContext context) {
      print('MyWidgetState - build');
      return Row(
        children: [
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Switch(
                  value: _isOn,
                  onChanged: (newValue) {
                    setState(() {
                      _isOn = newValue;
                    });
                  },
                ),
                MyAnotherWidget(),
              ],
            ),
          )
        ],
      );
    }
  }

  class MyAnotherWidget extends StatelessWidget {

    MyAnotherWidget({super.key});

    @override
    Widget build(BuildContext context) {
      print('MyAnotherWidget - build');
      return const Text('Another widget');
    }
  }

Below is the output every time you toggle the switch.

  MyWidgetState - build
  MyAnotherWidget - build

If you run the code, every time the Switch is toggled, the build method of _MyWidgetState is re-invoked. As a result, it will also invoke the build method of MyAnotherWidget.

In some cases, certain widgets do not need to be rebuilt. For example, in the code above, MyAnotherWidget doesn't use the _isOn state variable. As a result, it doesn't need to be rebuilt every time the user toggles the Switch. You can prevent rebuilds by using a const constructor if possible. If that's not possible, one of the alternatives is using StatefulBuilder.

  const StatefulBuilder({
    Key? key,
    required StatefulWidgetBuilder builder,
  });

To use the StatefulBuilder widget, you have to pass a StatefulWidgetBuilder function as the builder argument. The function is used to build a widget. It has two positional parameters whose types in order are BuildContext and StateSetter. The StateSetter itself is a function that's used to trigger a rebuild. The usage is similar to State.setState where you need to pass a function for setting the new values for the state variables.

When the StateSetter function is called, it will invoke the StatefulWidgetBuilder function. Therefore, only the widgets returned by the builder will be rebuilt. Since the builder function is re-invoked every time the StateSetter is called, the variables used to store the states should be defined outside the builder function.

  @override
  Widget build(BuildContext context) {
    print('MyWidgetState - build');
    return Row(
      children: [
        Expanded(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              StatefulBuilder(builder: (context, setState) {
                print('StatefulWidgetBuilder - build');
                return Switch(
                  value: _isOn,
                  onChanged: (newValue) {
                    setState(() {
                      _isOn = newValue;
                    });
                  },
                );
              }),
              MyAnotherWidget(),
            ],
          ),
        )
      ],
    );
  }

If you run the code, you'll notice the difference that it only rebuilds the widget returned by the StatefulWidgetBuilder when the toggle is pressed.

  StatefulWidgetBuilder - build

StatefulBuilder Parameters

Below is the list of parameters of the StatefulBuilder constructor.

  • Key? key: The widget's key, used to control how a widget is replaced with another.
  • required StatefulWidgetBuilder builder: The widget's key, used to control how a widget is replaced with another.

Full Code

  import 'package:flutter/material.dart';

  void main() => runApp(const MyApp());

  class MyApp extends StatelessWidget {

    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        theme: ThemeData.light().copyWith(
          bottomAppBarTheme: const BottomAppBarTheme(
            color: Colors.teal,
          )
        ),
        home: const MyPage(),
      );
    }
  }

  class MyPage extends StatelessWidget {

    const MyPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
          backgroundColor: Colors.teal,
        ),
        body: const MyWidget(),
      );
    }
  }

  class MyWidget extends StatefulWidget {

    const MyWidget({super.key});

    @override
    State<StatefulWidget> createState() {
      return _MyWidgetState();
    }
  }

  class _MyWidgetState extends State<MyWidget> {

    bool _isOn = false;

    @override
    Widget build(BuildContext context) {
      print('MyWidgetState - build');
      return Row(
        children: [
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                // Switch(
                //   value: _isOn,
                //   onChanged: (newValue) {
                //    setState(() {
                //       _isOn = newValue;
                //     });
                //   },
                // ),
                StatefulBuilder(builder: (context, setState) {
                  print('StatefulWidgetBuilder - build');
                  return Switch(
                    value: _isOn,
                    onChanged: (newValue) {
                      setState(() {
                        _isOn = newValue;
                      });
                    },
                  );
                }),
                MyAnotherWidget(),
              ],
            ),
          )
        ],
      );
    }
  }

  class MyAnotherWidget extends StatelessWidget {

    MyAnotherWidget({super.key});

    @override
    Widget build(BuildContext context) {
      print('MyAnotherWidget - build');
      return const Text('Another widget');
    }
  }

Summary

The StatefulBuilder widget can be used to eliminate unnecessary rebuilds. The usage is quite simple, you need to pass a StatefulWidgetBuilder function, which returns a child widget. Inside the function, you can update the states and Flutter will only rebuild the widget returned by the StatefulWidgetBuilder function.