all_observer 1.1.0
all_observer: ^1.1.0 copied to clipboard
Lightweight reactive state management for Flutter with zero external dependencies: observable values plus an auto-tracking Observer widget.
1.1.0 #
Additive release — no breaking changes, no new external dependencies.
Async:
ObservableFuture<T>: anObservable<AsyncState<T>>that runs aFuture<T> Function()and tracks its loading/data/error lifecycle. Auto-runs by default (autoStart: true) or viarun()/refresh(). Race-safe: a generation counter discards a stalerun()'s result if a newerrun()started before it resolved, and discards any result that arrives afterclose().AsyncState<T>sealed class:AsyncLoading<T>(withpreviousDatafor stale-while-loading UIs),AsyncData<T>,AsyncError<T>;when/maybeWhen,isLoading/hasData/hasError/valueOrNull, content-based==/hashCode.
Computed:
equalsnamed parameter onComputed's constructor, mirroringObservable's, for custom change-detection (e.g. floating-point tolerance) on the recomputed value.- Diamond-glitch mitigation: inside an active
Observable.batch(), aComputed's recompute is deferred (to the nextvalueread, or to end-of-batch, whichever comes first) instead of running eagerly per upstream notification — so a diamond-shaped dependency graph recomputes at most once per batch, with every dependency already at its final value. Outsidebatch, the previous eager-recompute-per-notification behavior is unchanged and documented as a known limitation.
Widgets:
Observer.withChildnamed constructor: rebuilds only the part of the subtree the builder itself constructs, reusing a staticchildwidget across rebuilds instead of reconstructing it — for expensive widgets that don't depend on any observable.
Observable:
Observable.setValue(T newValue): equivalent tovalue = newValue, usable as a tear-off (e.g. directly as anonChangedcallback) and the unambiguous way to assignnullto anObservable<T?>(unlikecall(null), which reads instead of assigns).Observable<T>.select<R>(R Function(T value) selector, {String? name})extension: sugar overComputed(() => selector(value), name: name)for a narrower, memoized derived value. Caller owns and mustclose()the returnedComputed.
Performance:
ListenerRegistrynow backed by aLinkedHashSet<VoidCallback>instead of aList, makingadd/remove/containsO(1) instead of O(n), while preserving insertion order and native deduplication. Seebenchmark/listener_registry_benchmark.dart.
Other:
example/reworked into a multi-page app (bottom navigation) with five focused demos: counter +Computed, debounced search,ObservableFutureasync states,Observable.batchform saving, andObserver/ValueListenableBuilderinterop — each in its own file underexample/lib/demos/.- README (EN/PT-BR): new "Migrating from other reactive state solutions" section (concept-based equivalence table) and a "Known limitations" section (diamond glitch outside
batch,Computedstaying active after first read untilclose(), single-isolate confinement).
1.0.0 #
Initial release.
Core:
Observable<T>reactive value, plusObservableInt,ObservableDouble,ObservableBool,ObservableString..obsextension for creating observables from literals and collections.ObservableList,ObservableMap,ObservableSetreactive collections; reading any member tracks it, mutating any member notifies at most once per call — bulk operations (addAll,removeWhere,retainWhere,insertAll,clear,sort,shuffle) never notify once per element, and no-op mutations (adding a duplicateSetelement,removeWherethat removes nothing,map[k]=vwith an identical value,clear()on an already-empty collection) don't notify at all.Observerwidget with automatic, stack-based dependency tracking and safe rebuild scheduling (no "defunct element" crashes).ObserverValuefor self-contained local reactive state.Computed<T>: lazy, memoized derived values reusing the same dependency tracker asObserver; supports dynamic/conditional dependencies; only notifies when the derived value actually changes;close()unsubscribes from all current dependencies.Observable.batch(() { ... }): coalesces multiple writes inside the callback into a single notification per changed observable/collection for manual (listen/ever) subscribers, with nested-batch support.equalsparameter on theObservable<T>constructor, for custom change-detection (e.g. floating-point tolerance) instead of==.listen/ObservableSubscription, stream-free manual subscriptions.- Workers:
ever,once,debounce,interval, plusWorker/Workers.
Robustness:
- An exception thrown inside one
ever/listencallback does not stop other listeners of the same observable from running; it is reported viaFlutterError.reportError(library: 'all_observer') instead of being swallowed or propagating. - A synchronous update cycle (a listener of A writing to B, whose listener writes back to A) stops after a bounded notification depth with a descriptive
FlutterError, instead of crashing with a raw stack overflow. Observer: if the builder throws during a tracked build, dependency disposers accumulated up to that point are still assigned, so the next build/unmount does not leak or double-register listeners.- The "write during build" warning covers both
Observable.value =and reactive-collection mutations, andObserverConfig.strictModeturns this case into a thrownObserverErroras well as the "empty Observer" case. listen()/close()on an already-close()dObservable/collection returns an inert (already-canceled) subscription instead of silently registering a listener that can never fire; a mutation attempted on a closed collection is a full no-op (the underlying data is left untouched), and double-close()is safe.- Documented, in
Observable's dartdoc, that writing to an observable from a different isolate does not work (no cross-isolate synchronization is attempted).
Other:
- Debug-only, ANSI-colored logging via
ObserverConfigand non-fatal misuse warnings (with optionalstrictMode). - Zero external dependencies; full
ValueListenableinteroperability.
See AUDIT_REPORT.md at the repository root for the full item-by-item performance/robustness audit this release was validated against.