最佳实践
为了确保代码具有良好的可维护性, 这里列出了您在使用 Riverpod 时应遵循的良好实践。
此列表并不详尽,并且可能会发生变化。
如果您有任何建议,请随时提出问题。
此列表中的项目没有任何特定的顺序。
这些建议的很大一部分可以通过 riverpod_lint 来执行。 请参阅入门指南了解安装说明。
避免!在小部件中初始化提供者程序
提供者程序应自行初始化。
它们不应由外部元素(例如小部件)初始化。
如果不这样做可能会导致可能的竞争条件和意外行为。
不要
class WidgetState extends State<MyWidget> {
void initState() {
super.initState();
// 坏的:提供者程序应该自己初始化自己
ref.read(provider).init();
}
}
考虑
对于这个问题,没有“一刀切”的解决方案。
如果您的初始化逻辑取决于提供者程序的外部因素,
则放置此类逻辑的正确位置通常是触发导航的按钮的 onPressed
方法中:
ElevatedButton(
onPressed: () {
ref.read(provider).init();
Navigator.of(context).push(...);
},
child: Text('Navigate'),
)
避免!使用本地小部件状态的提供者程序。
提供者程序被设计为共享业务状态。 它们不适合用于本地小部件状态,例如:
- 存储表单状态
- 当前选择的项目
- 动画
- Flutter 处理常见的 "controller" 相关的所有内容(例如
TextEditingController
)
如果您正在寻找一种处理本地小部件状态的方法,请考虑使用 flutter_hooks 代替。
不鼓励这样做的一个原因是这种状态通常仅限于一条路由。
如果不这样做,可能会破坏应用程序的后退按钮,因为新页面会覆盖上一页的状态。
不要!在提供者程序初始化期间执行副作用
提供者程序通常应用于表示“读”操作。 您不应该将它们用于“写”操作,例如提交表单。
使用提供者程序进行此类操作可能会产生意外行为,例如 如果执行了前一个操作,则跳过副作用。
如果您正在寻找一种处理副作用的加载/错误状态的方法, 请参阅执行副作用。
不要:
final submitProvider = FutureProvider((ref) async {
final formState = ref.watch(formState);
// 坏的:提供者程序不应用于“写”操作。
return http.post('https://my-api.com', body: formState.toJson());
});
首选!ref.watch/read/listen(和类似的 API)以及静态已知的提供者程序
Riverpod 强烈建议启用 lint 规则(通过 riverpod_lint)。
但为了使 lint 发挥作用,您的代码应该以可静态分析的方式编写。
如果不这样做,可能会更难发现错误或导致 lints 误报。
应该:
final provider = Provider((ref) => 42);
...
// 好的,因为提供者程序是静态已知的
ref.watch(provider);
不要:
class Example extends ConsumerWidget {
Example({required this.provider});
final Provider<int> provider;
Widget build(context, ref) {
// 不好,因为静态分析无法知道 `provider` 是什么
ref.watch(provider);
}
}
避免!动态创建提供者程序
提供者程序应该专门是顶级 final 变量。
应该:
final provider = Provider<String>((ref) => 'Hello world');
不要:
class Example {
// 不支持的操作。可能导致内存泄漏和意外行为。
final provider = Provider<String>((ref) => 'Hello world');
}
信息
允许将提供者程序创建为 static final 变量, 但代码生成器不支持。