####### Flutter riverPod状态管理库官方文档阅读笔记
官方文档地址如下:
https://riverpod.dev/zh-Hans/docs/introduction/getting_started
需要安装的依赖如下:
flutter pub add flutter_riverpod
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner
flutter pub add dev:custom_lint
flutter pub add dev:riverpod_lint
//装一个代码生成工具
flutter pub add dev json_serializable
flutter pub add dev freezed
自动生成命令行如下:
flutter pub run build_runner build --delete-conflicting-outputs
1.flutter组件的最外面的大组件的顶部一定要用 ProviderScope
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
2.provider提供者主要推荐使用2种方式
1.创建可读性provider,可以理解为get请求查询数据,不修改本身的provider缓存(这个是缓存在内存中) 就是把@riverpod注解依赖注入方式(这里需要注意比如:我们在使用ref.watch 查询 情况
1.如果参数个数相同的情况,比如第一consumer调用了ref.watch的时候,
只会调用最先的一次
2.比如consumer这边UI要调用ref.watch(activityProvider("tag"))//比如这时候是2个参数 ,调用参数的时候 说要减去ref参数情况 )
简单实例代码如下:
@riverpod
Result myFunction(Ref ref) {
<你的逻辑写这里>
//这里可以写自己的 代码逻辑 也可以处理自己的 比如ref.watch(otherProvider)程序
}
//这个provider是函数式组件 表示只读取的provider程序
执行副作用的provider
之前的Get的provider使用大体方式如下:
//这里自动生成dart实体类 使用的命令如下:flutter pub run build_runner build --delete-conflicting-outputs
@freezed
class Todo with _$Todo {
factory Todo({
required String description,
@Default(false) bool completed,
}) = _Todo;
}
@riverpod
Future<List<Todo>> todoList(Ref ref) async {
// 模拟一个网络请求。这通常来自真实的 API
return [
Todo(description: 'Learn Flutter', completed: true),
Todo(description: 'Learn Riverpod'),
];
}
现在看下post方式provider,号称通知者provider,比如我们发送消息,是2个步骤
//post方式请求后台
@riverpod
class MyNotifier extends _$MyNotifier {
@override
Result build() {
<你的业务逻辑在这里>
}
<你的方法在这里>
}
命名方式 官方文档原话如下:
当 @riverpod
注解被放置在一个类上时,该类被称为“通知者程序”。
类必须扩展 _$NotifierName
,其中 NotifierName
是类名。
通知者程序负责公开修改提供者程序状态的方法。
使用者可以使用 ref.read(yourProvider.notifier).yourMethod()
此类上的公共方法。
notice:除了内置的 state
之外,通知者程序不应具有公共属性,因为 UI 无法知道状态已更改。note
通知程序不应具有除内置之外的公共属性state
,因为 UI 无法知道状态已发生变化。
(意思是说在@riverpod
注解标注的post的provider内部持有本身的state状态,不应该由外部的consumer消费者来获取它的state状态)
实例代码如下:
@riverpod
class TodoList extends _$TodoList {
//意思是说在当前的class用@riverpod标注的class这里能拿到state状态实例
@override
Future<List<Todo>> build() async {
// 我们之前在 FutureProvider 中的业务逻辑现在位于 build 方法中。
//其实是这里会调用一次
return [
Todo(description: 'Learn Flutter', completed: true),
Todo(description: 'Learn Riverpod'),
];
}
Future<void> addTodo(Todo todo) async {
await http.post(
Uri.https('your_api.com', '/todos'),
// We serialize our Todo object and POST it to the server.
headers: {'Content-Type': 'application/json'},
body: jsonEncode(todo.toJson()),
);
}
}
我们现在有一个按钮,按下时会发出POST
请求。但是,目前我们的 UI 不会更新以反映新的待办事项列表。我们希望本地缓存与服务器的状态相匹配。上面的代码 是仅仅发送了一个todoBean实例给到了后台服务器,但是本地的todolist的provider里面并没有更新 此时有2种情况
1.手动方式 更新服务器请求的同时同步本地缓存数据(包括比如im消息更新在本地数据库)
手动方式更新本地数据缓存实例如下所示:
Future<void> addTodo(Todo todo) async {
// The POST request will return a List<Todo> matching the new application state
//第一步请求后台
final response = await http.post(
Uri.https('your_api.com', '/todos'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(todo.toJson()),
);
//第二步请求后台数据拿到成功之后 再调用了一次get
// We decode the API response and convert it to a List<Todo>
List<Todo> newTodos = (jsonDecode(response.body) as List)
.cast<Map<String, Object?>>()
.map(Todo.fromJson)
.toList();
// We update the local cache to match the new state.
// This will notify all listeners.
state = AsyncData(newTodos); //
}
上面的优点:(这里是直接post请求后台拿到最新的事件列表 ,后面调用了state = AsyncData(newTodos); 直接把服务器的同步更新到本地provider)
- UI 将尽可能保持最新状态。如果另一个用户添加了待办事项,我们也将看到它。
- 服务器是事实的来源。使用这种方法,客户端不需要知道新的待办事项需要插入到待办事项列表中的哪个位置。
- 仅需要一个网络请求。
缺点:缺点
- 这种方法只有在服务器以特定方式实现时才有效。如果服务器不返回新状态,则这种方法将不起作用。
- 如果相关的GET请求更复杂(例如具有过滤器/排序),则可能仍然不可行。
简单说就说如果服务器请求失败 这种方法就不可取。
2.自动方式 一键刷新(就说在c lass的provider里面调用ref.invalidateSelf()
刷新提供程序 直接一键刷新)
实例代码如下:
Future<void> addTodo(Todo todo) async {
// We don't care about the API response
await http.post(
Uri.https('your_api.com', '/todos'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(todo.toJson()),
);
// Once the post request is done, we can mark the local cache as dirty.
// This will cause "build" on our notifier to asynchronously be called again,
// and will notify listeners when doing so.
ref.invalidateSelf(); //直接只在class的provider的内部 只需要调用这个就直接一键刷新
// (Optional) We can then wait for the new state to be computed.
// This ensures "addTodo" does not complete until the new state is available.
await future;
}
手动更新本地缓存数据如下:
//调用了一次
final previousState = await future;
// We can then update the state, by creating a new state object.
// This will notify all listeners.
state = AsyncData([...previousState, todo]); //追加的方式
完整实例代码如下:
Future<void> addTodo(Todo todo) async {
// We don't care about the API response
await http.post(
Uri.https('your_api.com', '/todos'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(todo.toJson()),
);
// We can then manually update the local cache. For this, we'll need to
// obtain the previous state.
// Caution: The previous state may still be loading or in error state.
// A graceful way of handling this would be to read `this.future` instead
// of `this.state`, which would enable awaiting the loading state, and
// throw an error if the state is in error state.
final previousState = await future; //拿到上一次本地provider的state
// We can then update the state, by creating a new state object.
// This will notify all listeners.
state = AsyncData([...previousState, todo]); //追求最新状态数据
}