await NativeFlow.initialize(); // sync with native state; safe to repeat
await NativeFlow.attach(adapter); // RuntimeSession
await NativeFlow.start(const RuntimeOptions(
notification: ForegroundNotification(title: 'Online', body: 'Receiving rides',
actions: [NotificationAction(id: 'go_offline', label: 'Go offline')]),
persistent: true, // Android START_STICKY
restoreOnBoot: false, // opt-in reboot restore
));
NativeFlow.state; // RuntimeState
NativeFlow.states; // Stream<RuntimeState>
NativeFlow.sessions; // List<RuntimeSession>
await NativeFlow.detach('socket');
await NativeFlow.stop(); // stops adapters (reverse order) then the runtime; clears intentstart() and stop() complete only after the native side has really started
or stopped. On Android that means the service has entered the foreground, or
has been destroyed.
| State | Meaning |
|---|---|
stopped |
Not running, no recorded intent |
starting / stopping |
Transitional |
running |
Active |
interrupted |
The OS stopped it without the app asking (time limit, kill, refused restore). Intent is kept |
recovering |
Being restored after process recreation, relaunch or reboot |
state.isActive is true for running and recovering.
Each attached adapter has a RuntimeSession (state, states, attempts,
lastError).
idle ─▶ starting ─▶ running ◀─▶ paused
│ │
▼ ▼
recovering ◀─▶ waitingForNetwork
│
▼
failed (any) ─▶ stopping ─▶ stopped
Transitions are validated against a table. An illegal transition is a bug: it asserts in debug builds and is logged in release builds.
Flutter's default activity destroys its engine when the activity goes. The foreground service keeps the process alive, but your Dart sockets die with the engine. NativeFlow fixes this without fighting the OS:
- Register a
backgroundEntrypointininitialize. - When the runtime is active and no engine participates (UI swiped away,
process restarted by the OS, boot restore, a background task), NativeFlow
starts a background engine that runs it. Your entrypoint re-attaches the
same adapters; they receive
recover(runtimeRestored). - When the UI engine calls
initializeagain, NativeFlow asks the background engine toreleaseits adapters (≤ 3 s), destroys it, and only then answers the UI. The UI's adapters receiverecover(runtimeRestored). stop()destroys the background engine.
Adapters therefore run in exactly one engine at a time. NativeFlow.isBackgroundEngine
tells you which one you are in.
start() records intent and enables lifecycle coordination; there is no
service. See iOS setup → what iOS does.