Preface:

Provider is Google I/O 2019 conference announced now the official recommendation of state management, I feel very good after learning and using, because it is really easy to use, so write a blog record it!!

Provider GitHub address: github.com/rrousselGit…

Here are two more YouTube videos (the first one just talks about ChangeNotifierProvider, not MultiProvider) :

www.youtube.com/watch?v=xcS…

www.youtube.com/watch?v=d_m…

OK, let’s cut to the chase. Here is a detailed look at how to use providers for state management in Flutter.

I will use a small demo to show you how to use providers for state management. A single Provider and a MultiProvider implementation are used respectively (the first puts two data in the same class, and the second splits the two data into two different classes).

The demo function is very simple, count + switch themes. There are only two pages, and both pages share the same data.

I have uploaded the code for this small demo to GitHub: github.com/MzoneCL/Flu… (MultiProvider) github.com/MzoneCL/Flu… (Single Provider)

Take a look at the rendering:

Let’s take a look at how a single Provider and multiple providers do this.

A single Provider

ChangeNotifierProvider is a kind of Provider. It is convenient to use to manage situations where there is only one shared data class.

Step 1: Add dependencies

Add dependencies to the pubspec.yaml file.

Provider package pub address: pub. Dev /packages/pr…

Step 2: Create shared data classes

class DataInfo with ChangeNotifier {
  int _count = 0;
  ThemeData _themeData = ThemeData.light();

  get count => _count;
  get themeData => _themeData;

  addCount() {
    _count++;
    notifyListeners();
  }
  subCount() {
    _count--;
    notifyListeners();
  }
  changeTheme() {
    if (_themeData == ThemeData.light()) {
      _themeData = ThemeData.dark();
    } else{ _themeData = ThemeData.light(); } notifyListeners(); }}Copy the code

Data classes need to be with ChangeNotifier to use notifyListeners() functions to update the interface.

Step 3: Access the data

The Provider can obtain data status in two ways:

  1. . Use the Provider of < T > (context)
  2. The use of Consumer

Both methods require a ChangeNotifierProvider() on top:

1. The use of the Provider. Of < T > (context)

For example, the code for specifying the topic section looks like this:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var dataInfo = Provider.of<DataInfo>(context);
    returnMaterialApp( home: MyHomePage(), theme: dataInfo.themeData, ); }}Copy the code

Provider. Of <DataInfo>(context) to obtain the DataInfo instance, specify the specific data class after the of function. You can then access themeData directly through the getter.

2. The use of Consumer

Again, for example, specify the topic part of the code:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnConsumer<DataInfo>( builder: (context, dataInfo, _) => MaterialApp( home: MyHomePage(), theme: dataInfo.themeData, ), ); }}Copy the code

Consumer directly wraps widgets that need to use shared data, and Consumer also specifies the type.

Use MultiProvider to manage multiple shared data classes

This can be used with Stream. For Stream related explanations, this guy (@vadaski) has a great post: juejin.cn/post/684490…

Step 1: Add dependencies

Again, add the provider package.

Step 2: Create shared data classes

I created two classes here: CounterBloc and ThemeDataBloc to manage the count and ThemeData, respectively. (The name of the class suggests it borrows from BLoC)

class CounterBloc {
  StreamController<int> _streamController;
  Stream<int> _stream;
  int _count;

  CounterBloc() {
    _count = 0;
    _streamController = StreamController.broadcast();
    _stream = _streamController.stream;
  }

  Stream<int> get stream => _stream;
  int get count => _count;

  addCounter() {
    _streamController.sink.add(++_count);
  }
  subCounter() {
    _streamController.sink.add(--_count);
  }
  dispose() { _streamController.close(); }}Copy the code

Since CounterBloc is listening on both pages, _streamController needs to be broadcast and supports multiple subscriptions, otherwise an error will be reported.

class ThemeDataBloc {
  StreamController<ThemeData> _streamController;
  Stream<ThemeData> _stream;
  ThemeData _themeData;

  ThemeDataBloc() {
    _themeData = ThemeData.light();
    _streamController = StreamController();
    _stream = _streamController.stream;
  }

  Stream<ThemeData> get stream => _stream;

  changeTheme() { _themeData = _themeData == ThemeData.light()? ThemeData.dark():ThemeData.light(); _streamController.sink.add(_themeData); }dispose() { _streamController.close(); }}Copy the code

Because of the Stream used here, there is no need for the ChangeNotifier as above.

Step 3: Place the MultiProvider at the top of your application

MultiProvider has a mandatory parameter: providers. We need to pass it a list of providers. In this way, we can access related shared data in all child widgets.

main() {
  var counterBloc = CounterBloc();
  var themeDataBloc = ThemeDataBloc();
  runApp(MultiProvider(providers: [
    Provider<CounterBloc>.value(value: counterBloc),
    Provider<ThemeDataBloc>.value(value: themeDataBloc),
  ], child: MyApp()));
}
Copy the code

Step 4: Access the data

Use provider.of <T>(context) to get data of the specified type.

Again, take the code that specifies a topic:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      builder: (context, snapshot) {
        returnMaterialApp( home: MyHomePage(), theme: snapshot.data, ); }, initialData: ThemeData.light(), stream: Provider.of<ThemeDataBloc>(context).stream, ); }}Copy the code

Again, you need to specify the type after the of function to find the specific Provider.

Okay, that’s the basic use of providers for state management in Flutter!