---
name: riverpod-offline-16225b5a04f6
description: Persist Riverpod notifier state offline with Storage and persist(); riverpod_sqflite, JsonPersist, key, destroyKey, cache duration, testing with in-memory storage. Use when saving state across app restarts or offline. Use this skill when the user asks about offline persistence, persisting state, or Riverpod storage.
---

# Riverpod — Offline persistence (experimental)

## Instructions

**Offline persistence** stores provider state on device so it survives restarts and works offline. Riverpod is storage-agnostic; packages like **riverpod_sqflite** provide a **Storage** implementation. Only **Notifier**-based providers can be persisted. The feature is experimental.

### Creating a Storage

Install a package (e.g. riverpod_sqflite + sqflite) and create a Storage. With SQFlite:

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

### Persisting a notifier

Inside the notifier's `build`, call **persist** with: the Storage (e.g. `ref.watch(storageProvider.future)`), a unique **key**, and **encode**/ **decode** for your state. Do not await persist; Riverpod handles it.

```dart
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();
  }
}
```

### Keys

- **Unique** across all persisted providers (same key = same row, risk of corruption).
- **Stable** across restarts (changing the key loses restored state).
- For **family** providers, include the parameter in the key.

### JsonPersist (code generation)

With riverpod_sqflite and codegen, use **@JsonPersist()** so key/encode/decode are generated:

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

### Cache duration

By default state is cached for a short time (e.g. 2 days). For long-lived data (e.g. user preferences), set **StorageOptions**:

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

If using forever, plan to delete or migrate data when the app changes; Riverpod does not do migrations.

### Destroy key (simple migration)

When the data shape changes, use **destroyKey** so old data is discarded:

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

Bump the string in new releases; old persisted state is then ignored and the provider starts fresh.

### Waiting for decode

To initialize from persisted state instead of a network call, await the persist future:

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

### Testing

Override the storage provider with **Storage.inMemory()** so tests don't need a real database:

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

For advanced migrations or custom storage strategies, you may still need to work with the database directly.


---
Source attribution: dangdungvn
Original project: https://github.com/dangdungvn/review_system_app/blob/f397d2abe0a8d4c041cd274f0ffc5680d85e7c44/.github/skills/riverpod-offline/SKILL.md
License: MIT
License information: https://opensource.org/license/mit
Original upstream instructions; downloaded from Dave AI.

## Upstream license notice

MIT License

Copyright (c) 2024 Minh Nguyen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



# Dave AI resource metadata

Creator / publisher: dangdungvn
Original project: https://github.com/dangdungvn/review_system_app/blob/f397d2abe0a8d4c041cd274f0ffc5680d85e7c44/.github/skills/riverpod-offline/SKILL.md
Attribution basis: Source repository record

Fields of work: Development & Engineering
Roles: Backend Developer, Full-Stack Developer, Mobile Developer
Specialties: Backend, Mobile
Tools: Jsonpersist, Riverpod_Sqflite
Country applicability: Country not specified
Classification basis: Source catalog labels
Instruction SHA-256: d6e2adcec0d008552e1896108e3a19c5d440509ea5c76ff2604739ef16d93678
