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

Access BuildContext to navigate from Push Notification Service (Flutter)

我正在努力了解如何在 flutter 中选择通知时从推送通知导航 class。我需要访问 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 的新手,所以任何指导都将不胜感激。

I need access to the BuildContext

是的,您需要 context 才能导航。在 flutter 中,最好的做法是在小部件中包含导航代码。你有你的背景

来自 Andrea Bizzotto 的 tweet thread

RULE: Navigation code belongs to the widgets

如果您尝试将导航代码放入业务逻辑中,您将遇到困难,因为您需要 BuildContext 才能这样做。

解决方案:

  • 发出新的小部件状态
  • 监听小部件中的状态并在那里执行导航

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

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

传递给 MaterialApp:

 new MaterialApp(
          title: 'MyApp',
          onGenerateRoute: generateRoute,
          navigatorKey: navigatorKey,
        );

推送路线:

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

创建流

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();
  }