DevStack class

A long-lived external thing whose lifecycle is delegated to project commands and whose state is polled — the docker stack, the emulator suite, the database container the app talks to in development.

It owns nothing. flutterware runs probe to find out what is going on and runs start / stop when told to; the project's own CLI stays the authority on what those mean. That is the whole difference from a supervisor, and it is why a stack brought up in a terminal, by a teammate's script or by this plugin all read identically.

fw.use(DevStack.background(
  workingDirectory: 'packages/server',
  // Not `docker compose ps --quiet` on its own: that exits 0 whether or
  // not anything is up. And in a worktree, even this answers about
  // whichever compose project the working directory resolves to — which
  // is the checkout next door if this one has never come up. See
  // [Probe.exitCode], and prefer a [Probe.json] script in a monorepo.
  probe: Probe.exitCode(StackRun.command([
    'sh',
    '-c',
    'test -n "$(docker compose ps --quiet --status running)"',
  ])),
  start: StackRun.command(['docker', 'compose', 'up', '--wait']),
  stop:  StackRun.command(['docker', 'compose', 'down', '--volumes']),
  stopIsDestructive: true,
));

A project whose stack is behind its own CLI declares the same thing as scripts, and then nothing here names an executable at all:

fw.use(DevStack.background(
  workingDirectory: 'packages/server',
  probe: Probe.json(StackRun.script('tool/local_env.dart',
      args: ['status', '--json'])),
  start: StackRun.script('tool/local_env.dart', args: ['up']),
  stop:  StackRun.script('tool/local_env.dart', args: ['down']),
  stopIsDestructive: true,
));

Named .background for what it requires of the tool, not for how this is implemented: the command must return, leaving something running behind it. A tool you stop with Ctrl-C — firebase emulators:start, tilt up, ngrok http — cannot be declared this way, because there is no stop to name and nothing to ask whether it is up. That is a second constructor, .foreground, which is designed but not built: no project has needed it yet, and its readiness check wants a real tool to be designed against. The constructor is named now so that adding it is an addition rather than a rename.

One per project. A second stack needs an id the registry can resolve, and v1's registry is keyed on the exact id.

Inheritance

Constructors

DevStack.background({required Probe probe, StackRun? start, StackRun? stop, String? workingDirectory, Duration poll = const Duration(seconds: 10), Duration commandTimeout = const Duration(minutes: 10), bool stopIsDestructive = false, List<StackCommand> commands = const [], String? label})

Properties

commands → List<StackCommand>
Everything else the stack's CLI can do: logs, restart, recreate, prune.
final
commandTimeout → Duration
How long to wait for start, stop or a StackCommand before giving up on it. StackCommand.timeout overrides this per command.
final
config → Map<String, Object?>
Per-instance configuration, handed to the GUI-side implementation. Must be JSON-encodable — it crosses a process boundary.
no setteroverride
hashCode → int
The hash code for this object.
no setterinherited
id → String
Stable and globally unique — flutterware.scenarios, acme.deploy. The GUI resolves its native implementation by this string.
finalinherited
label → String
What the sidebar row says. Defaults to the last dotted segment of id.
finalinherited
poll → Duration
How often to re-run probe while something is watching.
final
probe → Probe
How to find out what state the stack is in.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
start → StackRun?
Brings it up. Null for a stack this machine only observes — a shared server, a system postgres — which is a complete declaration and gets a panel with a status and no controls.
final
stop → StackRun?
Takes it down. Null has the same meaning as a null start.
final
stopIsDestructive → bool
stop destroys data — down --volumes drops the database. Renderers make the control distinct and ask first.
final
workingDirectory → String?
Where the commands run, relative to the worktree root. The worktree root itself when null.
final

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toJson() → Map<String, Object?>
inherited
toString() → String
A string representation of this object.
inherited

Operators

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