Skip to content
Back to archipelago.banuacoder.com

Feature SDKs

How to ship over-the-air Flutter updates with Shorebird ​

Pro Reference for shorebird_sdk: every variable, option and package it generates. Command: archipelago generate shorebird_sdk

Generate the Shorebird SDK, run build_prepare before releasing, push Dart-only patches without a store submission, and set update strategies and prompt copy.

What you'll learn ​

  • Generating the Shorebird SDK feature with API/Impl split
  • Running build_prepare before releasing to ensure correct dependency swapping
  • Creating releases and pushing Dart-only patches without a full store submission
  • Configuring update strategies and custom prompt copy at runtime

Prerequisites ​

shorebird_sdk needs a Pro subscription or higher.

Step 1: Install the Shorebird CLI ​

bash
curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash

Restart your terminal, then authenticate:

bash
shorebird login

Step 2: Generate the Shorebird SDK ​

bash
archipelago generate shorebird_sdk

The prompts are:

  • shorebirdAppId: leave it blank and fill it in after shorebird init (default empty)
  • includeCiPatches: true writes docs/shorebird-ci-patches.md with CI patch steps (default true). An existing file is not overwritten.

Or use a config file:

json
{
  "shorebirdAppId": "",
  "includeCiPatches": true
}
bash
archipelago generate shorebird_sdk --config shorebird_config.json

Step 3: Understand the Generated Structure ​

text
features/shorebird/
├── shorebird_updater_api/       # Contract, config, sealed result models
│   └── lib/src/
│       ├── shorebird_updater_sdk.dart
│       └── models/              # ShorebirdConfig, PromptCopy, UpdateStrategy, results
└── shorebird_updater_impl/      # shorebird_code_push wrapper and DI
    └── lib/src/
        ├── shorebird_updater_sdk_impl.dart
        └── di/

apps/<appName>/shorebird.yaml    # app_id placeholder, completed by shorebird init
docs/shorebird-ci-patches.md     # If includeCiPatches

The API package has no plugin dependency, so you can import it anywhere.

Step 4: Initialise Shorebird in the App ​

bash
cd apps/<appName> && shorebird init

This creates shorebird.yaml with your app ID. Commit it:

bash
git add apps/<appName>/shorebird.yaml
git commit -m "chore: add shorebird.yaml"

If you left shorebirdAppId blank during generation, open shorebird.yaml and copy the generated app_id into your brick config or the DI module.

Step 5: Check the Registration ​

The post-generation hook registers ShorebirdUpdaterImplPackageModule in lib/di/injector.dart. You write no registration code.

ShorebirdUpdaterSDKImpl is an eager singleton. It joins FeatureSDKRegistry when DI builds it. With checkOnLaunch: true (the default), initialize() starts a background check and download. It does not block launch.

Step 6: Create a Release ​

Always run build_prepare before releasing

Shorebird captures the dependency graph at build time. Run build_prepare with the build flavor first, so Shorebird captures the production graph. That graph has the noop debug tooling. The internal graph does not.

bash
# Swap debug tooling out for the production flavor
dart run monorepo_toolkit build-prepare --flavor production

# Create the release (Android)
shorebird release android --flavor production -- -t lib/main.dart

# Create the release (iOS)
shorebird release ios --flavor production -- -t lib/main.dart

# Restore the dependencies
dart run monorepo_toolkit build-prepare --restore

Step 7: Push a Dart-Only Patch ​

When you fix a bug with no native code or asset changes, push a patch instead of a full release:

bash
shorebird patch android --flavor production
shorebird patch ios --flavor production

Patches only cover Dart changes

Native code changes (Kotlin/Swift), new Flutter plugins, or modified assets require a new full release, not a patch.

Step 8: Use the Runtime API ​

Take ShorebirdUpdaterSDK by constructor:

dart
if (!updater.isAvailable) return; // debug build, `flutter run` or web

// Silent background update
await updater.updateNow(UpdateStrategy.silent);

// Check first, then prompt
final result = await updater.checkForUpdate();
if (result is UpdateAvailable) {
  final confirmed = await showMyUpdateDialog(context, result.patchNumber);
  if (confirmed) await updater.downloadUpdate();
}

checkForUpdate() never throws. It returns NoUpdate, UpdateAvailable, UpdateDownloaded or UpdateCheckError. downloadUpdate() returns DeferredUntilRelaunch or UpdateFailed. A downloaded patch takes effect on the next cold start. To apply it at once, restart the app yourself, for example with the restart_app package. getCurrentPatchNumber() returns the installed patch or null.

UpdateStrategy.prompt and force behave the same inside updateNow: they only run a check. The prompt or forced-update UI is yours to build.

Step 9: Configure the Update Strategy ​

Override the defaults by providing your own ShorebirdConfig in an app module:

dart
@module
abstract class MyAppShorebirdModule {
  @lazySingleton
  ShorebirdConfig get shorebirdConfig => const ShorebirdConfig(
    defaultStrategy: UpdateStrategy.prompt,
    checkOnLaunch: true,
    promptCopy: PromptCopy(
      title: 'New version available',
      body: 'Update now for the latest features.',
      updateButton: 'Update now',
      laterButton: 'Maybe later',
    ),
  );
}

Step 10: CI Integration ​

Add SHOREBIRD_TOKEN to your repository secrets (generate with shorebird token create), then patch on every merge to main:

yaml
- name: Patch Android
  env:
    SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}
  run: |
    dart run monorepo_toolkit build-prepare --flavor production
    shorebird patch android --flavor production
    dart run monorepo_toolkit build-prepare --restore

Common Customizations ​

CustomizationWhere to Change
Change default update strategyShorebirdConfig.defaultStrategy in your app module
Disable launch-time checkSet checkOnLaunch: false in ShorebirdConfig
Custom update dialogCall checkForUpdate() and handle UpdateAvailable yourself
Multiple flavorsPass --flavor <name> to both shorebird release and shorebird patch

Next Steps ​

  • Set up Monitoring to track patch adoption rates
  • Read docs/shorebird-ci-patches.md to automate patch delivery in CI