levit_reactive 0.0.11
levit_reactive: ^0.0.11 copied to clipboard
Fine-grained reactive primitives for deterministic updates and derived state.
levit_reactive #
Purpose & Scope #
levit_reactive is Levit's pure Dart reactive runtime.
This package is responsible for:
- Mutable and immutable reactive values.
- Derived state (
LxComputed, async computed variants). - Side-effect workers (
LxWorkerfamily). - Keyed reactive resources (
LxFamily). - Deterministic propagation and batch semantics.
- Reactive middleware interception.
This package does not include:
- Dependency injection and lifecycle container semantics (
levit_scope). - Flutter widget bindings (
levit_flutter_core,levit_flutter).
Conceptual Overview #
Reactive objects expose values and notify observers. Observers subscribe implicitly (through dependency tracking) or explicitly (listeners/workers).
Core abstractions:
.lxextensions create reactive sources.LxComputeddefines derived values from dependencies.LxWorkerruns side-effects when dependencies change.Lx.batch/Lx.batchAsynccoalesce propagation.
Getting Started #
dependencies:
levit_reactive: ^latest
import 'package:levit_reactive/levit_reactive.dart';
void main() {
final count = 0.lx;
final doubled = LxComputed(() => count() * 2);
final worker = LxWorker(doubled, (value) {
print('doubled=$value');
});
count(1);
count(2);
worker.close();
doubled.close();
count.close();
}
Async Concurrency #
Async computed values and workers accept LxAsyncConcurrency:
latestkeeps the existing behavior: invalidations may overlap and only the latest result is published.exhaustLatestruns one computation at a time and coalesces any changes that arrive while it runs into exactly one trailing computation.
final results = LxComputed.async(
() => search(query()),
concurrency: LxAsyncConcurrency.exhaustLatest,
);
Re-bindable Streams #
LxStreamCompletionPolicy.close remains the default. Use retain when a
completed source should preserve its last status and be explicitly rebound:
final updates = LxStream<int>.idle(
completionPolicy: LxStreamCompletionPolicy.retain,
);
updates.restartDeferred(() => repository.watchUpdates());
Keyed Reactive Families #
LxFamily<K, R> lazily creates and caches one reactive resource per key:
final products = LxFamily<String, LxStream<Product>>(
(sku) => LxStream.defer(() => repository.watchProduct(sku)),
name: 'products',
eviction: const LxFamilyEviction.whenInactive(
gracePeriod: Duration(seconds: 30),
),
);
final selectedProduct = products(selectedSku);
products.invalidate(selectedSku);
products.close();
Raw keys are hashed in diagnostic names unless an application-controlled
debugKey is supplied, so high-cardinality or sensitive key values are not
exported by default.
Middleware Lifecycle (Token-Based) #
import 'package:levit_reactive/levit_reactive.dart';
const historyToken = #state_history;
void configure() {
Lx.addMiddleware(LevitReactiveHistoryMiddleware(), token: historyToken);
}
void teardown() {
Lx.removeMiddlewareByToken(historyToken);
}
Design Principles #
- Ordered, deterministic propagation.
- Fine-grained dependency tracking.
- Explicit lifecycle closure for long-lived resources.
- Predictable middleware interception.