前言Provider有两个重要的状态
提供者:提供数据
消费者:消费数据
一、提供者
Provider为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProvider、MultiProvider、ChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。
1、Provider组件
1、创建模型
class UserModel {
String name = "Jimi";
void changeName() {
name = "hello";
}
}
2、应用程序入口设置
ProviderExample内部处理数据
return Provider<UserModel>(
create: (_) => UserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProviderExample(),
),
);
3、使用共享数据
3.1 第一个Comsumer是用于读取模型的数据name
3.2 第二个Consumer用于改变模型的数据name
class ProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<UserModel>(
builder: (_, userModel, child) {
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
},
),
Consumer<UserModel>(
builder: (_, userModel, child) {
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
userModel.changeName();
},
child: Text("改变值"),
),
);
},
),
],
),
),
);
}
}
2、ChangeNotifierProvider
重点:ChangeNotifierProvider跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者)。
1、创建模型
这里定义模型主要有两处变化:
1.1 with混入了ChangeNotifier,
1.2 调用了notifyListeners(),model内部改变之后,调用监听
import 'package:flutter/material.dart';
class UserModel1 with ChangeNotifier {
String name = "Jimi";
void changeName() {
name = "hello";
notifyListeners();
}
}
2、应用程序入口设置,使用ChangeNotifierProvider
return ChangeNotifierProvider<UserModel1>(
create: (_) => UserModel1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProviderExample(),
),
);
3、共享数据
class ChangeNotifierProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ChangeNotifierProvider"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<UserModel1>(
builder: (_, userModel, child) {
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
},
),
Consumer<UserModel1>(
builder: (_, userModel, child) {
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
userModel.changeName();
},
child: Text("改变值"),
),
);
},
),
],
),
),
);
}
}
3、MultiProvider
前面的都只是返回了一个提供者,在实际开发过程中肯定会有多个提供者,我们虽然可以采用嵌套的方式来解决,但是这样无疑是混乱的,可读性级差。这个时候强大的MultiProvder就产生了,
1、创建两个模型
import 'package:flutter/material.dart';
class UserModel1 with ChangeNotifier {
String name = "Jimi";
void changeName() {
name = "hello";
notifyListeners();
}
}
class UserModel4 with ChangeNotifier {
String name = "Jimi";
int age = 18;
void changeName() {
name = "hello";
age = 20;
notifyListeners();
}
}
2、应用程序入口设置
2.1 使用笨方法,嵌套的方式,数据多的情况下会无限套娃
return ChangeNotifierProvider<UserModel1>( //第一次嵌套
create: (_) => UserModel1(),
child: ChangeNotifierProvider<UserModel4>( //第二次嵌套
create: (_) => UserModel4(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
),
);
2.2 使用MultiProvider
返回MultiProvider,然后在providers属性中创建。
return MultiProvider(
providers: [
ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()),
ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4()),
/// 添加更多
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
);
3 使用共享数据
通过Consumer去监听数据的改变,并渲染。
class MultiProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MultiProviderExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<UserModel1>(
builder: (_, userModel, child) {
return Text(userModel.name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
);
},
),
Consumer<UserModel4>(
builder: (_, userModel, child) {
return Text(userModel.age.toString(),
style: TextStyle(
color: Colors.green,
fontSize: 30
)
);
},
),
Consumer2<UserModel1, UserModel4>(
builder: (_, userModel1, userModel4, child) {
return Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
userModel1.changeName();
userModel4.changeName();
},
child: Text("改变值"),
),
);
},
),
],
),
),
);
}
}
二、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 一样极大地缩小控件刷新范围。
参考链接:
jimi的provider详细讲解
链接:https://juejin.cn/post/7067356022272163847