1. Package 설치
flutter pub add firebase_messaging
flutter pub outdated
2. 앱이 종료되었거나, 백그라운드에 있을때 메시지 수신하고, 백그라운드에서 특정 작업을 수행하는 로직을 처리한다.
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
debugPrint("----------------------------");
debugPrint("background message Title: ${message.notification!.title}");
debugPrint("background message Body: ${message.notification!.body}");
debugPrint("----------------------------");
}
class NotiMain extends StatefulWidget {
const NotiMain({super.key});
@override
State<NotiMain> createState() => _NotiMain();
}
class _NotiMain extends State<NotiMain> {
@override
void initState() {
super.initState();
// Background/Terminated message handler 등록
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// 메시지 리스너 등록
_initFirebaseMessaging();
}
...
FirebaseMessaging.onBackgroundMessage는 앱이 실행 중이 아닐 때 푸시 알림을 받고 그에 따른 특정 동작을 수행할 수 있게 해주는 핵심 메소드이다.
아래에서 설명할 onMessageOpenedApp.listen 메소드 또한 백그라운드 메시지 수신을 처리하는데,
차이는
onBackgroundMessage 는 데이터 처리용으로 사용자 클릭이 불필요하고,
onMessageOpenedApp.listen 는 사용자와의 상호작을 위한 것으로 클릭을 필요로한다는 차이가 있다.
참고로 @pragma('vm:entry-point') 어노테이션은
Dart 컴파일러가 앱의 최종 빌드 크기를 줄이기 위해 최적화 과정으로 불필요한 코드를 제거하게 하는데
Dart 컴파일러에게 해당 함수를 제거하지 않도록 지지하는 명령어이다.
즉 특정 코드가 런타임에 의해 제거되지 않도록 보호하는 역할을 한다.
이런 처리가 필요한 함수들은
-
백그라운드 서비스 (Background Services): Flutter 앱이 종료된 상태에서 OS(운영체제)가 특정 Dart 함수를 호출해야 할 때.
-
플랫폼 채널 (Platform Channels): 네이티브 코드(Kotlin/Swift)에서 Dart 코드를 호출할 때.
-
JSON 직렬화/역직렬화 (Serialization): 특정 라이브러리(예: json_serializable)에서 생성된 코드.
3. 앱이 열려있거나 백그라운드 일때 메시지 수신 처리한다.
...
@override
Widget build(BuildContext context) {
return Scaffold(
...
);
}
Future<void> _initFirebaseMessaging() async {
FirebaseMessaging fcm = FirebaseMessaging.instance;
// 알림 권한이 승인된 후에만 성공적으로 토큰을 발급받을 수 있다.
NotificationSettings settings = await fcm.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
debugPrint('User granted permissions: ${settings.authorizationStatus}');
String? _token = await fcm.getToken();
debugPrint('----------------------------------------------');
debugPrint('FCM Token: $_token');
debugPrint('----------------------------------------------');
// 토큰 갱신 모니터링
fcm.onTokenRefresh.listen((newToken) {
_token = newToken;
debugPrint('FCM new Token: $_token');
});
// Foreground 메시지 수신 처리 (앱이 열려있는 상태)
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
debugPrint('----------------------------------------------');
debugPrint('Foreground Message');
debugPrint('----------------------------------------------');
if (message.notification != null) {
_showMessage('Foreground', message.notification!);
}
});
// Background 메시지 수신처리 (앱이 백그라운드 상태)
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
debugPrint('----------------------------------------------');
debugPrint('Background Message');
debugPrint('----------------------------------------------');
if (message.notification != null) {
_showMessage('Background', message.notification!);
}
});
// 앱이 완전히 종료된 상태에서 사용자 알림을 클릭하여 앱이 실행될 때 수신된 FCM 메시지 데이터를 가저온다.
RemoteMessage? message = await fcm.getInitialMessage();
if (message != null) {
debugPrint('----------------------------------------------');
debugPrint('Terminated Message');
debugPrint('----------------------------------------------');
if (message.notification != null) {
_showMessage('Terminated', message.notification!);
}
}
}
void _showMessage(String type, RemoteNotification notification) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('$type 알림: ${notification.title}'),
content: Text('${notification.body}'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('확인'),
),
],
);
},
);
}
...
FirebaseMessaging.onMessage.listen : 앱이 Foreground상태일때 메시지 수신처리
FirebaseMessaging.onMessageOpenedApp.listen : 앱이 Background상태일때 메시지 수신처리
RemoteMessage? message = await fcm.getInitialMessage(); : 앱이 Terminated(종료)상태일때, 사용자가 알림을 클릭하면서 앱이 실행될때 메시지 수신처리