从推送通知服务中访问BuildContext以导航(Flutter)

3

我很难理解如何在flutter中从Push Notification类中导航到选择的通知。我需要访问BuildContext,或者想出一种不需要它的方法告诉我的应用程序进行导航。

我的代码如下所示:

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await PushNotificationService().setupInteractedMessage();
  runApp(const MyApp());
}

Future awaitDeepLink() async {
  StreamSubscription _sub;
  try {
    await getInitialLink();
    _sub = uriLinkStream.listen((Uri uri) {
      runApp(MyApp(uri: uri));
    }, onError: (err) {

    });
  } on PlatformException {
    print("PlatformException");
  } on Exception {
    print('Exception thrown');
  }
}

class MyApp extends StatelessWidget {
  final Uri uri;

  static final FirebaseAnalytics analytics = FirebaseAnalytics.instance;

  const MyApp({Key key, this.uri}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return OverlaySupport(
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          FocusScopeNode currentFocus = FocusScope.of(context);

          if (!currentFocus.hasPrimaryFocus &&
              currentFocus.focusedChild != null) {
            FocusManager.instance.primaryFocus.unfocus();
          }
        },
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          theme: buildThemeData(),
          home: CheckAuth(uri: uri),
        ),
      ),
    );
  }
}

PushNotificationService.dart

class PushNotificationService {
  Future<void> setupInteractedMessage() async {
    RemoteMessage initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();
    String token = await FirebaseMessaging.instance.getToken();

    var storage = const FlutterSecureStorage();
    storage.write(key: "fcm_token", value: token);

    if (initialMessage != null) {
      print(initialMessage.data['type']);
    }

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print("message opened app:" + message.toString());
    });

    await enableIOSNotifications();
    await registerNotificationListeners();
  }

  registerNotificationListeners() async {
    AndroidNotificationChannel channel = androidNotificationChannel();

    final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
        FlutterLocalNotificationsPlugin();

    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    var androidSettings =
        const AndroidInitializationSettings('@mipmap/ic_launcher');

    var iOSSettings = const IOSInitializationSettings(
      requestSoundPermission: false,
      requestBadgePermission: false,
      requestAlertPermission: false,
    );

    var initSettings = InitializationSettings(
      android: androidSettings,
      iOS: iOSSettings,
    );

    flutterLocalNotificationsPlugin.initialize(
      initSettings,
      onSelectNotification: onSelectNotification,
    );

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

      if (notification != null && android != null) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              icon: android.smallIcon,
              playSound: true,
            ),
          ),
          payload: json.encode(message.data),
        );
      }
    });

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
      print("onMessageOpenedApp: $message");

      if (message.data != null) {
        print(message.data);
      }
    });

    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  }

  Future onSelectNotification(String payload) async {
    Map data = json.decode(payload);

    if (data['type'] == 'message') {
      // NEED TO ACCESS CONTEXT HERE
      // Navigator.push(
      //   navigatorKey.currentState.context,
      //   CupertinoPageRoute(
      //     builder: (navigatorKey.currentState.context) => MessagesScreen(
      //       conversationId: data['conversation_id'],
      //       userId: data['user_id'],
      //       name: data['name'],
      //       avatar: data['avatar'],
      //       projectName: data['project_name'],
      //       projectId: data['project_id'],
      //       plus: data['plus'],
      //     ),
      //   ),
      // );
    }
  }

  Future<void> _firebaseMessagingBackgroundHandler(
      RemoteMessage message) async {
    print("onBackgroundMessage: $message");
  }

  enableIOSNotifications() async {
    await FirebaseMessaging.instance
        .setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

  androidNotificationChannel() => const AndroidNotificationChannel(
        'high_importance_channel', // id
        'High Importance Notifications', // title
        importance: Importance.max,
      );
}

从onSelectNotification()函数中可以看出,我正在尝试导航,但不知道如何操作。

我对dart/flutter相当陌生,有任何指导都将不胜感激。

4个回答

4

您可以为导航设置一个全局键:

   final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

将其传递给MaterialApp:
 new MaterialApp(
          title: 'MyApp',
          onGenerateRoute: generateRoute,
          navigatorKey: navigatorKey,
        );

推送路由:

    navigatorKey.currentState.pushNamed('/someRoute');

我怎样可以从 PushNotification 类中访问 navigatorKey? - user6073700
@user6073700 navigatorKey 是 GlobalKey,因此您可以在任何地方访问它。 - JEMISH VASOYA

1
创建一个流
StreamController<Map<String, dynamic>> streamController = StreamController<Map<String, dynamic>>();

然后在这里使用它

Future onSelectNotification(String payload) async {
    Map data = json.decode(payload);
      _streamController.add(data)
  }
}

然后你可以在MaterialApp组件下的主页上听取流媒体。

  @override
  void initState() {
    streamController.stream.listen((event) {
      if (data['type'] == 'message') {
        Navigator.of(context).push(
          CupertinoPageRoute(
            builder: (context) => MessagesScreen(
              conversationId: data['conversation_id'],
              userId: data['user_id'],
              name: data['name'],
              avatar: data['avatar'],
              projectName: data['project_name'],
              projectId: data['project_id'],
              plus: data['plus'],
            ),
          ),
        );
      }
    });
    super.initState();
  }

我应该在哪里定义控制器?在推送通知类中吗?我不知道如何在main.dart中访问它。 - user6073700

1
我需要访问BuildContext
是的,您需要context来进行导航。在Flutter中,将导航代码放在小部件中是最佳实践。您可以使用上下文。
来自Andrea Bizzotto的推文线程 规则:导航代码属于小部件
如果您尝试将导航代码放在业务逻辑中,那么您将会遇到麻烦,因为您需要一个BuildContext才能这样做。
解决方案:
  • 发出新的小部件状态
  • 在小部件中侦听状态并执行导航

我认为我理解了这种方法。但是当我发出事件时,我如何在小部件上监听它?在哪个小部件上?我的应用程序中有许多屏幕,我该如何在每个屏幕上处理它? - user6073700

0

自动路由解决方案

对于使用auto_route包进行导航的任何人,以下是在没有上下文情况下进行导航的步骤。

1-将您的AppRouter添加到get_it中

要在应用程序中的任何位置访问您的AppRouter,可以将其注册到服务定位器中,例如get_it

getIt.registerSingleton<AppRouter>(AppRouter());

2 - 在 GetIt 中访问 AppRouter

getIt.get<AppRouter>().push(HomeViewRoute())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接