Provider.of
Provider.of 方法是 Provider 库中最常用的获取共享数据的方法之一。它接收一个 BuildContext 对象和一个泛型类型参数 T,会查找 Widget 树中最近的一个类型为 T 的 Provider 对象,并返回它所提供的共享数据。当共享数据发生变化时,它会自动重新构建与该共享数据有依赖关系的 StatefulWidget。
模型
class UserModel with ChangeNotifier { String name = "Jimi"; int aa = 0; void changeName() { aa++; name = "hello"+aa.toString(); print(name); notifyListeners(); } }
入口设置
class MyApp9 extends StatelessWidget { const MyApp9({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider<UserModel>( create: (_) =>UserModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: Example(), ), ); } }
使用
class Example extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ConsumerExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( Provider.of<UserModel>(context).name, style: const TextStyle(color: Colors.red, fontSize: 50), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () { Provider.of<UserModel>(context, listen: false) .changeName(); //listen: false 必加 }, child: Text("点击加1"), ), ) ], ), ), ); } }
*Consumer
Consumer 是一个轻量级的组件,用于消费共享数据并构建 UI。它接收一个 BuildContext 对象和一个 builder 函数作为参数,在 builder 函数中构建 UI,该 UI 依赖于共享数据。当共享数据发生变化时,Consumer 会自动重新构建 UI。
里面有三个属性:
- context: 当前的上下文
- **Provider.of(context):** 模型对象
- child: 子组件(不需要刷新的部分)
模型
String name = "Jimi"; int aa = 0; void changeName() { aa++; name = "hello$aa"; print(name); notifyListeners(); } }
入口设置
class MyApp9 extends StatelessWidget { const MyApp9({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider<UserModel>( create: (_) =>UserModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: Example(), ), ); } }
使用
class Example extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ConsumerExample"), ), body: Consumer<UserModel>( builder: (_, UserModel userModel, child) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( userModel.name.toString(), style: const TextStyle(color: Colors.red, fontSize: 50), ), Padding( padding: const EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () { userModel.changeName(); }, child: const Text("点击加1"), ), ), Container( //每次都需要加载 child: const Column( children: [ Text("更多组件1"), Text("更多组件2"), Text("更多组件3"), Text("更多组件4"), Text("更多组件5"), Text("更多组件6"), ], ), ), child! //可选参数 1.不用每次都加载 ], ), ); }, child: const Column( //2.不用每次都加载 children: [ Text("1更多组件1"), Text("1更多组件2"), Text("1更多组件3"), Text("1更多组件4"), Text("1更多组件5"), ], ), ), ); } }
Selector
Selector
类和Consumer
类似,只是对build
调用Widget
方法时提供更精细的控制,简单点来说,Selector
也是一个消费者,它允许你可以从模型中准备定义哪些属性;Selector 是一个高级的组件,也用于消费共享数据并构建 UI。它接收一个 BuildContext 对象、一个 selector 函数和一个 builder 函数作为参数。selector 函数用于选择需要依赖的共享数据,当且仅当 selector 函数返回的数据发生变化时,Selector 才会重新构建 UI。这可以有效地避免不必要的重建,提高性能。
比如,用户模型中有50个属性,但是我只需要更新年龄,这样我希望不需要重建用户名、电话号码等组件;
模型
class UserModel6 with ChangeNotifier { String name = "Jimi"; int age = 18; String phone = "18888888888"; void increaseAge() { age++; notifyListeners(); //通知注册的监听器 } }
入口设置
class MyApp9 extends StatelessWidget { const MyApp9({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => UserModel6(), child: MaterialApp( debugShowCheckedModeBanner: false, home: SelectorExample(), ), ); } }
使用
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: const 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 内部使用的一个类,用于在 Widget 树中传递共享数据的上下文。在 Provider 中,它被优化为一个轻量级的实现。通常情况下,我们不需要直接使用 InheritedContext。
三大方式:
BuildContext.read:
BuildContext.read<CountNotifier1>()
可以替换掉Provider.of<CountNotifier1>(context,listen: false)
,它会找到CountNotifier1
并返回它。
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:
BuildContext.watch<CountNotifier1>()
可以替换掉Provider.of<CountNotifier1>(context,listen: false)
,看起来和read
没有什么不同,但是使用watch
你就不需要在使用Consumer
。
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:
BuildContext.select<CountNotifier1>()
可以替换掉Provider.of<CountNotifier1>(context,listen: false)
,看起来和watch
也没有什么不同,但是使用select
你就不需要在使用Selector
。
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 Provider状态管理---四种消费者使用分析 | Jimi (liujunmin.com)