Flutter Provider state management - analysis of four kinds of consumer usage

Provider.of

Provider.of<T>(context)It is Providera static method provided for us. When we use this method to get the value, it will return the nearest Ttype found providerto us, and it will not traverse the entire component tree. Let's look at the code below:

Step 1: Define the model

We define a CountNotifier1model, and all the following sample codes will be demonstrated around this model

import 'package:flutter/material.dart';

class CountNotifier1 with ChangeNotifier {

  int count = 0;

  void increment() {
    count++;
    notifyListeners();
  }
}

Step 2: Application entry setting 

return ChangeNotifierProvider(
  create: (_) => CountNotifier1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ConsumerExample(),
  ),
);

Step 3: Use Provider.of

Here, the value is read and the button +1 is clicked Provider.of<T>()to obtain and use it.

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/consumer_example/count_notifier1.dart';
import 'package:provider/provider.dart';

class ConsumerExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("ConsumerExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(Provider.of<CountNotifier1>(context).count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(
              padding: EdgeInsets.only(
                top: 20
              ),
              child: ElevatedButton(
                onPressed: (){
                  Provider.of<CountNotifier1>(context).increment();
                },
                child: Text("点击加1"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

error log

When we run the code, an error will be prompted, which indicates that we are trying to listen to Widgetthe value exposed by the provider from outside the tree. If we want to fix it, we can change it to listen. falseThis problem will actually Provider 4.0.2appear later, the most important thing is its default The behavior is ture, the error log is as follows:

======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<CountNotifier1>(context, listen: false);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

The context used was: ConsumerExample(dependencies: [_InheritedProviderScope<CountNotifier1?>])
'package:provider/src/provider.dart':
Failed assertion: line 276 pos 7: 'context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate'

When the exception was thrown, this was the stack: 
........
====================================================================================================

Set listen to false

Provider.of<CountNotifier1>(context, listen: false).increment();

Consumer

ConsumberIt is just Widgetcalled in Prvoider.of, and its construction is delegated to the constructor. For example Builder, if you Widgetrely on multiple models, it also provides a convenient call. Next, we will use it to modify Consumer23456the above caseConsumer

Wrap components with Consumer

There is a builderconstructor in it. When we change it to bodythe following and rerun it, we can find that Provider.ofthe result is the same as that used, but there is no need to Provider.ofwrite a large series of repetitive codes every time we use it.

There are three properties in it:

  • context:  the current context
  • **Provider.of(context):** model object
  • child:  child component (the part that does not need to be refreshed)
    body: Consumer(
      builder: (_, CountNotifier1 countNotifier1, child) {
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(countNotifier1.count.toString(),
                   style: TextStyle(
                     color: Colors.red,
                     fontSize: 50
                   ),
                  ),
              Padding(
                padding: EdgeInsets.only(
                  top: 20
                ),
                child: ElevatedButton(
                  onPressed: (){
                    countNotifier1.increment();
                  },
                  child: Text("点击加1"),
                ),
              )
            ],
          ),
        );
      },
    ),
    

    Optimizing Consumers

    Optimization Method 1: Adjust the position of Consumer as much as possible

    We found in the above code Centerthat Columnthe components are also Consumerwrapped, but these two components do not need to update the state, and every Widgettime we build, we will rebuild the whole body, so we optimize the code structure, it looks like Like this:

    body: Center(
      child: Consumer(
        builder: (_, CountNotifier1 countNotifier1, child) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  countNotifier1.count.toString(),
                  style: TextStyle(color: Colors.red, fontSize: 50),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () {
                      countNotifier1.increment();
                    },
                    child: Text("点击加1"),
                  ),
                ),
           			Container(
                  child: Column(
                    children: [
                      Text("更多组件1"),
                      Text("更多组件2"),
                      Text("更多组件3"),
                      Text("更多组件4"),
                      Text("更多组件5"),
                      Text("更多组件6"),
                    ],
                  ),
                )
              ],
            ),
          );
        },
      )
    )

    Optimization method 2: Use child for components that do not need to be refreshed but are wrapped by Consumer

    For example, above we have more components 1-6 or even hundreds of components that do not need to refresh the state, but because you use the Consumerpackage to refresh all of them, it will obviously lead to a decrease in performance. You may think of Consumercomponents that need to be refreshed with multiple packages alone It's solved, but isn't this putting the cart before the horse? It is a robust and repetitive code Providerto solve the code , so at this time we can use the parameters provided for us , as follows:Consumerchild

    body: Center(
      child: Consumer(
        builder: (_, CountNotifier1 countNotifier1, child) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  countNotifier1.count.toString(),
                  style: TextStyle(color: Colors.red, fontSize: 50),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () {
                      countNotifier1.increment();
                    },
                    child: Text("点击加1"),
                  ),
                ),
                child!
              ],
            ),
          );
        },
        child: Container(
          child: Column(
            children: [
              Text("更多组件1"),
              Text("更多组件2"),
              Text("更多组件3"),
              Text("更多组件4"),
              Text("更多组件5"),
              Text("更多组件6"),
            ],
          ),
        ),
      )
    ),

    Selector

    SelectorClasses are Consumersimilar, but provide more fine-grained control when buildcalling Widgetmethods. In simple terms, Selectorthey are also a consumer, which allows you to prepare which properties to define from the model.

    Let's take an example:

    For example, there are 50 attributes in the user model, but I only need to update the age, so I hope that there is no need to rebuild components such as user name and phone numberSelector , so it is used to solve this problem. Let's take a look at the example:

    第一步:定义模型
    import 'package:flutter/material.dart';
    
    class UserModel6 with ChangeNotifier {
    
      String name = "Jimi";
      int age = 18;
      String phone = "18888888888";
    
    
      void increaseAge() {
        age++;
        notifyListeners();
      }
    }
    第二步:应用程序入口设置
    return ChangeNotifierProvider(
      create: (_) => UserModel6(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: SelectorExample(),
      ),
    );
    第三步:使用Selector更精细的控制
    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/selector_example/user_model6.dart';
    import 'package:provider/provider.dart';
    
    class SelectorExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("SelectorExample"),
          ),
          body: Center(
            child: Selector<UserModel6, int>(
              selector: (_, userModel6) => userModel6.age,
              builder: (_, age, child) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(age.toString(),
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    ),
                    child!
                  ],
                );
              },
              child: Padding(
                padding: EdgeInsets.all(20),
                child: ElevatedButton(
                  onPressed: (){
                    Provider.of<UserModel6>(context, listen: false).increaseAge();
                  },
                  child: Text("改变年龄"),
                ),
              ),
            ),
          ),
        );
      }
    }

    Three ways:

  • BuildContext.read: BuildContext.read<CountNotifier1>() can be replaced Provider.of<CountNotifier1>(context,listen: false), it will find CountNotifier1and return it.
  • BuildContext.watch: BuildContext.watch<CountNotifier1>() Can be replaced Provider.of<CountNotifier1>(context,listen: false), it looks readthe same, but watchyou don't need to use it Consumer.
  • BuildContext.select: BuildContext.select<CountNotifier1>() It can be replaced Provider.of<CountNotifier1>(context,listen: false), it looks watchthe same as it, but selectyou don't need to use it Selector.
  • BuildContext.read

    The following two ways of using the result are the same

    Use Provider.of()

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
    
          /// Provider.of 获取值
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(Provider.of<CountNotifier2>(context).count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    Use BuildContext.read

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// read 获取值
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(context.read<CountNotifier2>().count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    BuildContext.watch

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
    
        /// 重要
        final countNotifier2 = context.watch<CountNotifier2>();
    
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// watch 
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(countNotifier2.count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => countNotifier2.increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
    
        );
      }
    }
    

    BuildContext.select

    Use Selector

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// Selector
          body: Center(
            child: Selector<CountNotifier2, int>(
              selector: (_, countNotifier2) => countNotifier2.count,
              builder: (_, count, child) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(count.toString(),
                      style: TextStyle(
                          color: Colors.red,
                          fontSize: 50
                      ),
                    ),
                    child!
                  ],
                );
              },
              child: Padding(
                padding: EdgeInsets.only(top: 20),
                child: ElevatedButton(
                  onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                  child: Text("点击加1"),
                ),
              ),
            ),
          ),
    
        );
      }
    }

    Use BuildContext.select

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        
        /// 重要
        final count = context.select((CountNotifier2 countNotifier2) => countNotifier2.count);
    
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// select
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }

    Summarize

    FlutterIt provides us with a variety of ways to read values. Above we use and analyze and compare the four categories of consumers. You can use the corresponding method according to your actual application scenario.

Guess you like

Origin blog.csdn.net/RreamigOfGirls/article/details/130064948