文章系列
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
InheritedContext
是Provider
内置扩大了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
为咱们提供了多种读取值的形式,下面咱们对消费者四大类的一个应用和剖析比照,大家可依据本人的理论利用场景去应用对应的形式。