【Flutter 专题】46 图解新的状态管理 Provider (一)

网友投稿 556 2022-05-29

2019 Google I/O 大会上重磅消息出了支持 flutter_web 之外,另一个便是弃用之前的状态管理 Provide,转而推荐相似的库 Provider;虽然只有一个字母之差使用方式差别却很大;小菜初步学习一下新的状态管理库 Provider;

【Flutter 专题】46 图解新的状态管理 Provider (一)

Flutter 针对不同类型对象提供了多种不同的 Provider;Provider 也是借助了 InheritWidget,将共享状态放到顶层 MaterialApp 之上;

Provider 方式

最基本的状态管理方式,以一个参数方式绑定和展示;

Provider 可在需要的 Widget 处进行数据绑定:

const Provider.value({ Key key, @required T value, this.updateShouldNotify, this.child, }) : dispose = null, super.value(key: key, value: value);

分析源码 Provider.value 并没有限制 value 类型,我们可以根据需求随意绑定需要的数据类型;当我们确定绑定的数据类型时,建议绑定时添加数据类型,如:Provider.value( value: ‘’, child:);

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: Provider.value( value: 'FirstPage Provider', child: MyHomePage(title: 'Peovider Demo'))); } }

Provider 需要在数据绑定的子 Widget 中进行获取;使用静态方法 Provider.of(BuildContext context),此方法从 BuildContext 关联的 Widget Tree 中查找最近的相同类型的数据进行展示;没有则报异常;

Text('${Provider.of(context)}'), Text('FirstPage Provider: ${Provider.of(context)} | ${Provider.of(context)} | ${Provider.of(context)}}'),

在我们实际开发中不会只绑定一条数据,当绑定多条数据时可以采用如下两种方式:嵌套绑定和聚合绑定;两种方式效果完全相同,小菜更倾向于 MultiProvider 绑定,层级更清晰简洁;

// 嵌套绑定 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: Provider.value( value: new User('Flutter', 300), child: Provider.value( value: 200, child: Provider.value( value: false, child: MyHomePage(title: 'Peovider Demo'))))); } } // 聚合方式 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MultiProvider(providers: [ Provider.value(value: new User('Flutter', 300)), Provider.value(value: 200), Provider.value(value: false) ], child: MyHomePage(title: 'Peovider Demo'))); } }

Provider 绑定数据类型比较灵活,并非只是基本数据类型,小菜定义了一个 User 类,可正常状态管理;小菜在获取 User 后重新设置 name 之后获取的 User 为最新的数据;

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MultiProvider(providers: [ Provider.value(value: new User('Flutter', 300)), Provider.value(value: 200), Provider.value(value: false) ], child: MyHomePage(title: 'Peovider Demo'))); } } Text( 'FirstPage Provider: ${Provider.of(context)} | ' '${Provider.of(context)} | ${Provider.of(context)} | ${Provider.of(context).name = 'Hello World!'}', style: TextStyle(color: Colors.redAccent)), Text('${Provider.of(context).name}'),

小菜在刚开始学习时被作用域卡到,实际文档说的很明白,获取绑定数据的范围是在绑定数据的子 Widget 中;小菜绘制了一下个人理解的基本作用域图,如有错误请多多指导;

void main() => runApp() 为范围最广的 application 作用域,其作用范围包括各个 Page 之间;FirstPage 中定义的 Provider A 作用在蓝色框范围内,Provider B 作用在粉色框范围内,SecondPage 中定义的 Provider C 作用在绿色范围内;超出范围则不能进行状态管理;

如上图作用域划分,在 FirstPage 多个作用域的粉色框中,若获取 String 类型的状态管理 Provider.value( value: ‘’, child:),首先在粉色作用域中查找,若存在则展示粉色框中绑定数据;若没有则查找上一层蓝色作用域,存在则为蓝色框绑定数据;若依然没有查找 application 作用域,存在则展示 application 作用域绑定数据;若均没有则报异常;

这也验证了文档中提及的子 Widget 作用域,一层一层往外层查找,直到可以正常获取;

ChangeNotifierProvider 方式

通过调用 ChangeNotifier.notifyListeners 对 ChangeNotifier 进行监听,将其公开给它的子 Widget 并重建依赖项;

ChangeNotifierProvider 绑定数据有两种方式:

ChangeNotifierProvider({Key key, @required ValueBuilder builder, Widget child })

通过构造器创建一个 ChangeNotifier,在 ChangeNotifierProvider 移除时自动处理;

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (_) => User('Flutter', 0), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(title: 'Peovider Demo'))); } }

ChangeNotifierProvider.value({Key key, @required T notifier, Widget child })

通过监听通知给子 Widget 并重建依赖项;

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( notifier: User('Flutter', 0), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(title: 'Peovider Demo'))); } }

获取数据的方式与直接使用 Provider 相似;

Text('${Provider.of(context).getName}'),

相对于 Provider,ChangeNotifierProvider 方式更加灵活,可以通过重写 get/set 方法来对状态管理进行修改和使用;

// User 实体 Bean class User with ChangeNotifier { var name; var age; User(this.name, this.age); void setName(String name) { this.name = name; notifyListeners(); } String get getName => this.name; } // 绑定 Provider class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (_) => User('Flutter', 0), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(title: 'Peovider Demo'))); } } // 获取 Provider 数据 Expanded( child: TextField( onChanged: (changed) => Provider.of(context).setName(changed), controller: _phonecontroller, decoration: InputDecoration( hintText: '请输入用户名', suffixIcon: IconButton( icon: Icon(Icons.clear, color: Colors.black45), onPressed: () { _phonecontroller.clear(); })))), Text('${Provider.of(context).getName}'),

问题小结

小菜在开始尝试时总是遇到如下问题,Could not find the correct Provider… 测试后了解是在子 Widget 中层级查找未找到对应的绑定数据;极有可能是绑定数据的 Widget 位置未绑定或绑定位置错误;

小菜对 Provider 的理解还很浅显,对于其他 Provider 的使用还未尝试;如有错误请多多指导!

来源:阿策小和尚

Flutter 数据结构

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:LaTex学习(三)LaTex文档类与宏包
下一篇:Python 进阶 — 协程
相关文章