persist<KeyT, EncodedT> method
FutureOr<void>
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 a Future that completes when the initial decoding is done. After awaiting that future, Notifier.state should be populated with the decoded value.
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
FutureOr<void> 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 futureOr.catchError(ref.container.defaultOnError);
}
return null;
} catch (err, stack) {
// Don't block the provider if decoding failed
ref.container.defaultOnError(err, stack);
}
}
}