flutter_compositions 0.2.6
flutter_compositions: ^0.2.6 copied to clipboard
Reactive composition primitives for Flutter inspired by the Vue 3 Composition API.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased] #
0.2.6 - 2026-08-12 #
Added #
useStreamnow accepts an optionalonErrorcallback; the old doc example that double-subscribed the stream (aStateErroron single-subscription streams) is corrected.AsyncValue.isSettled— true when the operation finished with data or error.useAsyncState— VueUse-aligned async state composable returning(state, isReady, isLoading, error, execute)with latest-wins concurrency.
Changed #
useFuturestarts its future during setup instead of after the first frame, removing one frame of latency.useAppLifecycleStateregisters its observer during setup, so lifecycle changes between setup and mount are no longer missed.widget()now returns a cachedComputedRefon repeated calls within the same setup.- Added unit test coverage for
customRef/CustomRef/ReadonlyCustomRef, including the alien_signals 2.x re-entrance guard intrigger(). - Added direct unit tests for
hotReloadableContainer(identity preservation and position-based restore across hot reload). - Timing-based benchmarks are now tagged
benchmarkand excluded from the default test run; usemelos run benchmarkto run them. - Internal cleanup: removed dead code (
getCurrentBuilderState, unused hot-reload type field), fixed corrupted doc-comment formatting in framework.dart, and removed unverifiable performance claims from doc comments. - Internal refactors: deduplicated rebuild scheduling closures,
manageChangeNotifiernow reusesmanageListenable, andComputedBuilderguardsmarkNeedsBuildwith a dirty check.
Deprecated #
AsyncValue.hasData— misleading name (also true on error); useisSettledorisDatainstead. Will be removed in 0.3.0.useAsyncValue— superseded byuseAsyncState, which returns already-split refs; will be removed in 0.3.0.
Fixed #
watchcallbacks now run untracked (matching Vue semantics): reactive reads inside the callback no longer become hidden dependencies, and read-write of the same ref inside a callback no longer causes a synchronous infinite loop.CompositionWidgetnow updates its props signal before the element rebuild, removing a redundant intra-frame rebuild on every props update.useAsyncData: changing the watched value while a request is in flight now issues a new request (latest-wins) instead of silently dropping it; stale results and errors no longer overwrite newer state.useAsyncData: calling withoutwatchand a non-nullableWnow fails with a clear assert message instead of an obscureCastError.useAsyncData/useAsyncValuedoc examples now match the actual 2-tuple return signature.useAnimationControllernow owns its internal ticker and disposes it after the controller, aligning teardown order with Flutter conventions (defensive improvement; the previous order was benign in current Flutter).injectnow correctly returns a legitimately providednullvalue instead of treating it as "no provider found".- Hot reload no longer leaks the previous render effect (which caused double builder execution and racy cached widgets after reassemble).
- Hot reload now clears previously provided values, so
provide()calls removed during hot reload no longer leave stale injections. This also fixes the ordering ofreassemble(): an element now re-runs its ownsetup()(refreshingprovide()s) before its descendants re-run theirs, so descendants no longer observe a stale provided value for one reassemble cycle. - Changing a
reforhotReloadableContainertype during hot reload no longer throws aCastError; the stored value is safely discarded and the new initial value is used; hot-reloadables newly added during a reload correctly use their initial value, and a genuinely stored null is preserved for nullable refs. manageListenable/manageChangeNotifier(and composables built on them such asuseController) no longer accumulate duplicate listeners on hot reload.useAppLifecycleStateno longer accumulates duplicateWidgetsBindingObservers on hot reload (same reassemble-cleanup mechanism asmanageListenable).- Reading InheritedWidgets (
Theme.of(context),MediaQuery.of(context), ...) directly in the builder function now correctly rebuilds when the inherited value changes.setup()still runs exactly once. useContextRef(anduseMediaQuery,useMediaQueryInfo, etc.) now compares values with==by default instead ofidentical, avoiding spurious triggers for equal value types likeSize.
0.2.5 - 2026-07-19 #
Changed #
- Upgraded development toolchain to Flutter 3.44.6 and verified compatibility with
alien_signals2.3.1. No functional changes.
0.2.3 - 2026-02-17 #
Fixed #
- Fix
setState() or markNeedsBuild() called during buildwhenuseContextRefis used in aCompositionBuildernested inside a parentCompositionWidget- Root cause:
ReadonlyCustomRef.trigger()used_version.value++which both reads (subscribing the current render effect) and writes (triggering synchronous flush in alien_signals 2.x), causing re-entrance of the render effect during initialization - Fix: Use
_version.raw(untracked read) intrigger()to prevent self-subscription during signal writes
- Root cause:
0.2.2 - 2026-02-17 #
Changed #
- BREAKING: Upgrade
alien_signalsfrom 1.x to 2.x- Signal write:
.call(value, true)→.set(value) - Effect/EffectScope disposal:
.dispose()→.call()(callable syntax) getActiveSub/setActiveSubnow imported frompackage:alien_signals/preset.dart
- Signal write:
- Upgrade
melosto ^7.4.0 - Upgrade
very_good_analysisto ^10.2.0 - Fix lint issues from
very_good_analysis10.2.0 in example app
0.2.1 - 2026-02-15 #
0.2.0 - 2026-02-15 #
Changed #
- BREAKING PERF: Complete StatelessWidget migration for all composition widgets
- Migrated
ComputedBuilder,CompositionWidget, andCompositionBuilderfromStatefulWidgettoStatelessWidgetwith customElementimplementations - Architecture Change: Removed
SetupContextMixin- functionality now directly integrated into custom Elements - Memory Savings:
ComputedBuilder: ~56 bytes per instance (~15% reduction)CompositionWidget: ~48 bytes per instance (~20% reduction)CompositionBuilder: ~48 bytes per instance (~20% reduction)
- Performance Improvements:
ComputedBuilder: 15-25% lower update latency for simple widgetsCompositionWidget/CompositionBuilder: 5-10% faster reactive updates
- Technical Benefits:
- Eliminates
scheduleMicrotaskoverhead (~200-500 CPU cycles per update) - Eliminates
setStateclosure creation overhead (~30 CPU cycles) - Direct
markNeedsBuild()calls for more predictable batching - Uses
ComponentElementlifecycle methods (update,didChangeDependencies,reassemble,unmount) - Reduced object creation overhead (2 objects instead of 3 per widget)
- Eliminates
- API Compatibility: Fully backward compatible - no changes required to existing code
- Lifecycle Handling:
- Props updates:
update(newWidget)replacesdidUpdateWidget - InheritedWidget dependencies:
didChangeDependencies()remains available - Hot reload:
reassemble()with state preservation support - Cleanup:
unmount()replacesdispose()
- Props updates:
- Provide/Inject: Uses duck typing to find parent
SetupContextacross both old and new architectures - Inspired by solidart PR #143 and flutter_hooks
- Migrated
- Upgrade Dart SDK constraint to
^3.10.0 - Add
useControllergeneric helper and improve core composables - Use
.rawfor controllers in builder widget parameters
Fixed #
- Correct
ComputedBuildermount timing and first build
0.1.1 - 2025-11-06 #
- FIX: ensure InheritedWidget composables update correctly.
- FIX: add example package to workspace configuration.
- FEAT: add examples for InheritedWidget composables and their usage.
- FEAT: useContext and add tests for context behavior.
- FEAT: init project.
- DOCS: update feature list formatting in README.md.
- DOCS: enhance README with detailed documentation sections and examples.
0.1.0 - 2025-10-27 #
Added #
- Initial release of Flutter Compositions
- Core reactivity system powered by
alien_signals CompositionWidgetbase class for creating reactive widgets- Reactive primitives:
ref,computed,writableComputed,untracked - Side effect APIs:
watch,watchEffect - Lifecycle hooks:
onMounted,onUnmounted,onBuild - Dependency injection:
provide,inject,InjectionKey - Reactive props access via
widget() - Hot reload support:
setup()re-executes during hot reload to pick up code changes - Automatic hot reload state preservation:
ref()values are automatically preserved during hot reload based on their position insetup()- no manual configuration needed, similar to flutter_hooks - Built-in composables:
- Controllers:
useScrollController,useTextEditingController,usePageController,useFocusNode - Animations:
useAnimationController,useSingleTickerProvider,manageAnimation - Async:
useFuture,useAsyncData,useStream,useStreamController,useAsyncValue - Framework:
useContext,useAppLifecycleState,useSearchController - Listenable:
manageListenable,manageValueListenable,manageChangeNotifier
- Controllers:
- AsyncValue sealed class for type-safe async state handling with pattern matching
- Custom ref:
customRef,ReadonlyCustomRef ComputedBuilderwidget for using computed values in StatelessWidgetCompositionBuilderfor functional composition API- Comprehensive documentation and examples
- Complete test coverage for all core features
Documentation #
- Complete API documentation
- Beginner guide: Reactivity Fundamentals
- Migration guide: From StatefulWidget
- Technical deep dive for experienced engineers
- Comparison with Provider, Riverpod, BLoC, and flutter_hooks
- VitePress documentation site with i18n support
Development #
- Monorepo structure with Melos
- Custom lint rules package
- Comprehensive test coverage
- GitHub Actions CI/CD