更详细文档: documentation.
用法
暴露一个值
暴露一个新的 object 对象
Providers 不仅允许暴露一个值, 还能创建/监听/销毁它.
暴露一个新的 object 对象, 使用provider默认构造函数. 不要使用 .value 构造函数 , 否则会有副作用。
查看 stackoverflow 的回答 ,解析了为什么不要用 .value 构造函数创建一个值.
支持写法:
Provider(
create: (_) => new MyModel(),
child: ...
)
不建议写法:
ChangeNotifierProvider.value(
value: new MyModel(),
child: ...
)
不要用一个会变化的变量创建你的对象.这会导致,该变量变化的时候,你的对象没有跟着更新。
int count;
Provider(
create: (_) => new MyModel(count),
child: ...
)
如果你确实想用一个会变化的变量创建对象,使用 ProxyProvider:
int count;
ProxyProvider0(
update: (_, __) => new MyModel(count),
child: ...
)
重用已经存在的对象:
如果你已经有一个对象实例,并且想要暴露她, 你必须使用 .value 构造函数.否则的话,可能导致该对象还在使用的时候,就被清除了。
使用 ChangeNotifierProvider.value 提供一个已存在的 ChangeNotifier.
MyChangeNotifier variable;
ChangeNotifierProvider.value(
value: variable,
child: ...
)
不要通过默认构造函数重用已存在的 ChangeNotifier
MyChangeNotifier variable;
ChangeNotifierProvider(
create: (_) => variable,
child: ...
)
读取数据
最简单的读取方法是使用静态方法 Provider.of
这个方法会从BuildContext开始查找组件树,并返回最近的类型为 T 的变量 (或者抛出没找到).
结合 暴露一个值的例子, 这个组件会读取暴露出来的字符串,并渲染 "Hello World."
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 `Provider.of`!
Provider.of(context)
);
}
}
如果不使用Provider.of, 可以使用Consumer 和 Selector.
这在提高性能或者在无法获得BuildContext 的时候很有用。
查看 FAQ 或者 Consumer 和 Selector 文档获取更多内容.
MultiProvider
如果在一个大的程序中注入很多的值, 可以将下面的嵌套
Provider(
create: (_) => Something(),
child: Provider(
create: (_) => SomethingElse(),
child: Provider(
create: (_) => AnotherThing(),
child: someWidget,
),
),
),
转化成
MultiProvider(
providers: [
Provider(create: (_) => Something()),
Provider(create: (_) => SomethingElse()),
Provider(create: (_) => AnotherThing()),
],
child: someWidget,
)
ProxyProvider
从3.0.0起, 有一个新的f provider: ProxyProvider.
ProxyProvider 将其他provider的多个值整合成一个新的对象, 并将结果发送给 Provider.
该新对象会在任何一个它依赖的provides更新的时候更新.
下面的例子用 ProxyProvider 基于counter 创建 translations.
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider(
create: (_, counter, __) => Translations(counter.value),
),
],
child: Foo(),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => '你点击了 $_value times';
}
它可以有多个变化来源, 如:
类名后面的数字来源于其他providers .
他们的工作方式相似,但 ChangeNotifierProxyProvider 会将它的值发送到ChangeNotifierProvider,而不是发送结果到 Provider, .
FAQ
我在initState中获取Providers的时候报错 .
报错的原因是你在一个不会再次调用的生命周期中监听provider .
你要么使用另一个生命周期 (didChangeDependencies/build),或者显示指定你并不关心更新.
把下面代码
initState() {
super.initState();
print(Provider.of(context).value);
}
改成
Value value;
didChangeDependencies() {
super.didChangeDependencies();
final value = Provider.of(context).value;
if (value != this.value) {
this.value = value;
print(value);
}
}
或者
initState() {
super.initState();
print(Provider.of(context, listen: false).value);
}
我使用了ChangeNotifier,更新的时候报错。
这很可能是因为你在组件树正在构建的时候修改了 ChangeNotifier 的子元素.
一个经典的场景是,发起一个 http 请求,其 future保存在 notifier 中:
initState() {
super.initState();
Provider.of(context).fetchSomething();
}
这是不允许的,因为因为修改是立马发生的。
这意味着,可能在发生改变前,一些组件已经进行 build 了, 而其他的组件会在发生改变后 build. 这可能会导致用户界面不一致,因此是不允许的。
你应该将改变放到一个对整个数都平等影响的地方:
class MyNotifier with ChangeNotifier {
MyNotifier() {
_fetchSomething();
}
Future _fetchSomething() async {}
}
这在没有外部参数的情况下很有用.
initState() {
super.initState();
Future.microtask(() =>
Provider.of(context).fetchSomething(someValue);
);
}
虽然不完美,但允许传递参数
在复杂的state中,我必须用 ChangeNotifier 吗?
不
你可以用任何的 object 代表你的 state. 例如, 另一种架构是用 Provider.value() 结合 StatefulWidget.
这里是一个使用者样结构 计数器的例子:
class Example extends StatefulWidget {
const Example({Key key, this.child}) : super(key: key);
final Widget child;
@override
ExampleState createState() => ExampleState();
}
class ExampleState extends State {
int _count;
void increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Provider.value(
value: _count,
child: Provider.value(
value: this,
child: widget.child,
),
);
}
}
读取state:
return Text(Provider.of(context).toString());
修改state:
return FloatingActionButton(
onPressed: Provider.of(context).increment,
child: Icon(Icons.plus_one),
);
或者,您可以创建自己的provider.
我可以创建自己的Provider吗?
是的. provider 由一系列暴露了的小组件组成
包括:
这是一个自定义 provider,使用 ValueNotifier作为state的例子: https://gist.github.com/rrousselGit/4910f3125e41600df3c2577e26967c91
我的组件 rebuilds 太频繁怎么办?
使用Consumer/Selector代替Provider.of .
他们的可选子参数参数允许 rebuild 组件树的具体某一部分:
Foo(
child: Consumer(
builder: (_, a, child) {
return Bar(a: a, child: child);
},
child: Baz(),
),
)
在这个例子中, 只有 Bar 会在A更新的时候l rebuild . Foo 和 Baz 不会更新.
更进一步,如果对组件树没影响,你可以用 Selector忽略变化 :
Selector(
selector: (_, list) => list.length,
builder: (_, length, __) {
return Text('$length');
}
);
这例子只有在list长度改变的时候才会rebuild. item改变的时候不会更新.
我能够使用同一个type获取不同的 providers 吗?
不行,但你可以有多个共享一个type的 providers, 一个组件只能获取他们中的一个:最近的祖先
相反,必须显式地为两个providers指定不同的类型
错误
Provider(
create: (_) => 'England',
child: Provider(
create: (_) => 'London',
child: ...,
),
),
推荐
Provider(
create: (_) => Country('England'),
child: Provider(
create: (_) => City('London'),
child: ...,
),
),
Existing providers
provider 为不同的对象提供了不同的 "provider"
全部对象列表 在这
名称 |
描述 |
Provider |
provider中最基础的Provider. 接收一个值,并把它暴露出去。 |
ListenableProvider |
用于监听对象的 provider . ListenableProvider 会监听对象并在监听被调用的时候rebuild依赖它的组件. |
ChangeNotifierProvider |
一个特殊的 ListenableProvider 用于改变通知. 它会在需要的时候自动调用 ChangeNotifier.dispose. |
ValueListenableProvider |
侦听ValueListenable并仅公开ValueListenable.value。 |
StreamProvider |
监听 Stream流,并暴露出最后emitt的值. |
FutureProvider |
接收一个 Future ,并在future完成时更新依赖。 |
翻译完真想吐槽TMD的,这官方文档写得真差,不知所云。
建议看一下:https://book.flutterchina.club/chapter7/provider.html ,这个作者自己封装了一个简单的provider,各种原理都很清晰。