Flutter Widgets粘合劑CustomScrollView NestedScrollView滾動控件
概述:
Flutter中常用的滑動布局 ScrollView 有 SingleChildScrollView、NestedScrollView、CustomScrollView。
SingleChildScrollView 用來處理簡單可滑動的頁面布局視圖,如一般的數(shù)據(jù)詳情頁面,當(dāng)內(nèi)容足夠多時(shí),一屏顯示不下時(shí),就需要滑動處理。
NestedScrollView 滑動組件是用來處理復(fù)雜情況下的滑動應(yīng)用場景,如向上滑動視圖時(shí),要折疊隱藏一部分內(nèi)容,這時(shí)候就需要使用到 NestedScrollView 與 SliverAppBar 的結(jié)合使用。
CustomScrollView 用來處理更為復(fù)雜的布局結(jié)合 SliverAppBar,SliverList和SliverGrid SliverPadding SliverToBoxAdapter SliverPersistentHeader, SliverFillRemaining,SliverFillViewport等來使用。
如一個(gè)詳情頁面中 即需要 GridView 來實(shí)現(xiàn)二維宮格效果,也需要 ListView 列表效果,如下圖所示的圖片效果,當(dāng)使用 CustomScrollView 結(jié)合 SliverList 和SliverGrid 就可輕松實(shí)現(xiàn),當(dāng)然結(jié)合一下 SliverAppBar 也能實(shí)現(xiàn)折疊效果的頭部布局,所以說 CustomScrollView 很強(qiáng)大。
在實(shí)際應(yīng)用開發(fā)中,如果只是一個(gè)簡單的適配頁面滑動,建議碼農(nóng)使用 SingleChildScrollView 就可以。
如圖:

CustomScrollView
CustomScrollView是使用Sliver組件創(chuàng)建自定義滾動效果的滾動組件,使用場景:
ListView和GridView相互嵌套場景,ListView嵌套GridView時(shí),需要給GridView指定高度,但我們希望高度隨內(nèi)容而變化(不指定),ListView和GridView作為整體滾動效果。
一個(gè)頁面頂部是AppBar,然后是GridView,最后是ListView,這3個(gè)區(qū)域以整體來滾動,AppBar具有吸頂效果。
CustomScrollView就像一個(gè)粘合劑,將多個(gè)組件粘合在一起,具統(tǒng)一的滾動效果。
Sliver系列組件有很多,比如SliverList、SliverGrid、SliverFixedExtentList、SliverPadding、SliverAppBar等。
相互嵌套場景
在實(shí)際業(yè)務(wù)場景中經(jīng)常見到這樣的布局,頂部是懸浮導(dǎo)航,接下來網(wǎng)格布局(GridView),然后是列表布局(ListView),滾動的時(shí)候做為一個(gè)整體,此場景是無法使用GridView+ListView來實(shí)現(xiàn)的,而是需要使用CustomScrollView+SliverAppBar+SliverGrid+SliverList來實(shí)現(xiàn),
CustomScrollView buildCustomScrollView() {
return CustomScrollView(
///反彈效果
physics: BouncingScrollPhysics(),
///Sliver 家族的 Widget
slivers: <Widget>[
///復(fù)雜的標(biāo)題
buildSliverAppBar(),
///間距
SliverPadding(
padding: EdgeInsets.all(5),
),
///九宮格
buildSliverGrid(),
///間距
SliverPadding(
padding: EdgeInsets.all(5),
),
///列表
buildSliverFixedExtentList()
],
);
}
SliverAppBar 常用來實(shí)現(xiàn)復(fù)雜的可折疊效果的頭布局,代碼如下:
SliverAppBar buildSliverAppBar() {
return SliverAppBar(
title: Text("講解組合滑動"),
);
}
CustomScrollView 中使用的九宮格你不能再去使用 GridView了,在Sliver家族中,有SliverGridView,當(dāng)然它與 GridView 的用法是一致的,代碼如下:
SliverGrid buildSliverGrid() {
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
///九宮格的列數(shù)
crossAxisCount: 3,
///子Widget 寬與高的比值
childAspectRatio: 2.0,
///主方向的 兩個(gè) 子Widget 之間的間距
mainAxisSpacing: 10,
/// 次方向 子Widget 之間的間距
crossAxisSpacing: 10,
),
///子Item構(gòu)建器
delegate: new SliverChildBuilderDelegate(
(BuildContext context, num index) {
///每一個(gè)子Item的樣式
return Container(
color: Colors.blue,
child: Text("grid $index"),
);
},
///子Item的個(gè)數(shù)
childCount: 10,
),
);
}
CustomScrollView 使用的列表你也不能使用 ListView了,需要使用SliverListView 或者是 SliverFixedExtentList,當(dāng)然在這里使用了 SliverFixedExtentList 代碼如下:
SliverFixedExtentList buildSliverFixedExtentList() {
return SliverFixedExtentList(
///子條目的高度
itemExtent: 40,
///子條目布局構(gòu)建代理
delegate: new SliverChildBuilderDelegate(
(BuildContext context, num index) {
///子條目的布局樣式
return Container(
color: Colors.red,
child: Text("list $index"),
margin: EdgeInsets.only(bottom: 10),
);
},
///子條目的個(gè)數(shù)
childCount: 40,
),
);
}
最終效果(上滑導(dǎo)航隱藏,下滑導(dǎo)航懸?。?/p>

實(shí)際項(xiàng)目中頁面頂部是AppBar,然后是GridView,最后是ListView,這3個(gè)區(qū)域以整體來滾動,AppBar具有吸頂效果,此效果也是我們經(jīng)常遇到的,用法如下:
CustomScrollView buildCustomScrollView1() {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 230.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('復(fù)仇者聯(lián)盟'),
background: Image.network(
'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
fit: BoxFit.fitHeight,
),
),
),
SliverGrid.count(crossAxisCount: 4,children: List.generate(8, (index){
return Container(
color: Colors.primaries[index%Colors.primaries.length],
alignment: Alignment.center,
child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
);
}).toList(),),
SliverList(
delegate: SliverChildBuilderDelegate((content, index) {
return Container(
height: 85,
alignment: Alignment.center,
color: Colors.primaries[index % Colors.primaries.length],
child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
);
}, childCount: 25),
)
],
);
}
運(yùn)行效果:

