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

发布于 2025-01-14 13:19:58 字数 5434 浏览 3 评论 0原文

我正在努力了解如何在颤动中选择通知时从推送通知类进行导航。我需要访问 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 am struggling to understand how to navigate from a Push Notification class on selecting a notification in flutter. I need access to the BuildContext or somehow figure out a way to tell my app to navigate without this.

My code looks like below:

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

As you can see in the onSelectNotification() function I am trying to navigate but do not know how.

I am quite new to dart/flutter so any guidance would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

感悟人生的甜 2025-01-21 13:19:58

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

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

将其传递给 MaterialApp:

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

推送路线:

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

You can set a global key for your navigation:

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

Pass it to MaterialApp:

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

Push routes:

    navigatorKey.currentState.pushNamed('/someRoute');
洛阳烟雨空心柳 2025-01-21 13:19:58

我需要访问 BuildContext

是的,您需要 context 进行导航。在 flutter 中,最佳实践是在小部件中包含导航代码。并且您的上下文

来自 Andrea Bizzotto 的 推文主题

规则:导航代码属于小部件

如果您尝试将导航代码放入业务逻辑中,您将遇到困难,因为您需要一个 BuildContext 来执行此操作。

解决方案:

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

I need access to the BuildContext

Yes you need context to navigate. In flutter its best practice to have navigation code in the widgets. and you have your context

From a tweet thread by Andrea Bizzotto

RULE: Navigation code belongs to the widgets

If you try to put your navigation code in the business logic, you'll have a hard time because you need a BuildContext to do so.

Solution:

  • emit a new widget state
  • listen to the state in the widget and perform the navigation there
神经暖 2025-01-21 13:19:58

创建一个流

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

create a stream

StreamController<Map<String, dynamic>> streamController = StreamController<Map<String, dynamic>>();

then use it here

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

and then you can listen to the stream on the home page under your MaterialApp widget

  @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();
  }
七色彩虹 2025-01-21 13:19:58

自动路线解决方案

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

1 - 将您的 AppRouter 添加到 get_it

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

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

2 - 在 GetIt 中访问 AppRouter

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

Auto Route Solution

For anyone using the auto_route package for navigation, here are the steps for navigating without context.

1 - Add your AppRouter to get_it

To access your AppRouter anywhere in your app, you can register it with a service locator like get_it:

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

2 - Access the AppRouter in GetIt

getIt.get<AppRouter>().push(HomeViewRoute())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文