跳到主要内容

最佳实践

为了确保代码具有良好的可维护性, 这里列出了您在使用 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 变量, 但代码生成器不支持。