Skip to content

Introduction

What is Flutter Compositions?

Flutter Compositions is a reactive framework for Flutter inspired by Vue's Composition API. It replaces StatefulWidget boilerplate with a declarative, signal-based programming model built on alien_signals.

At its core, you write a setup() method that runs once, declare reactive state with ref(), derive values with computed(), and return a builder function that automatically re-runs when dependencies change:

dart
class Counter extends CompositionWidget {
  const Counter({super.key});

  @override
  Widget Function(BuildContext) setup() {
    final count = ref(0);
    final doubled = computed(() => count.value * 2);

    return (context) => Column(
      children: [
        Text('Count: ${count.value}'),
        Text('Doubled: ${doubled.value}'),
        ElevatedButton(
          onPressed: () => count.value++,
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

No State class. No setState(). No manual dispose(). Just reactive state and a builder.

Why Choose Flutter Compositions?

1. Fine-Grained Reactivity

Backed by alien_signals, reading .value registers dependencies automatically. Only the widgets that touch a modified ref rebuild — no unnecessary subtree reconstructions.

2. Vue-like Developer Experience

APIs (ref(), computed(), watch()) mirror the Vue Composition API, lowering the learning curve for frontend developers. setup() centralizes state initialization, computed values, and lifecycle hooks.

3. Lifecycle and Resource Management

FeatureWhat it does
onMounted / onUnmounted / onBuildDeclarative lifecycle hooks
useScrollController, useAnimationController, …Auto-dispose controllers
Effect cleanup via effectScopeNo forgotten subscriptions

4. Built-in Type-Safe Dependency Injection

InjectionKey<T> powers provide / inject — pass Ref<T> or any object while staying reactive and type-safe. Works out of the box, yet remains compatible with Riverpod, GetIt, and other DI solutions.

5. Hot Reload & State Preservation

Each Ref keeps a stable position inside setup(). As long as the declaration order stays intact, hot reload preserves state. Builders rely on reactive signals, so only affected areas refresh after a reload.

How Does It Compare?

vs. StatefulWidget

AspectStatefulWidgetFlutter Compositions
State declarationSeparate State classInline in setup()
RebuildssetState() rebuilds entire subtreeOnly refs that changed
Prop changesManual didUpdateWidgetAutomatic via widget()
ControllersManual dispose()Auto-disposed use* helpers

See the full migration guide.

vs. flutter_hooks

AspectFlutter Compositionsflutter_hooks
Mental modelVue Composition APIReact Hooks
ReactivityFine-grained signalsFull build re-run
DIBuilt-in provide/injectExternal packages
LifecycleonMounted, onUnmounteduseEffect callbacks

vs. Vue Composition API

AspectFlutter CompositionsVue
RenderingFlutter widget treeVirtual DOM / templates
Reactivity enginealien_signals (explicit refs)Proxy-based (deep by default)
Propswidget() returns ComputedRefProps are reactive proxies
DI keysInjectionKey<T> (compile-time safe)String/Symbol keys

When It Shines

  • You want a development model that feels like Vue Composition API.
  • Performance matters and you need to avoid unnecessary rebuilds.
  • Controllers, subscriptions, or effects must be consistently disposed.
  • You value type-safe dependency injection without extra packages.
  • The project targets multiple platforms but you want a unified reactive style.

When to Think Twice

  • The app is tiny and setState already covers your needs.
  • The team is deeply invested in flutter_hooks, BLoC, or Redux with heavy custom tooling.
  • You prefer external/global state managers to drive the entire app.

Next Steps

Released under the MIT License.