Model–Update–View
dart_tui uses the same architecture as Elm and Bubble Tea: immutable state, a pure update function, and a pure view. This makes UIs predictable and trivially testable.
The loop
text
┌──────────────┐ Msg ┌──────────────┐
│ Model │ ──────▶ │ update │
│ (your state)│ │ (pure fn) │
└──────────────┘ └──────┬───────┘
▲ │ (Model, Cmd?)
│ ▼
│ ┌──────────────┐
└──── render ─── │ view │
│ (pure fn) │
└──────────────┘The pieces
| Concept | Description |
|---|---|
| Model | Immutable state. Implement init(), update(Msg) and view(). |
| Msg | A tagged event: key press, window resize, tick or custom data. |
| Cmd | FutureOr<Msg?> Function() — an async side-effect that delivers one message back. |
| View | Declared output string plus optional cursor position, mouse mode and window title. |
| Program | Owns the event loop, terminal raw mode, renderer and signal handling. |
update is a pure function: given the current model and a message, it returns a new model and an optional command. It never mutates state or touches the terminal directly — that's what makes it testable.
Returning a value (prompt-style)
A model can implement OutcomeModel<T> to return a value when it exits — the basis for prompts:
dart
abstract class OutcomeModel<T> implements Model {
T? get outcome; // non-null → program exits and returns this value
}
final String? result = await Program().runForResult<String>(MyPromptModel());Because
update is pure, you can unit-test your UI by feeding it messages and asserting on the returned model — no terminal required.