Slot Boundaries and Ownership¶
This chapter explains how slot boundaries define the ownership, responsibility, and allowed state access of a widget in SimpleAppState.
In team-based development, this is not just a technical detail — it is a contract between designers, implementers, and reviewers.
What is a slot boundary?¶
A slot boundary is the set of StateSlot objects that a widget is allowed to depend on.
In SimpleAppState, this boundary is declared explicitly by SlotStatefulWidget.slots.
class UserScreen extends SlotStatefulWidget {
const UserScreen({super.key});
@override
List<StateSlot> get slots => [
userSlot,
settingsSlot,
];
@override
SlotState<UserScreen> createState() => _UserScreenState();
}
This declaration means:
This screen is allowed to read userSlot and settingsSlot
Only these slots will trigger rebuilds
No other application state should influence this widget
The boundary is structural, not accidental.
Why explicit boundaries matter¶
In many state management systems, widgets can freely access any state at any time.
This makes it easy to start coding, but difficult to answer later:
What state does this screen really depend on?
Why does it rebuild?
Is this widget coupled to something it should not be?
SimpleAppState answers these questions up front by forcing the slot boundary to be declared explicitly.
This turns an implicit dependency into a visible design decision.
Ownership and responsibility¶
Slot boundaries also define ownership.
When a widget declares:
List<StateSlot> get slots => [ userSlot, settingsSlot ];
it is making a statement:
“This widget owns the responsibility of reacting to these states”
“These states are part of this widget’s contract”
Everything outside this list is, by design, out of scope.
Delegating work safely¶
This model works especially well when work is split between a manager (or architect) and implementers.
A typical delegation might be:
“Implement UserScreen”
“You may use userSlot and settingsSlot”
“Do not depend on any other application state”
Because the boundary is declared in slots, the implementer does not have to guess what is allowed.
Inside the state class, they can focus purely on UI:
class _UserScreenState extends SlotState<UserScreen> {
@override
Widget build(BuildContext context) {
final user = userSlot.get();
final settings = settingsSlot.get();
return Column(
children: [
Text(user.name),
Switch(
value: settings.darkMode,
onChanged: (v) {
settingsSlot.update(
settings.copyWith(darkMode: v),
);
},
),
],
);
}
}
Even if the implementer is not familiar with the rest of the application, they cannot accidentally subscribe to unrelated state.
Preventing accidental coupling¶
Some state-management approaches allow widgets to dynamically subscribe to state inside build methods (often called “watch”-style APIs).
While convenient, this often leads to:
Dependencies scattered across the widget tree
Unclear rebuild triggers
Difficult code reviews
Accidental coupling to unrelated state
By contrast, SlotStatefulWidget requires all slot dependencies to be declared in one place.
This makes the widget’s relationship to application state easy to see, easy to review, and easy to reason about.
A deliberate design constraint¶
Requiring explicit slot boundaries may feel restrictive.
However, this restriction acts as a guardrail.
It encourages:
Clear ownership of state
Predictable rebuild behavior
Safer task delegation
Long-term maintainability in large teams
In SimpleAppState, some convenience is intentionally traded for clarity and stability.
Slot boundaries are one of the most important examples of this.