Force Update in Flutter (Part 3): Remote Config Approach
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 2 used store comparison: "is there a newer build?" This part is the strategy I actually ship on apps with a backend: a remote minimum version.
The idea
bool isAppUpdateRequired() {
// skip web / desktop as needed
final requiredVersion = fetchRequiredVersion(); // remote
final currentVersion = packageInfo.version;
return currentVersion < requiredVersion;
}
On startup (after splash, before main UI):
- Fetch
required_versionfrom somewhere you control - Compare with
package_info_plus - If outdated → non-dismissible "Update now" → store listing
- If OK → enter the app
upgrader vs remote min version
| upgrader | Remote required_version | |
|---|---|---|
| Trigger | Newer build exists in store | You raised the floor above installed version |
| Control | App stores | You |
| Good for | Always stay current | API breaks, security, staged deprecation |
| Risk | Nags on every release | Mis-set version bricks UX if store build is missing |
I use remote min version when I need to say: "1.4.2 and below must die; 1.5.0+ is fine even if 1.6 is already live."
When remote force update saves you
- Backend contract changed; old clients error in production
- Security issue in a dependency that needs a binary rebuild
- Auth / login path broken on older builds
- You are migrating data models and cannot support infinite client history
Workflow:
- Ship fixed build to stores
- Wait until it is downloadable
- Raise
required_versionremotely - Old clients hit the wall on next open
What "remote" can mean
You will implement one of these in the next parts:
- GitHub Gist — static JSON, zero infra, rate limits
- Firebase Remote Config — ideal if Firebase is already in the app
- Your API / Dart Shelf — full control, multi-env, no gist limits
The client algorithm stays the same. Only the fetch changes.
Soft vs hard with remote policy
Remote config can expose more than one field:
{
"required_version": "1.4.0",
"recommended_version": "1.5.2",
"force": true
}
current < required→ hard forcerequired <= current < recommended→ soft banner- else → nothing
Start with a single required_version + hard dialog. Add soft later if product wants it.
Failure modes (design these first)
| Situation | Policy I use |
|---|---|
| Offline / fetch fails | Use last cached required version; if none, fail open |
| Timeout | Same as fail |
| required > any store build | Do not raise the flag yet — support nightmare |
| Web | Skip store force path |
Architecture sketch
lib/src/feature/force_update/
domain/
version_policy.dart # pure compare
data/
required_version_source.dart # gist / RC / API
application/
force_update_service.dart
presentation/
force_update_gate.dart # wraps app or home
Keep comparison pure and unit-tested. Network code stays thin.
Summary
- Remote min version = you decide when a binary is dead
- Fetch once at startup, compare with real semver, then gate UI
- Same client pattern for Gist, Firebase, or custom API
- Never raise required_version before the fix is live on stores
← Previous Part 2: Upgrader
Next → Part 4: force_update_helper package



