persist<KeyT, EncodedT> method
PersistResult
persist<KeyT, EncodedT>(
- FutureOr<
Storage< storage, {KeyT, EncodedT> > - required KeyT key,
- required EncodedT encode(
- ValueT state
- required ValueT decode(
- EncodedT encoded
- StorageOptions options = const StorageOptions(),
Persist the state of a provider to a database.
When calling this method, Riverpod will automatically listen to state changes, and invoke Storage methods to persist the state.
It is generally recommended to call this method at the very top of Notifier.build This will ensure that the state is persisted as soon as possible.
Calling persist returns PersistResult that contains a Future which completes when decoding has finished. See PersistResult.future. In general, you should not await this future, as awaiting it would only delay the core logic, such as fetching data from an API.
Note: The decoding of the state is only performed once, the first time the provider is built. Calling persist multiple times will not re-trigger the decoding.
Implementation
PersistResult persist<KeyT, EncodedT>(
FutureOr<Storage<KeyT, EncodedT>> storage, {
required KeyT key,
required EncodedT Function(ValueT state) encode,
required ValueT Function(EncodedT encoded) decode,
StorageOptions options = const StorageOptions(),
}) {
_debugAssertNoDuplicateKey(key, this);
var didChange = false;
listenSelf((_, __) async {
didChange = true;
try {
final futureOr = _callEncode(storage, key, encode, options);
if (futureOr is Future) {
unawaited(futureOr.onError(ref.container.defaultOnError));
}
} finally {
didChange = false;
}
});
if (ref.isFirstBuild) {
try {
// Let's read the Database
final futureOr = storage.then(
(storage) => storage.read(key).then((value) {
// The state was initialized during the decoding, abort
if (didChange) return null;
// Nothing to decode
if (value == null) return null;
// New destroy key, so let's clear the cache.
if (value.destroyKey != options.destroyKey) {
return storage.delete(key);
}
if (value.expireAt case final expireAt?) {
final now = clock.now();
if (expireAt.isBefore(now)) {
return storage.delete(key);
}
}
final decoded = decode(value.data);
_setStateFromValue(decoded);
}),
);
if (futureOr is Future) {
return PersistResult._(
future: futureOr.catchError(ref.container.defaultOnError),
);
}
} catch (err, stack) {
// Don't block the provider if decoding failed
ref.container.defaultOnError(err, stack);
}
}
return PersistResult._(future: null);
}