Local Way to Prompt Users to Update Your Flutter App Based on App Version Check and Handling Staged Rollouts
Keeping your users on the latest version of your app is crucial for providing them with new features, bug fixes, and improvements. In mobile app development, it’s essential to notify users when a new version is available in the app store and prompt them to update. In Flutter, you can implement this functionality locally using Firebase Firestore and package_info_plus
and url_launcher
to retrieve the app's current version, compare it with the latest version available, and re-route for an app update.
In this blog post, we’ll dive into how to prompt users to download a new version of your app based on an app update check, covering the following:
Setting up Firebase Firestore to store the latest app version
Retrieving the current app version using
package_info_plus
Checking for an update and prompting the user
Handling different scenarios (iOS and Android)
Step 1: Setting up Firebase Firestore to Store the Latest App Version
Before you can notify users of a new update, you need a way to track the latest version of your app. Firebase Firestore is a great choice for this since it allows for real-time updates and easy retrieval of data.
Create a Firestore Collection: In your Firebase console, create a collection (e.g.,
current_app_version
) where you’ll store the latest app version.Go to the Firebase console → Firestore Database.
Create a new collection named
current_app_version
.Add a document with a field
version
that holds the latest app version (e.g.,"1.2.0"
).
Example:
{
"version": "1.2.0"
}
Firestore Structure: Ensure that your collection follows this structure:
Collection:
current_app_version
Document:
version
(with the latest app version as the value)
Step 2: Retrieving the Current App Version Using package_info_plus
In Flutter, the package_info_plus
package provides an easy way to get the app version installed on the user’s device. This version will be compared to the version stored in Firestore.
Adding package_info_plus
and url_launcher
to Your Project
Open your
pubspec.yaml
and add the following dependency:dependencies: url_launcher: ^6.3.0 package_info_plus: ^8.0.2
Run
flutter pub get
to install the package.
Fetching the App Version
Now, use package_info_plus
to get the current version of the app installed on the user’s device. Here’s a utility function to retrieve the app version:
import 'package:package_info_plus/package_info_plus.dart';
Future<String> getDeviceAppVersion() async {
try {
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
} catch (e) {
print('Error fetching app version: $e');
return 'unknown';
}
}
This function safely retrieves the app version and handles any errors that might occur.
Step 3: Checking for an Update and Prompting the User
With the app version now retrievable, the next step is to compare it with the version stored in Firestore. If the stored version is higher than the current app version, prompt the user to download the new version.
Firestore Setup
Ensure you have the Firebase SDK configured in your app. You can follow the official Firebase documentation for adding Firebase to your Flutter app: Firebase Setup.
Once Firebase is set up, use the following function to check the latest version from Firestore:
import 'package:cloud_firestore/cloud_firestore.dart';
Future<String> getLatestAppVersionFromFirestore() async {
final FirebaseFirestore firestore = FirebaseFirestore.instance;
try {
QuerySnapshot snapshot = await firestore.collection('current_app_version').get();
if (snapshot.docs.isNotEmpty) {
return snapshot.docs.first.get('version');
} else {
throw Exception('No version data found');
}
} catch (e) {
print('Error fetching latest app version: $e');
return 'unknown';
}
}
Comparing App Versions and Showing an Update Dialog
Now, put everything together and check if an update is needed. If the version from Firestore is greater than the installed version, show a dialog that prompts the user to download the update. This can be plugged into the initState
of your entry screen
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void checkForUpdate(BuildContext context) async {
String currentVersion = await getDeviceAppVersion();
String latestVersion = await getLatestAppVersionFromFirestore();
if (latestVersion != 'unknown' && currentVersion != 'unknown') {
if (currentVersion != latestVersion) {
// Show update dialog
_showUpdateDialog(context);
}
}
}
void _showUpdateDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Update Available'),
content: Text('A new version of the app is available. Please update to continue using the app.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Later'),
),
TextButton(
onPressed: () async {
// Launch the app store or play store URL
const url = 'https://play.google.com/store/apps/details?id=com.example.yourapp'; // Change this URL for your app
if (await canLaunch(url)) {
await launch(url);
}
},
child: Text('Update Now'),
),
],
);
},
);
}
In the _showUpdateDialog
function:
If the app is outdated, a dialog is displayed prompting the user to update the app.
The
url_launcher
package is used to open the app’s store page (either Google Play or the App Store) when the user clicks "Update Now".
Ensure that your app’s store URL is correct. For Android, it should look like:
https://play.google.com/store/apps/details?id=com.example.yourapp
For iOS, it would look like:
https://apps.apple.com/us/app/example-app/id123456789
Step 4: Handling iOS and Android Versions
Both iOS and Android users can be notified of updates using the same approach. However, it’s important to ensure that the URLs provided to url_launcher
are platform-specific. You can easily add a conditional check for the platform using Flutter’s Platform
class.
import 'dart:io' show Platform;
void _showUpdateDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Update Available'),
content: Text('A new version of the app is available. Please update to continue using the app.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Later'),
),
TextButton(
onPressed: () async {
String url = '';
if (Platform.isAndroid) {
url = 'https://play.google.com/store/apps/details?id=com.example.yourapp';
} else if (Platform.isIOS) {
url = 'https://apps.apple.com/us/app/example-app/id123456789';
}
if (await canLaunch(url)) {
await launch(url);
}
},
child: Text('Update Now'),
),
],
);
},
);
}
Handling Staged Rollouts with Firebase Remote Config
When using staged rollouts (where only a subset of users get the update initially), it's essential to avoid prompting users for an update if they're not yet part of the rollout. Firebase Remote Config can be used to control the update prompt so that it only shows up for users who are part of the rollout.
Here’s how to handle it:
Configure Firebase Remote Config:
In the Firebase console, go to Remote Config and create a parameter (e.g.,
is_update_required
) with a boolean value. You can use Firebase's percentage-based targeting to ensure only a specific percentage of users receive the update prompt.Retrieve Remote Config Data:
In your Flutter app, you can check the value of
is_update_required
from Firebase Remote Config to determine if the user should be prompted for an update.Example code to retrieve the Remote Config value:
import 'package:firebase_remote_config/firebase_remote_config.dart'; Future<bool> isUpdateRequired() async { final RemoteConfig remoteConfig = RemoteConfig.instance; try { await remoteConfig.fetchAndActivate(); return remoteConfig.getBool('is_update_required'); } catch (e) { print('Error fetching remote config: $e'); return false; // Default to no update required } }
Check the Update Condition:
Now you can modify the existing app version check logic to also consider the
is_update_required
flag from Remote Config. If the flag istrue
, proceed to compare the app versions.void checkForUpdate(BuildContext context) async { String currentVersion = await getDeviceAppVersion(); String latestVersion = await getLatestAppVersionFromFirestore(); bool updateRequired = await isUpdateRequired(); if (updateRequired && latestVersion != 'unknown' && currentVersion != 'unknown') { if (currentVersion != latestVersion) { // Show update dialog only if the rollout applies to this user _showUpdateDialog(context); } } }
Control Rollouts via Firebase:
In Firebase Remote Config, you can control the percentage of users who will see the update prompt. Initially, you can target 20-30% of users, and once you're confident the update is stable, increase the percentage to 100%.
By integrating Firebase Remote Config, you can manage staged rollouts efficiently, ensuring that only users who have access to the update are prompted to install it. This ensures a smoother user experience, preventing cases where users are directed to the store but cannot find the update.
Prompting users to download the latest version of your app is crucial for ensuring they have access to the latest features and bug fixes. With Firebase Firestore and package_info_plus
, you can easily compare the app version installed on the user’s device with the latest version available, and notify them when an update is necessary.
By following the steps in this guide, you’ve implemented a robust system for checking and prompting users to update your Flutter app across iOS and Android. This enhances user experience and ensures that your app runs as efficiently as possible with the latest updates.
Takeaways:
Use Firebase Firestore to store and manage your app’s latest version.
Retrieve the installed app version using
package_info_plus
.Use
url_launcher
to redirect users to the app store for updates.Make the solution platform-specific to handle both Android and iOS.
By implementing these best practices, you ensure your users always have the latest version of your app with a smooth update experience!
This can also be achieved using the upgrader package in pub.dev