본문 바로가기
728x90
반응형

re오늘은 회사에서 백그라운드 알림을 해보았습니다 :)

카톡이나, 인스타그램을 보면 메세지가 왔을 때 알람이 오는데, 그러한 알림 서비스 기능을 만드는 작업을 해보았는데요 :ㅐ

한 번 정리해보곘습니다~!

메인 사진

 

저희는 메세지 푸시 알림을 위해 패키지를 사용할 예정입니다. 

아래 패키지는 Firebase Cloud Messaging(FCM)을 Flutter 애플리케이션에서 사용할 수 있도록 지원하는 패키지로, 우리는 앱에서 발급 받을 FCM 토큰을 발급받아 사용할 예정입니다. 

https://pub.dev/packages/firebase_messaging

 

firebase_messaging | Flutter package

Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS.

pub.dev

개발 환경 세팅을 위해서 아래 링크를 통해 iOS의 알림을 주기 위한 세팅을 해주어야합니다.

아무래도 Flutter는 패키지를 쓰기 위해서 안드로이드와, iOS 환경을 따로 설정해줘야할 때가 있는데여, 여기서는 iOS와 Firebase간의 연결을 위해 APN 키를 발급 받아 firebase 클라우드 메세징에 추가해주어야합니다. 

https://developers.fingerpush.com/app-push/sdk-manual/ios/apn-key

 

APN 인증 키(.p8) 발급 및 업로드 - APP PUSH

4) 저장한 인증 키를 업로드하고 Key ID, Team ID, Bundle ID를 입력 후 저장합니다.

developers.fingerpush.com

 

그럼 코드에 대해서 알아보겠습니다 :)

아래와 같이 각 코드들을 세팅해주세요. 저는 상태관리로 provider를 사용하였습니다.

코드의 흐름은 다음과 같습니다. 

  1. 앱 로딩시 알림을 보낼 수 있는 FCM 토큰을 발급을 발급한다.
  2.  백그라운드에서도 알림을 받을 수 있도록 세팅한다.
    • iOS와 안드로이드 별로 세팅이 다르고, 알림을 받았을 때 로컬 푸시에서 다시 알림을 줄 수 있도록 해주는 것 입니다. 
  3. FCM 토큰을 서버에 저장하여 연동할 수 있도록 한다.(이 코드는 없습니다.)
  • ios/Runner/AppDelegate.swift
import UIKit
import Flutter
import Firebase


@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, MessagingDelegate {
override func application(
    _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
 FirebaseApp.configure()
    
 Messaging.messaging().delegate = self
 GeneratedPluginRegistrant.register(with: self)

  if #available(iOS 10.0, *) {
    // For iOS 10 display notification (sent via APNS)
    UNUserNotificationCenter.current().delegate = self
    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
      func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
          print("Firebase registration token: \(fcmToken!)")

          let dataDict:[String: String] = ["token": fcmToken!]
          NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
          // If necessary send token to application server
      }
    UNUserNotificationCenter.current().requestAuthorization(
            options: authOptions,
            completionHandler: {_, _ in })
} else {
    let settings: UIUserNotificationSettings =
    UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
    application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
   }

 

  • lib/provider/push_notification_provider.dart
import 'dart:developer'; // 디버깅을 위한 라이브러리
import 'dart:io'; // I/O 관련 라이브러리
import 'package:firebase_messaging/firebase_messaging.dart'; // Firebase Cloud Messaging을 위한 라이브러리
import 'package:flutter/material.dart'; // Flutter UI 툴킷 라이브러리
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; // 로컬 알림을 위한 라이브러리
import 'package:device_info_plus/device_info_plus.dart'; // 디바이스 정보를 얻기 위한 라이브러리

class PushNotificationController extends ChangeNotifier {
  final FirebaseMessaging _fcm = FirebaseMessaging.instance; // FirebaseMessaging 인스턴스 생성
  FlutterLocalNotificationsPlugin? flutterLocalNotificationsPlugin; // 로컬 알림을 위한 플러그인

