soroq_flutter

soroq_flutter is the Flutter-facing plugin for Soroq.

It bundles the Dart runtime bridge, the Android plugin/bootstrap layer, and the native startup/update wiring that Soroq uses on Android. On iOS, the public plugin is limited to config/data OTA storage and reporting.

What It Includes

  • a reusable Dart API for the soroq/engine_runtime method channel
  • runtime info, cold-start state, and auto-update state models
  • Android startup wiring through a manifest-merged init provider
  • native startup/update coordinators and a bundled arm64 Android JNI runtime
  • iOS config/data OTA snapshot storage through the Flutter method channel
  • re-exports of the pure Dart soroq_sdk models and client

For the smallest public onboarding flow, start here:

For Android build-stack readiness, use the repo preflight:

source ../../scripts/engine_env.sh
../../scripts/check_soroq_android_toolchain.sh

The current public-alpha lane is documented in docs/android-toolchain-readiness.md.

Installation

dependencies:
  soroq_flutter: ^0.1.16

This is the latest local package target in this checkout. App teams should use flutter pub add soroq_flutter so pub.dev resolves the latest published plugin until the staged train is published. Repository maintainers should run make package-release-train-status and follow its next_step for the immediate publish/proof action; use publish_plan only when the train is staged for release.

Basic Setup

Add a project-level soroq.yaml:

app_id: com.example.app
channel: stable

Import the package from Dart:

import 'package:soroq_flutter/soroq_flutter.dart';

Then use SoroqEngineRuntimeBridge to inspect runtime state, read startup results, and configure auto-update behavior.

Device Patch Status

Apps and dashboards should avoid vague ready or live labels by themselves. Read SoroqEngineRuntimeBridge.getAutoUpdateState() and display:

  • state.devicePatchLabel for the lifecycle phase
  • state.devicePatchIdentityLabel for the concrete patch number and id
  • state.devicePatchDetailLabel for the operator-facing explanation

For example, an active patch can render as Active patch #13 on this device with identity Patch #13 (patch-live-13). A staged patch can render as Staged patch #14 - restart required with identity Patch #14 (patch-staged-14).

For operator handoffs that reconcile hosted patch identity with device state, use docs/operator-patch-identity-handoff.md.

The public CLI flow around this package is now:

soroq init --app-id com.example.app
soroq status --check
soroq app create --name "Example App" --if-not-exists
soroq app list
soroq app status
soroq inspect android --artifact /absolute/path/to/app-release.aab
soroq release android
soroq release list --app-id com.example.app
soroq release status --release-id my-release
soroq patch android
soroq preview android
soroq patch config --config-file /absolute/path/to/config.json --release-id my-release
soroq patch list --app-id com.example.app --channel stable
soroq patch status --patch-id my-patch
soroq patch health --patch-id my-patch
soroq patch rollout --patch-id my-patch --percent 25
soroq patches set-track --patch-id my-patch --track stable
soroq rollback --patch-id my-patch --verify

soroq status should show release ready: yes and patch ready: yes before you run the release or patch commands. Those commands use the same local project preflight and will stop early if app_id, channel, or the package dependency shape is invalid. Add --check to make that readiness check fail CI or local scripts until the project is ready.

The short release command runs flutter build appbundle --release, discovers the Android artifact from the usual Flutter output directories or release-candidates/, then records an immutable copy under .soroq/releases/.... The short patch command runs the same release build, uses that recorded base, and discovers the newest compatible candidate artifact. Explicit --artifact, --base-artifact, --candidate-artifact, --release-id, and --build=false remain available for CI and unusual build layouts. Flutter build flags can be passed after --, for example soroq patch android -- --flavor internal --dart-define=API_ENV=prod.

soroq preview android gives you a Shorebird-style preflight: it resolves the hosted Android release, verifies the downloaded release artifact metadata, runs runtime patch-check, can download the signed patch artifacts, and can install or launch the release on an explicitly selected Android device or emulator. For staged rollout, use soroq patch rollout --percent N. For Shorebird-style tracks, publish or preview with --track staging or a custom track such as --track beta, then promote with soroq patches set-track --patch-id <patch-id> --track stable.

Config Patches

