如何从 PageView 类颤动关闭屏幕

时间:2021-01-09 04:00:13

标签: flutter dart flutter-layout flutter-dependencies flutter-animation

您好, 我有一个非常具体的问题要问。我必须用步骤和图片来解释它,所以它们就在那里。 我有一个包含三个屏幕的应用:

主画面, enter image description here

主聊天和请求屏幕, enter image description here enter image description here

主配置文件屏幕, enter image description here

而且它们都是 PageView 的一部分。这个 PageView 类是在一个叫做 main_tab_controller.dart 的类中控制的。在该类中,在 initState() 中,我有一个 Firebase Messaging 方法,每次收到通知 (onMessage) 时都会调用该方法。因此,每次收到此通知时,我都会显示如下所示的叠加层。

enter image description here

它在这三个主屏幕上完美运行。如果是聊天通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,然后打开聊天屏幕。如果是请求通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,并打开请求屏幕。

但我遇到的问题如下。在我的 MainFeedScreen 和 MainProfileScreen 中,我打开了一些其他屏幕。例如在 MainFeedScreen 中,我打开 UserDetailsS​​creen 或 FilterScreen。或者在 MainProfileScreen 中,我打开 SettingsScreen 或 EditUserProfileScreen。

所以我的问题是:例如,如果我导航到 MainProfileScreen 并在该屏幕中打开 SettingsScreen,并且我收到覆盖顶部消息,我该如何关闭当前打开的 SettingsScreen 并导航回第二个屏幕,即 MainChatsAndRequestsScreen来自位于 main_tab_controller.dart 的 initState() 中的 Firebase 消息传递函数,它是所有其他屏幕的父级。 你有下面的图片:

enter image description here

我已经尝试了所有方法,Navigator.popUntil(context)、Navigator.pushReplacement(context)、使用 Navigator.pushNamed(context) 但没有任何效果。如果有人可以帮助我,将不胜感激。

只是为了让您更好地了解屏幕: 父屏幕是具有三个屏幕的 PageView:

  1. 主供稿屏幕
  2. 主聊天和请求屏幕
  3. 主配置文件屏幕

然后在主供稿屏幕中您有:

  1. 过滤器屏幕
  2. 个人资料详情屏幕

在主聊天和请求屏幕中,您有两个 TabBar 屏幕:

  1. 聊天画面
  2. 请求屏幕

在主配置文件屏幕中,您有:

  1. 设置屏幕
  2. 编辑个人资料屏幕

PageView 代码片段:

    @override
  void initState() {
    pageController = PageController(initialPage: _currentIndex);
    chatAndRequestController = TabController(length: 2, vsync: this);
    var chatAndRequestProvider =
        Provider.of<ChatAndRequestProvider>(context, listen: false);
    super.initState();
    fbm.requestNotificationPermissions();
    fbm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        bool isRequest;
        var mode = (Platform.isIOS) ? message['mode'] : message['data']['mode'];
        var imageUrl = '';
        switch (mode) {
          case 'chat':
            isRequest = false;
            imageUrl =
                chatAndRequestProvider.chatsList.first[kProfilePictureUrl];
            break;
          case 'sentRequest':
            isRequest = true;
            imageUrl = (Platform.isIOS)
                ? message['profilePictureUrl']
                : message['data']['profilePictureUrl'];
            break;
          case 'acceptRequest':
            isRequest = false;
            imageUrl = (Platform.isIOS)
                ? message['profilePictureUrl']
                : message['data']['profilePictureUrl'];
            break;
          default:
            isRequest = false;
            break;
        }
        AudioCache player = new AudioCache();
        const alarmAudioPath = "sounds/notification_sound.mp3";
        player.play(alarmAudioPath);
        print('Show this ting');
        if (_currentIndex != 1) {
          if (!isDialogOpen) {
            isDialogOpen = true;
            _showAnimatedBox(
              context,
              (Platform.isIOS)
                  ? message['aps']['alert']['title']
                  : message['notification']['title'],
              (Platform.isIOS)
                  ? message['aps']['alert']['body']
                  : message['notification']['body'],
              imageUrl,
              isRequest,
            );
          }
        }
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
      },
    );
    notificationPlugin
        .setListenerForLowerVersions(onNotificationInLowerVersions);
    notificationPlugin.setOnNotificationClick(onNotificationClick);
    _children.addAll([
      MainFeedScreen(
        analytics: widget.analytics,
        observer: widget.observer,
        latitude: widget.latitude,
        longitude: widget.longitude,
      ),
      MainChatAndRequestScreen(
        analytics: widget.analytics,
        observer: widget.observer,
        pageContoller: chatAndRequestController,
      ),
      MainProfileScreen(analytics: widget.analytics, observer: widget.observer),
    ]);
  }

