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_preparebefore 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
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- Monorepo Toolkit already generated (see Monorepo Toolkit Setup)
- A paid Shorebird account for production releases
shorebird_sdk needs a Pro subscription or higher.
Step 1: Install the Shorebird CLI
curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bashRestart your terminal, then authenticate:
shorebird loginStep 2: Generate the Shorebird SDK
archipelago generate shorebird_sdkThe prompts are:
- shorebirdAppId: leave it blank and fill it in after
shorebird init(default empty) - includeCiPatches:
truewritesdocs/shorebird-ci-patches.mdwith CI patch steps (defaulttrue). An existing file is not overwritten.
Or use a config file:
{
"shorebirdAppId": "",
"includeCiPatches": true
}archipelago generate shorebird_sdk --config shorebird_config.jsonStep 3: Understand the Generated Structure
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 includeCiPatchesThe API package has no plugin dependency, so you can import it anywhere.
Step 4: Initialise Shorebird in the App
cd apps/<appName> && shorebird initThis creates shorebird.yaml with your app ID. Commit it:
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.
# 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 --restoreStep 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:
shorebird patch android --flavor production
shorebird patch ios --flavor productionPatches 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:
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:
@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:
- 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 --restoreCommon Customizations
| Customization | Where to Change |
|---|---|
| Change default update strategy | ShorebirdConfig.defaultStrategy in your app module |
| Disable launch-time check | Set checkOnLaunch: false in ShorebirdConfig |
| Custom update dialog | Call checkForUpdate() and handle UpdateAvailable yourself |
| Multiple flavors | Pass --flavor <name> to both shorebird release and shorebird patch |
Next Steps
- Set up Monitoring to track patch adoption rates
- Read
docs/shorebird-ci-patches.mdto automate patch delivery in CI