文章系列

Flutter Provider状态治理---介绍、类图剖析、根本应用

Flutter Provider状态治理---八种提供者应用剖析

Flutter Provider状态治理---四种消费者应用剖析

Flutter Provider状态治理---MVVM架构实战

视频系列

Flutter Provider状态治理---介绍、类图剖析、根本应用

Flutter Provider状态治理---八种提供者应用剖析

Flutter Provider状态治理---四种消费者应用剖析

Flutter Provider状态治理---MVVM架构实战

源码仓库地址

github仓库地址

前言

在上一篇文章中咱们对Provider的8种提供者进行了具体的形容以及用对应的案例阐明他们的区别,那么这一节咱们来聊一聊Provider的消费者,如果去优化你的我的项目构造以及它们的应用区别。

Provider.of

Provider.of<T>(context)Provider为咱们提供的静态方法,当咱们应用该办法去获取值的时候会返回查找到的最近的T类型的provider给咱们,而且也不会遍历整个组件树,上面咱们看下代码:

第一步:定义模型

咱们定义了一个CountNotifier1的模型,前面所有的示例代码将围绕该模型来演示

import 'package:flutter/material.dart';class CountNotifier1 with ChangeNotifier {  int count = 0;  void increment() {    count++;    notifyListeners();  }}

第二步:应用程序入口设置

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

第三步:应用Provider.of

这里读取值和点击按钮+1时都是通过Provider.of<T>()来获取及应用。

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"),              ),            )          ],        ),      ),    );  }}

谬误日志

当咱们运行代码的时候会提醒一个报错,它提醒说试图从Widget树内部监听提供者公开的值,如果要修复能够把listen改成false,这个问题其实是在Provider 4.0.2后会呈现的,最次要的是它的默认行为就是ture,谬误日志如下:

======== 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 calledProvider.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 theevent 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: ........====================================================================================================

设置listen为false

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

运行后果

Consumer

Consumber只是在Widget中调用了Prvoider.of,并将其结构实现委托给了结构器,比方咱们常见的Builder,如果你的Widget依赖多个模型,那么它还提供了Consumer23456不便调用,咱们接下来对下面的案例采纳Consumer来批改

用Consumer包裹组件

外面有个builder结构器,当咱们把body改成上面从新运行后能够发现和应用Provider.of的后果一样,然而这里不须要在像应用Provider.of那样每次应用都要写一大串的重复性代码。

外面有三个属性:

  • context: 以后的上下文
  • Provider.of<T>(context): 模型对象
  • child: 子组件(不须要刷新的局部)
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"),            ),          )        ],      ),    );  },),

优化Consumer

优化形式一:尽可能调整Consumer的地位

咱们在下面的代码中发现Center以及Column组件也被Consumer包裹了进来,然而这两个组件是不须要更新状态的,而咱们每次构建的Widget的时候,会重建整个body,所以咱们优化一下代码构造,看起来就像上面这样:

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"),                ],              ),            )          ],        ),      );    },  ))

优化形式二:不须要刷新但被Consumer包裹的组件用child

比方下面咱们有更多组件1-6甚至数百个组件无需刷新状态,但因为你用Consumer包裹会导致全副刷新,那么显著会导致性能的降落,你可能会想到独自用多个Consumer包裹须要刷新的组件就解决了,但这不就是轻重倒置了吗,自身Provider是解决代码的强壮反复的代码,所以这个时候咱们能够采纳Consumer为咱们提供的child参数,如下:

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

Selector类和Consumer相似,只是对build调用Widget办法时提供更精密的管制,简略点来说,Selector也是一个消费者,它容许你能够从模型中筹备定义哪些属性。

咱们来举个例子:

比方,用户模型中有50个属性,然而我只须要更新年龄,这样我心愿不须要重建用户名电话号码等组件,那么Selector就是用于解决这个问题,咱们看一下示例:

第一步:定义模型

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("扭转年龄"),            ),          ),        ),      ),    );  }}

运行后果

InheritedContext

InheritedContextProvider内置扩大了BuildContext,它不保留了组件在树中本人地位的援用,咱们在下面的案例中见到Provider.of<CountNotifier1>(context,listen: false),其实这个of办法就是应用Flutter查找树并找到Provider子类型为CountNotifier1而已。

三大形式:

  • BuildContext.read: BuildContext.read<CountNotifier1>()能够替换掉Provider.of<CountNotifier1>(context,listen: false),它会找到CountNotifier1并返回它。
  • BuildContext.watch: BuildContext.watch<CountNotifier1>()能够替换掉Provider.of<CountNotifier1>(context,listen: false),看起来和read没有什么不同,然而应用watch你就不须要在应用Consumer
  • BuildContext.select: BuildContext.select<CountNotifier1>()能够替换掉Provider.of<CountNotifier1>(context,listen: false),看起来和watch也没有什么不同,然而应用select你就不须要在应用Selector

BuildContext.read

上面两种应用形式后果是一样的

应用Provider.of<T>()

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"),              ),            ),          ],        ),      ),    );  }}

应用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

应用Consumer

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"),      ),      /// Consumer 获取值      body: Center(        child: Consumer<CountNotifier2>(          builder: (_, countNotifier2, child) {            return 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.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

应用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"),            ),          ),        ),      ),    );  }}

应用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"),              ),            )          ],        ),      ),    );  }}

总结

Flutter为咱们提供了多种读取值的形式,下面咱们对消费者四大类的一个应用和剖析比照,大家可依据本人的理论利用场景去应用对应的形式。