blackbox 0.0.1
blackbox: ^0.0.1 copied to clipboard
BlackBox framework
Blackbox #
Blackbox is a small, explicit reactive computation core built around Boxes, a deterministic Graph scheduler, and a composable Flow. It is designed for cases where you want predictable recomputation, explicit dependencies, and testable business logic without UI coupling or implicit magic.
This package is intentionally low-level. It can be used standalone (CLI/backend) or as a foundation for application state management (including Flutter).
Why Blackbox #
Use Blackbox when you need:
- Deterministic recomputation (a clear dependency model, stable invariants)
- Explicit dependency wiring (no implicit context/watch trees)
- Strict error semantics (fail-fast by default, opt-in error handling)
- High testability (pure boxes, predictable scheduling)
- Zero UI assumptions (works equally well in Flutter, server, CLI)
Not a good fit if you want:
- an opinionated UI state manager out of the box,
- implicit reactivity with lots of reflection/magic,
- Redux-style global stores as the primary abstraction.
Installation #
dart pub add blackbox
Quick start #
1) A synchronous Box #
final class CounterBox extends Box<int> {
int _value = 0;
void inc() => signal(() => _value++);
@override
int compute() => _value;
}
final counter = CounterBox();
final cancel = counter.listen((o) {
print("counter=${o.value}");
});
counter.inc(); // prints: counter=1
counter.inc(); // prints: counter=2
cancel();
2) A Graph with dependencies #
final g = Graph();
final a = CounterBox();
final b = AddOneBox(); // BoxWithInput<int,int>
g.add(a);
g.addWithDependencies(
b,
dependencies: (d) => d.of(a), // <- explicit dependency
);
b.listen((o) => print("b=${o.value}"));
a.inc(); // b recomputes accordingly
3) A Flow that aggregates sources #
final flow = FlowBuilder<String>()
.on<int>(a, (v) => "a=$v")
.build(initial: "init");
final cancel = flow.listen(print); // prints: "a=0" (sync overrides initial)
cancel();
Concepts #
Output #
Every box exposes an Output (sync or async). Output is a state container: ready / loading / error.
SyncOutput<T>: always readyAsyncLoading<T>: not readyAsyncData<T>: readyAsyncError<T>: not ready (error state)
Output.valueis only valid whenOutput.isReady == true. OtherwiseStateErroris thrown (fail-fast by design).
Box #
A Box is a unit of computation that produces a value (O).
Box<O>: synchronous boxAsyncBox<O>: asynchronous boxBoxWithInput<I,O>/AsyncBoxWithInput<I,O>: boxes that depend on an input value
Boxes are intentionally small and testable. Treat compute() as your pure(ish) business function.
Graph #
A Graph wires boxes together. It owns scheduling and recomputation order.
g.add(sourceBox)registers a dependency source (no input)g.addWithDependencies(dependentBox, dependencies: ...)registers an input box and defines how to build its input
Invariants (important):
- Dependencies used via
DependencyResolver.of(x)must be registered in the graph (added earlier). - If a dependency is not ready,
DependencyResolver.ofthrowsStateErrorby default. - You may swallow errors via
onError, otherwise errors are fatal (re-thrown immediately).
Flow #
A Flow represents aggregated reactive state. It subscribes to sources and emits mapped values.
initialis a fallback, not a guaranteed “first emission”.- If a sync source already has a value, it overrides
initialimmediately (no “init flicker”).
Error handling #
By default, Graph is fail-fast: if a dependency is missing or not ready, it throws.
To explicitly handle transient dependency states (e.g. async loading), provide onError:
g.addWithDependencies(
dep,
dependencies: (d) => d.of(asyncBox),
onError: (e) => e is StateError, // swallow "not ready"
);
This makes the node resilient to temporary failures and allows recomputation later when dependencies become ready.
Pub.dev readiness checklist #
This repository layout usually scores well on pub.dev (pana) when you include:
README.md(this file) with usage examples and concept explanation ✅CHANGELOG.mdmentioning the current version ✅LICENSEfile ✅repository:(orhomepage:) field inpubspec.yaml✅dart analyzeclean (no warnings) ✅- good
environment:SDK constraints ✅
See doc/ for detailed Russian documentation and internals.
Documentation #
- Russian docs overview:
doc/README_RU.md - API:
doc/box_ru.mddoc/graph_ru.mddoc/flow_ru.md
- Internals (design notes):
doc/internals_ru.md - Testing guidance:
doc/testing_ru.md - FAQ:
doc/faq_ru.md
License #
MIT. See LICENSE.