coralline 0.1.0
coralline: ^0.1.0 copied to clipboard
An elegant Dart library implementing Chain of Reactivity and Lazy-computation as a Pipeline.
Coralline #
"Don't write boilerplates. To handle complex reactive pipelines, all you have to do is call a line (Coralline)."
Stop tangling your logic wiresβa composable pipeline for reactive structures. Simply call a line.
Coralline is an elegant, zero-overhead Dart reactive pipeline engine based on CORAL (Chain of Reactivity And Lazy-computation). Designed to solve the computational flooding and verbosity of modern Flutter state management libraries, Coralline powers state management with the exact same architecture as Flutter's RenderObject rendering engine: Push-Dirty, Pull-Data.
π‘ Why Coralline? #
Traditional push-based reactive frameworks (e.g., standard Streams) continuously flood heavy data payloads downstream to every pipeline stage whenever upstream data mutates.
flowchart LR
A0["Data Change"] -->|"Compute & Push"| B0["Intermediate Stage 1"]
B0 -->|"Compute & Push"| C0["Intermediate Stage 2"]
C0 -->|"Compute & Push"| D0["UI Rebuild"]
(Intermediate computations & object allocations executed immediately on every mutation)
Coralline solves this problem with elegance:
Phase 1: Push-Dirty (COR: Chain of Reactivity)
flowchart LR
A1["CoralController"] -->|"Push-Dirty O(1)"| B1["CoralBroadcaster"]
B1 --> C1["Coral.diverge"]
C1 --> D1["Trunk.converge"]
D1 -->|"O(1) Invalidation"| E1["CoralTerminal.onDirty"]
E1 --> F1["UI Render Queue"]
(No data computation or allocation; only broadcasts an O(1) dirty flag down the Chain of Reactivity)
Phase 2: Pull-Data (AL: And Lazy-computation)
flowchart RL
F2["UI Render (Display)"] -->|"VSYNC Render Tick"| E2["CoralTerminal.snapshot"]
E2 --> D2["Trunk.converge"]
D2 -->|"Upstream Pull"| C2["Coral.diverge"]
C2 --> B2["CoralBroadcaster"]
B2 --> A2["CoralController"]
(Triggers backward Pull from Terminal up to Source ONLY during VSYNC render tick to compute exactly ONCE)
- Push-Dirty ($O(1)$ Invalidation): When a state source changes, Coralline broadcasts a lightweight, dirty flag down the DAG graph. No actual data is computed or pushed.
- Coalescing Buffer: Multiple synchronous state mutations within the same event loop are coalesced into a single invalidation frame, eliminating mid-frame layout thrashing.
- Pull-Data (Strictly Lazy Computation): Intermediate transformations (
map,derive,aggregate) are executed only when the UI Terminal explicitly requests.snapshotor.data. - Fasttrack Direct Pointer Bypass: Topology resolution is pre-cached ($O(1)$ direct jump), bypassing $O(d)$ parent-chain traversals during runtime state updates.
β‘ 15-Second Quick Start #
1. Counter Pipeline
import 'package:coralline/coralline.dart';
void main() {
// 1. Create a reactive controller (Source)
final controller = CoralController<int>(0);
// 2. Build a lazy transformation pipeline ("Call a line")
final doubleCoral = controller.coral.map((count) => count * 2);
// 3. Connect and activate a terminal (Destination)
late final CoralTerminal<int> terminal;
terminal = doubleCoral.toTerminal(() {
print('Doubled count is: ${terminal.data}');
});
terminal.activate();
// 4. Update data imperatively
controller.set(1); // Output: Doubled count is: 2
controller.set(5); // Output: Doubled count is: 10
}
2. Async Data & Pattern Matching
final searchController = CoralController<String>('');
// Async pipeline with cascade and toCoral()
final searchResults = searchController.coral.cascade((query) {
return fetchSearchResults(query).toCoral();
});
late final CoralTerminal<String> terminal;
terminal = searchResults.toTerminal(() {
switch (terminal.snapshot) {
case CoralSnapshotValid(:final data):
print('Results: $data');
case CoralSnapshotEmpty():
print('Type a search query...');
case CoralSnapshotDamaged(:final error):
print('Error loading results: $error');
}
});
terminal.activate();
π‘ Tip: Do I need to write a
switchstatement for every snapshot?
Not at all! For routine UI displays or fallback defaults, ergonomic accessors handle it in a single line:
- Fallback Default Value:
final count = terminal.snapshot.dataOrElse(() => 0);- Null Operator Convenience:
final text = terminal.snapshot.dataOrNull?.toString() ?? 'Loading...';- Direct Access on Guaranteed Sources:
final data = controller.coral.data;
π Coralline & Dart Stream (Complementary Architecture) #
"Can Coralline completely replace Dart
Streams?"
Answer: Coralline does not viewStreams as a target for total replacement. Instead, it advocates an elegant separation of concerns and complementary relationship:Streamfor low-level asynchronous event collection, andCorallinefor lazy UI state transformations and computation pipelines.
1. Architecture & Feature Comparison
| Dimension | Dart Stream |
Coralline |
|---|---|---|
| Reactive Paradigm | Eager Push Model (Heavy payloads emitted downstream immediately on mutation) |
Push-Dirty, Pull-Data ($O(1)$ Dirty flag push, synchronous lazy pull on demand) |
| Execution Timing | Eager Computation Intermediate map/where & allocations run immediately |
Lazy Computation Computed exactly ONCE when UI renders, cached until dirty |
| Sync vs Async | Async Event Loop Bound to microtask queue; 1-frame latency potential |
Synchronous Pure Computation Pipeline executes synchronously for 0ms UI frame rendering |
| Frame Coalescing | Manual Operators Required Requires debounceTime for multi-mutations in 1 frame |
Automatic Frame Coalescing 100 mutations in 1 frame computed exactly ONCE on render |
| State & Error | Separated Event + onErrorAmbiguous whether null means loading or data |
CoralSnapshot BoxingValid, Empty, Damaged containers with fail-fast safety |
| Multi-Source Union | Complex Stream Combination | Native Trunk.of([...]).converge(...) Support |
| 1:N Multicasting | BroadcastStreamManual listener tracking & cleanup required |
CoralBroadcasterSmart reference counting & Lazy Deactivation (zero hot-reload flicker) |
| Runtime Source Swapping | Requires Teardown & Re-subscribe | CoralCouplerRuntime Coldswap / Hotswap sockets preserving downstream |
π‘ Role Allocation Guide
- Areas Where Coralline Replaces Stream (Highly Recommended)
- UI State Management & Reactive Pipelines: Replaces legacy
StreamorStreamBuilderpipelines used for data transformation and UI feeding. - Multi-Source Combination & Form Validation: Business logic deriving final state from multiple fields or input streams.
- UI State Management & Reactive Pipelines: Replaces legacy
- Areas Where Stream is Retained alongside Coralline (Complementary)
- Low-Level Asynchronous Event Ingestion: Asynchronous event sources (WebSocket, hardware sensors, OS callbacks) are captured via
Streamand bound into Coralline via.toCoral().
- Low-Level Asynchronous Event Ingestion: Asynchronous event sources (WebSocket, hardware sensors, OS callbacks) are captured via
// Recommended Pattern: Capture events via Stream, bind to Coralline via .toCoral()
final Stream<Location> rawGpsStream = GpsSensor.stream();
// Automatically unsubscribes from Stream when active subscriber count drops to 0
final Coral<Location> locationCoral = rawGpsStream.toCoral();
// Build a lazy pipeline computed synchronously ONLY when UI requests a frame
final Coral<String> displayAddressCoral = locationCoral
.cascade((location) => AddressFormatter.format(location));
π§© Core Topologies #
Beyond simple 1:1 pipelines, Coralline provides first-class declarative support for complex reactive graph topologies:
1. N:1 Multi-Source Convergence (Trunk)
Bundles multiple independent upstream sources into a single computation. Regardless of how many times upstream sources mutate, lazy computation executes exactly once during UI rendering.
graph TD
A["Coral A"] --> T["Trunk.of([A, B])"]
B["Coral B"] --> T
T --> Conv["converge((prices) => A + B)"]
Conv --> Term["CoralTerminal (UI)"]
2. 1:N Multicast Sharing (CoralBroadcaster)
Eliminates redundant upstream computations when sharing a single source among multiple downstream consumers with automatic reference counting. Microtask-deferred lazyDeactivation prevents UI flicker during hot reloads.
graph TD
Source["Upstream Source (Coral.resource)"] --> Broadcaster["CoralBroadcaster"]
Broadcaster --> BranchA["Branch A"] --> TermA["UI Terminal A"]
Broadcaster --> BranchB["Branch B"] --> TermB["UI Terminal B"]
3. Dynamic Runtime Source Swapping (CoralCoupler)
Swaps upstream sources dynamically at runtime using Coldswap or Hotswap without tearing down or re-instantiating downstream UI widgets and pipelines.
graph TD
SourceA["Source A (Guest API)"] -. "couple()" .-> Coupler["CoralCoupler"]
SourceB["Source B (Auth API)"] ==> Coupler
Coupler ==> Pipe["Pipeline"] ==> Term["UI Terminal"]
π Feature Comparison Matrix #
| Dimension | BLoC (flutter_bloc) |
Riverpod | Signals (dart_signals) |
Coralline |
|---|---|---|---|---|
| Core Paradigm | Event Stream Transformer | Provider Dependency Tree | Fine-grained Reactive Signals | Chain of Reactivity & Lazy Pipeline (CORAL) |
| Reactivity Mode | Eager Stream Emission | Eager Invalidation & Re-compute | Eager Signal Effect Mutation | Push-Dirty ($O(1)$) + Strictly Lazy Pull-Data |
| Flutter Engine Harmony | Indirect (Stream-based) | Custom Container Tree | Atomic Variable Tracking | 100% Identical to Flutter RenderObject Pipeline |
| Topology Resolution | $O(d)$ Context Lookup | Container Element Tree | Dynamic Dependency Graph | $O(1)$ Fasttrack Direct Pointer Bypass |
| Boilerplate Cost | High (Events, States, Blocs) | Medium-High (Providers, Ref) | Low (Signal, Computed) | Ultra Low ("Just call a line") |
| Release Overhead | Stream allocation cost | Element tracking overhead | Graph tracking overhead | Zero-Overhead Assertions (0% Release Cost) |
| Error Handling | Exception in Stream | AsyncValue Enum |
Imperative Try/Catch | Struct-like CoralSnapshot<T> Boxing |
π― Architecture for Flutter & Dart Engineers #
Coralline was built from the ground up to align perfectly with Flutter's internal architecture:
- Pure Dart 3 Engine: Zero dependencies on
flutter/widgets.dartin the core engine. Runs seamlessly on Dart VM, Server, CLI, Web (Wasm), and Flutter. - Zero-Overhead Assertions: Invariant safety checks, intent firewalls, and cycle detection are wrapped in
assert(() { ... return true; }());. Release builds incur 0.00% runtime overhead. - Microtask Lazy Deactivation: Deactivation of shared broadcast pipelines is deferred to the end of the microtask queue, guaranteeing zero-flicker UI updates during hot-reloads and route switches.
π Comprehensive Documentation #
Explore our detailed manuals tailored for different audiences:
- π Quickstart & Core Concepts Guide - Build your first pipeline in 5 minutes.
- π Pipeline & Broadcasting Guide - Master 1:N sharing (
CoralBroadcaster), dynamic topology (CoralCoupler), and snapshots. - π Public API Reference Manual - Comprehensive catalog of all public Classes, Mixins, and Extensions.
- π¬ Flutter & Dart Engineering Whitepaper - Deep technical analysis of Push-Dirty Pull-Data, Fasttrack $O(1)$, and Wasm performance.
π License #
Coralline is open-source software licensed under the Apache 2.0 License.
Copyright 2023-2026 Youngjune Jeon All rights reserved.