如何在Flutter中构建一个聊天信息UI

432 阅读4分钟

你是否曾经想建立一个聊天应用程序,并想知道如何创建这个消息用户界面?

虽然这个用户界面看起来很简单,但有几件事需要考虑。

  • 所有的文本信息都显示在一个带有圆角和填充颜色的 "聊天气泡 "内。
  • 聊天气泡是左对齐 或右对齐的,这取决于谁发送的信息
  • 每个气泡中的文本如果不适合在一行中显示,则应包起来。

所以,让我们看看如何建立这个!

在这一过程中,我们将学习如何装饰对齐和为我们的小部件添加自定义样式

创建一个ChatBubble小部件

作为第一步,我们可以创建一个自定义的widget类来代表我们的聊天气泡。

class ChatBubble extends StatelessWidget {
  const ChatBubble({
    Key? key,
    required this.text,
    required this.isCurrentUser,
  }) : super(key: key);
  final String text;
  final bool isCurrentUser;

  // TODO: Implement build method
}

这个widget将textisCurrentUser 作为参数,并需要确定聊天气泡的排列风格

作为参考,我们可以在父小部件中这样使用它。

ListView(
  children: const [
    ChatBubble(
      text: 'How was the concert?',
      isCurrentUser: false,
    ),
    ChatBubble(
      text: 'Awesome! Next time you gotta come as well!',
      isCurrentUser: true,
    ),
    ChatBubble(
      text: 'Ok, when is the next date?',
      isCurrentUser: false,
    ),
    ChatBubble(
      text: 'They\'re playing on the 20th of November',
      isCurrentUser: true,
    ),
    ChatBubble(
      text: 'Let\'s do it!',
      isCurrentUser: false,
    ),
  ],
)

在现实世界的应用程序中,所有的聊天数据都会来自于后台,应该用ListView.builder 来显示,以确保只有可见的聊天气泡被呈现。

接下来,让我们研究一下build 方法中的内容。

1.添加一个装饰盒

第一步是把一个Text widget放在一个DecoratedBox 里面,像这样。

@override
Widget build(BuildContext context) {
  return DecoratedBox(
    // chat bubble decoration
    decoration: BoxDecoration(
      color: isCurrentUser ? Colors.blue : Colors.grey[300],
      borderRadius: BorderRadius.circular(16),
    ),
    child: Padding(
      padding: const EdgeInsets.all(12),
      child: Text(
        text,
        style: Theme.of(context).textTheme.bodyText1!.copyWith(
            color: isCurrentUser ? Colors.white : Colors.black87),
      ),
    ),
  );
}

注意:我们可以用一个Container 来替代DecoratedBox 。但是Container 在后台做了很多事情,而DecoratedBox 更加轻量,所以我们可以用它来获得更好的性能。

如果我们运行这段代码,聊天气泡会像这样出现。

这不是一个坏的开始,但我们不希望聊天气泡在水平方向伸展。而且我们可以做一些填充。

2.添加一个对齐小部件

让我们通过添加AlignPadding 小组件来修复这个布局。

@override
Widget build(BuildContext context) {
  return Padding(
    // add some padding
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
    child: Align(
      // align the child within the container
      alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
      child: DecoratedBox(
        // chat bubble decoration
        decoration: BoxDecoration(
          color: isCurrentUser ? Colors.blue : Colors.grey[300],
          borderRadius: BorderRadius.circular(16),
        ),
        child: Padding(
          padding: const EdgeInsets.all(12),
          child: Text(
            text,
            style: Theme.of(context).textTheme.bodyText1!.copyWith(
                color: isCurrentUser ? Colors.white : Colors.black87),
          ),
        ),
      ),
    ),
  );
}

最重要的一行是这个。

alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,

这将确保每个聊天气泡都能正确对齐,不会填满所有可用的宽度。

这样就好多了,而且如果需要的话,文字会被包裹在多行上。

让我们做最后的调整。

3.最后的调整:不对称的填充物

聊天气泡应该在左边或右边包括一点填充物,以防止它在文字包裹时使用全部可用宽度。

下面是如何做到这一点。

@override
Widget build(BuildContext context) {
  return Padding(
    padding: EdgeInsets.fromLTRB(
      isCurrentUser ? 64.0 : 16.0,
      4,
      isCurrentUser ? 16.0 : 64.0,
      4,
    ),
    child: Align(...)
  );
}

这是它在文字包裹时的样子。

就是这样!我们建立了一个响应式的聊天气泡用户界面,我们可以在我们的应用程序中重新使用。

下面是ChatBubble 类的完整代码。

class ChatBubble extends StatelessWidget {
  const ChatBubble({
    Key? key,
    required this.text,
    required this.isCurrentUser,
  }) : super(key: key);
  final String text;
  final bool isCurrentUser;

  @override
  Widget build(BuildContext context) {
    return Padding(
      // asymmetric padding
      padding: EdgeInsets.fromLTRB(
        isCurrentUser ? 64.0 : 16.0,
        4,
        isCurrentUser ? 16.0 : 64.0,
        4,
      ),
      child: Align(
        // align the child within the container
        alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
        child: DecoratedBox(
          // chat bubble decoration
          decoration: BoxDecoration(
            color: isCurrentUser ? Colors.blue : Colors.grey[300],
            borderRadius: BorderRadius.circular(16),
          ),
          child: Padding(
            padding: const EdgeInsets.all(12),
            child: Text(
              text,
              style: Theme.of(context).textTheme.bodyText1!.copyWith(
                  color: isCurrentUser ? Colors.white : Colors.black87),
            ),
          ),
        ),
      ),
    );
  }
}

学到的经验

完成的实现是简单而优雅的。

但是当我开始研究这个问题时,我走错了路,试图用Row 来对齐东西。而我最终使用LayoutBuilderConstrainedBox 来对抗一些布局问题。如果你对这种方法感到好奇,这里有一个Twitter线程。

构建一个带有这样的信息气泡的Flutter聊天用户界面应该很容易吧?

不是那么快的!🧵pic.twitter.com/zBZvDMkZg4-

Andrea Bizzotto 💙 (@biz84)September 13, 2021

因此,有时最好退一步,寻找一个更简单的解决方案。

阿尔伯特-爱因斯坦说得最好。

"一切都应该尽可能简单,但不能更简单"。

事实证明,这对Flutter应用开发来说是个很好的建议。 😀

编码愉快!