Flutter 中常用的滑动布局
-
SingleChildScrollView主要处理简单可滑动的页面布局视图。当页面内容一屏不能完全展示时,就需要滑动处理。 -
NestedScrollView主要处理复杂情况下的滑动应用场景。如需要要折叠隐藏一部分内容时就需要使用到 NestedScrollView 与 SliverAppBar 的结合使用。 -
CustomScrollView用来处理更为复杂的布局。 常需要结合 SliverAppBar,SliverList和SliverGrid SliverPadding SliverToBoxAdapter SliverPersistentHeader, SliverFillRemaining,SliverFillViewport 等来使用。
SingleChildScrollView
类似于 iOS/Android 中的ScrollView,通常只应在期望的内容不会超过屏幕太多时使用
Scrollbar(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(10),
reverse: false, //是否滚动到末尾
child: Column(children:
str.split("").map((e) => Text(e)).toList(),
),
),
);
CustomScrollView
CustomScrollView是使用Sliver组件创建自定义滚动效果的滚动组件。
使用场景:
-
ListView和GridView相互嵌套场景,ListView嵌套GridView时,需要给GridView指定高度,但我们希望高度随内容而变化(不指定),ListView和GridView作为整体滚动效果。
-
一个页面顶部是AppBar,然后是GridView,最后是ListView,这3个区域以整体来滚动,AppBar具有吸顶效果。
CustomScrollView(
slivers: [
_buildBox(), //tag1
SliverPadding(
padding: EdgeInsets.all(8),
sliver: _buildSliverGrid(),
),
_buildSliverList(),
],
),
//通常只有 `Sliver` 开头的组件才可以作为CustomScrollView滑动的内容
//所以需要在外层套上 SliverToBoxAdapter
Widget _buildBox() {
return SliverToBoxAdapter(
child: Container(
height: 60,
color: Colors.amber,
),
);
}
NestedScrollView
滚动隐藏AppBar
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[SliverAppBar(
title: Text('标题'),
)];
},
body: ListView.builder(itemBuilder: (BuildContext context,int index){
return Container(
height: 80,
color: Colors.primaries[index % Colors.primaries.length],
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(color: Colors.white, fontSize: 20),
),
);
},itemCount: 20,),
)
SliverAppBar展开折叠
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[SliverAppBar(
expandedHeight: 230.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('复仇者联盟'),
background: Image.network(
'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
fit: BoxFit.fitHeight,
),
),
)];
},
body: ListView.builder(itemBuilder: (BuildContext context,int index){
return Container(
height: 80,
color: Colors.primaries[index % Colors.primaries.length],
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(color: Colors.white, fontSize: 20),
),
);
},itemCount: 20,),
)
与TabBar 结合
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 230.0,
pinned: true,
flexibleSpace: Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: PageView(
children: [
Image.network('http://0918/.jpg'),
Image.network('http://44164.jpg'),
],
),
),
),
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
child: TabBar(
labelColor: Colors.black,
controller: this._tabController,
tabs: <Widget>[
Tab(text: '资讯'),
Tab(text: '技术'),
],
),
),
),
];
},
body: TabBarView(
controller: this._tabController,
children: <Widget>[
RefreshIndicator(
onRefresh: (){
print(('onRefresh'));
},
child: _buildTabNewsList(_newsKey, _newsList),
),
_buildTabNewsList(_technologyKey, _technologyList),
],
),
)
StickyTabBarDelegate 代码
class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar child;
StickyTabBarDelegate({required this.child});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Theme.of(context).backgroundColor,
child: child,
);
}
@override
double get maxExtent => child.preferredSize.height;
@override
double get minExtent => child.preferredSize.height;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}