Provider简介
通过使用 provider 去取代手写 InheritedWidget, 你将得到:
- s资源的简化分配/处置
- 懒加载
- 通过每次创建class类的方式,极大的减少了代码体积
- devtool很方便,很容易查看
- 一个通用的方式消费这些InheritedWidgets (通过 Provider.of/Consumer/Selector)
- 使用复杂性呈指数增长的侦听机制提高类的可扩展性 (例如 ChangeNotifier, 用于发送通知).
2.使用方法
2.1 暴露一个值
2.2 暴露一个实例对象
Providers不仅允许你暴露一个值,而且他还允许你创建/监听/销毁它
注:使用 .value 是将已经存在的对象实例暴露出来,如果是新的模型对象,务必使用 create 。
- DO create a new object inside
create.
Provider(
create: (_) => MyModel(),
child: ...
)
- DON'T use
Provider.valueto create your object.
ChangeNotifierProvider.value(
value: MyModel(),
child: ...
)
-
DON'T create your object from variables that can change over time.
该情况下, 当值改变时你的对象将不会更新.
int count;
Provider(
create: (_) => MyModel(count),
child: ...
)
使用 ProxyProvider解决上面值改变的问题:
int count;
ProxyProvider0(
update: (_, __) => MyModel(count),
child: ...
)
注意:
在使用提供者的create/update回调时,值得注意的是,这个回调默认是惰性调用的。
这意味着在至少一次请求该值之前,不会调用create/update回调。
如果您想使用以下参数预先计算一些逻辑,则可以禁用此lazy行为:
MyProvider(
create: (_) => Something(),
lazy: false,
)
2.3 复用现有对象实例:
如果您已经有一个对象实例并且想要公开它,最好使用.value提供者的构造函数。
不这样做可能会在您dispose仍在使用时调用您的对象方法。
- 用
ChangeNotifierProvider.value提供现有的 ChangeNotifier。
MyChangeNotifier variable;
ChangeNotifierProvider.value(
value: variable,
child: ...
)
- 不要使用默认构造函数复用现有的ChangeNotifier
MyChangeNotifier variable;
ChangeNotifierProvider(
create: (_) => variable,
child: ...
)
2.4 读取值
读取值的最简单方法是使用 [BuildContext] 上的扩展方法:
context.watch<T>(),widget监听T的变化context.read<T>(),返回T的值,不监听context.select<T, R>(R cb(T value)),它允许一个widget只监听T的部分变化.
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
// Don't forget to pass the type of the object you want to obtain to `watch`!
context.watch<String>(),
);
}
}
也可以使用静态方法Provider.of<T>(context),其行为类似于watch. 当listen参数设置为false(如Provider.of<T>(context, listen: false))时,其行为类似于read。
值得注意的是,context.read<T>()当值更改并且无法在StatelessWidget.build/State.build内部调用时,不会重建widget。另一方面,它可以在这些方法之外自由调用。
context.watch<Model>() // 没找到会抛错
context.watch<Model?>() // 传出空值
多层Provider嵌套
Provider<Something>(
create: (_) => Something(),
child: Provider<SomethingElse>(
create: (_) => SomethingElse(),
child: Provider<AnotherThing>(
create: (_) => AnotherThing(),
child: someWidget,
),
),
),
简洁方式
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: someWidget,
)
proxy Provider的使用
根据Provider(Counter)的count值,生成新的Provider(Translations)
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider<Counter, Translations>(
update: (_, counter, __) => Translations(counter.value),
),
],
child: Foo(),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => 'You clicked $_value times';
}
使用Consumer方式
class ProviderDemo extends StatelessWidget {
const ProviderDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Consumer<Person>( /// 在程序任何地方都可以拿到person对象,读取数据
builder: (_,person,child){
return Text(person.name);
}
),
),
);
}
}
ChangeNotifierProvider
监听模型对象的变化,而且当数据改变时,它也会重建Consumer,更新UI。
- 继承、混入ChangeNotifier
- 调用了
notifyListeners()
它不会重复实例化模型,除非在个别场景下。如果该实例已经不会再被调用,ChangeNotifierProvider 也会自动调用模型的dispose()方法。
- ChangeNotifier
它类似于一个Observable,继承自Listenable,内部维护了一个 ObserverList _listeners,提供添加和删除listener的方法,有一个notify方法,用来通知所有的观察者(在Provider或者称为消费者Consumer)。
示例:
- Model
class Person with ChangeNotifier{
String name = "ChangeNotifierProvider";
/// 提供一个改变name的方法
void changName({required String newName}){
name = newName;
notifyListeners();
}
}
- 程序入口设置
return ChangeNotifierProvider<Person>(
create: (ctx)=> Person(),
child: const MaterialApp(
home: ChangeNotifierProviderDemo(),
),
);
- View
class ChangeNotifierProviderDemo extends StatelessWidget {
const ChangeNotifierProviderDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("ChangeNotifierProvider"),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// 拿到person对象,读取数据
Consumer<Person>(builder: (ctx,person,child) => Text(person.name),),
/// 拿到person对象,调用方法
Consumer<Person>(builder: (ctx,person,child){
return ElevatedButton( /// 点击按钮,调用方法更新
onPressed: () => person.changName(newName: "ChangeNotifierProvider更新了"),
child: const Text("点击更新"),
);
},),
],
),
),
);
}
}
- 结果 显示数据;点击按钮数据成功修改。