[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


<<:  Mind Map 与 Roadmap

>>:  後记

Day19. 後端工程师需要具备的前端常识

请问你只会<br /> 跟&nbsp; 吗 ? 如果你只会这些的话,这篇会对你的...

[30天 Vue学好学满 DAY27] axios-mock-adapter-2

带参数 mock mock_adapter.onGet("/todo-list"...

day30 : 写不完所有东西的最後一天

30天的最後一天,写到最後几天才发现有一些想分享的没有篇幅能写入了,所以今天我认为分享的内容偏实用的...

Promise 方法

今天继续认识四种 Promise 可以使用的方法,基础的用法可以先参考昨天的文章 Promise.a...

那些多人混战的开发经验谈

今年年初,刚好被大学学长推坑 COSCUP 开发组,这次的官网是以去年为基础去做修改的,所以并没有花...