Provider
Provider的三个消费者:
Provider第三方是基于InheritedWidget封装的:InheritedWidget
Provider.of、Consumer(会刷新不必要刷新的组件)r、Selector(更精细化)
1、Provider.of
InheritedWidget有个默认的约定:如果状态是希望暴露出的,应当提供一个 “of” 静态方法来获取其对象,开发者便可直接通过该方法来获取。
static T of<T>(BuildContext context, {bool listen = true})
其中 listen
:默认true监听状态变化,false为不监听状态改变。
Provider.of<T>(context)
是 Provider
为我们提供的静态方法,当我们使用该方法去获取值的时候会返回查找到的最近的 T
类型的 provider
给我们,且也不会遍历整个组件树。
2、Consumer
Provider 中使用比较频繁的消费者,查看源码:
Consumer({
Key? key,
required this.builder,
Widget? child,
}) : super(key: key, child: child);
...
@override
Widget buildWithChild(BuildContext context, Widget? child) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
发现它就是通过 Provider.of<T>(context)
来实现的。而且实际开发中使用 Provider.of<T>(context)
比 Consumer
简单好用太多,那 Consumer
有什么优势吗?
对比一下,我们发现 Consumer
有个 Widget? child
,它非常重要,能够在复杂项目中,极大地缩小你的控件刷新范围。
我们通过一个简单的计数器示例来说明:
return Scaffold(
body: Consumer(
builder: (BuildContext context,CounterModel counterModel,Widget? child){
return Column(
children: [
Text("${counterModel.count}"),
ElevatedButton(
onPressed: ()=> counterModel.increment(),
child: const Text("点击加1"),
),
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
],
);
},
),
);
在上述示例中,我们后面很多的 Text
组件没有用到模型数据且不需要更新状态的,但是因为被Consumer
包裹,导致每次数据改变都会重新构建!严重影响性能且不优雅!
解决以上问题,一方面我们可以尽可能调整 Consumer 的位置,在需要使用数据的组件包裹 Consumer ,但这也会存在一个问题,单独用大量 Consumer
包裹,也跟 Provider
诞生的理念背道而驰。这时候我们就可以使用到 Widget? child
了。我们针对上述示例优化一下:
return Scaffold(
body: Consumer(
builder: (BuildContext context,CounterModel counterModel,Widget? child){
return Column(
children: [
Text("${counterModel.count}"),
ElevatedButton(
onPressed: ()=> counterModel.increment(),
child: const Text("点击加1"),
),
child!
],
);
},
child: Column(
children: [
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
Text("其他更多组件"),
],
),
),
);
我们使用 Widget? child
将不需要更新状态的组件包裹起来,大大提升了性能。
3、Selector
Selector 也是一个消费者。与 Consumer 类似,只是对build
调用Widget
方法时提供更精细的控制。 Consumer 是监听一个 Provider 中所有数据的变化,Selector 则是监听某一个/多个值的变化。
比如用户模型 Person:有姓名、性别、年龄、身高、体重等信息,但是我们可能只会更新下年龄,其他的信息我们不希望重建,就可以使用 Selector
实现这个功能。
示例:
- Model
class Person with ChangeNotifier {
String name = "小虎牙";
int age = 18;
double height = 180.0;
// 年龄改变
void increaseAge() {
age ++;
notifyListeners();
}
}
- 程序入口设置:
return ChangeNotifierProvider(
create: (ctx) => Person(),
child: const MaterialApp(
home: SelectorDemo(),
),
);
- View
class SelectorDemo extends StatelessWidget {
const SelectorDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Selector"),),
body: Center(
child: Selector<Person,int>(
selector: (ctx,person) => person.age,
builder: (ctx,age,child) {
return Column(
children: [
Text("年龄为:$age"),
child!
],
);
},
child: Padding(
padding: const EdgeInsets.only(top: 50),
child: ElevatedButton(
onPressed: () => Provider.of<Person>(context,listen: false).increaseAge(),
child: const Text("点击改变年龄"),
),
),
)
)
);
}
}
- 结果:
显示 年龄为18
,点击后年龄加1。
这里也使用到了
Widget? child
,同 Consumer 一样极大地缩小控件刷新范围。
作者:RichTime
链接:https://juejin.cn/post/7067356022272163847
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。