Coordinating Multiple States

AppStateGroup coordinates batch updates across multiple SimpleAppState and RefAppState instances.

When any member of the group calls batch, all members are batched together as a unit. UI listeners and state listeners are notified only once across the entire group.


The problem AppStateGroup solves

In larger applications, state is often split across multiple instances:

final appState = SimpleAppState();
final refState = RefAppState();

When updating both in response to a single user action, calling batch on one instance does not affect the other:

appState.batch(() {
  valueSlot.set(42);
  refSlot.set(newObject); // refState is not batching — notifies immediately
});

This causes UI listeners registered on refState to fire during the batch, breaking the “one action, one notification” guarantee.

AppStateGroup solves this by coordinating batch across all members.


Creating a group

Pass state instances to the AppStateGroup constructor:

final appState = SimpleAppState();
final refState = RefAppState();

final group = AppStateGroup([appState, refState]);

Or add them later:

final group = AppStateGroup();
appState.joinGroup(group);
refState.joinGroup(group);

Both styles produce the same result.


Batching across the group

Once states are in a group, calling batch on any member batches all members together:

appState.batch(() {
  valueSlot.set(42);
  refSlot.set(newObject);
});

// UI listeners on both appState and refState are notified once,
// after the batch completes.

You can also call batch directly on the group:

group.batch(() {
  valueSlot.set(42);
  refSlot.set(newObject);
});

Both forms are equivalent.


Subscriber ID deduplication across states

A widget may subscribe to slots from multiple states using the same subscriber ID.

This is common when a SlotStatefulWidget watches slots from both SimpleAppState and RefAppState.

Without a group, the widget’s rebuild callback would be triggered once per state.

With a group, the callback is triggered only once across all states, regardless of how many members are involved.

appState.addUIListener(valueSlot, 'widget_1', () => setState(() {}));
refState.addUIListener(refSlot,   'widget_1', () => setState(() {}));

group.batch(() {
  valueSlot.set(42);
  refSlot.set(newObject);
});

// setState is called only once, not twice.

Leaving a group

A state can leave its group at any time:

appState.leaveGroup();

After leaving, appState resumes independent batch processing.

The group continues to coordinate the remaining members.


Nested batches are safe

Nested calls to batch within a group behave correctly.

Only the outermost batch flushes notifications:

group.batch(() {
  group.batch(() {
    valueSlot.set(1);
  });
  valueSlot.set(2); // still inside the outer batch
});

// Notifications are flushed once, after the outer batch completes.

State listener behavior

Each member’s state listener is called independently after the batch completes.

appState.setStateListener((_) => fsm.push(appState));
refState.setStateListener((_) => fsm.push(refState));

group.batch(() {
  valueSlot.set(42);
  refSlot.set(newObject);
});

// Both listeners are called once each, after the batch.

Summary

AppStateGroup:

  • coordinates batch updates across multiple state instances

  • notifies each subscriber ID at most once per batch, across all members

  • supports any mix of SimpleAppState and RefAppState

  • preserves nested batch safety

  • allows states to join or leave the group at any time

Use AppStateGroup when a single user action updates slots from multiple state instances.