patch_app 0.2.0 copy "patch_app: ^0.2.0" to clipboard
patch_app: ^0.2.0 copied to clipboard

A lightweight helper to patch your Flutter app at runtime using shorebird code push and terminate restart

Patch App #

A lightweight helper to patch your Flutter app at runtime using shorebird_code_push and terminate_restart.

It automatically checks for Shorebird updates, applies patches, and restarts your app safely when accepted.


Features #

  • Check and apply Shorebird patches dynamically
  • Show a customizable restart confirmation dialog
  • Restart the app safely with one line of code
  • Register with context:, navigatorKey:, or binding: so startup checks can wait for the navigator to be ready
  • Optional timeout to stop deferred navigator/context retries after a max wait duration
  • Built-in minInterval to limit check frequency and prevent redundant checks
  • Optional error handling via callback or PatchResult
  • Only report PatchResult.restartRequired when the restart dialog is accepted (or cannot be shown), while rejected prompts return PatchResult.cancelled

Setup #

iOS #

Add the following to your Info.plist to enable restarts with terminate_restart:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>
  </dict>
</array>

Android #

No configuration required.


Usage #

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  final patchApp = PatchApp(
    confirmDialog: (context) => patchAppConfirmationDialog(context),
    onError: (error, stack) => debugPrint('Update failed: $error'),
  );

  @override
  Widget build(BuildContext context) {
    return PatchAppScope(
      patchApp: patchApp,
      child: FilledButton(
        onPressed: () => patchApp.checkAndUpdate(context), // Check and update manually
        child: const Text('Check and Update'),
      ),
    );
  }
}

PatchAppScope #

  • Wrap any subtree in PatchAppScope to register automatically for the wrapped widget. It calls register() in initState() and unregister() in dispose() for you.
  • If you are wrapping an app root that needs to wait for a navigator, pass a navigatorKey to the scope.
  • If you need to control deferred registration scheduling in tests or custom bindings, pass a binding to the scope.
  • If you want deferred registration to stop after a maximum wait, pass timeout:
PatchAppScope(
  patchApp: patchApp,
  navigatorKey: navigatorKey,
  timeout: const Duration(seconds: 5),
  child: const MyApp(),
)

register(context:) and unregister() #

  • register(context) Automatically checks for updates when the app starts or resumes. Should be called once in initState(). If you need to wait for the navigator to become available, pass a navigatorKey instead: patchApp.register(navigatorKey: navigatorKey). To stop retrying deferred registration after a max duration, pass timeout: patchApp.register(navigatorKey: navigatorKey, timeout: const Duration(seconds: 5)). The context: and navigatorKey: arguments are named.

  • unregister() Cleans up the lifecycle listener created by register(). Always call this in dispose().


Patch Results #

enum PatchResult {
  noUpdate,        // No updater or no patch available
  upToDate,        // Already on the latest version
  cancelled,       // Restart prompt dismissed or skipped
  restartRequired, // Patch applied; restart needed
  failed,          // Error during the update
}

cancelled is returned when the confirmation dialog is dismissed, while restartRequired is only emitted after the user accepts a restart (or if the dialog cannot be shown because the context was unmounted), signaling that a restart is still needed.


Tips #

  • Always provide an onError callback in production to capture unexpected failures.

    • If onError is provided, the method returns PatchResult.failed on error.
    • If omitted, the error is rethrown.
5
likes
160
points
196
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight helper to patch your Flutter app at runtime using shorebird code push and terminate restart

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, shorebird_code_push, terminate_restart

More

Packages that depend on patch_app