provider 跨组件状态管理
provider 跨组件状态管理
Provider 包是由 Remi Rousselet 创建旨在尽可能快速地处理状态。在 Provider 中,小部件会监听状态的变化,并在收到通知后立即更新。
因此,当有状态改变时,而不是重建整个 widget 树,只改变受影响的 widget,从而减少工作量并使应用程序运行得更快更流畅。
一、原理
Model变化后会自动通知ChangeNotifierProvider
(订阅者),ChangeNotifierProvider
内部会重新构建InheritedWidget
,而依赖该InheritedWidget
的子孙Widget就会更新。
原有普遍方式:通过参数传递数据,并setState实时更新组建;
使用Provider益处:
-
业务代码更关注数据,只要更新Model,则UI会自动更新,而不用在状态改变后再去手动调用
setState()
来显式更新页面。 -
数据改变的消息传递被屏蔽了,我们无需手动去处理状态改变事件的发布和订阅了,这一切都被封装在Provider中了。这真的很棒,帮我们省掉了大量的工作!
-
在大型复杂应用中,尤其是需要全局共享的状态非常多时,使用Provider将会大大简化我们的代码逻辑,降低出错的概率,提高开发效率。
-
二、状态管理方式
1、Provider 方式
provider 不需要被监听,有的常量或者方法,根本不需要“牵一发而动全身”,也就是说他们不会被要求随着变动而变动,这样的需求使用Provider;
最基本的状态管理方式,以一个参数方式绑定和展示;
1) 绑定单条数据
Provider 可在需要的 Widget 处进行数据绑定;
基本单条数据绑定:
当我们确定绑定的数据类型时,建议绑定时添加数据类型,如:Provider<String>.value( value: '', child:);不确定类型Provider.value( value: '', child:);
1 2 3 4 5 | Column( children: [ Provider<String>.value( value: 'Provider 基础传值' , child: const BaseValueWidget()), ], ) |
2)获取数据
1 2 3 4 5 6 7 8 | class BaseValueWidget extends StatelessWidget { const BaseValueWidget({Key? key}) : super(key: key); @ override Widget build(BuildContext context) { return Text(Provider.of<String>(context)); } } |
3)绑定多条数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Column( children: [ /// 单条传值 Provider<String>.value( value: 'Provider 基础传值' , child: const BaseValueWidget()), /// 多条传值 UserModel实体 //嵌套绑定 Provider<UserModel>.value( value: UserModel( '嵌套绑定' , 18), child: Provider< int >.value( value: 20, child: Provider< bool >.value( value: false , child: const NestingWidget()))), // 聚合方式 推荐 MultiProvider(providers: [ Provider<UserModel>.value(value: UserModel( '聚合方式' , 10)), Provider< int >.value(value: 11), Provider< bool >.value(value: false ) ], child: const NestingWidget()), ], ) |
1 2 3 4 5 | class UserModel { String name; int age; UserModel( this .name, this .age); } |
获取数据
1 2 3 4 5 6 7 8 9 10 11 | class NestingWidget extends StatelessWidget { const NestingWidget({Key? key}) : super (key: key); @override Widget build(BuildContext context) { return Text( 'Provider: ' '${Provider.of<int>(context)} | ${Provider.of<bool>(context)} | ${Provider.of<UserModel>(context).name} |${Provider.of<UserModel>(context).name = ' Hello World! '}' , style: const TextStyle(color: Colors.redAccent)); } } |
4)作用域
2、ChangeNotifierProvider 方式
ChangeNotifierProvider 它会随着某些数据改变而被通知更新,也就是说,比如这个 Model 被用在多个 page,那么当其中一处被改变时,他就应该告诉其他的地方,改更新了,这样的需求就使用ChangeNotifierProvider;
通过调用 ChangeNotifier.notifyListeners 对 ChangeNotifier 进行监听,将其公开给它的子 Widget 并重建依赖项;
1) 绑定数据
ChangeNotifierProvider({Key key, @required ValueBuilder builder, Widget child });通过构造器创建一个 ChangeNotifier,在 ChangeNotifierProvider 移除时自动处理;
1 2 3 4 | ChangeNotifierProvider( create: (_) => PersonChangeNotifier( 'ChangeNotifier方式1' , 11), child: const NotifierWidget(), ), |
ChangeNotifierProvider.value({Key key, @required T notifier, Widget child }) 通过监听通知给子 Widget 并重建依赖项;
1 2 3 4 | ChangeNotifierProvider<PersonChangeNotifier>.value( value: PersonChangeNotifier( 'ChangeNotifier方式2' , 1), child: const NotifierWidget(), ) |
class PersonChangeNotifier with ChangeNotifier { String name; int age; PersonChangeNotifier(this.name, this.age); updateName(String name) { this.name = name; notifyListeners(); } }
2) 获取数据
获取数据的方式与直接使用 Provider 相似;
Text(Provider.of<PersonChangeNotifier>(context).name);
相对于 Provider,ChangeNotifierProvider 方式更加灵活,可以通过重写 get/set 方法来对状态管理进行修改和使用;
3)更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class NotifierWidget extends StatelessWidget { const NotifierWidget({Key? key}) : super (key: key); @override Widget build(BuildContext context) { return Column( children: [ Text(Provider.of<PersonChangeNotifier>(context).name), InkWell( child: const Text( '点击更新name' ), onTap: () { context.read<PersonChangeNotifier>().updateName( 'name' ); }, ), ], ); } } |
3、ChangeNotifierProxyProvider
1 2 3 4 5 6 7 8 | ChangeNotifierProvider( create: (context) { return MyChangeNotifier( myModel: Provider.of<MyModel>(context, listen: false ), ); }, child: ... ) |
三、有选择地更新状态
该Consumer控件只允许子控件,而不在 widget 树影响其他部件重建,小部件进行更新。
我们通过Text
用 a 包装两个小部件Column
并builder
在Consumer
小部件公开的函数处返回它来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Consumer<UserDetailsProvider>( builder: (context, provider, child) { return Column( children: [ Text( 'Hi ' + provider.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( 'You are ' + provider.age.toString() + ' years old' , style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w400, ), ), ], ); }, ) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?