Android

The whole Android flow, from an app that has never used OverPush to a fix running on your users' phones. Your normal Flutter installation is all you need.

Before you start

1. Set up the project

  1. Add the package

    $ flutter pub add overpush
  2. Connect the project

    From the project root, the folder with pubspec.yaml:

    $ overpush init --app-id cp_your_app_id

    This writes overpush.yaml and stores a project key in .overpush/key, which is added to .gitignore for you. It also adds the INTERNET permission to AndroidManifest.xml if your app doesn't have it yet.

    You don't change main.dart, MainActivity, or any Gradle file. Your app's own signing setup is used as it is.

2. Ship a release

  1. Build the release

    $ overpush release android

    This builds an app bundle (.aab), the format the Play Store takes, and records the release so it can be patched. It covers every Android phone Flutter runs on: 64-bit and 32-bit ARM, and x86_64.

    Distributing outside the Play Store? Build an APK instead. It also covers every phone:

    $ overpush release android --artifact apk

    Patches only reach phones running a build made by overpush release. A build from plain flutter build runs normally but never receives patches.

  2. Try it on your phone first

    With a phone connected over USB:

    $ overpush preview android

    This installs exactly the build your users get, the same way the Play Store does, and opens it. The first preview downloads a small tool from Google, once.

  3. Upload it to the Play Store

    The CLI prints where the file is: build/app/outputs/bundle/release/app-release.aab (an APK goes to build/app/outputs/flutter-apk/app-release.apk). It's signed with your app's own release key from android/key.properties. Then, in Play Console:

    1. Open your app and go to Test and release.
    2. Pick a track: Internal testing is a good first stop, Production for everyone.
    3. Click Create new release and upload app-release.aab.
    4. Add release notes, click Next, then save and roll it out. On Production, Play sends it for review first.

    There's nothing else to run: overpush release android already saved the release to your account, so any computer or CI runner logged in to it can patch it.

    Raise versionCode for every upload. Play Console rejects a bundle whose version code it has seen before.

Testing on another phone with an APK

To hand the app to a tester, or try it on a phone that isn't connected to your computer, build an APK:

$ overpush release android --artifact apk

The APK is at build/app/outputs/flutter-apk/app-release.apk. Get it onto the phone either way:

Then patch it like any release:

$ overpush patch android

Open the app, wait a few seconds, close it fully and open it again: the patch runs.

The APK is its own release. overpush patch android patches the newest release, so after building the APK it targets the APK, not the bundle you uploaded to Play. To patch the Play Store build, name it: overpush patch android --release-version 1.7.0+66. The console's Releases tab shows both.

If the phone already has your app from the Play Store, installing the APK fails with a signature error unless the APK is signed with the same key. Uninstall the store version first.

3. Publish a patch

  1. Change some Dart

    Fix the bug, change a screen, add a new one. Anything in Dart can go out as a patch. See what a patch can change.

  2. Check it first (optional)

    $ overpush patch android --dry-run

    Builds and checks the patch without publishing anything.

  3. Publish

    $ overpush patch android

    The CLI builds the patch for the newest release, checks that it really changed something and that nothing outside Dart changed, and publishes it. To start with a share of users:

    $ overpush patch android --rollout-percent 10
  4. See it on the phone

    Open the app. A few seconds after it starts, it downloads the patch in the background. Close the app fully and open it again, and the patched code runs.

  5. Watch it in the console

    The release page in the console shows each patch with its installs and failures, and the app's overview charts patch installs and failures. When it looks healthy, widen the rollout:

    $ overpush patches rollout 1 --percent 100          # everyone
    $ overpush patches revoke 1 --reason "wrong total"   # stop it everywhere

    If the project also has iOS releases, add --platform android. Both are also in the console's patch menu, along with rolling a patch forward again. See Rollouts & rolling back.

What a patch can change

ChangeAs a patch?
Dart code: logic, widgets, layouts, text, new screens, new classes✅ Yes
Pure-Dart packages (no native code)✅ Yes
Kotlin or Java, AndroidManifest.xml, Gradle files❌ Needs a store release
Adding, removing or upgrading a plugin with native code❌ Needs a store release
Images, fonts and other assets❌ Needs a store release

Before publishing, the CLI compares the project with the release. If anything in the second half of this table changed, it stops and says what. If you're sure the change doesn't matter to the patch, such as a comment in a Gradle file, --allow-native-diffs and --allow-asset-diffs publish anyway.

Shipping a new store version

When you need a store update, bump the version in pubspec.yaml and run overpush release android again. Phones that install the new version start clean: patches made for the old version are removed automatically, and new patches go to the new release.

Obfuscation

Add obfuscate: true to overpush.yaml and release again. Patches for that release are obfuscated the same way, with nothing extra to pass. See Configuration.

What to expect

SpeedPatched apps run at the same speed as the original release.
Patch sizeOnly the difference from your store release. A small fix is usually under a megabyte. It downloads in the background and only once per patch.
When it appliesOn the next cold start after the download finishes.
Store updatesWhen a user installs a new version from the store, patches for the old version are removed automatically.
DevicesEvery Android phone Flutter runs on: 64-bit and 32-bit ARM, and x86_64. App bundles and APKs both cover all of them.
A bad patchIf a patch stops the app from starting, the phone removes it and runs the store build again on its own. See Automatic rollback.

Checking a device

OverPush logs what it does at startup. If a patch doesn't seem to apply, check the device log with the phone connected over USB:

$ adb logcat | grep OverPush
Log lineMeaning
patch loader installedOverPush is active in this build. Printed on every launch.
applied patch N; it loads on the next cold startPatch N was downloaded and verified.
booting patched Dart codeThis launch is running the patch.
the APK changed since this patch was appliedA new store version was installed, so the old patch was removed.

If you only ever see patch loader installed, check that the installed build came from overpush release android and that the device is inside the patch's rollout.

Common problems

"The phone has your app signed with a different key"

Shown by overpush preview android when the phone already has your app from the store, which is signed with your release key. Uninstall it from the phone, or keep your release key in android/key.properties (Flutter's standard setup) so previews are signed the same way.

"Install canceled by user"

Some phones, Xiaomi and Redmi among them, ask on screen before installing over USB. Keep the phone unlocked and tap Install when it asks, or turn on Install via USB in the phone's developer options.

"This build is byte-identical to the release"

The patch build came out the same as the release, so there's nothing to send. Check that your edit is in Dart code under lib/, and saved.

More in Troubleshooting.