puer 1.0.0 copy "puer: ^1.0.0" to clipboard
puer: ^1.0.0 copied to clipboard

A clean and predictable state management solution inspired by The Elm Architecture.

Puer #

Puer

Pub CI License: MIT


A clean and predictable state management solution inspired by The Elm Architecture.

Business logic as pure functions. Side effects as explicit data. Unidirectional data flow.


Features #

Pure update function(State, Message) → (State?, List<Effect>). Synchronous, deterministic, testable without mocks
Effects as data — Side effects are plain values. EffectHandlers execute them separately from logic
Unidirectional flow — One way to change state: send a Message
Full traceability — Every state change is caused by a message and recorded in transitions
Pure Dart — No Flutter dependency. Works in CLI tools, backend services, and Flutter apps
Time-travel ready — Drop-in support via puer_time_travel package


Quick example #

import 'package:puer/puer.dart';

// State: just data
final class CounterState {
  const CounterState({required this.count});
  final int count;
}

// Messages: sealed class for exhaustive handling
sealed class CounterMessage {}
final class Increment extends CounterMessage {}
final class Decrement extends CounterMessage {}

// Update: pure function (State, Message) → (State?, Effects)
Next<CounterState, Never> counterUpdate(
  CounterState state,
  CounterMessage message,
) =>
    switch (message) {
      Increment() => next(state: CounterState(count: state.count + 1)),
      Decrement() => next(state: CounterState(count: state.count - 1)),
    };

// Create feature and use it
void main() {
  final feature = Feature<CounterState, CounterMessage, Never>(
    initialState: const CounterState(count: 0),
    update: counterUpdate,
  );

  feature.init();

  print(feature.state.count); // 0
  feature.add(Increment());
  print(feature.state.count); // 1

  feature.dispose();
}

Core concepts #

Concept Description
State Immutable data representing your feature's current state
Message A value describing something that happened (event, intent)
Update Pure function (State, Message) → (State?, List<Effect>)
Effect Plain data describing a side effect to perform (HTTP call, storage, etc.)
EffectHandler Executes effects asynchronously and emits messages back
Feature Wires everything together: holds state, runs update, dispatches effects

Adding side effects #

When you need async work (HTTP, storage, timers), return an Effect from update and handle it separately:

// Effect types
sealed class CounterEffect {}
final class SaveCount extends CounterEffect {
  const SaveCount(this.count);
  final int count;
}

// Update now returns effects
Next<CounterState, CounterEffect> counterUpdate(
  CounterState state,
  CounterMessage message,
) =>
    switch (message) {
      Increment() => next(
          state: CounterState(count: state.count + 1),
          effects: [SaveCount(state.count + 1)],
        ),
      Decrement() => next(
          state: CounterState(count: state.count - 1),
          effects: [SaveCount(state.count - 1)],
        ),
    };

// Handler performs the actual async work
final class SaveCountHandler
    implements EffectHandler<CounterEffect, CounterMessage> {
  const SaveCountHandler(this._storage);
  final CounterStorage _storage;

  @override
  Future<void> call(
    CounterEffect effect,
    MsgEmitter<CounterMessage> emit,
  ) async {
    switch (effect) {
      case SaveCount(:final count):
        await _storage.saveCount(count);
        // Fire-and-forget: no message emitted back
    }
  }
}

// Register the handler
final feature = Feature<CounterState, CounterMessage, CounterEffect>(
  initialState: const CounterState(count: 0),
  update: counterUpdate,
  effectHandlers: [SaveCountHandler(storage)],
);

Key insight: update stays pure and testable. Async work happens in handlers, which are tested separately.


Packages #

Package Pub Description
puer pub package Core TEA implementation with Feature, update, and effect handlers. Pure Dart foundation.
puer_flutter pub package Flutter integration: FeatureProvider, FeatureBuilder, FeatureListener widgets.
puer_effect_handlers pub package Composable wrappers for debouncing, sequential execution, and isolate offloading.
puer_test pub package Testing utilities for concise update and handler tests. Add to dev_dependencies.
puer_time_travel pub package Time-travel debugging with DevTools extension. Use in debug builds to inspect history.

Why pure functions and explicit effects? #

Testability: Test your entire business logic with synchronous function calls. No mocks, no async coordination, no flakiness.

import 'package:puer_test/puer_test.dart';
import 'package:test/test.dart';

void main() {
  test('Increment increases count by 1', () {
    counterUpdate.test(
      state: const CounterState(count: 5),
      message: Increment(),
      expectedState: const CounterState(count: 6),
    );
  });
}

Traceability: Every state change is caused by a message. Your transitions stream shows the complete flow:

Transition {
  before: CounterState(count: 5),
  message: Increment(),
  after: CounterState(count: 6),
  effects: [SaveCount(6)]
}

Predictability: Given the same state and message, update always returns the same result. No hidden behavior, no surprises.


When to use puer #

Good fit:

  • Features with non-trivial business logic that must be unit-tested
  • Need for explicit, traceable, independently-testable side effects
  • Value in Elm/MVI mental model: one state, one way to change it
  • Time-travel debugging is valuable
  • Composable effect-execution policies (debounce, sequencing, etc.)

Not the right fit:

  • Small apps with minimal logic (overhead not worth it)
  • Teams new to Dart/Flutter (adds conceptual complexity)
  • Simple local UI state (use setState or ValueNotifier)

Learn more #


License #

MIT

5
likes
160
points
212
downloads

Documentation

API reference

Publisher

verified publishervorky.io

Weekly Downloads

A clean and predictable state management solution inspired by The Elm Architecture.

Homepage
Repository (GitHub)
View/report issues

Topics

#state-management #architecture #unidirectional-data-flow #mvi #tea

License

MIT (license)

Dependencies

meta

More

Packages that depend on puer