Update Flutter apps without store review
What is Shorebird?
Shorebird is a service that allows Flutter apps to be updated directly at runtime. Removing the need to build and submit a new app version to Apple Store Connect or Play Console for review for every app update.
Flutter already comes with hot reload/restart that allows developers to quickly test code changes during development. Shorebird is similarly used for changes in production apps already in the hands of end-users.
Why?
Enabling production code to be updated without needing to go through the usual store review process, which greatly reduces the time required to release a critical bug fix. With finer control over app updates it would also let us develop mobile apps with smaller release windows and flexibility similar to web site deployments.
How?
Shorebird uses a modified version of the Flutter engine that allows updated code changes to be run at runtime using the Dart Virtual Machine. Since running code in the Virtual Machine is a lot slower then regular compiled code, Shorebird is smart enough to detect only the changed code parts to minimize its usage.
Broadly there are two main concepts.
Release:
A full app binary containing the whole Flutter app, the same as you would normally build when releasing an app. With the difference it also contains a custom Flutter engine modified to run code in a virtual machine and functionality to check for updates. The release app needs to be reviewed and distributed through app stores as usual.
Patch: The patch only contains the code changes required for a Shorebird code push update. It's created by comparing the changes to the targeted release binary.
Both are created using the Shorebird CLI tool with the shorebird release
and shorebird patch
commands respectively. Behind the scenes this wraps the usual flutter build command.
Simple usage flow
- Install Shorebird CLI tools
- Register your Flutter app with Shorebird, using the shorebird init command
- Create a new app release with the shorebird release command
- Distribute the shorebird release app to app stores (or applicable distribution channels)
- Modify some dart code in the app (adding new functionality/fixing a bug)
- Create a patch with the shorebird patch command
- After the patch has been uploaded to Shorebird servers, on the next app start the patch will be downloaded and applied.
By default the app will check for any new patches and update the app at startup. The update check can also be done programmatically for more advanced use cases.
Example demo
For this example we will be following the Getting Started guide from the Shorebird documentation. In short we will be adding Shorebird to the Flutter counter example app, change some code and push a patch to the app.
- First we need to install the Shorebird CLI tools by using the handy install script.
curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash
For this demo I'm using a Mac setup but there are also instructions for Windows environments in the documentation. See the Getting Started link above.
- After the CLI tools have been installed we now have to set up the Flutter project to use Shorebird using the shorebird init command. You will also be prompted to select a name for your app. It's only used to manage your app in the Shorebird console and won't be shown to end-users.
user@my-mac shorebird_test % shorebird init
✓ No product flavors detected. (19.2s)
? How should we refer to this app? (shorebird_test) shorebird_test
🐦 Shorebird initialized successfully!
✅ A shorebird app has been created.
✅ A "shorebird.yaml" has been created.
✅ The "pubspec.yaml" has been updated to include "shorebird.yaml" as an asset.
Reference the following commands to get started:
📦 To create a new release use: "shorebird release".
🚀 To push an update use: "shorebird patch".
👀 To preview a release use: "shorebird preview".
For more information about Shorebird, visit https://shorebird.dev
✓ Shorebird is up-to-date (1.4s)
✓ Flutter install is correct (15.4s)
✓ AndroidManifest.xml files contain INTERNET permission (1 fix applied) (83ms)
✓ Has access to storage.googleapis.com (0.2s)
✓ shorebird.yaml found in pubspec.yaml assets (9ms)
Checking the Shorebird console we can see our app has been added.
- Now we can create our first Shorebird supported release binary using the shorebird release command. Creating an apk file for Android only just to simplify things. This would need to be run again for the iOS app.
user@my-mac shorebird_test % shorebird release android --artifact apk
✓ Downloading patch... (0.4s)
✓ Extracting patch... (0.6s)
✓ Downloading bundletool.jar... (0.7s)
✓ Extracting bundletool.jar... (1.9s)
✓ Downloading aot-tools.dill... (0.2s)
✓ Extracting aot-tools.dill... (1.5s)
✓ Fetching apps (0.4s)
✓ Building app bundle with Flutter 3.24.1 (3f7041c5e9) (75.8s)
✓ Release version: 1.0.0+1 (1.3s)
✓ Fetching releases (0.4s)
🚀 Ready to create a new release!
📱 App: shorebird_test (ab486a37-ft3d-472d-l520-ba1h7e3c3801)
📦 Release Version: 1.0.0+1
🕹️ Platform: android
🐦 Flutter Version: 3.24.1 (3f7041c5e9)
Would you like to continue? (y/N) Yes
✓ Fetching releases (0.9s)
✓ Creating release (0.7s)
✓ Updating release status (0.3s)
✓ Creating artifacts (9.0s)
✓ Updating release status (1.8s)
✅ Published Release 1.0.0+1!
Your next step is to upload the app bundle to the Play Store:
/Users/christofer_henriksson/shorebird_test/build/app/outputs/bundle/release/app-release.aab
For information on uploading to the Play Store, see:
https://support.google.com/googleplay/android-developer/answer/9859152?hl=en
To create a patch for this release, run shorebird patch --platforms=android --release-version=1.0.0+1
Note: shorebird patch --platforms=android without the --release-version option will patch the current version of the app.
- Installing and running the release shows the default counter example as expected.
- Now let's make some small changes to the app.
Changing the colorScheme
to green.
- colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
Adding a decrement button
+ void _decrementCounter() {
+ if (_counter > 0) {
+ setState(() {
+ _counter--;
+ });
+ }
+ }
- floatingActionButton: FloatingActionButton(
- onPressed: _incrementCounter,
- tooltip: 'Increment',
- child: const Icon(Icons.add),
- ),
+ floatingActionButton: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ FloatingActionButton(
+ onPressed: _decrementCounter,
+ tooltip: 'Decrement',
+ child: const Icon(Icons.remove),
+ ),
+ const SizedBox(width: 8),
+ FloatingActionButton(
+ onPressed: _incrementCounter,
+ tooltip: 'Increment',
+ child: const Icon(Icons.add),
+ ),
+ ],
+ ),
- Next we will create the patch for the above changes with the
shorebird patch
command.
user@my-mac shorebird_test % shorebird patch android
✓ Fetching apps (0.5s)
✓ Fetching releases (0.3s)
Which release would you like to patch? 1.0.0+1
✓ Fetching aab artifact (0.3s)
✓ Downloading aab (2.2s)
✓ Building patch with Flutter 3.24.1 (3f7041c5e9) (41.2s)
✓ Verifying patch can be applied to release (28ms)
[WARN] Your app contains asset changes, which will not be included in the patch.
Changed files:
base/assets/flutter_assets/fonts/MaterialIcons-Regular.otf
Continue anyways? (y/N) Yes
✓ Fetching release artifacts (0.9s)
✓ Downloading release artifacts (4.0s)
✓ Creating patch artifacts (4.2s)
🚀 Ready to publish a new patch!
📱 App: shorebird_test (a1ac6a37-bc3d-459d-b820-bd1f4e3c5391)
📦 Release Version: 1.0.0+1
🕹️ Platform: android [arm32 (87.71 KB), arm64 (84.91 KB), x86_64 (89.81 KB)]
🟢 Track: Production
Would you like to continue? (y/N) Yes
✓ Creating patch (1.7s)
✓ Uploading artifacts (2.8s)
✓ Fetching channels (0.3s)
✓ Promoting patch to stable (0.3s)
✅ Published Patch 1!
The tool detected that we added a new Material icon that won't be included in the patch. For our demonstration we choose to continue anyways.
- Checking the Shorebird console we can see our first patch has been added.
- Restarting the app to download and apply the patch. After restarting the app we can confirm that the new changes have been remotely updated successfully.
Note that the newly added button's icon is not showing. This is as expected since the Material icon asset is missing.
Limitations and considerations
-
Shorebird can only update Dart code. So native iOS (Objective-C/Swift) and Android (Java/Kotlin) code updates are not supported. For any native code changes like plugins we are still required to prepare a new release build as normal.
-
Currently assets like fonts or images are not supported but are planned to be added. Also some care needs to be considered when changing icons. Since icon assets are tree-shaken when the app is built, you can't patch an app to use a new icon that is not already used somewhere in your app.
-
For now Shorebird only supports Flutter mobile apps (iOS/Android). But Flutter desktop app support is also planned to be added later.
-
Shorebird is not a replacement for existing app stores or other side loading solutions. It is only a tool for making code updates more convenient. You still need to have the app released to your users through an app store or other distribution method.
-
Since new updates need to be downloaded from the Shorebird backend, apps that are exclusively offline are not supported. In fact the shorebird init command will add the required internet permissions to the Android manifest if it's missing.
-
Flutter versions cannot be updated through Shorebird. For example an app released using Flutter 3.24 cannot be patched to use another version of Flutter. Conversely, since Shorebird is using a customized version of Flutter, there might be a small delay until a new Flutter version has been prepared for Shorebird.
-
Both App Store and Play Console have both technical and policy rules that limit what a remote update is allowed to contain. Shorebird is built to comply with the technical limitations, but for policy rules it's up to the developers to make sure they are not broken. For example we are not allowed to "engage in deceptive behavior" via pushed updates. Like changing the original purpose of the app. Failing to follow the content policies can result in both your Shorebird account and developer account being terminated. More details can be found at the Shorebird’s Q&A here.
For more information I recommend checking out the official documentation and the helpful Discord server.