zenify 1.10.2
zenify: ^1.10.2 copied to clipboard
Powerful asynchronous state management, automatic caching, hierarchical DI, and zero-boilerplate reactivity. A holistic architecture for modern Flutter apps.
1.10.2 #
Added #
ZenInfiniteQuery.maxPages— memory safety limit for infinite scrolling. Automatically evicts older pages from RAM when users scroll deeply, while preserving the exact cursors needed for seamless bi-directional refetching.ZenMutation.anyMutating&ZenMutation.activeMutations— global tracking hooks. Perfect for showing universal loading indicators or disabling navigation while mutations are actively in-flight anywhere in the app.
1.10.1 #
Added #
-
scope.require<T>({String? tag})— idiomatic throwing dependency lookup onZenScope. This is the one genuinely new addition.scope.find<T>()is nullable (returnsT?), which meansscope.find<T>()!is a silent footgun.scope.require<T>()is the non-nullable, throwing form that surfaces aZenDependencyNotFoundExceptionwith the missing type name, scope name, tag, and a copy-paste registration suggestion.// ❌ Before — NPE crash, no diagnostic context final svc = scope.find<AuthService>()!; // ✅ After — ZenDependencyNotFoundException with actionable message final svc = scope.require<AuthService>();Note:
Zen.find<T>()(global API) already throws when missing — it behaves likeGet.find<T>(). The newrequire<T>()is only onZenScopewherefindis nullable. -
ZenQueryConsumer<T>— self-contained widget that creates, fetches, and renders aZenQuerywith no controller or module boilerplate. TheuseQueryequivalent for Zenify.// No controller. No module. One widget. ZenQueryConsumer<User>( queryKey: 'user:123', fetcher: (_) => api.getUser(123), data: (user) => UserProfile(user), loading: () => const CircularProgressIndicator(), error: (err, retry) => ErrorView(err, onRetry: retry), )Supports all
ZenQueryBuilderfeatures:initialData,config,showStaleData,autoFetch, and dynamicqueryKeychanges (disposes the old query, creates a new one).
Docs Updated #
doc/hierarchical_scopes_guide.md—ZenModule.registerexamples updated toscope.require<T>().doc/real_world_patterns.md—NetworkModuleandAuthModuleexamples updated toscope.require<T>().doc/migration_guide.md— DI section clarifies thatZen.find<T>()already throws likeGet.find<T>(), and introducesfindOrNull<T>()as the explicit nullable escape hatch.
Tests #
test/di/zen_require_test.dart— 21 tests:scope.require<T>()happy path (untagged, tagged, hierarchical, lazy), error path (exception type, type name, scope name, tag, suggestion), parity withfindRequired<T>(), disposed scope, andZen.find<T>()throwing behaviour.test/widgets/zen_query_consumer_test.dart— 14 widget tests: happy path,initialData, loading state (custom + default), error state (custom + default + retry), idle state,queryKeychange, disposal, config passthrough, typed generics, and barrel export smoke test.
1.10.0 #
Added #
ZenInfiniteQuery.when()— declarative builder for infinite-scroll UI. Handles all states (loading, error, data, loadingMore, empty) with optionalfooterslot for a "load more" trigger.ZenMutation.when()— reactive mutation state builder usingAnimatedBuilder+Listenable.merge. Covers idle, loading, success, and error states with full type parameters.doc/gorouter_guide.md— comprehensive guide for integratingZenRoutewith GoRouter, covering nested routes, auth guards, and scope lifecycle.- Codecov badge in README.
Bug Fixes #
These bugs were uncovered through the new test suite ("coverage-driven debugging"). All are fixed in this release.
RxTransformations.skip()never updated after creation — the closure returnednullwithout accessingvalue, so the sourceRxwas never registered as a dependency. The computed was permanently dead and would never re-evaluate when the source changed. Fixed by always accessingvaluebefore branching.RxTransformations.take()lost its dependency after exhaustion — same root cause. Fixed for consistency.RxTimingExtensions.take/skipcaused API-wide ambiguity —RxTimingExtensionsdefined callback-styletake(int, callback)andskip(int, callback)onRx<T>, conflicting withRxTransformations.take(int)and.skip(int). Dart could not resolve either name. Renamed totakeFirst/skipFirst— consistent with Flutter'sfirstWhere/firstOrNullpattern.RxTimingExtensions.map/wherecaused API-wide ambiguity —RxTimingExtensionsdefined callback-stylemap(transformer, callback)andwhere(condition, callback)onRx<T>, conflicting withRxComputedExtensions.map/.wherewhich both return computed values. Renamed tolistenMapped(transformer, callback)andlistenWhere(condition, callback)— thelisten*prefix makes the side-effect callback intent explicit and unambiguous.RxMapExtensions.update()was unreachable dead code —Rx<T>has a class-levelupdate(T Function(T) updater)that always shadows the extension methodupdate(K key, V Function(V), ...). Users could never call the map-keyed version. Removed; usetryUpdate(key, fn)directly.RxTransformations.whereNotNull()caused ambiguity for nullable types — conflicted withRxNullableTransformations.whereNotNull(). Removed fromRxTransformations<T>;RxNullableTransformations<T>.whereNotNull()is the correct location.ZenTestMode.mockAll()silently registers underdynamic(breaking change documented) — callingZen.put(instance)whereinstanceis typed asdynamicloses the generic type parameterTto type erasure. All mocks were registered underdynamicinstead of the intended interface type, makingZen.find<T>()always returnnullregardless of what was mocked. Deprecated with a clear runtime warning explaining the bug. Use chained.mock<T>()calls instead.ZenModuleRegistrydead code removed — the missing-dependency guard inregisterModuleswas unreachable because_collectAllDependencies()already recursively resolves all transitive dependencies before the check was reached. Removed to prevent misleading error messages.ZenController.autoDispose<T>()did not dispose handle on error — if the callback threw an exception, the handle was orphaned. It now callshandle.dispose()in the catch block, consistent with the no-leak guarantee.ZenController.limited<T>()did not dispose handle on error — same issue. Now disposes on callback exception.
Tests #
- Comprehensive Test Coverage: Over 2,120+ unit and widget tests validating core logic, widget interactions, garbage collection, and strict state invariants. Line test coverage has reached >95.0% overall!
Big Fixes & Minor Tweaks #
- Fixed Lifecycle Hook Warnings: Resolved
mustCallSuperlint warnings within ZenController'sonInitandonReadyhooks, ensuring developers properly chain initialization methods. - Widget Testing Stability: Fixed pending timer exceptions during test disposal by ensuring immediate flush of garbage collection timers within Widget tests affecting
ZenQueryBuilder,ZenInfiniteQuery.when(), and cache operations.
Dead Code Audit #
As part of the final push for test coverage, we removed/ignored non-reachable safety pathways:
- Scope clearAll fallback:
// coverage:ignore-lineadded to the error catch insideclearAllsincedisposecompletely swallows exceptions within ZenScope's controller array cleanup phase. - ControllerScope key update: Standard Flutter validation (
Widget.canUpdateverifies identical keys) effectively prevents instances with different keys from executingdidUpdateWidget. Thus, key differences trigger complete teardowns via widget replacement rather than widget updates. This redundant internal key check insideZenControllerScope.didUpdateWidgethas been marked with// coverage:ignore.
1.9.1 #
Added #
ZenQuery.when()— declarative shorthand forZenQueryBuilder. Supportsdata,loading,error, andidlebuilders.tool/migrate_from_getx.dart— migration script that auto-converts GetX code to Zenify (imports, controllers,.obs→.obs(), DI calls, widgets,permanent:→isPermanent:). Flags navigation, workers, and other patterns for manual review.- GitHub Actions CI workflow (
flutter test,dart analyze, format check) - GetX migration guide (
doc/migration_guide.md) complete rewrite with verified API examples - README callout for GetX developers linking to the migration guide
1.9.0 #
Added #
tagsparameter onZenQueryfor group-based cache managementZenQueryCache.invalidateQueriesByTag(tag)andrefetchQueriesByTag(tag)ZenQueryCache.invalidateQueriesByPattern(pattern)andrefetchQueriesByPattern(pattern)— supports glob-style*wildcardsZenQueryCache.getQueriesByTag(tag)andgetKeysByTag(tag)for tag inspectionInMemoryStorage— a built-in zero-dependencyZenStorageadapter for testing and ephemeral storage- Warning log when a wildcard pattern matches all queries (e.g.
'*')
1.8.0 #
Breaking Changes #
- Removed
ZenInspectorOverlaywidget. Remove the wrapper from your app —Zen.init(registerDevTools: true)continues to work as before.
// Before
runApp(ZenInspectorOverlay(child: MyApp()));
// After
runApp(MyApp()); // DevTools integration unchanged
Changed #
- DevTools UI moved to a separate optional package:
zenify_devtools_extension - Core package is ~25MB smaller as a result
1.7.0 #
Added #
- Flutter DevTools extension with 3-tab inspector: Scope Inspector, Query Cache Viewer, Metrics Dashboard
ZenServiceExtensionsclass for VM Service Protocol integration- Automatic service extension registration via
Zen.init(registerDevTools: true)in debug builds - Zero performance impact in release builds
1.6.6 #
Added #
ZenExceptionbase class with compact (default) and verbose (ZenConfig.verboseErrors = true) error formatting- Specific exception types:
ZenDependencyNotFoundException,ZenCircularDependencyException,ZenDisposedScopeException,ZenScopeNotFoundException,ZenControllerNotFoundException,ZenControllerDisposedException,ZenOfflineException,ZenQueryException,ZenMutationException,ZenModuleException,ZenLifecycleException,ZenRouteException ZenLogger.logException(e)for formatted exception outputZenConfig.verboseErrorstoggle
1.6.5 #
Added #
- "Pending Mutations" card in DevTools Stats View
ZenMutationQueue.pendingCountandpendingJobsfor debugging- Visual offline queue status indicator in DevTools
1.6.4 #
Added #
ZenObserver— alias forObxwidgetscope.get<T>()— alias forscope.find<T>()scope.remove<T>()— alias forscope.delete<T>()scope.has<T>()— alias forscope.exists<T>()
1.6.3 #
Added #
Zen.get<T>()— alias forZen.find<T>()Zen.remove<T>()— alias forZen.delete<T>()Zen.has<T>()— alias forZen.exists<T>()Zen.queryCache— shorthand forZenQueryCache.instance
1.6.2 #
Breaking Changes #
OptimisticMutationrenamed toZenMutation(helpers are now static methods)listAdd→listPut,listUpdate→listSet,add→put,update→set
// Before
OptimisticMutation.listAdd<Post>(queryKey: 'posts', mutationKey: 'create', mutationFn: ...)
// After
ZenMutation.listPut<Post>(queryKey: 'posts', mutationFn: ...) // mutationKey auto-generated
Changed #
mutationKeyis now optional; auto-generated fromqueryKeyif omitted
1.6.1 #
Added #
ZenMutation.listPut<T>,listSet<T>,listRemove<T>— optimistic list helpers with automatic rollbackZenMutation.put<T>,set<T>,remove()— optimistic single-value helpers
1.6.0 #
Added #
ZenStorageinterface for pluggable persistence backends (SharedPreferences, Hive, SQLite, etc.)- Auto-hydration: queries load cached data from storage on startup
- Offline mutation queue: mutations triggered while offline are queued and replayed on reconnect
ZenQueryCache.setQueryData()for function-based optimistic cache updatesNetworkModeenum:online,offlineFirst,alwaysexample/zen_offline— full offline-first example app
1.5.0 #
Breaking Changes #
refetchOnMount,refetchOnFocus,refetchOnReconnectchanged frombooltoRefetchBehaviorenum
// Before
ZenQueryConfig(refetchOnMount: true)
// After
ZenQueryConfig(refetchOnMount: RefetchBehavior.ifStale)
// New option: RefetchBehavior.always — force refetch regardless of staleness
Added #
RefetchBehavior.alwaysfor real-time/critical data use casesZenQueryConfig.retryDelayFn— custom function for error-aware retry delays
1.4.4 #
Fixed #
ZenInspectorOverlaycrashes when used without aScaffold— replacedScaffoldMessengerwith internal toast system- Layout overflows and data preview scrolling in inspector
1.4.3 #
1.4.1 #
Added #
ZenQueryClientandZenQueryClientOptionsfor managing global query defaultsZenQueryConfig.copyWith()for partial config overrides
1.4.0 #
Added #
pause()andresume()methods onZenQueryandZenStreamQueryZenQueryConfig.autoPauseOnBackgroundandrefetchOnResumeZenQueryCache.getAllQueries()for lifecycle management- Exponential backoff:
maxRetryDelay,retryBackoffMultiplier,retryWithJitter
Breaking Changes #
ZenStreamQuerynow defaults toautoPauseOnBackground: false(wastruein 1.3.x)
// To restore previous behavior
ZenStreamQuery(config: ZenQueryConfig(autoPauseOnBackground: true))
1.3.6 #
Fixed #
Zen.reset()no longer requirestestWidgets()— works in plaintest()blocksZenLifecycleManagerhandles missingWidgetsBindingin test environments gracefully
1.3.5 #
1.3.4 #
Changed #
- Fixed
createControllersyntax in all documentation - Rewrote
state_management_patterns.mdwith clearer examples
1.3.3 #
Fixed #
- 1-pixel
RenderFlexoverflow inZenInspectorOverlayon iOS/Android with no dependencies - Improved overlay positioning with explicit
Positioned.fill
1.3.2 #
Added #
ZenInspectorOverlay— in-app debug overlay with real-time scope hierarchy, query cache, and performance stats
1.3.1 #
Added #
- Automatic query tracking: queries created in
onInit()are automatically disposed when the controller closes — noonClose()boilerplate required
1.3.0 #
Changed #
- Internal architecture refactoring: replaced
ZenScopeManager(541 lines) andZenScopeStackTrackerwith a singleZen.currentScopenavigation bridge (~80% reduction in scope management code) ZenRoutereduced from 526 to 336 lines
Added #
parentScopeparameter onZenRoutefor explicit scope control
Breaking Changes #
- None. All public APIs unchanged.
1.2.3 #
Fixed #
ZenStreamQuerynow pauses oninactiveandhiddenapp states (web tab switching)- Lifecycle events (
onPause,onResume) now fire correctly when query is not registered in DI
1.2.1 #
Added #
ZenInfiniteQuery.getPreviousPageParam()andfetchPreviousPage()for bidirectional paginationhasPreviousPageandisFetchingPreviousPagereactive properties- Call-time callbacks on
mutate():onSuccess,onError,onSettled placeholderDatainZenQueryConfig— temporary initial data that doesn't persist to cachekeepPreviousDataonZenQueryBuilderandZenStreamQueryBuilderto prevent flash-of-loading on key changes
1.2.0 #
Added #
ZenStreamQuery— reactive wrapper forStream<T>with lifecycle management and optimistic updatesZenStreamQueryBuilderwidget
Changed #
- Folder restructure: builders moved to
lib/widgets/builders/, query internals tolib/query/core|logic|extensions/ import 'package:zenify/zenify.dart'is unaffected
1.1.9 #
Added #
ZenStorageinterface (foundation for 1.6.0 offline engine)persist,fromJson,toJson,storageoptions onZenQueryConfig- Auto-hydration from storage on query initialization
1.1.8 #
Breaking Changes #
- Fetcher signature now requires a
ZenCancelTokenparameter
// Before
fetcher: () => api.getData()
// After
fetcher: (_) => api.getData()
Added #
ZenCancelTokenfor platform-agnostic request cancellation- Automatic cancel on dispose and on re-trigger (prevents stale data race conditions)
1.1.7 #
1.1.6 #
Added #
enabledparameter andRxBool enabledproperty onZenQueryfor dependent queriesZenQueryCache.instance.prefetch()for pre-loading data without creating listeners
1.1.4 #
Added #
ZenInfiniteQueryfor paginated lists and infinite scrollingqueryKeynow acceptsObject(includingList) in addition toString
1.1.3 #
Added #
ZenMutationfor reactive write operations (create, update, delete)- Lifecycle hooks:
onMutate,onSuccess,onError,onSettled ZenQueryCache.invalidateQuery()integration pattern
1.1.1 #
Added #
scopeparameter onZenQueryfor automatic disposal with scope lifecycleZenQueryCache.invalidateScope(),refetchScope(),clearScope(),getScopeQueries(),getScopeStats()
1.1.0 #
Added #
ZenQuery— async state management with caching, deduplication, retries, and background refetchZenQueryBuilder— reactive widget with loading/error/success states and SWR patternZenQueryCache— global cache manager with invalidation and memory management
1.0.1 #
1.0.0 #
Breaking Changes #
EagerRef,LazyRef,ControllerRef,ZenRefreplaced by a singleRef<T>.put(),.asRef(),.register()extension methods removed — useZen.put()Zen.putFactory()removed — useZen.putLazy(..., isPermanent: false)- Debug methods moved to
ZenDebugclass:ZenDebug.dumpScopes()etc.
Added #
Ref<T>universal reference withcall()shorthand- Improved
ZenBuilderwith proper DI cleanup and ownership tracking
0.6.3 #
Added #
ZenEnvironment.productionVerboseenvironment presetZenConfig.shouldLogRoutesandshouldLogNavigationhelpers
0.6.2 #
Fixed #
- Inverted logic in
shouldLog()causing logs to appear when suppressed - Rx tracking logs now respect
ZenConfig.enableRxTracking
0.6.1 #
0.6.0 #
0.5.0 #
Initial pub.dev release.
0.4.1 #
0.4.0 #
0.3.0 #
Added #
RxResult<T>for success/failure error handling patternsRxComputedfor automatic dependency trackingRxFuturefor reactive async operations- Circuit breaker pattern for resilient reactive operations
- Batch operations and bulk updates for collections
0.1.7 #
0.1.5 #
Added #
- Hierarchical scope system
- Circular dependency detection
- Module/binding system for organized registration
- Lazy initialization support
0.1.4 #
Added #
- Generic type constraints on
RxList<E>,RxMap<K,V>,RxSet<E> ControllerRef<T>typed provider references
0.1.3 #
0.1.2 #
Fixed #
- Minimum Dart SDK updated to 2.19.0
- Deprecated
IndexErrorusage - Collection type safety in
RxList,RxMap,RxSet
0.1.1 #
Initial release — reactive state, controller lifecycle management, route-based disposal.