Flutter · State Management

May 2026 · 12 min read

Orchestra: Rethinking State Management for Flutter Apps

What if business logic was truly stateless? What if events were first-class citizens rather than callbacks buried in widget trees? Orchestra proposes a strict separation of data, behavior, and events to keep Flutter architecture scalable.

By Ehsan Rashidi, Senior Medical Software Developer and Co-founder

I have shipped production Flutter apps for clinical tools, real-time monitoring systems, and complex ePRF platforms. In most projects, architecture that feels clean at week two becomes fragile by week twenty.

Provider, Riverpod, BLoC, and GetX are all strong tools. But for large products with layered business logic, branch-heavy state transitions, and interacting modules, many teams eventually blend data, logic, and event orchestration into hard-to-test flows.

"Most solutions couple the what (data), the how (logic), and the when (events). Orchestra enforces strict separation between all three."

The Problem at Scale

As apps grow, business logic leaks into widget callbacks, state holders become mixed with dependencies, and tests require deep provider setup. The issue is not API quality. The issue is weak architectural boundaries under delivery pressure.

Concern Common approach Orchestra approach
State Distributed across blocs/notifiers Typed Component<T> entities
Logic Mixed in widgets/providers Stateless System classes
Events Callbacks and ad-hoc streams First-class Event and DataEvent
Modules Global or nested scopes Self-contained Orchestration units

Core Concepts

These entities are data-only by design. Logic belongs to systems.

Systems: Where Behavior Lives

Systems are stateless executors operating on entities. No internal mutable fields. This makes behavior deterministic and easy to unit test.

Reactive example

class IncrementSystem extends ReactiveSystem {
  @override
  Set<Type> get reactsTo => {IncrementEvent};

  @override
  Set<Type> get interactsWith => {CounterComponent};

  @override
  void react() {
    final counter = get<CounterComponent>();
    counter.update(counter.value + 1);
  }
}

Lifecycle Coverage

Orchestra models a full feature lifecycle with dedicated system types:

Quick Start

The manual API makes architecture explicit; the Composer generator removes boilerplate. Both preserve strict typing and system isolation.

Install

# pubspec.yaml
dependencies:
  orchestra: ^1.0.0

dev_dependencies:
  orchestra_generator: ^1.0.0
  build_runner: ^2.0.0

Run generator

dart run build_runner build

Testing Benefits

Because systems are stateless and orchestrations are self-contained, tests stay focused: trigger event, assert component state. No widget tree scaffolding required for business logic verification.

test('IncrementSystem increments counter', () {
  final orchestrator = Orchestrator(
    orchestrations: {CounterOrchestration()},
  );

  orchestrator.get<IncrementEvent>().trigger();
  expect(orchestrator.get<CounterComponent>().value, 1);
});

Inspector DevTools

Orchestra includes an Inspector extension for Flutter DevTools, giving live visibility into entities, orchestration state, and system execution timing. This significantly shortens debugging loops in reactive workflows.

Why It Matters

In domains where architectural mistakes are expensive, explicit boundaries are not stylistic preferences. They are operational safeguards. Orchestra enforces those boundaries by type and lifecycle rather than convention alone.

"The constraint is the feature: when your framework separates data from logic, discipline stops depending on willpower."

Orchestra is available now on pub.dev, with source and documentation on GitHub.

← Back to blog