Practice with Firebase Remote Config in Flutter application
This tutorial is a practice on firebase_remote_config package for Flutter application.
π About Firebase Remote Config
A cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. More information: firebase.google.com/docs/remote-config
π Which use case should use Remote Config?
- A/B testing
- UI/UX localization
- Small app data
π Implementation
1. Add dependencies to pubspec.yaml
of Flutter project:
firebase_core: ^1.6.0
firebase_remote_config: ^0.10.0+5
2. Configure Firebase project
Note: In this tutorial, I made it for Android and iOS only.
2.1. Setup for Android:
- Guideline: firebase.flutter.dev/docs/installation/andr.. (This guide is out of date, see the new one at https://firebase.google.com/docs/flutter/setup?platform=android)
- Quick steps (For those who are lazy to read above π΄)
- Download the config file (google-services.json) from console.firebase.google.com and copy to /android/app/
- Add this to
android/build.gradle
:
buildscript {
dependencies {
//...
classpath 'com.google.gms:google-services:4.3.8'
}
}
- Add this to
android/app/build.gradle
:
apply plugin: 'com.google.gms.google-services'android {
defaultConfig {
//...
multiDexEnabled true
}
}dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
- Add this to
android/app/src/debug/AndroidManifest.xml
:
<application android:usesCleartextTraffic="true">
</application>
2.2. Setup for iOS:
- Guideline: firebase.flutter.dev/docs/installation/ios (This guide is out of date, see the new one at https://firebase.google.com/docs/flutter/setup?platform=ios)
- Quick steps (For those who are lazy to read above π΄)
- Download the config file (GoogleService-Info.plist) from console.firebase.google.com
- Open Xcode /ios/{projectName}.xcworkspace
- Right-click on Runner, choose
Add files
and navigate to selectGoogleService-Info.plist
file. - Add this to
ios/Runner/Info.plist
:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
- Add this to
ios/Podfile
:
# Uncomment this line to define a global platform for your project
platform :ios, '10.0'
#...
target 'Runner' do
#...
pod 'Firebase/Core'
end
2.3. Add Remote Config data:
- Go to console.firebase.google.com
- Engage > Remote Config
- Add data with name/key and value. For eg:
- Name/Key:
user_data
- Value:
{
"name":"erik",
"age":"100",
"address":"711-2880 Nulla St.",
"job":"Poor developer",
"email":"erik@gmail.com"
}
Remember to publish changes !!!
3. Implementation in Flutter project
- Init Firebase:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
- Create entity that maps with value from RemoteConfig:
class UserEntity {
String? name;
String? age;
String? address;
String? job;
String? email; UserEntity({
this.name,
this.age,
this.address,
this.job,
this.email}); UserEntity.fromJson(dynamic json) {
name = json['name'];
age = json['age'];
address = json['address'];
job = json['job'];
email = json['email'];
} Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map['name'] = name;
map['age'] = age;
map['address'] = address;
map['job'] = job;
map['email'] = email;
return map;
}}
- Init a RemoteConfig instance:
class _MyHomePageState extends State<MyHomePage> { final RemoteConfig remoteConfig = RemoteConfig.instance;
@override
void initState() {
super.initState();
() async {
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval: const Duration(hours: 1),
));
}();
}
- I want to parse json String in background, so I will use Isolate here. By the way, for anyone needed, this video explains the diff between
Async vs Isolates
(ez to understand):
class Util {Future<UserEntity> parseJsonConfig(String rawJson) async {
final Map<String, dynamic> parsed = await compute(decodeJsonWithCompute, rawJson);
final userEntity = UserEntity.fromJson(parsed);
return userEntity;
} static Map<String, dynamic> decodeJsonWithCompute(String rawJson) {
return jsonDecode(rawJson);
}}
- Function to fetch remote data:
Future<void> _syncData() async {
showLoading(context);
try {
await remoteConfig.fetchAndActivate();
final rs = remoteConfig.getString(remote_user_data_key);
Navigator.pop(context); // hide loading
} catch (e) {
print(e);
}
}void showLoading(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 8.0),
Text('Loading...')
],
),
);
},
);
}
- Now I need a notifier to UI when data has changed. I donβt like
setState()
very much, I'm going to useValueNotifier
here:
class DataValueNotifier extends ValueNotifier<UserEntity?> {
DataValueNotifier() : super(null);
}
...class _MyHomePageState extends State<MyHomePage> {
final dataNotifier = DataValueNotifier();
final util = Util(); ... Future<void> _syncData() async {
...
dataNotifier.value = await util.parseJsonConfig(rs);
... }
- Now, when the data has already, letβs show it:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ValueListenableBuilder(
valueListenable: dataNotifier,
builder: (context, UserEntity? value, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Name: ${value?.name}'),
Text('Age: ${value?.age}'),
Text('Address: ${value?.address}'),
Text('Email: ${value?.email}'),
Text('Job: ${value?.job}'),
],
),
);
}
),
floatingActionButton: FloatingActionButton(
onPressed: () => _syncData(),
tooltip: 'Sync',
child: Icon(Icons.sync),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
π Demo
youtube.com/watch?v=p1BfHKKcpNw
π Full example source code
flutter-firebase-remoteconfig-sample
π Stay in touch with me for updates:
- Twitter: https://twitter.com/HuyNguyenTw
- GitHub: https://github.com/huynguyennovem
- LinkedIn: https://linkedin.com/in/huynguyennovem