开发 Flutter 项目中碰到一个常见需求,Column
嵌套 ListView
,这种 UI 设计很常见,但我们实现起来就要考虑嵌套后滑动冲突的问题。
滑动
先分享 Column
内容滑动和滚动的使用,如果我们的布局在垂直内容很长,超出屏幕,直接运行,是会报错的。
A RenderFlex overflowed by *** pixels on the bottom.
这个超出屏幕的报错很常见,相信都见过。我们需要加一个滚动控件 SingleChildScrollView
包裹。
body: SingleChildScrollView(
child: Column(
children: [
Container(
height: 300,
color: Colors.yellow,
),
Container(
height: 300,
color: Colors.red,
),
...
],
),
),
嵌套 ListView
但如果往里面嵌套一个 ListView
,就会报如下错误。
RenderBox was not laid out: RenderViewport#80017 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1979 pos 12: 'hasSize'
这是因为 ListView
会在滚动方向上尽可能占据最大空间。而 Column
会要求每一个 Child Widget
有一个明确的高度。当 ListView
嵌套在 Column
里面当做子控件时,它们俩的需求就矛盾了。Column
希望子控件给它一个具体高度,ListView
希望占据最大的高度,需求相互矛盾,就报错了。
那我们给 ListView
外层添加一个高度,这个问题就解决了。
SizedBox(
height: 200,
child: buildListView()
)
Widget buildListView() {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(title: Text(_items[index]));
},
itemCount: _items.length,
);
}
设置一个明确的高度虽然可以解决问题,但如果 ListView
内容超过高度限制,就会在高度里滚动,在这个小范围内滑动。这应该不会满足我们的需求,我们需要 ListView
在整个页面上下滑动,随着屏幕滚动。
再说回 ListView
,它的默认特点是占据最大高度,但可以通过 shrinkWrap
属性设置,让它只占据内容高度,也就是内容计算出来多高,就占据多高。有点像 Android 开发里的 match_parent
和 wrap_content
。
ListView.builder(
shrinkWrap: true,
...
);
shrinkWrap
默认值是 false
,占据最大高度,设置成 true
会包裹内容,占据包裹内容高度。
上面代码运行后,还是不对,因为 ListView
滑动还是只会在小范围内滑动,不会拖动整个屏幕,再加上下面属性。
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
...
);
所有问题都解决了,实际布局中 ListView
会自动给下方加一个间距,手动设置 padding
解决。
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
...
);
嵌套 Column
如果将上面的 ListView
换成 Column
,也就是 Column
里面 嵌套 Column
,也会报上面的错。因为 Column
默认也会占据最大高度,设置子 Column
的主轴,从 max 设置到 min。
Column(
children: [
Column(
mainAxisSize: MainAxisSize.min
)
],
),
OK,希望以上内容对你有用。
本文由老郭种树原创,转载请注明:https://guozh.net/column-nested-listview/