flutter_ota_kit
Ship Dart & asset hotfixes over the air. Your backend. No store review. No monthly bill.
flutter_ota_kit is open-source code push for Flutter Android. Push a bug fix or a new screen, and your users get it on the next cold start — no Play Store round-trip, no vendor lock-in, no per-seat pricing. The patch lives on your infrastructure (Supabase, Postgres, Cloudflare, AWS S3, PocketBase, or your own CDN), signed and integrity-checked, with automatic crash rollback if anything goes wrong.
If you've used Shorebird, CodePush, or Expo EAS Update, this is the same idea for Flutter Android — MIT-licensed, self-hosted, and yours to keep.

dependencies:
flutter_ota_kit: ^0.2.0 # one package — every backend included
v0.2.0 is a single package. Everything — the device SDK plus all five backends — now ships inside
flutter_ota_kit. There are no moreflutter_ota_kit_supabase/_postgres/_cloudflare/_aws/_pocketbasepackages to add. One import, one dependency.
Why teams pick it
- You own the pipeline. Patches sit in your Supabase project, your S3 bucket, your Cloudflare account — not someone else's cloud. No third party sees your code or your users.
- Zero recurring cost. MIT-licensed. The only bill is your own storage, which for OTA payloads is pennies.
- Ships in one dependency. Add
flutter_ota_kit, runflutter-ota init, deploy. Five backends are built in; you configure the one you use. - Safe by default. MD5 + Ed25519 verification, automatic crash rollback, a bad-patch blacklist, and staged rollouts — all on by default.
- Zero-code forced-update UI. Opt in and the SDK renders a polished full-screen install progress screen for you. Write no UI.
How it compares
| flutter_ota_kit | Shorebird | CodePush (React Native) | |
|---|---|---|---|
| Framework | Flutter | Flutter | React Native |
| Platforms | Android | Android + iOS | Android + iOS (retired 2025) |
| Hosting | Your backend / your CDN — see Backends | Shorebird cloud (managed) | AppCenter cloud (deprecated) |
| Update scope | Dart AOT + Flutter assets | Dart code (engine-level diff) | JS bundle |
| Takes effect | Next cold start | Next restart | Next restart |
| Cost | Free (MIT) — you pay only your own storage | Free tier + paid plans | — |
| Backend flexibility | Supabase / Postgres / Cloudflare / AWS / PocketBase / BYO CDN | Cloud-managed only | — |
| Forced updates | Yes — built-in progress UI | Yes | Yes |
| Crash rollback | Automatic + bad-patch blacklist | Yes | Yes |
| Signing | Ed25519 (Android 13+) + MD5 | Yes | — |
Choose Shorebird if you need iOS today or want a fully managed cloud.
Choose flutter_ota_kit if you want OTA on infrastructure you control — enterprise apps, regional distribution, air-gapped deployments, or non-Play channels. Bring your own backend, CDN, and auth.
Google Play and some stores restrict downloading executable code at runtime. flutter_ota_kit targets self-controlled, enterprise, or permissive distribution channels. Verify your channel's policy before shipping.
Features
- OTA code push — replaces the Dart AOT
libapp.soand Flutter assets on the next cold start, with MD5 + Ed25519 integrity checks. - Five backends, built in — Supabase (fully automated), Postgres, Cloudflare (D1 + R2), AWS (S3 + optional CloudFront), and PocketBase (single-binary, self-hosted). Or point at your own CDN.
- Automatic crash rollback — a patch that fails to boot is rolled back and
blacklisted so it's never retried. Tunable via
maxCrashCount/verifyAfter. A runtime crash hours after a healthy boot no longer reverts a good patch (bounded crash-attribution window). - Forced updates with a built-in UI — set
autoApplyUpdates: trueand wrap your app inFlutterOtaApp; the SDK shows a full-screen terminal-style install screen (steps, live progress bar, speed/ETA, server message) and cold-restarts into the new build. You write no UI. - Staged rollout — server-side cohort math (per-mille + target cohorts) lets you ship 1% → 5% → 20% → 50% → 100% safely.
- Batteries-included tooling — the
flutter-otaCLI (init,build,deploy,bundle,channel,rollback,migrate,doctor, and a bundled PocketBase manager), plus runtime diagnostics and a sample app.
Try it in 5 minutes (no server needed)
git clone https://github.com/HYPER12755/flutter-ota-kit.git
cd flutter-ota-kit/example
flutter pub get
flutter build apk --release
flutter install
- Launch the app — it shows the original demo screen.
- Tap Apply patch.
- Swipe the app away from recents and reopen.
- The screen has changed — the patch took effect.
- Tap Rollback → restart → the original is back.
The example bundles a precompiled patch; everything runs offline on the device. For a full backend walkthrough, see the Beginner Guide or Getting Started.
Requirements
| Item | Requirement |
|---|---|
| Platform | Android only (iOS / macOS / Windows / Linux / Web are no-ops) |
| Dart SDK | >=3.13.2 <4.0.0 |
| Flutter | >=3.47.2 |
Android minSdk |
24 |
Android compileSdk |
36 |
| ABI | armeabi-v7a / arm64-v8a / x86_64 |
| NDK | 27.0.12077973+ |
| Java / JVM | 17 |
On non-Android platforms every API is safe to call but is a no-op — the
plugin logs a one-time warning and returns safe defaults, so you can ship
flutter_ota_kit in cross-platform code without guarding every call site.
Quick start
1. Install
dependencies:
flutter_ota_kit: ^0.2.0
2. Scaffold your backend (CLI)
# installs the flutter-ota CLI globally
npm i -g @_nazmiforreal/flutter-ota
# scaffold config + a generated setup file for your backend
flutter-ota init supabase # or: postgres | cloudflare | aws | pocketbase
init writes a .flutter_ota_kit/config.json, an .env template for secrets,
and a lib/flutter_ota_kit_setup.dart helper you call from main().
3. Initialize in your app
Call the generated setupFlutterOta() (or wire it by hand) before runApp:
import 'package:flutter/material.dart';
import 'package:flutter_ota_kit/flutter_ota_kit.dart';
import 'flutter_ota_kit_setup.dart'; // generated by `flutter-ota init`
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await setupFlutterOta(); // configures the backend + forced updates
runApp(const FlutterOtaApp(child: MyApp()));
}
FlutterOtaApp is the zero-code wrapper that lets the SDK render its built-in
forced-update screen. Disable it app-wide with
FlutterPatcher.showUpdateUi = false (or FlutterOtaApp(showUpdateUi: false)),
or host it yourself by setting MaterialApp.navigatorKey = FlutterPatcher.navigatorKey.
Prefer wiring the backend directly? Every backend has a configureX helper:
FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
supabaseUrl: 'https://<ref>.supabase.co',
anonKey: '<anon-key>', // public, RLS-protected reads
bucket: 'bundles',
channel: 'production',
platform: Platform.android,
updateStrategy: UpdateStrategy.appVersion,
appVersion: '1.0.0', // or omit — auto-detected from versionName
));
await FlutterPatcher.init(autoApplyUpdates: true);
4. Build a patch
Rebuild the release APK, then pack it into a device-ready patch.zip:
flutter build apk --release
flutter-ota build \
--apk build/app/outputs/flutter-apk/app-release.apk \
--version 1.0.1 \
--target-version-code 100
Every ABI in the APK is included, so one bundle serves all devices. Add
--assets assets/hero.png,assets/strings/zh.json to overlay updated assets.
5. Deploy
flutter-ota deploy \
--source dist \
--channel production \
--target-app-version 1.0.0 \
--force
That's it — devices pick up the patch on their next check.
6. Roll back (server side)
flutter-ota rollback --channel production
Or on the device, FlutterPatcher.rollback() deletes the local patch and
reverts to the APK's built-in version on the next cold start.
How it works
┌─────────────────┐ Supabase / Postgres / Cloudflare / AWS / PocketBase / CDN
│ Your backend │ stores: patch.zip (the diff) + manifest.json (metadata)
└────────▲────────┘
│ 1. check-for-update on launch
│ 2. backend returns PatchInfo (url, md5, signature, force flag)
┌────────┴────────┐
│ The app │ 3. download patch.zip
│ │ 4. verify MD5 + Ed25519 signature
│ │ 5. write to local patch dir (atomic rename)
│ │ 6. cold-restart (forced) OR next cold start (staged)
│ │ 7. loader hook reads patched libapp.so + asset overlays
│ │ 8. boot ok → keep · boot fails → auto-rollback + blacklist
└─────────────────┘
A patch is a byte-for-byte diff of the new APK's libapp.so against the
version the user has installed; asset patches overlay flutter_assets/ by path.
Nothing is swapped into the running process — it loads on the next cold start.
Full internals: Architecture.
What can and can't be patched
| ✅ Hot-patchable | ❌ Not hot-patchable |
|---|---|
Anything in lib/ — widgets, logic, routes, constants |
Native code (Kotlin / Java / C++ in android/src/main/) |
| Pure-Dart package upgrades (same native side) | AndroidManifest.xml changes |
Flutter asset files registered in pubspec.yaml |
APK res/ (icons, layouts, strings.xml) |
New Image.asset() / rootBundle.load() calls |
Flutter Engine upgrades |
| Adding or removing native plugins | |
| Removing assets that exist in the base APK |
See API Reference → What can be patched for edge cases (ProGuard/R8, multi-ABI/flavors, state migrations).
Safety
- Crash protection (on by default). If a patch fails to boot, the SDK
auto-rolls back and blacklists the offending version. Tune with
maxCrashCount(default 1) andverifyAfter(default 5s). Crash attribution is bounded to a boot window, so an unrelated crash long after a healthy boot won't revert a working patch. See Crash Protection. - Integrity & signing. MD5 is strongly recommended; Ed25519 signature
verification is available on Android 13+ (falls back to MD5-only below, with a
warning). Patches are bound to the host APK's
versionCode, so stale patches expire after an app upgrade. Keep private signing keys on your server, never in the app bundle. - Staged rollout. Ship 1% → 5% → 20% → 50% → 100% and watch crash rate at
each stage. The SDK records
lastBootDiagnosticper device.
Full release workflow: Production Playbook.
Documentation
Guides
- Beginner Guide — zero to first OTA, narrated
- Getting Started — scaffold → build → deploy
- Developer Guide — full workflow reference
- Configuration — env vars,
.env, resolution order - Backends — Supabase / Postgres / Cloudflare / AWS / PocketBase setup
- Production Playbook — staged rollout, diagnostics, rollback
Reference
- API Reference —
FlutterPatcher, configs, overlay, error codes - Architecture — internals, protocol, signing
- CLI Reference — every command and flag
- Crash Protection — rollback, blacklist, Android differences
- FAQ — versioning, cold start, store policy
Contributing
Issues and PRs welcome. Before submitting:
flutter analyzereports no issuesflutter testis fully green- If you touched native code, run a real-device patch / rollback end-to-end
- Document new public APIs (they surface on pub.flutter-io.cn)
License
MIT