flutter 에서 FCM의 동작원리를 파헤쳐 보아서 다음 알림 기능 구현시 편리한 사용을 하고자 한다.
lib
| main.dart
|
\---noti
| importNoti.dart
|
\---notis
| app.dart
| err.dart
|
\---ab
abNoti.dart
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
firebase_core: ^1.0.3
http: ^0.13.1
firebase_messaging: ^9.1.1
flutter_local_notifications: ^5.0.0+1
permission_handler: ^6.1.1
// 권한
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIVRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.USE_FRLL_SCREEN_INTENT"/>
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
// 버전 맞추기
<meta-data android:name="flutterEmbedding" android:value="2" />
noti 인스턴스를 생성하고 initState를 활용하여 초기화를 진행한다.
// 원본 소스 import는 dart.io, dart.html로 따로 분기하도록 되어있음
import 'package:crypto_alarm/noti/importNoti.dart' as appNoti;
void main() => runApp(MaterialApp(
home: CryptoAlarm(),
));
class CryptoAlarm extends StatefulWidget {
_CryptoAlarmState createState() => _CryptoAlarmState();
}
class _CryptoAlarmState extends State<CryptoAlarm> {
Noti noti = appNoti.AppNoti();
void initState() {
Future(noti.init);
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
child: Text("Local Notifications Show!"),
onPressed: () async => await noti.show(),
),
),
);
}
}
환경에 따라 다른 분기
export 'package:crypto_alarm/noti/notis/err.dart'
if (dart.library.io) "package:crypto_alarm/noti/notis/app.dart";
import 에러 핸들링, abNoti 추상 클래스 선언 상속받아서 사용
import 'ab/abNoti.dart';
class AppNoti implements Noti {
Future<bool> init() async => false;
Future<void> show() async {
print("Error");
return;
}
}
import 'dart:io';
import 'package:crypto_alarm/noti/notis/ab/abNoti.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';
class AppNoti implements Noti {
// FlutterLocalNotificationsPlugin 선언
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// 알림의 세부사항들을 설정하는 단계
// Android => id: channelId, notiTitle: channelName, notiDesc: channelDescription, importance: 알림의 중요도, priority: 우선순위
// IOS
AndroidNotificationDetails android = AndroidNotificationDetails(
'id', 'notiTitle', 'notiDesc',
importance: Importance.max, priority: Priority.max);
IOSNotificationDetails ios = IOSNotificationDetails();
// 각각의 플랫폼을 위한 구체적인 세부사항들을 포함한다.
NotificationDetails detail;
// 초기화를 진행하는 메서드
// 백그라운드 동작의 경우 최상위 함수로 설정해야함
// static 사용
static Future<void> backInit(RemoteMessage message) async {
// Firebase 기능을 사용하려면 초기화를 진행해야함
await Firebase.initializeApp();
print('Handling a background message ${message.messageId}');
return;
}
Future<bool> init() async => await Future(() async {
// 접근 요청. 만약 접근이 불가하면 접근 동의 알람을 보냄
// permissionHandler를 사용하였다.
// request 메서드만 정석으로 사용하는것으로 널리 알려져 있지만(?)
// 사용자가 알림 권한을 해제할때 등, 예외적인 상황이 있으니 고려해 보시라고 함(?)
PermissionStatus status = await Permission.notification.request();
// 접근권한이 없으면 앱 설정 페이지 열고 권한 받는것을 대기
while (status.isDenied) {
status = await Permission.notification.request();
await openAppSettings();
}
// 안드로이드 알림 아이콘과 함께 초기화
AndroidInitializationSettings androidInitializationSettings =
AndroidInitializationSettings('app_icon');
// IOS 소리, 알람, 앱 뱃지 권한 설정
IOSInitializationSettings iosInitializationSettings =
IOSInitializationSettings(
requestSoundPermission: true,
requestAlertPermission: true,
requestBadgePermission: true,
);
// 각각의 플랫폿마다 셋팅하기
InitializationSettings initSettings = InitializationSettings(
android: androidInitializationSettings,
iOS: iosInitializationSettings,
);
// 최종 셋팅 flutterLocalNotificationsPlugin.initialize(initSettings);
detail = NotificationDetails(android: android, iOS: ios);
// 채널을 만듬
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
));
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
return;
// Future init end
}).then((_) async {
// 초기화
await Firebase.initializeApp();
// 백그라운드 초기화 FirebaseMessaging?.onBackgroundMessage(AppNoti.backInit);
RemoteMessage r = await FirebaseMessaging.instance.getInitialMessage();
print("INIT r : ${r ?? 'r'}");
// 기기별로 토큰 얻기
// 토큰은 앱 삭제시 변경된다.
// 항상 서버에 보내서 토큰이 정상적인지 체크해야함
String token = await FirebaseMessaging.instance.getToken();
print('token : ${token ?? 'token NULL'}');
if (Platform.isIOS) {
// Apple 장치에서는 응용 프로그램이 백그라운드에 있거나 종료된 경우에만 알림 메시지가 표시됩니다. 이 방법을 호출하면 응용 프로그램이 포그라운드에 있는 동안 알림 표시 동작을 사용자 지정할 수 있도록 이러한 옵션이 업데이트됩니다.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
sound: true,
badge: true,
);
}
// 앱이 포그라운드 일때 알림을 listen
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
detail,
);
}
});
// 앱이 백그라운드 일때 알림을 listen
FirebaseMessaging.onMessageOpenedApp
.listen((RemoteMessage message) => print('ON_APP : $message'));
return;
});
// 알림을 보여주는 메서드
Future<void> show() async =>
this.flutterLocalNotificationsPlugin.show(1, "p 제목", "p 내용", this.detail);
}
알림 예제를 통해 구현 해보았다. 갈길이 멀다 열심히하자.