在 Flutter 中成功实现施聊天界面的技巧

在实施聊天用户界面时,有许多错综复杂的细节需要考虑。要创建一个我们每天都要使用数十次的聊天应用程序,我们需要考虑看似显而易见的细节,并在考虑用户体验(UX)的基础上实现高质量的聊天功能。

本篇文章将介绍如何开发一款聊天应用,将 WhatsApp、KakaoTalk 和 Line 等代表性聊天应用中的用户界面交互逻辑应用到这款应用中。

基本结构

首先,让我们来看看聊天界面的基本结构。

    Scaffold(
      appBar: AppBar(
        title: const Text("Chat"),
        backgroundColor: const Color(0xFF007AFF),
      ), // <-- App bar
      body: Column(
        children: [
          Expanded(
            child: ListView.separated(...), // <- Chat list view
          ), 
           _BottomInputField(), // <- Fixed bottom TextField widget
        ],
      ),
    );

一般来说,聊天界面的结构很简单。它由 AppBarChat ListView(聊天列表视图) 和固定在底部的TextField(文本字段)组成。

这里有一点很重要,聊天列表视图和文本字段必须封装在一个Column部件中,而聊天列表视图部分必须封装在一个Expanded部件中。如下图:

在 Flutter 中成功实现施聊天界面的技巧

聊天列表视图和包裹在列部件中的输入框是垂直排列的,由于聊天列表视图部分包裹在展开部件中,因此输入框视图自然会固定在底部。这样做的好处是无需使用堆叠和定位部件将输入框固定在底部。

请注意,我将继续展示的示例也是以这种结构排列的。

1. 当检测到虚拟键盘区域时,输入框和聊天列表视图部分会响应变化的交互方式

在 Flutter 中成功实现施聊天界面的技巧

首先要考虑的聊天交互是当虚拟键盘出现时,输入栏和聊天列表视图部分如何响应变化。当虚拟键盘出现时,输入框和聊天列表视图会自然跟随移动,这对用户体验非常重要。