Because this package re-exports soroq_sdk, Flutter apps can consume hosted JSON config patches without manually reading the patch bundle. For apps that want persistence and boot-report receipts, use SoroqConfigOtaController:

final client = SoroqControlPlaneClient(
  baseUrl: Uri.parse('https://api.example.com'),
);
final controller = SoroqConfigOtaController(
  client: client,
  request: const SoroqPatchCheckRequest(
    appId: 'com.example.app',
    runtimeId: 'runtime-fingerprint',
    currentPatchNumber: 0,
    channel: 'stable',
    clientId: 'example-client',
  ),
  store: const SoroqMethodChannelConfigOtaStore(),
  validator: (patch) {
    return patch.config['enabled'] is bool ? null : 'enabled must be a bool';
  },
);

final result = await controller.checkAndApply();
if (result.status == SoroqConfigOtaStatus.accepted) {
  final config = result.snapshot!.config;
  // Apply values through behavior already present in the app binary.
}

Config patches are download_only: Soroq delivers and parses the payload, then your app chooses how to validate and apply it. On iOS, the public plugin provides the same soroq/engine_runtime channel name for compatibility, but it does not bundle or link an iOS runtime patching framework. Its native implementation is scoped to config/data OTA state and snapshot storage. The executable/runtime Android OTA methods return ios_code_ota_unavailable on iOS by design.

For a runnable iOS consumer, see examples/soroq_ios_config_harness.

What Is Proven Today

  • Android cold-start bootstrap through the shared plugin/runtime layer
  • hosted patch-check and boot-report flows
  • staged asset/config OTA on the Android public-alpha lane
  • hosted release/AOT code patching for supported Soroq-compatible Android artifacts
  • packaged Android runtime JNI libraries for armeabi-v7a, arm64-v8a, and x86_64
  • iOS config/data OTA consumer support through the public Flutter plugin
  • runtime_managed_dart protocol/model support for the continuing research lane; this is not a public iOS App Store/TestFlight claim

Compatibility Truth

This package is not a claim of universal stock-Flutter compatibility yet.

The current real OTA lanes are validated against a Soroq-compatible pinned Flutter toolchain, including tracked Flutter framework/engine patch seams in this repository. If a team is on a different Flutter version, they should use an explicitly supported Soroq toolchain release rather than assuming arbitrary cross-version compatibility.

In practical terms:

  • the pure Dart control-plane layer is easier to share broadly
  • the Android plugin/runtime layer is more version-sensitive
  • the full OTA shipping path currently depends on Soroq-maintained Flutter fork/patch alignment

Status

This package is part of the current Soroq Android public alpha. It is real and proven on Android, but the broader product is still being hardened toward a more Shorebird-like developer experience. The public package should be described as Android asset/config OTA plus supported Android release/AOT code patching, with iOS config/data OTA only. Arbitrary stock-Flutter version support is tracked through the Soroq compatibility matrix instead of being assumed automatically for every Flutter toolchain.

Published Consumer Proof

Repository maintainers can verify from the repository root that pub.dev consumers are not relying on local path dependencies:

make package-post-publish-proof

That target checks published package versions and archive freshness, then creates fresh Dart and Flutter apps, installs soroq_sdk and soroq_flutter from pub.dev, imports the public APIs, runs Flutter analysis, builds an Android debug APK from the published plugin, and finishes the non-device public-alpha readiness proof.

Because it installs from pub.dev and finishes the public-alpha readiness proof, it requires network access to pub.dev and the hosted Soroq public-alpha services after both packages are published.

Local Repo Development

This repository may use a local pubspec_overrides.yaml while developing both packages together. Published consumers should depend on the pub.dev package versions, not the local override.

The published Android plugin bundles the arm64 Soroq runtime JNI library so a fresh Flutter app does not need this repository's runtime/ checkout just to build. Repository maintainers can force a local Rust rebuild with SOROQ_BUILD_RUST_JNI=1. The Gradle rebuild path remaps local filesystem prefixes out of Rust diagnostics so regenerated JNI artifacts do not leak developer machine paths into the package.

Internal Proof Helpers

For repository-local validation, the repo still includes the existing proof helpers:

source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_app.sh
source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_aot_code_patch.sh
source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_aot_rollback.sh

Libraries

soroq_flutter