Model class abstract TUI

Abstract interface for TUI application models.

The Model represents the state of a TUI application and defines the core functions of the Elm Architecture:

  • init - Returns an optional command to run on startup
  • update - Handles messages and returns new state + optional command
  • view - Renders the current state as a string

The TUI runtime follows The Elm Architecture (TEA) pattern, which separates state, logic, and presentation:

  • Model: The state of your application. It should be immutable.
  • Update: A pure function that takes a Msg and the current Model, and returns a new Model and an optional Cmd.
  • View: A pure function that takes the Model and returns a String (or a View object for advanced metadata) representing the UI.
┌─────────────────────────────────────────────────────┐
│                     Program                          │
│                                                      │
│    ┌───────┐     ┌────────┐     ┌──────┐            │
│    │ Model │────▶│ update │────▶│ view │            │
│    └───────┘     └────────┘     └──────┘            │
│        ▲              │              │               │
│        │              │              ▼               │
│        │         ┌────────┐     ┌────────┐          │
│        └─────────│  Cmd   │     │ Screen │          │
│                  └────────┘     └────────┘          │
│                       │                              │
│                       ▼                              │
│                  ┌────────┐                          │
│                  │  Msg   │◀──── User Input          │
│                  └────────┘                          │
└─────────────────────────────────────────────────────┘

Example: Counter

class CounterModel implements Model {
  final int count;
  CounterModel([this.count = 0]);

  @override
  Cmd? init() => null; // No initialization needed

  @override
  (Model, Cmd?) update(Msg msg) {
    return switch (msg) {
      KeyMsg(key: Key(type: KeyType.up)) =>
        (CounterModel(count + 1), null),
      KeyMsg(key: Key(type: KeyType.down)) =>
        (CounterModel(count - 1), null),
      KeyMsg(key: Key(type: KeyType.runes, runes: [0x71])) => // 'q'
        (this, Cmd.quit()),
      _ => (this, null),
    };
  }

  @override
  String view() => '''
Counter: $count

Press ↑/↓ to change, q to quit
''';
}

Example: Async Data Loading

class DataModel implements Model {
  final bool loading;
  final List<String> items;
  final String? error;

  DataModel({this.loading = false, this.items = const [], this.error});

  @override
  Cmd? init() => Cmd.perform(
    () => fetchItems(),
    onSuccess: (items) => ItemsLoadedMsg(items),
    onError: (e, _) => ErrorMsg(e.toString()),
  );

  @override
  (Model, Cmd?) update(Msg msg) {
    return switch (msg) {
      ItemsLoadedMsg(:final items) =>
        (DataModel(items: items), null),
      ErrorMsg(:final message) =>
        (DataModel(error: message), null),
      KeyMsg(key: Key(type: KeyType.runes, runes: [0x72])) => // 'r' to refresh
        (DataModel(loading: true), init()),
      _ => (this, null),
    };
  }

  @override
  String view() {
    if (loading) return 'Loading...';
    if (error != null) return 'Error: $error\n\nPress r to retry';
    return items.map((i) => '• $i').join('\n');
  }
}
Implementers

Constructors

Model()
const

Properties

hashCode int
The hash code for this object.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

init() Cmd?
Returns an optional command to execute on program startup.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited
update(Msg msg) → (Model, Cmd?)
Handles a message and returns the new model state and optional command.
view() Object
Renders the current model state for display.

Operators

operator ==(Object other) bool
The equality operator.
inherited