通過scrollDirection和reverse參數(shù)控制其滾動方向,用法如下:
CustomScrollView( scrollDirection: Axis.horizontal, reverse: true, ... )
scrollDirection滾動方向,分為垂直和水平方向。
reverse參數(shù)表示反轉(zhuǎn)滾動方向,并不是垂直轉(zhuǎn)為水平,而是垂直方向滾動時(shí),默認(rèn)向下滾動,reverse設(shè)置false,滾動方向改為向上,同理水平滾動改為水平向左。
設(shè)置scrollDirection:Axis.horizontal效果:

primary設(shè)置為true時(shí),不能設(shè)置controller,因?yàn)閜rimarytrue時(shí),controller使用PrimaryScrollController,這種機(jī)制帶來的好處是父組件可以控制子樹中可滾動組件的滾動行為,例如,Scaffold正是使用這種機(jī)制在iOS中實(shí)現(xiàn)了點(diǎn)擊導(dǎo)航欄回到頂部的功能。
controller為滾動控制器,可以監(jiān)聽滾到的位置,設(shè)置滾動的位置等,用法如下:
_scrollController = ScrollController();
//監(jiān)聽滾動位置
_scrollController.addListener((){
print('${_scrollController.position}');
});
//滾動到指定位置
_scrollController.animateTo(20.0);
CustomScrollView(
controller: _scrollController,
...
)

physics表示可滾動組件的物理滾動特性,系統(tǒng)提供的ScrollPhysics有:
AlwaysScrollableScrollPhysics:總是可以滑動
NeverScrollableScrollPhysics:禁止?jié)L動
BouncingScrollPhysics :內(nèi)容超過一屏 上拉有回彈效果
ClampingScrollPhysics :包裹內(nèi)容 不會有回彈
NestedScrollView
可以在其內(nèi)部嵌套其他滾動視圖的組件,其滾動位置是固有鏈接的。
在普通的ScrollView中, 如果有一個(gè)Sliver組件容納了一個(gè)TabBarView,它沿相反的方向滾動(例如,允許用戶在標(biāo)簽所代表的頁面之間水平滑動,而列表則垂直滾動),則該TabBarView內(nèi)部的任何列表都不會相互作用 與外部ScrollView。例如,瀏覽內(nèi)部列表以滾動到頂部不會導(dǎo)致外部ScrollView中的SliverAppBar折疊以展開。
滾動隱藏AppBar
代碼如下:
_buildNestedScrollView(){
return NestedScrollView(
headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
return [
SliverAppBar(title: Text('導(dǎo)航測試'),)
];
},
body: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(itemBuilder: (BuildContext context,int index){
return Container(
height: 120,
color: Colors.primaries[index%Colors.primaries.length],
alignment: Alignment.center,
child: Text(
'組合ListView $index',
style: TextStyle(color: Colors.white,fontSize: 30),
),
);
}))
);
}
運(yùn)行效果:

