Force Update in Flutter (Part 4): force_update_helper
Flutter Development
7 min read
July 15, 2026

Force Update in Flutter (Part 4): force_update_helper

Part 4 — wire force_update_helper so fetch, compare, and store open are one cohesive flow instead of copy-paste dialogs.

Muhammad Nabi Rahmani

Muhammad Nabi Rahmani

Flutter Developer passionate about creating beautiful mobile experiences

Force Update in Flutter (Part 4): force_update_helper

Force Update series: 1. Why you need it · 2. Upgrader · 3. Remote config intro · 4. force_update_helper · 5. GitHub Gist · 6. Firebase Remote Config · 7. Dart Shelf API

Part 3 defined the remote min-version policy. This part is the packaging: force_update_helper so you do not re-implement dialogs and store URLs every project.

What the package is for

You still own:

  • Where required_version lives (Gist / Firebase / API)
  • When to raise it

The package owns:

  • Calling your fetchRequiredVersion callback
  • Comparing with the installed version
  • Showing the update UI
  • Opening App Store / Play with the right ids

Dependencies

dependencies:
  force_update_helper: ^0.3.0
  package_info_plus: ^9.0.0
  url_launcher: ^6.3.2
  dio: ^5.9.0   # if you fetch via HTTP

Android store links need a query intent in AndroidManifest.xml:

<queries>
  <intent>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" />
  </intent>
</queries>

Without it, url_launcher can fail silently on modern Android.

Client shape

ForceUpdateClient(
  fetchRequiredVersion: () async {
    // Part 5/6/7 plug in here — Gist, Firebase, or Shelf
    final response = await dio.get('https://example.com/required_version');
    return response.data as String; // e.g. "1.4.2"
  },
  iosAppStoreId: Env.appStoreId, // numeric App Store id
);

Android package name usually comes from the running app; iOS needs the numeric App Store ID (not the bundle id).

Gate the app once

I mount the check at startup — after bindings / env load, before authenticated home:

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (context, child) {
        return ForceUpdateWidget(
          // package API names vary by version — see README
          client: forceUpdateClient,
          child: child ?? const SizedBox.shrink(),
        );
      },
      home: const HomeScreen(),
    );
  }
}

Read the package README for the exact widget name and parameters for the version you pin — APIs evolve; the architecture above stays stable.

What I customize

  1. Copy — "Update required" + one sentence of why (security / data / login)
  2. Hard only when current < required — no Later button for true force
  3. Analyticsforce_update_shown, force_update_opened_store
  4. Timeout — do not spin forever on a dead endpoint

Testing checklist

  • Lower required_version remote value → app opens normally
  • Raise above installed version → hard prompt
  • Tap Update → correct store listing
  • Airplane mode with empty cache → fail open (or cached policy)
  • iOS and Android both open the right listing

Summary

  • force_update_helper is the glue; policy and remote source are still your job
  • Inject different fetch implementations per environment
  • Fix Android queries + iOS App Store id before blaming the package

← Previous Part 3: Remote config intro
Next → Part 5: GitHub Gist as remote config

Share:

Keep Reading

More articles you might enjoy