drip_flutter 0.5.1-alpha
drip_flutter: ^0.5.1-alpha copied to clipboard
Flutter render layer for DRIP — direct RenderObject binding with zero widget rebuilds.
0.5.1-alpha - 2026-05-16 #
Added #
DripSemantics: Accessibility bridge forDripReadable<T>. Synchronizes reactive values to the semantics tree with debounced updates.DripLifecycle: High-level widget for managingDripNodeorDripScopelifetimes withoutInheritedWidget. Enforces context-free injection.DripScope.asWidget(): Extension to easily bind a scope's disposal to a widget's lifecycle.
Deprecated #
DripNodeProvider,context.node(),DripRouteNode,DripList, andDripListVieware now deprecated.- Migration: Use
DripLifecyclefor node management andDripBuilderfor list updates.
Changed #
DripBinding: Now integrates withDripTraceto provide diagnostic context on render property updates.
0.5.0-alpha - 2026-05-16 #
Fixed #
Subscription Lifecycle — DripRenderParagraph (Risk 4 / CI-1.2)
- Root cause:
unbindState()was the only teardown path and always set_source = null. When Flutter reuses the sameRenderObjectinstance after unmounting a widget (same slot / same type),RenderObject.attach()fired on remount but_createBinding()returned early because_sourcewasnull— no new subscription was registered. - Fix: Split teardown into two methods:
detachBinding()— removes theDripBindingsubscription but preserves_source, soattach()can re-subscribe on remount. Called fromdidUnmountRenderObject.unbindState()— full teardown (clears both_bindingand_source). Called only when the source is replaced viabindState()or theRenderObjectis permanently disposed.
Test Timing — flutter_test two-pump pattern
- Root cause:
DripBatchschedules propagation viaFuture.microtask. Influtter_test'sFakeAsync, microtasks drain after the frame's build phase. Callingwrite()→ onepump()delivers the notification and callssetState, but the resulting dirty build is not picked up until the next frame. - Fix (
callback_identity_test.dart/scratch_test.dart): Changed all write-then-assert sequences to use two pumps after a reactive write:- First pump: microtask drains →
_onChangedfires →setStatemarks element dirty. - Second pump: Flutter builds the dirty element → widget remounts →
attach()→ subscription re-registered → listener count correct.
- First pump: microtask drains →
- Also added
await tester.pump()beforepumpWidget()in the unmount step so thewrite(false)notification is committed before the tree is replaced.
Internal #
- Diagnostic
printinstrumentation added toDripBuilder._onChanged,initState, anddisposeto trace the Flutter element lifecycle — removed after root cause confirmed. - All 113
drip_fluttertests pass. Zerodart analyzewarnings.
0.4.0-alpha #
Added #
DripBuilder<T>— general-purpose reactive builder widget. Accepts anyDripReadable<T>(DripState, DripComputed, or DripAsync) and rebuilds its subtree when the value changes. Scoped setState — only the builder's own subtree rebuilds.DripSelect— multi-source reactive builder. Uses an internalDripComputedto combine multiple reactive sources. Rebuilds only when the combined output changes (version-clock and equality checked). Supports Dart 3 record types as combined value.DripAsyncBuilder<T>— async state widget with exhaustive sealed-class switching. Providesloading,data, anderrorbuilder callbacks. BothloadinganderrorreceivepreviousDatafor continuity patterns. Sensible defaults for unimplemented callbacks (debug warnings included).DripAsyncNodemixin — addsasyncState<T>(),asyncFromFuture(), andasyncFromStream()toDripNodesubclasses. All async states are automatically scoped to the node'sDripScope.
Dependencies #
drip_coreconstraint updated to^0.2.0-alpha
0.3.0-alpha - 2026-05-15 #
Added — Node System #
DripNode— abstract feature module with an ownedDripScope. Extend to create a named, scoped business-logic unit. All state, computed values, and effects are automatically disposed when the node is disposed.DripNode.register<T>/DripNode.resolve<T>— scoped dependency injection. Singleton by default; no global service locators.DripNodelifecycle:onInit,onDispose,onBackground,onForeground.DripNodeProvider<N>—StatefulWidget+InheritedWidgetthat creates a node on mount, forwards app-lifecycle events, and disposes it on unmount. Uses aBuilderchild to ensure the context passed tobuildersits below theInheritedWidgetsocontext.node<N>()resolves correctly.DripRouteNode—DripNodesubclass with route-lifecycle hooks (onRouteEnter,onRouteLeave). Integrates with Flutter'sRouteObserver.BuildContext.node<N>()/BuildContext.maybeNode<N>()— ergonomic node lookup extensions.
Added — List System #
DripList<T>— reactive list with item-level subscriber granularity. Updating indexinotifies only the subscriber registered for indexi.DripListView<T>— list widget that rebuilds only the tile at the changed index. Structural changes (add/remove) trigger a minimal list-level rebuild.
Fixed #
DripNodeProvider.of()now injects the actual runtime type name$Nin the missing-provider error message (was$Nas a literal string due to escaped interpolation).DripNodeProvider.buildercontext now correctly resolves its ownInheritedWidgetby wrapping the child in aBuilder.
Architecture #
DripValue<T>interface introduced indrip_core— shared readable/subscribable contract implemented by bothDripState<T>andDripComputed<T>. All Flutter render widgets now acceptDripValue<T>, allowing computed values to be passed directly without casting.DripListener/ListenerSubscribermoved todrip_state_base.dartto be accessible package-wide without importing implementation files.DripNodeis pure Dart — fully unit-testable without a Flutter widget tree.
Testing (drip_flutter) #
drip_node_test.dart— 20+ tests coveringDripNodelifecycle,register/resolve, effects, and disposal.drip_node_provider_test.dart— 8 tests covering mount/unmount, lifecycle forwarding,Provider.of,context.node, error messages, and nested providers.drip_route_node_test.dart— route lifecycle integration tests.drip_list_test.dart— 10 tests coveringadd,removeAt,[]=,insert,replaceAll,update,dispose.drip_list_view_test.dart— 8 tests including a 10,000-item single-tile-rebuild benchmark.
0.2.0-alpha - 2026-05-13 #
First release of drip_flutter — the Flutter direct render binding layer.
Added #
DripBinding<T>— liveRenderObjectsubscriber. Applies state changes directly to render properties, bypassing the widget/element rebuild cycle.DripText— zero-rebuild text widget. BindsDripValue<String>toRenderParagraph.textviamarkNeedsLayout().DripOpacity— zero-rebuild opacity widget. BindsDripValue<double>(clamped[0.0, 1.0]) viamarkNeedsPaint().DripColor— zero-rebuild background color binding viamarkNeedsPaint().DripTransform— zero-rebuildMatrix4transform binding viamarkNeedsPaint().DripImage—ImageProviderbinding with async image resolution.DripCustomBinding<T>— abstract base class for customRenderObjectbindings.DripFrame<T>/DripFrameBuilder<T>— controlled rebuild boundary for deliberate structural updates.
Performance #
- Verified: 1,000 simultaneous
DripStatewrites → 0 widgetbuild()calls. - Demo app
demo_grid: 1,000-cell live grid at 60 fps, zero rebuilds.
Architecture #
- Invariant 2 enforced: zero
setState()calls in binding code path. - Invariant 7 enforced: all bindings deregistered in
didUnmountRenderObject.