为此,您需要设置以下两个属性。

  • resizeToAvoidBottomInset property
  return Scaffold(
      resizeToAvoidBottomInset: true, // assign true
      appBar: AppBar(
        title: const Text("Ximya"),
        backgroundColor: const Color(0xFF007AFF),
      ),

首先,您需要将 Scaffold widget 的 resizeToAvoidBottomInset 属性设置为 true。当该属性设置为 true 时,Scaffold 部件会自动调整其大小,以避免在虚拟键盘出现时与虚拟键盘重叠。

  • reversed property
ListView.separated(
 reverse: true,
    itemCount: chatList.length,
    ...
 )

其次,需要将 ListView widget 的 reversed 属性设置为 true。该属性指定是否按相反顺序排列列表项。将 reversed 设置为 true 后,项目将从下往上排列,虚拟键盘的大小变化也会被检测到。

注意:索引和位置当设置为 true 时,ListView 中的项目将从下到上排列。因此,屏幕上项目的索引和位置会颠倒。在操作传递给 ListView 的数据时需要考虑这一点。如果需要进行数据操作,在将数据传递到 ListView 之前再次反转值可能是解决方案。
例如,controller.chatList.reversed.toList().

2. 添加聊天并向下滚动时的互动

在 Flutter 中成功实现施聊天界面的技巧

当一条信息添加到聊天列表时,它应该位于底部并自然滚动。为此,您需要将 ListView 的 reversed 属性设置为 true。将反转属性设置为 true 后,项目将从下往上排列。因此,当添加一条信息时,ListView 的区域会扩大,滚动位置也会改变。

3. 将聊天信息对齐到顶部

在 Flutter 中成功实现施聊天界面的技巧

到目前为止,我已经告诉您需要将 ListView widget 的反转属性设置为 true。但是,这会导致聊天列表部分被放置在屏幕的最下方。

 Align(
 alignment: Alignment.topCenter,
 child: ListView.separated(
 shrinkWrap: true,
 reverse: true,
    itemCount: chatList.length,
    itemBuilder: (context, index) {
    return Bubble(chat: chatList[index]);
       },
    );
   ),

由于将 reverse 属性设置为 true 会将聊天列表部分置于屏幕的最下方,因此需要进行一些修改才能使聊天信息显示在屏幕的顶部。用对齐方式包裹 ListView widget,并将对齐方式属性设置为 Alignment.topCenter,使其位于顶部。此外,你还需要在 ListView 上设置 shrinkWrap: true 属性。这样,ListView 就会根据内部内容调整大小,并在 Alignment widget 的影响下位于顶部。

4. 优化发送消息后优化滚动位置:

在 Flutter 中成功实现施聊天界面的技巧

发送聊天信息时,无论当前滚动位置在哪里,滚动位置都应更改为最底部。为此,您可以使用 ScrollController 控制 ListView 的滚动行为。

final scrollController = ScrollController()

...

ListView.separated(
 shrinkWrap: true,
 reverse: true,
    controller: scrollController                                  
    itemCount: chatList.length,
    itemBuilder: (context, index) {
  return Bubble(chat: chatList[index]);
     },
 );            

首先,初始化 ScrollController 变量。然后,将该变量传递给 ListView 的控制器属性。现在,您可以控制 ListView 的滚动行为了。

Future<void> onFieldSubmitted() async {
  addMessage();
   
  // Move the scroll position to the bottom
  scrollController.animateTo(
    0,
    duration: const Duration(milliseconds: 300),
    curve: Curves.easeInOut,
  );

  textEditingController.text = '';
}

然后,将 scrollController.animatedTo 事件应用到添加聊天时发生的方法中,以添加滚动到最底部的动画。我们将偏移值 0 传递给 animatedTo 方法的原因是,在 listview.buidler 设置为 reversed:true 时,0 的位置基本上意味着列表的最底部。

5. 点击聊天区时关闭虚拟键盘

最后,在典型的聊天应用程序中,有一种交互方式,即当虚拟键盘在上时点击普通聊天列表区域,虚拟键盘就会隐藏起来。要实现这一点,只需添加一段简单的代码即可。

         Expanded(
            child: GestureDetector(
              onTap: () {
                FocusScope.of(context).unfocus(); // <-- Hide virtual keyboard
              },
              child: Align(
                alignment: Alignment.topCenter,
                child: Selector<ChatController, List<Chat>>(
                  selector: (context, controller) =>
                      controller.chatList.reversed.toList(),
                  builder: (context, chatList, child) {
                    return ListView.separated(
                      shrinkWrap: true,
                      reverse: true,
                      padding: const EdgeInsets.only(top: 12, bottom: 20) +
                          const EdgeInsets.symmetric(horizontal: 12),
                      separatorBuilder: (_, __) => const SizedBox(
                        height: 12,
                      ),
                      controller:
                          context.read<ChatController>().scrollController,
                      itemCount: chatList.length,
                      itemBuilder: (context, index) {
                        return Bubble(chat: chatList[index]);
                      },
                    );
                  },
                ),
              ),
            ),
          ),

使用“GestureDetector”小部件包装聊天列表部分,并将“FocusScope.of(context).unfocus()”事件传递给 onTap 函数。

// 1. Initialization
final focusNode = FocusNode();


// 2. Passing the focusNode object
TextField(
 focusNode :  focusNode,
...
),


// 3. When the chat section is tapped
onChatListSectinoTapped() {
 focusNode.unfocus()

另一种方法是使用 FocusNode 对象隐藏虚拟键盘。初始化 FocusNode 对象并在文本字段中设置 focusNode 属性。然后,当点击聊天列表部分时,调用 focusNode.unfocus() 隐藏虚拟键盘。

结论

在本文章中,我们介绍了在构建聊天应用程序时需要考虑的交互。虽然这些细节看似微不足道,但我相信考虑到这些交互会大大提高聊天功能的完整性。如果您想了解整体结构,而不仅仅是上面提到的交互,我建议您克隆包含示例代码的 GitHub 代码库

作者:Ximya
译自medium.

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/im/37186.html

(0)

相关推荐

发表回复

登录后才能评论