Flutter FCM
How to use Firebase Cloud Messaging in Flutter
This article was updated on April 15, 2020
Pengenalan
Firebase Cloud Messaging atau lebih sering kita singkat dengan sebutan FCM merupakan sebuah teknologi yang berfungsi untuk mengirimkan data (messaging) yang dikirimkan melalui cloud dan bisa diterima oleh 1 client atau bahkan lebih dari 1 client. Penggunaan FCM sering saya lihat dipakai untuk mengirimkan informasi dari server side ke client side atau dari client side ke server side terus ke client side lagi. Dan pemanfaatannya yang saya ketahui biasanya untuk fitur notifikasi atau bahkan bisa juga berfungsi untuk meng-trigger client side untuk melakukan sesuatu hal. Di Flutter, untuk plugin-nya sudah tersedia dan saat tulisan ini dipublikasikan sudah mencapai versi 6.0.13.
Contoh Projek
Untuk contoh projek kali ini kita akan membuat aplikasi sederhana saja yaitu, kita akan buat sebuah aplikasi yang berfungsi untuk menerima data FCM. Dan seperti yang kita ketahui bahwa di FCM ada 2 jenis untuk pengiriman data yaitu sebagai berikut.
Pada tulisan ini kita akan mencontohkan 2 jenis pengiriman tersebut ya.
Dan sebagai catatan saja bahwa pada tulisan ini saya akan berusaha untuk memberikan contohnya untuk Android dan IOS.
Buat Projek
Silakan kita buat projek baru dengan nama flutter_fcm dan package name-nya default saja ya yaitu com.example.flutter_fcm.
Atur pubspec.yaml
Langkah selanjutnya, kita buka file pubspec.yaml dan kita tambahkan plugin firebase_messaging kedalam projek kita.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
# Flutter plugin for Firebase Cloud Messaging.
firebase_messaging: ^6.0.13
Kemudian, unduh paket dari plugin tersebut dengan cara jalankan perintah flutter pub get
.
Buat Projek Firebase
Untuk memulai menggunakan Firebase Cloud Messaging kita perlu membuat projek di Firebase. Silakan ikuti langkah-langkah berikut.
- Buka website https://console.firebase.google.com lalu, pilih Tambahkan project.
- Lalu, kita masukkan nama projek yang mau kita buat di Firebase Console tersebut. Contohnya kita bisa beri nama flutter fcm. Jika sudah, kita pilih Lanjutkan.
- Kemudian kita pilih Lanjutkan lagi.
- Kemudian, kita harus memilih untuk akun Google Analytics-nya. Jika teman-teman sudah pernah membuatnya maka, silakan pilih yang sudah ada ya. Jika belum pernah membuatnya silakan dibuat terlebih dahulu ya. Untuk proses pembuatannya tidak akan saya jelaskan disini ya. Khawatir akan kepanjangan nanti tulisannya.
- Kemudian, pilih Buat project.
- Selanjutnya, silakan tunggu beberapa saat sampai projek kita selesai dibuat ya.
- Jika sudah selesai dibuat silakan pilih Lanjutkan.
Integrasi Android
Lalu, untuk langkah berikutnya adalah kita akan menambahkan app Android kedalam projek Firebase. Caranya silakan ikuti langkah berikut.
- Pilih icon Android pada halaman dasbor yang muncul setelah selesai membuat projek Firebase tadi.
- Kemudian, silakan kita masukkan package name dari app kita dan nama app-nya. Mohon untuk package name-nya disesuaikan dengan projek lokal masing-masing ya.
- Untuk teman-teman yang tidak tahu apa package-name-nya dilokal projeknya bisa buka file android/app/src/main/AndroidManifest.xml dan pada file tersebut nanti teman-teman bisa lihat pada tulisan
package='...'
.
- Kemudian, kita pilih Daftarkan aplikasi dan kita akan diberikan petunjuk untuk mengunduh file google-services.json. Silakan kita unduh file tersebut dengan cara pilih Download google-services.json dan kemudian, pilih Berikutnya.
- Untuk langkah selanjutnya silakan kita pilih Berikutnya.
- Lalu, kita pilih Lewati langkah ini.
- Kemudian, masukkan file google-services.json yang kita dapatkan dari Firebase tadi kedalam projek kita. Letakkan file tersebut didalam direktori android/app.
- Lalu, kita buka file android/build.gradle dan tambahkan
classpath 'com.google.gms:google-services:4.3.2'
kedalam file tersebut. Untuk versinya pastikan menggunakan versi terbaru ya.
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.2'
}
}
- Lalu, kita buka file android/app/build.gradle dan tambahkan kode berikut diakhir baris dari file tersebut.
apply plugin: 'com.google.gms.google-services'
- Langkah selanjutnya adalah kita perlu melakukan sync file build.gradle-nya. Caranya ada 2 yaitu, kita bisa menggunakan Android Studio atau dari command line. Untuk dari Android Studio, kita tinggal buka file android/app/build.gradle dan kita pilih Open for Editing in Android Studio.
- Lalu, di aplikasi Android Studio-nya kita tinggal buka menu File->Sync Project with Gradle Files.
Pastikan ketika kita menjalankan Sync Project with Gradles Files kita harus dalam kondisi terhubung ke internet.
- Untuk langkah berikutnya kita perlu menambahkan
<intent-filter>
kedalam file AndroidManifest.xml. Silakan buka file android/app/src/main/AndroidManifest.xml dan tambahkan kode berikut yang saya tandain dengan bold.
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Fungsi dari kode diatas adalah agar ketika notifikasi di-tap oleh si pengguna maka, nanti kita akan menerima callback-nya yang masuk ke fungsi onResume atau onLaunch di FCM-nya.
Integrasi IOS
Jika kamu tidak memiliki device IOS maka, kamu bisa lewati langkah ini ya. Untuk langkah-langkah ini kamu diharuskan memiliki Apple Developer Account. Kamu bisa ikuti langkah-langkahnya di sini ya.
Ubah File main.dart
Sekarang kita buka file main.dart dan ubah kodenya menjadi seperti berikut.
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final scaffoldState = GlobalKey<ScaffoldState>();
final firebaseMessaging = FirebaseMessaging();
final controllerTopic = TextEditingController();
bool isSubscribed = false;
String token = '';
static String dataName = '';
static String dataAge = '';
static Future<dynamic> onBackgroundMessage(Map<String, dynamic> message) {
debugPrint('onBackgroundMessage: $message');
if (message.containsKey('data')) {
String name = '';
String age = '';
if (Platform.isIOS) {
name = message['name'];
age = message['age'];
} else if (Platform.isAndroid) {
var data = message['data'];
name = data['name'];
age = data['age'];
}
dataName = name;
dataAge = age;
debugPrint('onBackgroundMessage: name: $name & age: $age');
}
return null;
}
@override
void initState() {
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
debugPrint('onMessage: $message');
getDataFcm(message);
},
onBackgroundMessage: onBackgroundMessage,
onResume: (Map<String, dynamic> message) async {
debugPrint('onResume: $message');
getDataFcm(message);
},
onLaunch: (Map<String, dynamic> message) async {
debugPrint('onLaunch: $message');
getDataFcm(message);
},
);
firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: true),
);
firebaseMessaging.onIosSettingsRegistered.listen((settings) {
debugPrint('Settings registered: $settings');
});
firebaseMessaging.getToken().then((token) => setState(() {
this.token = token;
}));
super.initState();
}
@override
Widget build(BuildContext context) {
debugPrint('token: $token');
return Scaffold(
key: scaffoldState,
appBar: AppBar(
title: Text('Flutter FCM'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Text(
'TOKEN',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(token),
Divider(thickness: 1),
Text(
'TOPIC',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextField(
controller: controllerTopic,
enabled: !isSubscribed,
decoration: InputDecoration(
hintText: 'Enter a topic',
),
),
Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('Subscribe'),
onPressed: isSubscribed ? null : () {
String topic = controllerTopic.text;
if (topic.isEmpty) {
scaffoldState.currentState.showSnackBar(SnackBar(
content: Text('Topic invalid'),
));
return;
}
firebaseMessaging.subscribeToTopic(topic);
setState(() {
isSubscribed = true;
});
},
),
),
SizedBox(width: 16),
Expanded(
child: RaisedButton(
child: Text('Unsubscribe'),
onPressed: !isSubscribed ? null : () {
String topic = controllerTopic.text;
firebaseMessaging.unsubscribeFromTopic(topic);
setState(() {
isSubscribed = false;
});
},
),
),
],
),
Divider(thickness: 1),
Text(
'DATA',
style: TextStyle(fontWeight: FontWeight.bold),
),
_buildWidgetTextDataFcm(),
],
),
),
);
}
Widget _buildWidgetTextDataFcm() {
if (dataName == null || dataName.isEmpty || dataAge == null || dataAge.isEmpty) {
return Text('Your data FCM is here');
} else {
return Text('Name: $dataName & Age: $dataAge');
}
}
void getDataFcm(Map<String, dynamic> message) {
String name = '';
String age = '';
if (Platform.isIOS) {
name = message['name'];
age = message['age'];
} else if (Platform.isAndroid) {
var data = message['data'];
name = data['name'];
age = data['age'];
}
if (name.isNotEmpty && age.isNotEmpty) {
setState(() {
dataName = name;
dataAge = age;
});
}
debugPrint('getDataFcm: name: $name & age: $age');
}
}
Testing Send Data to Specific Device
Sekarang kita akan testing apakah data dari FCM berhasil masuk ke app. Caranya, kita akan pakai app Insomnia saja. Btw, selain Insomnia kamu juga bisa pakai Postman atau sejenisnya ya. Kita buka app-nya dan pastikan bagian headernya seperti gambar berikut.
Untuk bagian Authorization-nya nilai key=xyz
itu bisa kamu dapatkan dari Console Firebase-nya.
Lalu, untuk bagian body-nya kita buat menjadi seperti berikut.
Untuk nilai to
silakan kamu isi dengan nilai token yang ada didevice. Caranya bisa kamu lihat dari console log di app-nya.
Testing Send Data to Topic
Sekarang kita akan test yang kirim data FCM ke topic.
Testing onResume FCM
Jika kamu perhatikan pada kode yang kita buat tadi ada property onResume
dimana, callback tersebut akan masuk ketika app kita dalam keadaan minimized. Sekarang kita test ya.
Testing onLaunch FCM
Untuk property onLaunch
sebenarnya ini hampir sama seperti onResume
hanya saja onLaunch
berjalan ketika app kita benar-benar mati.
Testing onBackgroundMessage
Untuk onBackgroundMessage
ini hampir sama seperti onLaunch
hanya saja onBackgroundMessage
dibuat untuk data FCM yang tidak ada data notification-nya.
Btw, bagi teman-teman yang device-nya merk chinese dan notifikasi FCM-nya tidak masuk mohon baca tulisan berikut ini ya.
Di tulisan tersebut saya ada memberikan solusi buat teman-teman yang device-nya merk chinese karena ada sebagian dibeberapa device bahwa Push Notification-nya itu di-disable mereka. Jadi, kita perlu setup pengaturannya secara manual.
Bonus: Kirim Notifikasi Gambar
Di Android, kita bisa mengirimkan data notifikasi gambar. Yang perlu kita lakukan adalah hanya menambahkan property image
didalam body request-nya.
Kesimpulan
Jadi, kesimpulannya adalah FCM di Flutter menurut saya sudah bagus dan siap pakai juga untuk production. Yang perlu dicatat ialah bahwa kita harus tahu kapan handle onMessage
, onResume
, onLaunch
dan onBackgroundMessage
.