首页 > 解决方案 > 为什么我的 WillPopScope 只是偶尔在 Flutter 导航中工作是有原因的?

问题描述

我正在编写一个 Flutter 应用程序,它使用我认为可能不是浏览屏幕的正确方式。我正在实现一些提供程序,因此我试图避免声明式 Navigator.push 导航风格,这导致我基本上使用索引来告诉应用程序在任何给定时间显示多个屏幕中的哪个。In the example below, the messages screen is a tab inside an IndexedStack, and when a specific "chat" is selected it changes the messagesIndex, changing the return value from a list of all chats to a display of the specific chat.

列出所有聊天的消息屏幕:

class MessagesScreen extends StatefulWidget {
  final Function(bool) hideBottomNavigationBar;

  MessagesScreen({@required this.hideBottomNavigationBar});
  @override
  _MessagesScreenState createState() => _MessagesScreenState();
}

class _MessagesScreenState extends State<MessagesScreen> {
  RoseUser roseUser;
  Database database = Database();

  List<Chat> chats = [];

  Chat
      selectedChat; // whichever chat the user clicks on, to provide to chat view screen

  int messagesIndex = 0; // 0 is messages list, 1 is specific chat

  @override
  Widget build(BuildContext context) {
    DocumentSnapshot roseUserSnap = Provider.of<DocumentSnapshot>(context);
    if (roseUserSnap != null) roseUser = RoseUser.fromDatabase(roseUserSnap);
    if (messagesIndex == 1)
      return ChatViewScreen(selectedChat, updateMessagesIndex: (index) {
        setState(() {
          messagesIndex = 0;
          selectedChat = null;
          widget.hideBottomNavigationBar(false);
        });
      });
    return StreamBuilder<QuerySnapshot>(
        stream: database.streamChatPreviews(roseUser),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return Center(
              child: Text('Error streaming chat previews'),
            );
          }

          if (snapshot.hasData) {
            List<DocumentSnapshot> chatSnaps = snapshot.data.docs;
            chats = chatSnaps.map((e) => Chat.fromDatabase(e)).toList();
            chats.sort(
                (a, b) => b.lastMessage.sentAt.compareTo(a.lastMessage.sentAt));
            return ListView.builder(
                itemCount: chats.length,
                itemBuilder: (context, i) {
                  Chat chat = chats[i];
                  return GestureDetector(
                      onTap: () {
                        setState(() {
                          selectedChat = chat;
                          messagesIndex = 1;
                          widget.hideBottomNavigationBar(true);
                        });
                      },
                      child: ChatListCard(
                        chat: chat,
                        roseUser: roseUser,
                      ));
                });
          }

          return Center(
            child: CircularProgressIndicator(),
          );
        });
  }
}

特定聊天的聊天视图屏幕:

class ChatViewScreen extends StatefulWidget {
  final Chat chat;
  final Function(int) updateMessagesIndex;

  ChatViewScreen(this.chat, {@required this.updateMessagesIndex});

  @override
  _ChatViewScreenState createState() => _ChatViewScreenState();
}

class _ChatViewScreenState extends State<ChatViewScreen> {
  Chat chat;
  RoseUser roseUser;

  Database database = Database();

  bool sentMessage = false;

  @override
  void initState() {
    chat = widget.chat;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    DocumentSnapshot roseUserSnap = Provider.of<DocumentSnapshot>(context);
    if (roseUserSnap != null) roseUser = RoseUser.fromDatabase(roseUserSnap);
    if (roseUser == null)
      return Center(
        child: CircularProgressIndicator(),
      );
    return WillPopScope(
      onWillPop: () {
        widget.updateMessagesIndex(0);
        return Future.value(false);
      },
      child: StreamBuilder<QuerySnapshot>(
          stream: database.streamChatMessages(chat.chatId),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              print(snapshot.error.toString());
              return Center();
            }
            if (snapshot.hasData) {
              QuerySnapshot messageDocs = snapshot.data;
              chat.messages =
                  messageDocs.docs.map((e) => Message.fromDatabase(e)).toList();
            }
            return Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(chat.title,
                          style: TextStyle(
                            fontSize: 18,
                          )),
                      IconButton(
                          icon: Icon(Icons.info),
                          onPressed: () {
                            print('go to chat info screen');
                          })
                    ],
                  ),
                ),
                Expanded(
                  child: ListView.separated(
                    reverse: true,
                    itemCount: chat.messages.length,
                    itemBuilder: (context, index) {
                      Message message = chat.messages.toList()[index];

                      return Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 8.0),
                        child: message.senderID == roseUser.uid
                            ? MyMessageBubble(message: message)
                            : MessageBubble(message: message),
                      );
                    },
                    separatorBuilder: (context, index) {
                      if (index >= chat.messages.length - 1) return Container();
                      DateTime nextSent = chat.messages[index].sentAt;
                      DateTime firstSent = chat.messages[index + 1].sentAt;
                      if (nextSent.difference(firstSent).inHours.abs() >= 24)
                        return Padding(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 18.0, vertical: 8),
                          child: Column(
                            children: [
                              Divider(),
                              Text(DateFormat('MMMM d, yyyy').format(nextSent))
                            ],
                          ),
                        );
                      return Container();
                    },
                  ),
                ),
                Padding(
                  padding:
                      const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
                  child: MessageComposer(
                    onMessageSent: (text) async {
                      Message message = new Message(
                          text: text,
                          sender: roseUser.firstName + ' ' + roseUser.lastName,
                          sentAt: DateTime.now(),
                          containsMedia: false,
                          senderID: roseUser.uid);
                      setState(() {
                        chat.messages.add(message);
                      });
                      bool sendSuccess =
                          await database.sendMessage(chat, message);
                      if (!sendSuccess) {
                        setState(() {
                          chat.messages.remove(message);
                        });
                      } else {
                        sentMessage = true;
                      }
                    },
                  ),
                ),
              ],
            );
          }),
    );
  }
}

系统乍一看正常工作,在聊天视图屏幕中,我使用 WillPopScope 捕捉 android 系统返回手势,以便“弹出”回消息列表,实际上只是通过回调将 messagesIndex 更改回 0功能。但是,当我发送一条消息(它调用一个对聊天文档及其消息子集合的 firebase firestore 调用)时,OnWillPop 方法变得完全没有响应,并且系统返回手势根本不会触发任何东西。

我知道我正在使用这个自定义导航系统在 jank 领域进行操作,但我希望能够在这里找出问题而不是处理 Navigator 2.0。如果有人有任何想法,我会全力以赴。

谢谢。

标签: firebaseflutterdartgoogle-cloud-firestoreflutter-provider

解决方案


推荐阅读