我正在构建我的应用程序,遇到了一个问题,即试图使我的Chips的ListView小部件的行为类似于标记在YouTube应用程序主页上的行为。对于不熟悉的用户,YouTube移动应用程序具有一种ListView小部件,其中包含一些用于过滤您首页上的视频的标签,我已在文章底部添加了该标签。
看来AppBar与位于其下方的实际水平ListView是分开的,但只要它出现就出现(似乎是SliverAppBar的某种行为,看起来),就像它被某种方式卡在了它上一样。是否有一种不使用AppBar的'bottom'属性或使用底部特征但仍用边框/阴影分隔两个小部件的好方法呢?
[特别是,我正在寻找这种情况下的最佳实践,因为我是Flutter的新手,因此不想提出低于标准的解决方案。
谢谢!
[我使用CustomScrollView
到达此解决方案,其中两个SliverAppBar
依次出现(第一个包含YouTube徽标,第二个包含ChoiceChip
小部件列表)。
这就是您需要的。唯一的问题是我无法解决设置应用栏高度的问题。与YouTube中的那个相比太高了。设置minimumHeight
的功能似乎正在研究this PR:
int _selectedIndex = 0;
List<String> chipTitles = [
'Broski',
'Atom',
'Elon Musk',
'Lemonade',
'Ginger',
'Tiger',
'Drama',
'Comedy',
'COVID-19',
'US'
];
SliverAppBar(
pinned: true,
floating: false,
expandedHeight: 50.0,
flexibleSpace: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: chipTitles.length,
separatorBuilder: (context, index) => SizedBox(
width: 10.0,
),
itemBuilder: (context, index) {
return ChoiceChip(
selected: _selectedIndex == index,
label: Text('${chipTitles[index]}'),
onSelected: (selected) {
if (selected) {
setState(() {
_selectedIndex = index;
});
}
},
);
},
),
),
还包括完整的StatefulWidget
类作为完整示例:
class ScrollerDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _ScrollerDemoState();
}
class _ScrollerDemoState extends State<ScrollerDemo> {
int _selectedIndex = 0;
List<String> chipTitles = [
'Broski',
'Atom',
'Elon Musk',
'Lemonade',
'Ginger',
'Tiger',
'Drama',
'Comedy',
'COVID-19',
'US'
];
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
// Results in the logo app bar mimicking the one used in the YouTube app.
pinned: false,
floating: true,
expandedHeight: 80.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('CustomScrollView Demo'),
),
),
SliverAppBar(
pinned: true,
floating: false,
expandedHeight: 50.0,
flexibleSpace: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: chipTitles.length,
separatorBuilder: (context, index) => SizedBox(
width: 10.0,
),
itemBuilder: (context, index) {
return ChoiceChip(
selected: _selectedIndex == index,
label: Text('${chipTitles[index]}'),
onSelected: (selected) {
if (selected) {
setState(() {
_selectedIndex = index;
});
}
},
);
},
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('Grid item $index'),
);
},
childCount: 20,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List item $index'),
);
},
childCount: 36,
),
),
],
);
}
}