Flutter 在启动时阻止 iOS 上的推送通知权限

Posted

技术标签:

【中文标题】Flutter 在启动时阻止 iOS 上的推送通知权限【英文标题】:Flutter block push notification permission on iOS at startup 【发布时间】:2021-03-18 15:47:06 【问题描述】:

我正在部署一个颤振应用程序,但我遇到了一个问题:在 ios 设备上运行它时,我在启动时获得了推送通知请求权限,但我想在未来的屏幕上请求权限。 具体来说,我想在带有按钮的专用页面中向用户请求许可:

Future<NotificationSettings> checkPermissions() async 
    settings = await FirebaseMessaging.instance.requestPermission(
      announcement: true,
      carPlay: true,
      criticalAlert: true,
    );
    return settings;
  

这是我的 main.dart

import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';


Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async 
  await Firebase.initializeApp();
  print('------- onBackgroundMessage ----------');


// ACTIVATE android NOTIFICATION IN FOREGROUND
const AndroidNotificationChannel channel = AndroidNotificationChannel(
  'high_importance_channel', // id
  'High Importance Notifications', // title
  'This channel is used for important notifications.', // description
  importance: Importance.high,
);

// ACTIVATE ANDROID NOTIFICATION IN FOREGROUND
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

//RemoteMessage globalMessage;

Future<void> main() async 
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  // ACTIVATE ANDROID NOTIFICATION IN FOREGROUND
  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);

  // ACTIVATE IOS NOTIFICATION IN FOREGROUND
  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );

  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      routes: 
        '/': (context) => Application(),
        '/pushNotification': (context) => MessageScreen()
      ,
    );
  


class Application extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _Application();


class _Application extends State<Application> 
  var initializationSettings;
  var initializationSettingsAndroid;
  var initializationSettingsIOS;
  RemoteMessage foregroundMessage;

  @override
  void initState() 
    super.initState();

    print("_Application : inizio initState()");

    initializationSettingsAndroid =
        AndroidInitializationSettings('launch_background');
    initializationSettingsIOS = IOSInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: false,
      onDidReceiveLocalNotification: (id, title, body, payload) async 
        // your call back to the UI
      ,
    );
    initializationSettings = InitializationSettings(
        android: initializationSettingsAndroid, iOS: initializationSettingsIOS);

    setOnNotificationClick(onNotificationClick);

    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage message) 
      if (message != null) 
        //Navigator.push(context,
        //new MaterialPageRoute(builder: (context) => new LoginScreen()));
        Navigator.pushNamed(context, '/pushNotification',
            arguments: MessageArguments(message, true));
      
    );

    FirebaseMessaging.onMessage.listen((RemoteMessage message) 
      RemoteNotification notification = message.notification;
      AndroidNotification android = message.notification?.android;

      foregroundMessage = message;

      print('-------- onMessage ----------');

      //serve per notificare in android quando l'app è in foreground
      if (notification != null && android != null) 
        flutterLocalNotificationsPlugin.show(
            notification.hashCode,
            notification.title,
            notification.body,
            NotificationDetails(
              android: AndroidNotificationDetails(
                channel.id,
                channel.name,
                channel.description,
                // TODO add a proper drawable resource to android, for now using
                //      one that already exists in example app.
                icon: 'launch_background',
              ),
            ),
            payload: message.data.toString());
      
    );

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) 
      print('---- onMessageOpenedApp ------------------');
      //Navigator.push(context,
      //  new MaterialPageRoute(builder: (context) => new LoginScreen()));
      Navigator.pushNamed(context, '/pushNotification',
          arguments: MessageArguments(message, true));
    );
  

  setOnNotificationClick(Function onNotificationClick) async 
    await flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onSelectNotification: (String payload) async 
      onNotificationClick(payload);
    );
  

  onNotificationClick(String payload) 
    print('---- onNotificationClick ------------------');
    print(payload);
    Navigator.pushNamed(context, '/pushNotification',
        arguments: MessageArguments(foregroundMessage, false));
  

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'XYZ',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      onGenerateRoute: RouteGenerator.generateRoute,
    );
  

你有什么想法吗?

谢谢

【问题讨论】:

【参考方案1】:

我是这样解决的:

initializationSettingsIOS = IOSInitializationSettings(
      requestAlertPermission: false,
      requestBadgePermission: false,
      requestSoundPermission: false,

这是关于初始化方法的sdk建议:

/// Call this method on application before using the plugin further.
    ///
    /// Will return a [bool] value to indicate if initialization succeeded. On iOS this is dependent on if permissions have been granted to show
    /// notification When running in environment that is neither Android and iOS (e.g. when running tests), this will be a no-op and return true.
    ///
    /// Note that on iOS, initialisation may also request notification permissions where users will see a permissions prompt. This may be fine in
    /// cases where it's acceptable to do this when the application runs for the first time. However, if your application needs to do this at a
    /// later point in time, set the [IOSInitializationSettings.requestAlertPermission], [IOSInitializationSettings.requestBadgePermission] and
    /// [IOSInitializationSettings.requestSoundPermission] values to false. [IOSFlutterLocalNotificationsPlugin.requestPermissions] can then be
    /// called to request permissions when needed.



await flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onSelectNotification: (String payload) async 
      onNotificationClick(payload);

【讨论】:

【参考方案2】:

我们遇到了同样的问题,我们发现 FlutterLocationNotificationsPlugin 是罪魁祸首。在我们的例子中,我们不再需要本地通知插件(我们使用 Firebase 远程消息,并且只使用本地通知进行测试)。

所以要么:从pubspec.yml 中删除flutter_local_notifications,或者在使用它们之前应用@masudani 描述的补丁。

关于 Flutter 的 Github 问题的信息:https://github.com/FirebaseExtended/flutterfire/issues/5897

【讨论】:

以上是关于Flutter 在启动时阻止 iOS 上的推送通知权限的主要内容,如果未能解决你的问题,请参考以下文章

如果应用程序未启动,Flutter Firebase 推送通知无法数据

Android 5 Asus ZenFone 2 在应用关闭时阻止推送通知

iOS Flutter 没有收到推送通知

当应用程序在后台或终止时,iOS 设备不会推送通知。导航到下一个屏幕在 Flutter 的 IOS 设备中也不起作用?

收到消息时向 iOS 推送通知不起作用

更新远程推送通知上的 UIMutableUserNotificationAction 按钮标题