Future _showAnimatedBox(context, topText, bottomText, imageUrl, isRequest) {
    showDialog(
        context: context,
        builder: (BuildContext builderContext) {
          _timer = Timer(Duration(seconds: 4), () {
            Navigator.of(context).pop();
            isDialogOpen = false;
          });
          return Dismissible(
            key: Key('dismissible'),
            direction: DismissDirection.up,
            onDismissed: (_) {
              Navigator.of(context).pop();
              isDialogOpen = false;
            },
            child: FunkyNotification(
              () {
                var chatAndRequestProvider =
                    Provider.of<ChatAndRequestProvider>(context, listen: false);
                // var contextProv =
                //     Provider.of<ContextProvider>(context, listen: false);
                chatAndRequestProvider.setAreThereNewChatsAndRequestFalse();
                if (isRequest) {
                  pageController.jumpToPage(1);
                  chatAndRequestController.animateTo(1);
                  Navigator.of(context).pop();
                  // Navigator.of(contextProv.context).pop();
                  // SystemChannels.platform.invokeMethod('SystemNavigator.pop');
                  // Navigator.popUntil(
                  //  context,
                  //  ModalRoute.withName('/mainProfileScreen'),
                  // );
                  // Navigator.of(context)
                  //     .popUntil(ModalRoute.withName('/mainProfileScreen'));
                  // Navigator.pushAndRemoveUntil(
                  //   context,
                  //   MaterialPageRoute(
                  //     builder: (BuildContext context) => MainTabBarController(
                  //       analytics: null,
                  //       observer: null,
                  //       latitude: 100.23423234,
                  //       longitude: 12.324234234,
                  //       isProfileBlocked: false,
                  //       isVersionGood: true,
                  //     ),
                  //   ),
                  //   (route) => true,
                  // );
                } else {
                  var chatAndRequestProvider =
                      Provider.of<ChatAndRequestProvider>(context,
                          listen: false);
                  pageController.jumpToPage(1);
                  chatAndRequestController.animateTo(0);
                  Navigator.of(context).pop();
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => ChatScreen(
                        appMode:
                            chatAndRequestProvider.chatsList.first[kAppMode],
                        peerId: chatAndRequestProvider.chatsList.first[kUserId],
                        peerAvatar: chatAndRequestProvider
                            .chatsList.first[kProfilePictureUrl],
                        peerName: chatAndRequestProvider
                            .chatsList.first[kNameAndSurname],
                        friendshipStatus: chatAndRequestProvider
                            .chatsList.first['friendsStatus'],
                        analytics: widget.analytics,
                        observer: widget.observer,
                      ),
                    ),
                  );
                }
              },
              topText,
              bottomText,
              imageUrl,
            ),
          );
        }).then((val) {
      if (_timer.isActive) {
        _timer.cancel();
      }
      isDialogOpen = false;
    });
  }

1 个答案:

答案 0 :(得分:1)

我会尽量使我的回答笼统,以便其他人更容易跟进。

简而言之,问题在于您有一组嵌套的屏幕分布在一组浏览量之间,并且您希望在来自外部事件(在本例中为叠加层)的浏览量之间切换。

下面是一个例子:

Structure


TL;DR

我无法提供完整代码,因为我没有您的完整源代码。但这里有一个例子?

注意:此示例使用 Provider

示例事件代码

  // Remove all the screens in the route
  Navigator.of(context).popUntil((route) => route.isFirst); // If the screen is not the first replace the check

  // Change the second pageview page
  Provider.of<ChatSelectPageView>(context, listen: false).setPage(selectedSecondPageViewPage);

  // In case it is required to add intermediate screens between the first and the second pageview it must be added here

  // Change the main pageview page
  _mainPageViewcontroller.animateToPage(1);

第二页浏览

  // Reads the page index present in the provider
  int selectedPage = Provider.of<ChatSelectPageView>(context, listen: false).page;

  // Changes to the cotroller page to the selected page
  _pageController.jumpToPage(selectedPage);

ChatSelectPageView

  class ChatSelectPageView extends ChangeNotifier {
    int page = 0;
  
    void setPage(int _page) {
      page = _page;

      // Depending on your implementation it might be better to remove this line to reduce the number of builds
      notifyListeners();
    }

  }

TS;WM

为了实现所需的行为,有多种方法可以实现。如果我们想坚持您的实施,我们将受到一些限制。但是在这种情况下,我建议您使用某种全局状态管理库,例如提供程序,无需任何库即可完成,但状态很快就会变得非常混乱。

正如您上面提到的,您尝试了 Navigator.popUntil 但它没有用,我怀疑这是因为您提供了错误的上下文。由于 Navigator.**** 依赖于上下文才能工作,即要弹出屏幕,您必须提供其上下文。或者路由检查错误。

此代码将写入外部事件中,在您的情况下,它将写入叠加层的点击侦听器中。

使用状态管理解决方案(例如 Provider)将状态传递给主页面视图的后代,向下传递到屏幕。此提供程序的类型为 ChangeNotifierProvider。单击叠加层时,将设置一个标志为所需的综合浏览量页面索引(我说的是第二次综合浏览量)。在您的情况下,此标志用于选择聊天或请求。

完成后,您调用 Navigator.of(context).popUntil((route) => route.isFirst); 假设综合浏览量出现在您应用的第一页上。如果它不在该页面上,您将不得不使用带有自定义逻辑的 Navigator.of(context).popUntil()

之后,我们将不得不导航回第二个综合浏览量,或者在您的情况下将第一个综合浏览量更改为第二个页面。由于我们之前更改了 provider 中的标志,因此第二个页面浏览将已经切换。