Есть ли причина, по которой мой WillPopScope работает только изредка в моей навигации по флаттеру?

# #firebase #flutter #dart #google-cloud-firestore #flutter-provider

Вопрос:

Я пишу приложение Flutter, которое использует то, что, как я полагаю, вероятно, не является правильным способом навигации по экранам. Я внедряю некоторые поставщики, поэтому стараюсь избегать декларативного стиля навигации Navigator.push, и это привело к тому, что я, по сути, использую индексы, чтобы указать приложению, какой из нескольких экранов отображать в любой момент времени. В приведенном ниже примере экран сообщений представляет собой вкладку внутри индексированного пакета, и когда выбран конкретный «чат», он изменяет индекс сообщений, изменяя возвращаемое значение из списка всех чатов на отображение конкретного чата.

Экран сообщений со списком всех чатов:

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

The system works normally at first glance, and within the chat view screen I use a WillPopScope to catch the android system back gesture in order to «pop» back to the messages list, which in practicality is just changing the messagesIndex back to 0 via callback function. However, when I send a message (which invokes a firebase firestore call to a chat document and its subcollection of messages), the OnWillPop method becomes totally nonresponsive, and the system back gesture ceases to trigger anything at all.

I know I’m operating in jank territory with this custom navigation system, but I would love to be able to figure out the problem here rather than deal with Navigator 2.0. If anyone has any ideas, I’m all ears.

Thanks.