iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Mobile Development

Flutter with GetX, loading*175%歷程 系列 第 26

[Day26] Flutter with GetX Push Notification

前面繁瑣的設定請參考Mark飛大大的文章

然而在Mark飛大大文章的 5.接收階段,
因為flutter firebase messaging版本有變
API的接法也有所變更, 本篇將以後續改動接著處理下去
目前處理的版本 firebase_messaging: ^10.0.6

廢話不多說 直接上code
首先是main.dart檔案 在進入點的時候先 new firebase messaging

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await PushNotificationManager.init();
  await UserDefault().init();
  runApp(MyApp());
}

接著PushNotificationManager是將FirebaseMessaging再封裝一次
大家也可以選擇自己喜歡的命名

init()裡面先取得權限與拿到FCM token,
接著就是精彩的各種狀態handle
onMessage
onMessageOpenedApp
onBackgroundMessage
getInitialMessage

比較特別的兩點:

1. App打開在前景時 Android系統會沒有推播通知
但還是會進onMessage這個function, 有些人會用 flutter_local_notifications處理
這邊用GetX snackbar(因為長得也像推播的橫幅 XDD)

2. 如果APP是關掉(拉掉)狀態的時候點開推播
查了一下stack overflow

  • 如果沒有用GetX的夥伴
    看到的作法是在首頁的initState()方法裡
    跑FirebaseMessaging.instance.getInitialMessage()
    檢查是否有推播訊息
  • 用GetX的夥伴 可以試著在
    首頁的GetxController onInit()方法裡
    跑FirebaseMessaging.instance.getInitialMessage()
    檢查是否有推播訊息
class PushNotificationManager {
  static Future<void> init() async {
    await Firebase.initializeApp();
    await requestPermission();
    await getToken();

    //iOS啟用前台通知
    if (Platform.isIOS) {
      // Required to display a heads up notification
      await FirebaseMessaging.instance
          .setForegroundNotificationPresentationOptions(
        alert: true,
        badge: true,
        sound: true,
      );
    }

    //從關掉狀態點開推播
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage? message) {
      if (message != null) {
        print("從關掉狀態點開推播");
        print('on Resume(getInitial): $message');
        pushToPage(message);
      }
    });

    //App打開在前景時
    FirebaseMessaging.onMessage.listen((message) {
      if (message.notification != null) {
        print("-------收到前景推播--------");
        print(message.notification!.body);
        print(message.notification!.title);
        if (Platform.isAndroid) showForegroundFakeNotification(message);
      }
    });

    //App退到背景,當回到App後 或是點了推播通知後
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      //AndroidNotification? android = message.notification?.android;
      if (message.notification != null) {
        print(message.notification!.body);
        print(message.notification!.title);
      }
      print("App退到背景,當回到App後 或是點了推播通知後");
      pushToPage(message);
    });

    ///後台消息
    FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
  }

  static getToken() async {
    if (Platform.isIOS) {
      String apnstoken = await FirebaseMessaging.instance.getAPNSToken() ?? "";
      print("APNSToken: $apnstoken");
    }
    String fcmToken = await FirebaseMessaging.instance.getToken() ?? "";
    print("FCMToken: $fcmToken");
    UserDefault().fcmToken = fcmToken;
  }

  static requestPermission() async {
    if (Platform.isIOS) {
      NotificationSettings settings =
          await FirebaseMessaging.instance.requestPermission(
        alert: true,
        announcement: false,
        badge: true,
        carPlay: false,
        criticalAlert: false,
        provisional: false,
        sound: true,
      );
      print('User granted permission: ${settings.authorizationStatus}');
    }
  }
}

Future<void> backgroundMessageHandler(RemoteMessage message) async {
  print("收到後臺訊息(onBackground): ${message.messageId}");
  print("後臺訊息title: ${message.notification!.title}");
  print("後臺訊息data: ${message.data.toString()}");
  await Firebase.initializeApp();
  pushToPage(message);
}

pushToPage(RemoteMessage message) {
  //這邊主要處理, 點開系統的推播通知後, 要push進哪一頁
  Get.offAll(() => FirstPage(), binding: PagesBind());
}

///安卓在App打開(前景)的時候系統不會有推播通知 利用GetX的snackbar 加一個假的
showForegroundFakeNotification(RemoteMessage message) async {
  final title = message.notification?.title ?? "";
  final body = message.notification?.body ?? "";
  Get.snackbar(
    title,
    body,
    barBlur: 50,
    icon: Image.asset('assets/ic_notification.png',
        fit: BoxFit.contain, width: 26, height: 26),
    duration: const Duration(seconds: 20),
    margin: const EdgeInsets.only(top: 4),
    onTap: (obj) {
      //print(obj);
      pushToPage(message);
    },
  );
}

本篇的GitHub source code

下一篇將為大家介紹 connectivity_plus


上一篇
[Day25] Flutter GetX API AnimatedSwitcher
下一篇
[Day27] Flutter with GetX connectivity
系列文
Flutter with GetX, loading*175%歷程 30

尚未有邦友留言

立即登入留言