注意, 如不不加MediaQuery.removePadding移除頂部間距,有個(gè)小bug。ListView和Appbar之間存在一個(gè)很大的間距,這個(gè)時(shí)候就需要MediaQuery去移除ListView的padding。

SliverAppBar展開折疊
_buildNestedScrollView1(){
return NestedScrollView(
headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
return [
SliverAppBar(
expandedHeight: 230,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('復(fù)仇者聯(lián)盟'),
background: Image.network(
'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
fit: BoxFit.fitHeight,
),
),
)
];
},
body: ListView.builder(itemBuilder: (BuildContext context,int index){
print('create $index');
return Container(
height: 100,
color: Colors.primaries[index%Colors.primaries.length],
alignment: Alignment.center,
child: Text('$index 測試ListView',style: TextStyle(fontSize: 30),
),
);
}));
}
運(yùn)行效果(白色間距可以增加removepadding去除):

與TabBar配合使用
用法如下:
_buildNestedScrollViewTabBar() {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
const SliverAppBar(
expandedHeight: 230,
pinned: true,
flexibleSpace: Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: WyPageView(),
),
),
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
TabBar(
labelColor: Colors.black,
controller: this._tabController,
tabs: [
const Tab(text: '資訊',),
const Tab(text: '技術(shù)',),
],
),
),
)
];
},
body: TabBarView(
controller: this._tabController,
children: [
RefreshIndicator(
child: _buildTabNewsList('----資訊類----'),
onRefresh: _handelRefresh,
),
_buildTabNewsList('----技術(shù)類----'),
])
);
}
//Refresh異步刷新方法
Future<Null >_handelRefresh()async{
print('加載數(shù)據(jù)');
return null;
}
//構(gòu)建newstlist列表
_buildTabNewsList(String name) {
return ListView.separated(itemBuilder: (context, int index) {
return Column(
children: [
Text('$name $index 通過scrollDirection和reverse參數(shù)控制其滾動方向,用法如下:',
style: TextStyle(fontSize: 18),),
Text(
'作者 csdn賬號 ', style: TextStyle(fontSize: 12, color: Colors.grey),),
],
);
},
separatorBuilder: (context, index) => Divider(),
itemCount: 50);
}
}
//StickyTabBarDelegate 代碼如下:
class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar child;
StickyTabBarDelegate(this.child);
@override
Widget build(BuildContext context, double shrinkOffset,
bool overlapsContent) {
// TODO: implement build
return Container(
color: Theme
.of(context)
.backgroundColor,
child: this.child,
);
}
@override
// TODO: implement maxExtent
double get maxExtent => this.child.preferredSize.height;
@override
// TODO: implement minExtent
double get minExtent => this.child.preferredSize.height;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
// TODO: implement shouldRebuild
throw true;
}
}
運(yùn)行效果:

總結(jié):
本篇主要介紹了CustomScrollView和NestedScrollView,CustomScrollView的自定義靈活性更大,所有子組件都用Sliver家族組件,一般需求用NestedScrollView即可。
以上就是Flutter Widgets粘合劑CustomScrollView NestedScrollView滾動控件的詳細(xì)內(nèi)容,更多關(guān)于Flutter Widgets滾動控件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS中設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間+模擬的方法詳解
這篇文章主要介紹了在iOS中設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間+模擬的方法,文中介紹的非常詳細(xì),相信對大家具有一定的參考價(jià)值,需要的朋友們下面來跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-04-04
快速解決低版本Xcode不支持高版本iOS真機(jī)調(diào)試的問題方法
這篇文章主要介紹了快速解決低版本Xcode不支持高版本iOS真機(jī)調(diào)試的問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
iOS應(yīng)用開發(fā)中使用設(shè)計(jì)模式中的觀察者模式的實(shí)例
這篇文章主要介紹了iOS應(yīng)用開發(fā)中使用設(shè)計(jì)模式中的觀察者模式的實(shí)例,包括Cocoa框架使用中的KVO機(jī)制的相關(guān)配合運(yùn)用,代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-03-03
CAMediaTiming ( 時(shí)間協(xié)議)詳解及實(shí)例代碼
這篇文章主要介紹了CAMediaTiming / 時(shí)間協(xié)議詳解及實(shí)例代碼的相關(guān)資料,這里附有實(shí)例代碼,幫助大家學(xué)習(xí)參考,需要的朋友可以參考下2016-12-12
iOS tableView上拉刷新顯示下載進(jìn)度的問題及解決辦法
這篇文章主要介紹了 iOS tableView上拉刷新顯示下載進(jìn)度的問題及解決辦法,需要的朋友可以參考下2017-03-03

