mcpField
R
riverpod-offline

riverpod-offline

github.com/serverpod开发1078 次安装34 周前更新
代码仓库

关于 riverpod-offline

这项 Riverpod 技能介绍如何将基于 Notifier 的 Provider 状态保存在设备上,以便应用重启后恢复并支持离线使用。内容涵盖存储配置、序列化、缓存期限、密钥失效和内存存储测试。

主要功能

  1. 01持久化 Notifier Provider 状态
  2. 02配置存储、密钥和编解码器
  3. 03使用 JsonPersist 生成持久化代码
  4. 04设置缓存期限并丢弃旧数据
  5. 05使用内存存储进行测试

适用场景

  1. 01在应用重启后保留 Provider 状态
  2. 02无需网络请求即可恢复状态
  3. 03数据结构变更后重置持久化数据
  4. 04无需数据库即可测试持久化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serverpod/skills-registry --skill riverpod-offline

安装后在 Claude Code 中输入/riverpod-offline确认已加载。

详细介绍

Riverpod — 离线持久化(实验性)

说明

离线持久化会将提供程序状态存储在设备上,使其在重启后仍然保留,并支持离线使用。Riverpod 不依赖特定存储方式;riverpod_sqflite 等软件包提供 Storage 实现。只有基于 Notifier 的提供程序可以持久化。此功能仍处于实验阶段。

创建 Storage

安装一个软件包(例如 riverpod_sqflite 和 sqflite),然后创建 Storage。使用 SQFlite 时:

final storageProvider = FutureProvider<Storage<String, String>>((ref) async {
  return JsonSqFliteStorage.open(
    join(await getDatabasesPath(), 'riverpod.db'),
  );
});
持久化 Notifier

在 Notifier 的 build 中,调用 persist,并提供:Storage(例如 ref.watch(storageProvider.future))、唯一的 key,以及用于状态的 encode/decode。不要等待 persist;Riverpod 会处理。

class TodoList extends AsyncNotifier<List<Todo>> {
  @override
  Future<List<Todo>> build() async {
    persist(
      ref.watch(storageProvider.future),
      key: 'todo_list',
      encode: (todos) => todos.map((todo) => {'task': todo.task}).toList(),
      decode: (json) => (json as List).map((todo) => Todo(task: todo['task'] as String)).toList(),
    );
    return fetchTodosFromServer();
  }
}
键
  • 在所有已持久化的提供程序之间必须唯一(相同的键会对应同一行,存在数据损坏风险)。
  • 在重启后必须保持稳定(更改键会导致无法恢复状态)。
  • 对于 family 提供程序,请将参数包含在键中。
JsonPersist(代码生成)

使用 riverpod_sqflite 和代码生成时,可使用 @JsonPersist() 生成 key/encode/decode:

@riverpod
@JsonPersist()
class TodoList extends _$TodoList {
  @override
  Future<List<Todo>> build() async {
    persist(ref.watch(storageProvider.future));
    return fetchTodosFromServer();
  }
}
缓存时长

默认情况下,状态只会缓存一段较短的时间(例如 2 天)。对于长期保存的数据(例如用户偏好设置),请设置 StorageOptions:

persist(
  ref.watch(storageProvider.future),
  options: const StorageOptions(cacheTime: StorageCacheTime.unsafe_forever),
  // ...
);

如果选择永久保存,请规划好在应用发生变化时删除或迁移数据;Riverpod 不会执行迁移。

Destroy key(简单迁移)

数据结构发生变化时,请使用 destroyKey 丢弃旧数据:

options: const StorageOptions(destroyKey: '1.0'),

在新版本中更新该字符串;此后旧的持久化状态将被忽略,提供程序会从头开始。

等待解码

若要从持久化状态而非网络调用进行初始化,请等待 persist 返回的 future:

await persist(ref.watch(storageProvider.future), key: 'todo_list', ...).future;
return state.value ?? <Todo>[];
测试

将 Storage 提供程序替换为 Storage.inMemory(),这样测试就不需要真实数据库:

ProviderScope(
  overrides: [
    storageProvider.overrideWith((ref) => Storage<String, String>.inMemory()),
  ],
  child: const MyApp(),
)

对于高级迁移或自定义存储策略,你可能仍需直接操作数据库。

评论

支持 Markdown