English | 简体中文
Open-source, self-hosted over-the-air (OTA) hotfix tool for Flutter (Android & iOS). Ship critical bug fixes directly from your own servers or CDN without waiting for app store reviews.
flutter_patcher is a Flutter plugin with a command-line tool:
buildbuilds the app you release.- After a fix,
patchmakes one patch file. - You put the file on your HTTPS server. The app downloads it and runs it from the next cold start.
Changes to native code or to the Flutter version still need a store release.
Requirements
| Flutter | Android | iOS |
|---|---|---|
| 3.47.0 – 3.47.9 | ✅ | ✅ |
| For | You need |
|---|---|
| Android devices | Android 9 (API 28) or later, arm64-v8a. On Android 7 and 8 the app runs as built, without patches |
| iOS devices | iOS 13 or later |
build android |
Linux x64, Windows x64 or an Apple Silicon Mac |
build ios |
An Apple Silicon Mac with Xcode |
patch android, patch ios |
Any of the computers above |
What can be patched
| Change | Patchable | Why |
|---|---|---|
| Your Dart code | ✅ | |
Pure-Dart dependencies, such as dio or intl |
✅ | |
| Added or changed images, fonts and other assets | ✅ | |
| Removed assets | ❌ | A patch overrides assets and cannot remove them |
Native code, platform plugins and FFI packages, such as mmkv or isar |
❌ | A patch carries only Dart code and assets |
| The Flutter SDK version | ❌ | A patch is built with the Flutter version that built its release |
patch checks these limits and fails with an error code when a change breaks one. The full list is in How It Works.
Quick start
-
Add the dependency, initialize, and check for a patch:
flutter pub add flutter_patcher:1.1.1 dart run flutter_patcher init # Creates flutter_patcher.yaml and the patch key, signing offFuture<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await FlutterPatcher.init(); // Initializes flutter_patcher; call it before runApp. runApp(const MyApp()); } // Checks for a new patch, by default at 127.0.0.1:8686 where serve offers it. final check = await FlutterPatcher.checkUpdate(); if (check.hasUpdate) { // Downloads and verifies the patch; it takes effect on the next cold start. await FlutterPatcher.applyPatch(check.patch!); } -
Build the app and install it.
buildalso records the baseline that every later patch of this build is compared with:dart run flutter_patcher build android # Or build ios, on an Apple Silicon Mac -
Change the Dart code, then make the patch.
patchprints the path of the patch file asfile:dart run flutter_patcher patch android # Writes one file, such as 1-arm64-v8a.fptc -
Offer the patch to a phone connected over USB, then open the app twice: the first start installs the patch, the second runs it.
dart run flutter_patcher serve # Offers the newest patch at 127.0.0.1:8686; development only
Each step with its expected output is in Getting Started.
Before you ship
To keep development and debugging simple, flutter_patcher makes three main features optional. Turning on all three before you release is strongly recommended:
- Update address: pass the HTTPS address that serves your patch files as
checkUpdate(url: …), because without it the app asksserveon your computer. An app with custom delivery does not need it. - Baseline store: set
baseline_storeinflutter_patcher.yamlto durable storage, because a build whose baseline is lost can no longer be patched (WARNING: BASELINE_STORE_LOCAL). - Signing: set
trust.enabled: trueinflutter_patcher.yaml, because an unsigned app installs any intact patch file of its build, so whoever can hand it a file can run code in it (WARNING: UNSIGNED).
The steps are in the Production Deployment Guide.
Note Some app stores and regulated industries restrict downloading executable code. Check the rules of the stores you ship in.
Documentation
| Document | What it covers |
|---|---|
| Getting Started | From adding the dependency to a patch running on a phone |
| How It Works | Hosts, baselines, the two patch strategies, the security model, what can be patched |
| CLI Reference | Every command, option, output and exit code; flutter_patcher.yaml |
| API Reference | The Dart API the app calls |
| Production Deployment Guide | Hosting patch files, keeping baselines, signing, rolling back, upgrading from 1.0 |
| Advanced Operations | KMS signing, keys in CI, stopping signing, custom host policy, S3, delivery from your own server |
| Upgrading from 1.0 | Keep patching apps shipped with 1.0.0 |
| Crash Protection | What happens when a patch fails at startup; boot diagnostics |
| Troubleshooting | Every error code and warning, with fixes |
| FAQ | Short answers with links |
Contributing
Issues and pull requests are welcome. Before submitting, run:
flutter pub get
flutter analyze
flutter test
A change to native code also needs the on-device acceptance tests in test/acceptance, because unit tests do not load a patch on a device.