показывать снэк-бар в уведомлении fcm на каждом экране - PullRequest
3 голосов
/ 02 апреля 2020

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

Это мой HomeScreen.dart:

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero, () {
      NotificationManger.init(context: context);
      Fcm.initConfigure();
    });
  }

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, Store<AppState>>(
      converter: (store) => store,
      onInit: (store) => initApp(store),
      builder: (context, store) {
        return BlocProvider<HomeBloc>(
          create: (context) {
            return HomeBloc(homeRepository: homeRepository)..add(ScreenOpened());
          },
          child: BlocListener<HomeBloc, HomeState>(
            listener: (context, state) async {},
            child: BlocBuilder<HomeBloc, HomeState>(
              builder: (context, state) {
                return Scaffold(
                  key: _scaffoldKey,
                  ...
                );
              },
            ),
          ),
        );
      },
    );
  }
}

Это мой Fcm.dart

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
  if (message.containsKey('data')) {
    final dynamic data = message['data'];
  }
  if (message.containsKey('notification')) {
    final dynamic notification = message['notification'];
  }
}

class Fcm {
  static final FirebaseRepository repository = FirebaseRepository();
  static final FirebaseMessaging _fcm = FirebaseMessaging();

  static initConfigure() {
    if (Platform.isIOS) _iosPermission();

    _fcm.requestNotificationPermissions();
    _fcm.autoInitEnabled();

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async => NotificationManger.onMessage(message),
      onLaunch: (Map<String, dynamic> message) async => NotificationManger.onLaunch(message),
      onResume: (Map<String, dynamic> message) async => NotificationManger.onResume(message),
      onBackgroundMessage: myBackgroundMessageHandler,
    );

    _fcm.getToken().then((String token) {
      print('token: $token');
      repository.setUserNotifToken(token);
    });
  }

  static _iosPermission() {
    _fcm.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
    _fcm.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
      print("Settings registered: $settings");
    });
  }
}

, и это мой NotificationManager.dart:

class NotificationManger {
  static BuildContext _context;

  static init({@required BuildContext context}) {
    _context = context;
  }

  static onMessage(Map<String, dynamic> message) {
    print(message);
    _showSnackbar(data: message);
  }

  static onLaunch(Map<String, dynamic> message) {
    print(message);
  }

  static onResume(Map<String, dynamic> message) {
    print(message);
  }

  static _showSnackbar({@required Map<String, dynamic> data}) {
    // showDialog(context: _context, builder: (_) => );
    SnackBar snackBar = SnackBar(
      content: Text(
        data['data']['title'],
        style: TextStyle(
          fontFamily: 'Vazir',
          fontSize: 16.0,
        ),
      ),
      backgroundColor: ColorPalette.primary,
      behavior: SnackBarBehavior.floating,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(45.0),
      ),
      elevation: 3.0,
    );
    Scaffold.of(_context).showSnackBar(snackBar);
  }
}

main.dart

class App extends StatelessWidget {
  final Store<AppState> store;
  App(this.store);
  @override
  Widget build(BuildContext context) {
    return StoreProvider(
      store: store,
      child: MaterialApp(
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        supportedLocales: [
          Locale("fa", "IR"),
        ],
        locale: Locale("fa", "IR"),
        title: Strings.appTitle,
        theme: ThemeData(
          primaryColor: ColorPalette.primary,
          accentColor: ColorPalette.secondary,
          primaryIconTheme: IconThemeData(color: Colors.white),
          fontFamily: 'Yekan',
        ),
        onGenerateRoute: Router.generateRoute,
        initialRoute: Routes.initialRoute,
      ),
    );
  }
}

Я перехожу с ящика, и мой код:

Routing.popAndPushNamed(
    context: context,
    route: item.route,
    statusBarColor: item.screenStatusBarColor,
    popStatucBarColor: Colors.teal,
);

и моя функция:

  static void popAndPushNamed({
    BuildContext context,
    String route,
    Object arguments,
    Color statusBarColor,
    Color popStatucBarColor,
    Color popNavigationBarColor,
    Color navigationBarColor,
  }) async {
    changeSystemColor(statusBarColor, navigationBarColor);
    await Navigator.popAndPushNamed(
      context,
      route,
      arguments: arguments,
    ).then((res) {
      changeSystemColor(popStatucBarColor, popNavigationBarColor);
    });
  }

Я использую Redux и Blo c так что любой подход с этими инструментами мне подходит.

Это мой пример экрана:


class Reminders extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: appBar,
      body: Center(
        child: Text('reminders'),
      ),
    );
  }
}

РЕШЕНИЕ : добавление NotificationManger.init(globalKey: _scaffoldKey); ко всем экранам решает проблема.

class Reminders extends StatelessWidget {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    NotificationManger.init(globalKey: _scaffoldKey);

    return Scaffold(
            key: _scaffoldKey,
      appBar: appBar,
      body: Center(
        child: Text('reminders'),
      ),
    );
  }
}

Ответы [ 2 ]

2 голосов
/ 02 апреля 2020

Проблема заключается в регистрации строительных лесов для вашего виджета NotificationManager, поскольку каждый раз, когда новые леса добавляются в стек для нового экрана, вам необходимо зарегистрировать строительные леса этого экрана в NotificationManager. Это потому, что строка:

Scaffold.of(_context).showSnackBar(snackBar);

в вашем NoticicationManager будет искать дерево виджетов только до первого найденного скаффолда и вызывать его там. Поскольку вы вызываете NotificationManger.init(context: context); в своем виджете HomeScreen и передаете контекст HomeScreen, он будет жить только внутри этого каркаса. Таким образом, если вы перейдете от HomeScreen к новому виджету с другим каркасом, у него не будет NotificationManager в качестве дочернего элемента.

Чтобы устранить эту проблему, обязательно вызовите Fcm.initConfigure(); на первой странице, которая загружает для приложения и для любых страниц, по которым вы переходите, чтобы вызвать NotificationManger.init(context: context); в методе initState () для виджетов с сохранением состояния, чтобы зарегистрировать текущий каркас этой страницы, или если они являются виджетами без сохранения состояния, вы можете добавить его в метод сборки перед возвратом эшафот.

1 голос
/ 18 апреля 2020

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

в вашем методе onMessage:

onMessage(message){
Get.snackbar("message", message); // this line
}

Да, закусочная будет отображаться независимо от экрана пользователь включен, и вам не понадобится ни контекст скаффолда, ни какой-либо контекст, просто вызовите метод библиотеки и все.

lib: https://pub.dev/packages/get

...