Flutter State Management (Provider)

  • img
    Muhammad T S
  • September 21,2020

The State of an application is simply information stored in the memory, which can be accessed from anywhere across your application.

How State and Its Changes Behave in Flutter?

The best answer to the question is Flutter is Declarative.

Flutter builds its user interface to reflect the current state of your app.

When the state of your app changes (for example, the user flips a switch in the settings screen), you change the state, and that triggers a redraw of the user interface. There is no imperative changing of the UI itself (like widget.setText)—you change the state, and the UI rebuilds from scratch.

Let's Use a Simple State Management Approach

Let's add the dependency to our pubspec.yaml first:

name: Counter
description: Simple Counter app.

# ...

dependencies:
  flutter:
    sdk: flutter

  provider: ^4.0.5

dev_dependencies:
  # ...

Now you can import 'package:provider/provider.dart'and start building.

3 Key Concepts to Understand

Consider, we are going to make a simple counter app and Counter is our model class.

ChangeNotifier

A simple class provides change notifications to its listeners. The only code that is specific to ChangeNotifier is the call to notifyListeners().

class Counter with ChangeNotifier {
  int value = 0;

  void increment() {
    value += 1;
    notifyListeners();
  }
}

The call notifyListeners() tells the widgets, which are listening to this model to rebuild.

ChangeNotifierProvider

ChangeNotifierProvider is the widget that provides an instance of a ChangeNotifier to its descendants.
We need to put ChangeNotifierProvider above the widgets that need to access.

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

If you have more than one class to provide, there is MultiProvider.

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => Counter()),
        Provider(create: (context) => SomeOtherClass()),
      ],
      child: MyApp(),
    ),
  );
}

Consumer

Our Counter is provided to widgets through ChangeNotifierProvider declaration; we can start using it through Consumer widget.

Consumer<Counter>(
    builder: (context, counter, child) => Text('${counter.value}'
    ),
),

We must specify the type of the model that we want to access. In this case, we want Counter, so we write Consumer<Counter>. If you don’t specify the generic (<Counter>), the provider package won’t be able to help you.

If you don't really need the data, but to call a method, we don't need to use Consumer because, we’d be asking the framework to rebuild a widget that doesn’t need to be rebuilt.

For this use case, we can use Provider.of, with the listen parameter set to false.

For our Increment button:

floatingActionButton: FloatingActionButton(
        onPressed: () =>
            Provider.of<Counter>(context, listen: false).increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
)

The complete source code is in GitHub.

There are other approaches like Redux, BLoC/Rx, MobX for app state management.
You can learn more at Flutter.dev.

Subscribe to newsletter
Need more tech news? Tune in to our weekly newsletter to get the latest updates