[Flutter] FCM을 통해 푸시 알림을 보낼 때 OS별 고려 사항

안녕하세요, 도쿄 도내에서 업로드를 시작할 때 Fluter x Firebase를 사용하세요
나는 엔지니어 실습을 하는 등나무다.
이번에FCM(Firebase Cloud Messaging) 추진 알림 때 막힌 곳을 소개합니다.
Flutter로 FCM을 제어하는 방법여기.의 note 기사를 쉽게 알 수 있도록 하고 싶습니다.

🙅‍♀️ 문제점


Cloud Function Push 알림을 당초에 이런 식으로 구현했습니다.
언뜻 보기에는 순조로운 것 같지만 다음과 같은 문제가 발생했다.
const options = {
  priority: "high",
};
const payload: admin.messaging.MessagingPayload = {
  notification: {
    title: message.title,
    body: message.body,
    click_action: "FLUTTER_NOTIFICATION_CLICK",
    badge: message.badgeNum,
    sound: "default",
  },
};
await admin.messaging().sendToDevice(message.fcmToken, payload, options);

🤖 Android

  • 알림 트레이에서 프로그램을 시작할 때notification ペイロード가 비어 있습니다.
  • 화면 위에 알림을 표시하고 싶습니다.( Heads-up通知 )
  • 그림은 우리측 응용 개발 중인 것으로 메시지를 받는 것을 촉발점으로 하고 응용 프로그램에 대화상자를 표시합니다.
    알림 트레이에서 프로그램을 시작하면 빈 대화상자가 표시됩니다.
    blank
    또 채팅 기능이 있어 다른 앱을 시작할 때도 화면에 알림을 표시하고 싶어한다.
    heads-up
    이런 느낌.(이하 태그는 Heads-up通知

    🍎 iOS

  • 알림 트레이에서 프로그램을 시작할 때 두 번의 메시지를 표시합니다
  • 프로그램이 완전히 잃어버린 상태에서 알림 트레이에서 시작하면 같은 정보 대화상자가 두 번 표시됩니다.

    🙆해결 방안과 주의점을 탐색하다


    🤖 Android


    1. onResume, onLaunch를 트리거점으로 할 때Payload에 데이터 속성을 보내야 합니다.


    백그라운드에서 프로그램은 알림 트레이에서 유효 하중을 수신하고 사용자가 알림을 눌렀을 때만 유효 하중을 처리합니다.
    출전:공식 문서
    즉, 유효 하중이 없으면 알림 트레이에서 헤더를 뽑을 때 빈 데이터를 읽는다
    빈 대화 상자가 표시됩니다.
    이전 Function의 유효 하중 변경 후 문제점 ①을 지웁니다.
    const options = {
      priority: "high",
    };
    const payload: admin.messaging.MessagingPayload = {
      notification: {
        title: message.title,
        body: message.body,
        click_action: "FLUTTER_NOTIFICATION_CLICK",
        badge: message.badgeNum,
        sound: "default",
      },
      // 以下追加
      data: {
        title: message.title,
        body: message.body,
      },
    };
    await admin.messaging().sendToDevice(message.fcmToken, payload, options);
    

    2. Heads-up 알림을 위해 알림 채널을 잘 설정해야 합니다.


    Android 8.0(API 클래스 26) 이후 알림이 채널에 모두 할당되어야 합니다.모든 채널에 대해 모든 채널 알림에 사용할 디스플레이와 오디오 동작을 설정할 수 있습니다.
    출전:공식 문서
    따라서 FCM이 기본적으로 제작하는 알림 채널이라면 Heads-up 알림의 허가가 취소된 것 같습니다.
    그림その他의 알림 채널은 기본값이지만 사용자가 팝업 각도를 자동으로 열지 않으면
    Heads-up 알림이 오지 않습니다.
    또한 다시 설치할 때 반드시 설치해야 한다.
    channel
    방법은 다음과 같다.
  • 에 기본 알림 채널의 설정을 추가합니다.

  • 이용flutter_local_notifications.
  • Kotolin 로컬 코드에서 채널을 만듭니다.
  • 1 단순히 이해가 부족해서 플utter에서 움직일 수 없어서 포기한 것 같다.
    2 새 프로젝트에서는 문제가 없는 선택이지만 이번에는 판단 기능이 너무 많아 채택하지 않는다.
    이번에 Kotlin의 로컬 코드를 호출해서 알림 채널을 만들었습니다.
    package com.example   // 自身のプロジェクトコード(ユニークキー)を入れてください
    import androidx.annotation.NonNull
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodChannel
    import android.content.Context
    import android.content.ContextWrapper
    import android.content.Intent
    import android.content.IntentFilter
    import android.os.Build.VERSION
    import android.os.Build.VERSION_CODES
    import android.app.NotificationManager;
    import android.app.NotificationChannel;
    import android.net.Uri;
    import android.media.AudioAttributes;
    import android.content.ContentResolver;
    
    class MainActivity: FlutterActivity() {
      private val CHANNEL = "com.example/channel" // チャンネルの名前
    
      override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
          // FCMService()からのinvoke()
          call, result ->
          if (call.method == "createNotificationChannel"){
            val argData = call.arguments as java.util.HashMap<String, String>
              val isCompleted = createNotificationChannel(argData)
              if (isCompleted == true){
                  result.success(isCompleted)
              }
              else{
                  result.error("Error Code", "Error Message", null)
              }
          } else {
            result.notImplemented()
          }
        }
    
      }
        // NotificationChannelの作成
        private fun createNotificationChannel(mapData: HashMap<String,String>): Boolean {
            val isCompleted: Boolean
            if (VERSION.SDK_INT >= VERSION_CODES.O) {
    	   // Flutter側からの値
                val id = mapData["id"]
                val name = mapData["name"]
                val descriptionText = mapData["description"]
                val importance = NotificationManager.IMPORTANCE_HIGH
                val myChannel = NotificationChannel(id, name, importance)
                myChannel.description = descriptionText
                val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannel(myChannel)
                isCompleted = true
            }
            else{
                isCompleted = false
            }
            return isCompleted
        }
    }
    
    (보충)
    Heads-up 알림을 위해 AndroidManifest.xml
     val importance = NotificationManager.IMPORTANCE_HIGH
    
    이 부분에서 그 설정을 했다.
    그림은 여기.에서 빌려온 것이다.
    importance
    메모 감사합니다!
    Flutter에서는 通知の重要度の設定が緊急になっているチャンネルの設定が必要부터 invoke 로컬 코드를 시작합니다.
      Future<void> createNotificationChannel() async {
        const _channel = sevices.MethodChannel('com.example/channel');
        const channelMap = {
          'id': 'SAMPLE_CHANNEL', // FCMからこの名前で呼び出す
          'name': 'サンプルアプリ', // エンドユーザーが設定でみる名前
          'description': 'サンプルアプリの通知です',
        };
        try {
          // kotlin側'MainActivity.kt'を呼び出し
          await _channel.invokeMethod('createNotificationChannel', channelMap);
        } catch (e) {
          print('error in FCM.createNotificationCannel(): ' + e.toString());
        }
      }
    
    에 나오는 채널은 이런 느낌이다.
    잘 튀어나온 것 같은데.
    head-up-success
    마지막으로 Function 측면MethodChannel을 지정합니다.
    const options = {
      priority: "high",
    };
    const payload: admin.messaging.MessagingPayload = {
      notification: {
        title: message.title,
        body: message.body,
        click_action: "FLUTTER_NOTIFICATION_CLICK",
        badge: `message.badgeNum,
        sound: "default",
        android_channel_id: 'SAMPLE_CHANNEL'
      },
      data: {
        title: message.title,
        body: message.body,
      },
    };
    await admin.messaging().sendToDevice(message.fcmToken, payload, options);
    

    🍎 iOS


    1. Kill 적용 시 알림에서 시작할 때 Channel ID, On Resume가 트리거됩니다.


    여기.에 실린 논평을 참고했습니다.
    즉, 이 프로그램의 경우 프로그램이 완전히 닫혔을 때 알림 트레이 등에서 시작합니다
    소~ 괜찮네.
    대화상자가 두 번 표시됩니다.
    같은 내용의 통지는 OnLaunch,onResume에서 나온 것처럼 감시가 필요하다.
    대화상자에 표시되는 방법으로 이전 알림과 이전 알림을 함께 통과합니다.
      // iOSでアプリkill時にOnResumeとOnLaunchが同時にトリガーされる問題を監視
      if (Theme.of(context).platform == TargetPlatform.iOS &&
          (trigger == 'onLaunch' || trigger == 'onResume') &&
          notification == lastNotification) return null;
    

    📌 끝맺다


    다시 한 번 소스의 중요성과 괴로움을 절감한다면 GiitHub의 issue를 보면 같은 문제가 있는 사람이 존재한다.
    이번에 소개한 내용이 틀리면 트위터에 글을 남겨주세요.
    (본사는'카라카'라는 게임 커뮤니티 앱을 만들고 있습니다. 부업이든 전업이든 플루터 엔지니어를 모집하고 있습니다. 관심 있는 분들은 DM부터 시작하세요!)

    좋은 웹페이지 즐겨찾기