  // FCM 토큰을 얻고, 로컬 알림을 초기화하고, FCM 메시지를 받아 로컬 알림을 표시하는 함수
  Future<void> initialize() async {
    if (flutterLocalNotificationsPlugin != null) {
      log("flutterLocalNotificationsPlugin already initialized"); // 이미 초기화 되었다면 로그 출력 후 함수 종료
      return;
    }
    String? token = await _fcm.getToken(); // FCM 토큰 얻기
    String? deviceId = await _getDeviceId(); // 디바이스 ID 얻기
    log("FCM Token: $token"); // 로그에 FCM 토큰 출력
    log("deviceId: $deviceId"); // 로그에 디바이스 ID 출력

    flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // 로컬 알림 플러그인 초기화
    await flutterLocalNotificationsPlugin!.initialize(initializationSettings); // 로컬 알림 설정 초기화

    // FCM 메시지를 받아 로컬 알림을 표시하는 이벤트 리스너
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;

      if (notification != null && android != null) {
        showNotification(notification); // 로컬 알림 표시
      }
    });

    // 백그라운드에서 FCM 메시지를 처리하는 함수 설정
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  }

  // 백그라운드에서 FCM 메시지를 받아 로컬 알림을 표시하는 함수
  Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
    log("Handling a background message: ${message.messageId}"); // 로그에 메시지 ID 출력
    RemoteNotification? notification = message.notification;
    AndroidNotification? android = message.notification?.android;

    if (notification != null && android != null) {
      showNotification(notification); // 로컬 알림 표시
    }
  }

  // 로컬 알림을 표시하는 함수
  Future showNotification(RemoteNotification notification) async {
    await flutterLocalNotificationsPlugin!.show(
      0,
      notification.title,
      notification.body,
      platformChannelSpecifics,
      payload: 'Default_Sound', // 알림에서 소리를 재생하도록 설정
    );
  }

  // 디바이스 ID를 얻는 함수
  Future<String?> _getDeviceId() async {
    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); // 디바이스 정보를 얻기 위한 플러그인
    if (Platform.isAndroid) { // 안드로이드 디바이스인 경우
      AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
      return androidInfo.id; // 안드로이드 디바이스 ID 반환
    } else if (Platform.isIOS) { // iOS 디바이스인 경우
      IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
      return iosInfo.identifierForVendor; // iOS 디바이스 ID 반환
    }
    return ''; // 디바이스 ID를 얻을 수 없는 경우 빈 문자열 반환
  }

  // 로컬 알림 초기화 설정
  static const initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); // 안드로이드 알림 아이콘 설정
  static const initializationSettingsIOs = DarwinInitializationSettings(); // iOS 알림 설정
  static const initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid, iOS: initializationSettingsIOs); // 로컬 알림 초기화 설정
  static const androidPlatformChannelSpecifics = AndroidNotificationDetails(
    'channel_ID',
    'channel name',
    importance: Importance.max,
    priority: Priority.high,
    showWhen: false, // 알림 시간을 표시하지 않도록 설정
  );
  static const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); // iOS 알림 상세 설정
  static const platformChannelSpecifics = NotificationDetails(
      android: androidPlatformChannelSpecifics,
      iOS: iOSPlatformChannelSpecifics); // 플랫폼별 알림 상세 설정
}
  • main.dart
    • 희한하게 위에 코드와 같이 한 번 더 await FirebaseMessaging.instance.getInitialMessage(); 선언을 안해주면, Function에 대해 없다고 뜨더라고요,, 그래서 넣어두었습니다! 
@pragma('vm:entry-point')
// 백그라운드에서 FCM 메시지를 처리하는 함수
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  final pushController = PushNotificationController(); // PushNotificationController 인스턴스 생성
  await pushController.initialize(); // pushController 초기화
  pushController.showNotification(message.notification!); // 받은 FCM 메시지를 로컬 알림으로 표시
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized(); // Flutter 엔진과의 통신을 초기화
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  ); // Firebase SDK 초기화
  // 앱이 백그라운드 상태일 때 FCM 메시지를 처리하는 함수 설정
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  final pushNotificationController = PushNotificationController(); // PushNotificationController 인스턴스 생성

  // 앱이 처음 실행될 때 FCM 메시지를 얻음
  RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();

  runApp(MultiProvider(
      providers: [
        // PushNotificationController 인스턴스를 프로바이더로 등록
        ChangeNotifierProvider(create: (context) => pushNotificationController),
      ],
      child: MyApp(
        isLogin: isLogin,
        initialRoute: initialRoute,
        pageIndex: pageIndex
      )));
}

 

 

 

 

 

728x90
반응형