Flutter状态共享与管理插件provider的使用
Flutter状态共享与管理插件provider的使用
简介
provider
是一个围绕 InheritedWidget
的包装器,旨在使其更易于使用和更具可重用性。通过使用 provider
而不是手动编写 InheritedWidget
,你可以获得以下优势:
- 简化资源分配/释放
- 懒加载
- 减少样板代码
- 开发者工具友好:使用 Provider 后,应用程序的状态将在 Flutter 开发者工具中可见
- 通用的消费方式:如
Provider.of
、Consumer
和Selector
- 提高复杂监听机制的可扩展性(例如
ChangeNotifier
)
更多关于 provider
的信息,请参阅其 官方文档。
使用方法
暴露值
创建新对象实例
要暴露一个新创建的对象,使用 Provider
的默认构造函数。不要使用 .value
构造函数来创建对象,否则可能会导致不必要的副作用。
正确做法:
Provider(
create: (_) => MyModel(),
child: ...
)
错误做法:
ChangeNotifierProvider.value(
value: MyModel(),
child: ...
)
如果需要传递可能随时间变化的变量,请考虑使用 ProxyProvider
。
int count;
ProxyProvider0(
update: (_, __) => MyModel(count),
child: ...
)
重用现有对象实例
如果你已经有一个对象实例并希望暴露它,最好使用 Provider
的 .value
构造函数。
正确做法:
MyChangeNotifier variable;
ChangeNotifierProvider.value(
value: variable,
child: ...
)
错误做法:
MyChangeNotifier variable;
ChangeNotifierProvider(
create: (_) => variable,
child: ...
)
读取值
最简单的方法是使用 BuildContext
上的扩展方法:
context.watch<T>()
:使小部件监听T
的变化context.read<T>()
:返回T
而不监听它context.select<T, R>(R cb(T value))
:仅监听T
的一部分
也可以使用静态方法 Provider.of<T>(context)
,当 listen
参数设置为 false
时,行为类似于 read
。
注意: context.read<T>()
不会使小部件在值变化时重建,并且不能在 StatelessWidget.build
或 State.build
中调用。但可以在这些方法之外自由调用。
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
context.watch<String>(),
);
}
}
还可以使用 Consumer
和 Selector
进行性能优化或难以获取 BuildContext
的情况。
可选依赖 Provider
有时我们希望支持 Provider 不存在的情况。例如,对于可在不同位置使用的可重用小部件,包括在 Provider 外部使用。
在这种情况下,当调用 context.watch
或 context.read
时,将泛型类型设置为可空。
context.watch<Model?>()
如果没有找到匹配的 Provider,将返回 null
而不是抛出异常。
MultiProvider
当注入多个值时,MultiProvider
可以简化代码结构。
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: someWidget,
)
ProxyProvider
ProxyProvider
将多个其他 Provider 的值组合成一个新对象,并将其发送给 Provider
。
ProxyProvider<Counter, Translations>(
update: (_, counter, __) => Translations(counter.value),
)
FAQ
我可以检查对象的内容吗?
可以使用 Flutter DevTools 查看对象内容。如果显示 “Instance of MyClass”,可以通过实现 DiagnosticableTreeMixin
或覆盖 toString
方法来改进显示。
在 initState
中获取 Provider 时出现异常怎么办?
应该在 build
方法中获取 Provider,或者明确指定不关心更新。
如何处理热重载?
让提供的对象实现 ReassembleHandler
接口。
使用 ChangeNotifier
更新时出现异常怎么办?
确保不在构建树时修改 ChangeNotifier
,可以在 create
回调中或异步执行更新操作。
是否必须使用 ChangeNotifier
处理复杂状态?
不一定,可以使用任何对象表示状态,例如结合 Provider.value()
和 StatefulWidget
。
可以创建自定义 Provider 吗?
可以,provider
暴露了所有用于创建完整 Provider 的小组件。
小部件重建过于频繁怎么办?
使用 context.select
仅监听特定属性,或使用 Consumer
和 Selector
优化重建范围。
可以使用同一类型获取两个不同的 Provider 吗?
不可以,应显式地为每个 Provider 分配不同的类型。
可以消费接口并提供实现吗?
可以,编译器需要类型提示以指示消费接口并在 create
中提供实现。
现有 Provider
provider
提供了几种不同类型的 Provider:
名称 | 描述 |
---|---|
Provider | 最基本的 Provider,暴露任意值 |
ListenableProvider | 专门用于 Listenable 对象 |
ChangeNotifierProvider | 专用于 ChangeNotifier 的 ListenableProvider |
ValueListenableProvider | 监听 ValueListenable 并仅暴露其值 |
StreamProvider | 监听 Stream 并暴露最新值 |
FutureProvider | 监听 Future 并在完成时更新依赖 |
解决 StackOverflowError
如果应用中有大量 Provider(150+),可能会导致某些设备抛出 StackOverflowError
。解决方案包括逐步挂载 Provider 或放弃使用 MultiProvider
。
赞助商
更多关于Flutter状态共享与管理插件provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html