AsyncValue switch, and (2) if they implement a custom StateStorage. (3) requires no code change — your persisted data is read as before.AsyncValue now has a fourth state, AsyncIdle, in addition to loading, data, and error. Any exhaustive switch you wrote over an AsyncValue is now non-exhaustive and will no longer compile.
// Before (no longer compiles — non-exhaustive):
final label = switch (state) {
AsyncLoading() => 'Loading…',
AsyncData(:final data) => 'Got $data',
AsyncError(:final error) => 'Error: $error',
};
You have two ways to fix it.
// After — option A (add the missing case):
final label = switch (state) {
AsyncIdle() => 'Idle',
AsyncLoading() => 'Loading…',
AsyncData(:final data) => 'Got $data',
AsyncError(:final error) => 'Error: $error',
};
// After — option B (recommended):
final label = state.when(
idle: () => 'Idle',
loading: (previousData) => 'Loading…',
data: (data) => 'Got $data',
error: (error, stackTrace, previousData) => 'Error: $error',
);
AsyncShardBuilder users are unaffected unless they want a distinct idle UI via onIdle:. And FutureShard/StreamShard never enter the idle state, so a switch over their value only needs AsyncIdle for completeness.The StateStorage interface now declares Future<void> delete(String key). Any class that implements StateStorage must add it, or it won't compile.
// Before:
class SharedPreferencesStorage implements StateStorage {
@override
Future<void> save(String key, String value) => _prefs.setString(key, value);
@override
Future<String?> load(String key) async => _prefs.getString(key);
}
// After — add delete:
class SharedPreferencesStorage implements StateStorage {
@override
Future<void> save(String key, String value) => _prefs.setString(key, value);
@override
Future<String?> load(String key) async => _prefs.getString(key);
@override
Future<void> delete(String key) => _prefs.remove(key);
}
For other backends, delegate to the backend's own delete:
box.delete(key)DELETE statement keyed on keyContract: after delete(key), a subsequent load(key) returns null, and deleting an absent key is a no-op.
Persisted values are now wrapped in a small envelope: {"__shard_v":N,"__shard_p":"…"}, where __shard_v is the schema version and __shard_p is your serialized payload.
No code change is required, and your existing data is safe. Reads are legacy-tolerant: 1.x data was written as bare payload, so it is read as version 1, and then rewritten in envelope form on the next save. Upgrading users keep everything.
If your persisted model changes shape over time, you can opt into schema migration by setting a version and providing a migrate callback that upgrades older payloads to the current shape. See Persistent Shard.
That's the entire breaking surface. Everything else in 2.0 is additive—new shards, mixins, widgets, and persistence options that you can adopt at your own pace. For the full tour, see What's New.
Next: head back to Core Concepts to put the new APIs to work, or revisit the Introduction.