小菜在學習過程中會在一個 Page 頁面同時用到 GridView 和 ListView 或多個 ListView,此時就會遇到常見的滑動沖突問題。小菜嘗試了兩種解決滑動沖突的方案,僅記錄一下基本的使用方式。小菜翻譯很不到位,可重點看代碼。
嘗試一:CustomScrollView + sliver
Flutter 提供了類似于 Android CollapsingToolbarLayout 的折疊效果,小菜借此了解到 CustomScrollView 這個元件,可以解決清單的滑動沖突。
CustomScrollView 允許包含多種滾動模型,例如清單/網格和擴充标題。但其子 Widget 必須為 sliver 類型的。
sliver 代表具有特定滾動效果的滾動模型,sliver 本身不包含滾動互動模型,需要通過 CustomScrollView 連接配接為一個整體。sliver 有衆多具體的 Widget,小菜也在嘗試過程中。
class _ScrollPageState extends State<ScrollPage3> {
List<int> gridData = List<int>();
_setGridData() {
for (int i = 0; i < 15; i++) {
gridData.add(i);
}
}
@override
void initState() {
_setGridData();
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text('方案一')), body: _bodyWid());
}
Widget _bodyWid() {
return CustomScrollView(slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return _typeTitleWid('熱門分類');
}, childCount: 1)),
SliverPadding(padding: const EdgeInsets.all(8.0), sliver: _typeGridWid()),
SliverList(
delegate:
SliverChildBuilderDelegate((BuildContext context, int index) {
return _typeTitleWid('智能推薦');
}, childCount: 1)),
_typeListWid()
]);
}
Widget _typeTitleWid(var titleStr) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding( padding: EdgeInsets.all(10.0),
child: Text(titleStr,
style:
TextStyle(color: Color(0xFF808080), fontSize: 14.0))),
Divider(color: Color(0xFF808080), height: 0.5)
]));
}
Widget _typeGridWid() {
return SliverGrid(
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0, childAspectRatio: 4.0),
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return Container( height: 64.0,
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(3.0)),
child: Center(
child: Text('分類 ${(index + 1)}', style: TextStyle(color: Color(0xFF333333), fontSize: 14.0))));
}, childCount: gridData.length));
}
Widget _typeListWid() {
return SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: Text('推薦精彩内容 ${(index + 1)}',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF333333), fontSize: 15.0))),
Padding(
padding: EdgeInsets.only(top: 4.0),
child: Divider(color: Color(0xFF808080), height: 0.5))
]));
}, childCount: gridData.length));
}
}
嘗試二:primary + shrinkWrap
小菜在使用 ListView 或 GridView 時發現有兩個屬性很重要。小菜的翻譯很不到位,建議大家仔細閱讀一下官網的介紹。
shrinkWrap 常用于内容大小不确定情況,如果滾動視圖(ListView/GridView/ScrollView 等)沒有收縮包裝,則滾動視圖将擴充到允許的最大大小。如果是無界限制,則 shrinkWrap 必須為 true。
primary 如果為 true,即使滾動視圖沒有足夠的内容來支撐滾動,滾動視圖也是可滾動的。否則,預設為 false 情況下,隻有具有足夠内容的使用者才能滾動視圖。
class _ScrollPageState extends State<ScrollPage3> {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text('方案二')), body: _bodyWid2());
}
Widget _typeGridWid2() {
return GridView.count(
primary: false,
shrinkWrap: true,
crossAxisCount: 4,
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
childAspectRatio: 4.0,
padding: EdgeInsets.all(10.0),
children: gridData.map((int index) {
return Container(
height: 64.0,
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(3.0)),
child: Center(
child: Text('分類 ${(index + 1)}',
style: TextStyle(
color: Color(0xFF333333), fontSize: 14.0))));
}).toList());
}
Widget _typeListWid2() {
return ListView.separated(
primary: false,
shrinkWrap: true,
itemCount: gridData.length,
separatorBuilder: (BuildContext context, int index) => new Divider(),
itemBuilder: (context, item) {
return Padding(
padding: EdgeInsets.all(12.0),
child: Text('推薦精彩内容 ${(item + 1)}',
style: TextStyle(color: Color(0xFF333333), fontSize: 15.0)));
});
}
Widget _bodyWid2() {
return ListView(children: <Widget>[
_typeTitleWid('熱門分類'),
_typeGridWid2(),
_typeTitleWid('智能推薦'),
_typeListWid2()
]);
}
}