shard 2.0.0-dev.1
shard: ^2.0.0-dev.1 copied to clipboard
A powerful, lightweight state management solution for Flutter with built-in persistence, debounce, throttle, and seamless widget integration.
2.0.0-dev.1 #
First 2.x development release. Three breaking changes (all with a clear migration path — see docs/superpowers/specs/2026-06-13-shard-2.0-migration.md) plus a wave of additive features and fixes. The "zero runtime dependencies" guarantee is unchanged.
Breaking changes #
- BREAKING:
AsyncValue<T>gained a fourth state,AsyncIdle<T>(the not-yet-started state). Any exhaustiveswitchover anAsyncValuein your code must now handleAsyncIdle(or switch to the newwhen/maybeWhen).FutureShard/StreamShardare unaffected (they begin loading), andAsyncShardBuilderkeeps working via a new optionalonIdle:builder that defaults to the loading widget. - BREAKING:
StateStoragegains a requireddelete(String key)method (per-key removal). CustomStateStorageimplementations must add it — e.g.await prefs.remove(key)/await box.delete(key). The bundledFakeStateStorageimplements it. - BREAKING: persisted state is now stored in a version envelope (
{"__shard_v":N,"__shard_p":"…"}) instead of the bare payload. Reads are legacy-tolerant — data written by 1.x is treated as version 1 — so upgrades keep their data. Only a 2.0→1.x downgrade cannot read 2.0-written data.
Async (AsyncValue / commands) #
- New:
AsyncValue.when/maybeWhen— exhaustive and partial pattern matching over idle/loading/data/error (loading and error receivepreviousData). - New:
AsyncValue.mapData/whenData— transform or read the success value while preserving the variant. - New:
AsyncValue.guard(future, {previousData})— runs a future and captures the outcome asAsyncData/AsyncError. - New:
AsyncShardBuilder.onIdle— optional builder for the idle state. - New:
CommandShard<Arg, Res>— aShard<AsyncValue<Res>>for one-shot async actions (form submit, create/update/delete). Starts inAsyncIdle;execute(arg)runs the action (AsyncLoading→AsyncData/AsyncError) with a double-submit guard and dispose-safety, returning the result or null.reset()returns to idle. Renders withAsyncShardBuilderviaonIdle:.
Shards & composition #
- New:
computedShard(sources, compute)andComputedShard<T>— a derived shard that listens to one or moreListenablesources and recomputes when any notifies. The function form is the recommended default; the subclass form gives a named type forShardProvider. - New:
HistoryMixin<T>— undo/redo for aShard(undo(),redo(),canUndo,canRedo,clearHistory(), configurablemaxHistory). Records viaonChangeand restores viasetStateInternal, so restores notify listeners/observers without being re-recorded. - New:
Shard.stream— a lazily-created broadcastStream<T>of state changes (does not replay the current value; closes on dispose). EnablesStreamBuilder,await for, andemitsInOrderstream assertions without a dependency. - New:
StreamShard.pause()/resume()/isPaused— pause and resume the underlying stream subscription (e.g. while a screen is backgrounded). Events are buffered while paused perStreamSubscriptionsemantics. - New:
deepEquals(a, b)— structural equality forList/Set/Map/Iterable(recursive), with a==fallback for scalars. Useful inbuildWhen/listenWhen, inside aShardSelector, or anywhere collection-aware comparison helps. - New:
DeepEqualityMixin<T>— aShardmixin that makesemitdedupe state withdeepEqualsinstead of==, so rebuilding a collection with identical contents no longer notifies listeners.
Widgets #
- New:
ShardListener/MultiShardListener— listener-only widgets for side effects (navigation, snackbars) that invoke a callback on state changes without rebuilding.MultiShardListenernests several via theSingleChildShardListenerinterface, likeMultiShardProvider.
Persistence #
- New:
StatePersistenceMixin.clearPersistence()(inherited byPersistentShard) — deletes this shard's persisted slice viaStateStorage.delete(cancelling any pending debounced save), for logout/wipe flows. Leaves in-memory state untouched. - New: schema versioning —
enablePersistenceandPersistentShard/SimplePersistentShardacceptversion(default 1) andmigrate(fromVersion, payload).migrateruns once when the stored version is older thanversion; it owns chaining intermediate migrations. - New:
flushOnPause—enablePersistenceandPersistentShard/SimplePersistentShardacceptflushOnPause(default false). When enabled, aWidgetsBindingObserverflushes a save onAppLifecycleState.paused/detached, so a backgrounded app doesn't lose the last change inside the debounce window. The observer is removed ondisablePersistence/dispose.
Caching #
- New:
MemoryCacheServicememory bounding:maxEntries— caps the number of retained entries (defaultnull, unbounded); the oldest entries are evicted onwriteonce the limit is exceeded.evictExpired()— removes all expired entries on demand and returns the count removed.entryCount— the number of entries currently held.readstill returns expired entries, so stale-while-revalidate flows (CacheMixin'sonErrorReturnOldCache) keep working.
- New:
CacheMixin.logCacheEventsandCacheMixin.onCacheLog(message)— toggle cache logging and redirect log lines (to a custom logger, a test recorder, etc.). The default sink isdart:developer.log(name: 'shard.cache').
Observability #
- New:
LoggingObserver— aShardObserversubclass that logs state changes and errors viadart:developer.log(name: 'shard')for DevTools integration. Defaults tokDebugModeso it's inert in release builds. Configurableprinter,shouldLogpredicate,includeStackTrace, and independent toggles forlogChanges/logErrors.
Testing #
- New:
package:shard/shard_test.dart— a separate public entry point for test utilities. Production builds do not link this code.ShardTester<T>for capturing and asserting state sequences (recordedStates,expectStates,expectNoMoreStates,waitForNext,waitFor,clear,dispose, plus ascopestatic helper).shardTest<S, T>()declarative helper for one-shotbuild → act → expecttests.FakeStateStorageandFakeCacheService— in-memory implementations of theStateStorage/CacheServiceinterfaces with failure injection, latency simulation, and call inspection.MockShardObserver(plusObservedChange<T>/ObservedErrorrecords) with ascopehelper that safely installs/restores the globalShard.observerin afinallyblock.ShardAssertionError/ShardTimeoutError— the error typesShardTesterthrows on failure.
Fixes #
- Fix:
StatePersistenceMixin.disposePersistenceIfEnabledpreviously reset_saveQueueto a fresh future and then calledsaveState(), which short-circuited because_isDisposedwas already true. As a result, a pending debounced save was silently dropped on disposal. Disposal now flushes the final write by chaining it onto the existing save queue and bypassing theisDisposedguard. - Fix:
ShardSelectornow rebuilds correctly when the selected value isnull. Previously a selector returningnull(for example selecting a nullable field) left the widget stuck onSizedBox.shrink()and never invokedbuilder. - Fix:
ShardBuilderandShardSelectornow rebind their listener when a different instance is supplied to theshard:parameter (viadidUpdateWidget). Previously they kept listening to the originally-provided instance. - Fix:
FutureShardno longer starts a second concurrentbuild()whenrefresh()is called while the initial fetch is still in flight. The initial fetch now participates in the same in-progress guard asrefresh(). - Fix:
CacheMixinno longer logs in release builds. Cache hit/miss/error logging is gated behind the newlogCacheEventsgetter (defaults tokDebugMode), so cache keys — which may contain user identifiers — are not emitted to release logs.
1.0.1 #
- Updated README with improved documentation and examples
- Added missing feature examples:
StreamShard,MultiShardProvider,CacheMixin,ShardObserver
1.0.0 #
- Stable release – Public API is now frozen for 1.x.
- No API changes from 1.0.0-dev.1.
1.0.0-dev.1 #
- New: [ShardLocator] for singleton registration
registerSingleton<T>(T instance)- Register an existing instanceregisterLazySingleton<T>(T Function() factory)- Register a factory, instantiated on first [get]get<T>()- Resolve the registered singletonisRegistered<T>()- Check if a type is registeredreset()- Clear all registrations (e.g. in test setUp)
- New:
stateSerializer<T>()factory function for JSON-based serialization- Eliminates the need to create separate serializer classes
- Works with any object that has
toJson()andfromJson()methods - Supports lists, maps, and nested objects
- New: [StringSerializer] for simple string state persistence
0.3.0 #
- New: Built-in caching support for [FutureShard]
- New: [CacheService] interface for cache storage backends
- New: [MemoryCacheService] singleton for in-memory caching
- New: [CacheEntry] class for cache entries with expiration
- New: [CacheMixin] for repository-level caching with
resolvemethod
0.2.0 #
- BREAKING CHANGE:
PersistentShard<T>is nowPersistentShard<T, K>T- Full state typeK- Persistence data type (can be a subset of T)- Allows persisting only the data you need, excluding loading states, errors, etc.
- BREAKING CHANGE:
StatePersistenceMixin<T>is nowStatePersistenceMixin<T, K> - BREAKING CHANGE: Added
onLoadComplete(K? data)callback- Called when load operation completes (replaces automatic state update)
- Receives the loaded data as parameter
nullis passed when storage is empty (first launch)- Developers have full control over how loaded data is merged into state
- BREAKING CHANGE: New abstract method
toPersistence(T state)required- Extracts the data to persist from the current state
- BREAKING CHANGE:
StateSerializer<T>is nowStateSerializer<K>in persistence config - BREAKING CHANGE: Removed
clear()method fromStateStorageinterface- The
clear()method was removed because it cleared all storage data, which is not useful for developers - Developers can reset state by using
emit(initialState)- auto-save will automatically sync to storage
- The
- BREAKING CHANGE: Removed
clearStorage()method fromStatePersistenceMixin - BREAKING CHANGE: Removed
clear()method fromPersistentShard- Developers now have full control over state reset using
emit()method
- Developers now have full control over state reset using
- New:
SimplePersistentShard<T>class for simple cases- Use when state type and persistence type are the same
- No need to override
toPersistenceoronLoadComplete - Reduces boilerplate for simple use cases
0.1.0 #
- Added MultiShardProvider widget. Simplifies providing multiple shards to the widget tree
- Type-safety improvements
- Removed unsafe
dynamiccasts in widget layer - Improved generic type preservation throughout the widget tree
- Better compile-time type checking
- Removed unsafe
- Development improvements
- Added
flutter_lintsas dev dependency for better code quality
- Added
0.0.2 #
- Updated package metadata and documentation links
- Added homepage URL pointing to documentation site
- Added issue tracker link
- Added documentation URL
- Added package topics for better discoverability
- Removed flutter_lints dependency from dev_dependencies
0.0.1 #
- Initial release of Shard - A powerful, lightweight state management solution for Flutter
- Core state management with [Shard] class
- Built-in persistence support with [PersistentShard]
- Async state management with [FutureShard] and [StreamShard]
- Debounce and throttle mixins
- Widget integration with [ShardProvider], [ShardBuilder], and [ShardSelector]
- Observer pattern for global state monitoring