Skip to content

Code Push for Android Hybrid Apps

This guide explains how to use Patchwing in an Android hybrid app scenario (that is, your app embeds Flutter UI in non-Flutter UI).

This guide assumes you have already have an Android app and a Flutter module. Our Android app will be named android_app and our Flutter module will be named flutter_module.

This guide also assumes that you have created a Patchwing account. If you have not, please see our code push guide for instructions.

The reference code for this guide is available at https://github.com/AstralWing/samples/tree/main/add_to_app.

First, run patchwing init in your Flutter module:

patchwing init

Create a Patchwing release for your Flutter module:

patchwing release aar --release-version 1.2.3+1

The release-version parameter needs to match the version of the Android app that uses this module (i.e., versionName+versionCode from the app’s app/build.gradle file).

Update your Android app to use the download.patchwing.net Maven repository

Section titled “Update your Android app to use the download.patchwing.net Maven repository”

In settings.gradle:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
+        maven {
+           // This is a relative path from this settings.gradle file to the
+           // my_flutter_module/build/host/outputs/repo directory.
+           url '../my_flutter_module/release'
+       }
+       maven {
-           url 'https://storage.googleapis.com/download.flutter.io'
+           url 'https://download.patchwing.net/download.flutter.io'
+      }
    }
}

Update your Android app to use this version of the Flutter module

Section titled “Update your Android app to use this version of the Flutter module”

In app/build.gradle, add the following:

buildTypes {
    release {
        // ...
+        // The Dart compiler builds libapp.so directly (does not use clang),and already strips
+        // symbols. However when llvm-strip is run on libapp.so, the symbols are re-sorted
+        // causing the hash of the library to change (which can confuse Patchwing tools).
+        // This line tells gradle to skip the unnecessary llvm-strip step for libapp.so
+        // thus ensuring that the libapp.so that Patchwing sees is byte-identical to the one
+        // which ends up in the APK/AAR/AAB.
+        packaging.jniLibs.keepDebugSymbols.add("**/libapp.so")
        // ...
    }
}

dependencies {
    // ...
+    releaseImplementation 'com.example.my_flutter_module:flutter_release:1.0'
    // ...
}

In Android Studio, update the active build variant to release and run your app. Your app should work as before with no differences.

To set the active build variant to release, click on the “Build Variants” tab in the lower left corner of Android Studio. Then select “release” from the “Active Build Variant” dropdown.

Android Studio build variant set to release

Attempting to build with a non-release build variant will not be able to resolve Flutter symbols in your app.

We won’t cover this step in detail here, but this is where you would submit your app to the Play Store. For code push to work, it is important that you submit with the same aar generated by the release command above.

Verify that Patchwing is working with a patch

Section titled “Verify that Patchwing is working with a patch”

Make an edit to the code in your Flutter module. Then run:

patchwing patch aar --release-version 1.2.3+1

As with the release command, the release version should be the version of the Android app that uses this module.

Now relaunch the app, navigate to the Flutter screen, and verify that the patch is recognized and applied. In logcat, you should see output like the following:

[INFO:patchwing.cc(109)] Patchwing updater: no active patch.
[INFO:patchwing.cc(113)] Starting Patchwing update
updater::network: Sending patch check request: PatchCheckRequest { app_id: "baad0583-6810-44a7-9034-6aadb8127f29", channel: "stable", release_version: "1.0.0+8", patch_number: None, platform: "android", arch: "aarch64" }
updater::updater: Patch 1 successfully installed.
updater::updater: Update thread finished with status: Update installed