一、问题背景
最近在开发直播的需求,有如图以下聊天室弹幕的场景,一开始想的是用ListView来做,然后Container包裹Text来实现每一个对话。
实现代码如下:
ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: widget.messages != null ? widget.messages.length : 0,
itemBuilder: (BuildContext context, int index) {
EMMessage message = widget.messages[index];
EMTextMessageBody messageBody = message.body;
return Container(
margin: EdgeInsets.only(top: 5, left: 15, right: 75),
child: Text(
"${messageBody.content}",
style:
TextStyle(color: Color(0xFF00B4E6), fontSize: 14),
),
padding:
EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
color: ThemeColors.live_background,
borderRadius: BorderRadius.circular(15)),
);
})
结果却不是我预期的效果,我发现这么写每一个对话都会撑满屏幕的宽度,不能根据文本的长度来进行自适应。
二、尝试解决
进过网上查阅资料,发现只要在Container外层套一个Row,然后设置mainAxisSize属性为 MainAxisSize.min,如下代码所示
ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: widget.messages != null ? widget.messages.length : 0,
itemBuilder: (BuildContext context, int index) {
EMMessage message = widget.messages[index];
EMTextMessageBody messageBody = message.body;
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 5, left: 15, right: 75),
child: Text(
"${messageBody.content}",
style:
TextStyle(color: Color(0xFF00B4E6), fontSize: 14),
),
padding:
EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
color: ThemeColors.live_background,
borderRadius: BorderRadius.circular(15)),
),
]);
})
发现这样确实可以根据文本自适应了,但是如果文案过长会有另一个问题,会导致宽度溢出问题,文本不会自动换行
然后又想着是不是给Container加上一层Expend就不会宽度溢出了,代码如下:
ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: widget.messages != null ? widget.messages.length : 0,
itemBuilder: (BuildContext context, int index) {
EMMessage message = widget.messages[index];
EMTextMessageBody messageBody = message.body;
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
margin: EdgeInsets.only(top: 5, left: 15, right: 75),
child: Text(
"${messageBody.content}",
style:
TextStyle(color: Color(0xFF00B4E6), fontSize: 14),
),
padding:
EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
color: ThemeColors.live_background,
borderRadius: BorderRadius.circular(15)),
),
),
]);
})
是不会宽度溢出了,但是其余的文本又不随文本宽度自适应了,这可咋整?后来我发现了最终的解决方案,不用加Expand,只需要把套在item外层的Row改成Column即可,最终代码:
ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: widget.messages != null ? widget.messages.length : 0,
itemBuilder: (BuildContext context, int index) {
EMMessage message = widget.messages[index];
EMTextMessageBody messageBody = message.body;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 5, left: 15, right: 75),
child: Text(
"${messageBody.content}",
style:
TextStyle(color: Color(0xFF00B4E6), fontSize: 14),
),
padding:
EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
color: ThemeColors.live_background,
borderRadius: BorderRadius.circular(15)),
),
]);
})
三、问题产生原因
那回归问题的本质,为什么ListView里的Item Container是自动撑满和父视图一样宽的呢?而不是像我们想的一样跟随子视图宽高进行自适应的。
后来我查阅了Flutter的约束篇文章,找到了答案,在弹性视图Row、Column以及滚动区域的子视图ListView、ScrollView内的子视图默认是无边界约束的,默认的宽高都是,所以ListView里的Item Container是自动撑满和父视图一样宽。