Доступ к BuildContext для перехода от службы push-уведомлений (Flutter)

Я изо всех сил пытаюсь понять, как перейти от класса push-уведомлений при выборе уведомления в флаттере. Мне нужен доступ к BuildContext или как-то найти способ сообщить моему приложению, чтобы он перемещался без этого.

Мой код выглядит следующим образом:

основной дротик

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(), я пытаюсь ориентироваться, но не знаю, как это сделать.

Я новичок в дартс/флаттере, поэтому буду признателен за любые рекомендации.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
48
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

I need access to the BuildContext

Да, вам нужно context для навигации. Во флаттере лучше всего иметь код навигации в виджетах. и у вас есть свой контекст

Из ветка твита Андреа Биззотто

RULE: Navigation code belongs to the widgets

Если вы попытаетесь поместить свой навигационный код в бизнес-логику, вам придется нелегко, потому что для этого вам нужен BuildContext.

Решение:

  • создать новое состояние виджета
  • слушайте состояние в виджете и выполняйте там навигацию

Думаю, я понимаю этот подход. Но когда я испускаю событие, как я могу прослушать его в виджете? На каком виджете? У меня много экранов в моем приложении, как бы я справился с этим на каждом?

user6073700 16.03.2022 18:50
Ответ принят как подходящий

Вы можете установить глобальный ключ для навигации:

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

Передайте его в MaterialApp:

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

Пуш-маршруты:

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

Как я могу получить доступ к этому navigatorKey из класса PushNotification?

user6073700 17.03.2022 11:23

@user6073700 navigatorKey — это GlobalKey, так что вы можете получить доступ куда угодно.

JEMISH VASOYA 18.03.2022 03:23

создать поток

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

Где я определяю контроллер. В классе push-уведомлений? Я не знаю, как я могу получить доступ к этому в main.dart

user6073700 16.03.2022 22:48

Другие вопросы по теме