你是否曾经想建立一个聊天应用程序,并想知道如何创建这个消息用户界面?
虽然这个用户界面看起来很简单,但有几件事需要考虑。
- 所有的文本信息都显示在一个带有圆角和填充颜色的 "聊天气泡 "内。
- 聊天气泡是左对齐 或右对齐的,这取决于谁发送的信息
- 每个气泡中的文本如果不适合在一行中显示,则应包起来。
所以,让我们看看如何建立这个!
在这一过程中,我们将学习如何装饰、对齐和为我们的小部件添加自定义样式。
创建一个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将text 和isCurrentUser 作为参数,并需要确定聊天气泡的排列和风格。
作为参考,我们可以在父小部件中这样使用它。
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.添加一个对齐小部件
让我们通过添加Align 和Padding 小组件来修复这个布局。
@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 来对齐东西。而我最终使用LayoutBuilder 和ConstrainedBox 来对抗一些布局问题。如果你对这种方法感到好奇,这里有一个Twitter线程。
构建一个带有这样的信息气泡的Flutter聊天用户界面应该很容易吧?
不是那么快的!🧵pic.twitter.com/zBZvDMkZg4-
Andrea Bizzotto 💙 (@biz84)September 13, 2021
因此,有时最好退一步,寻找一个更简单的解决方案。
阿尔伯特-爱因斯坦说得最好。
"一切都应该尽可能简单,但不能更简单"。
事实证明,这对Flutter应用开发来说是个很好的建议。 😀
编码愉快!