Flutter Widgets粘合劑CustomScrollView NestedScrollView滾動(dòng)控件
概述:
Flutter中常用的滑動(dòng)布局 ScrollView
有 SingleChildScrollView
、NestedScrollView
、CustomScrollView
。
SingleChildScrollView
用來處理簡(jiǎn)單可滑動(dòng)的頁面布局視圖,如一般的數(shù)據(jù)詳情頁面,當(dāng)內(nèi)容足夠多時(shí),一屏顯示不下時(shí),就需要滑動(dòng)處理。
NestedScrollView
滑動(dòng)組件是用來處理復(fù)雜情況下的滑動(dòng)應(yīng)用場(chǎng)景,如向上滑動(dò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è)簡(jiǎn)單的適配頁面滑動(dòng),建議碼農(nóng)使用 SingleChildScrollView 就可以。
如圖:
CustomScrollView
CustomScrollView是使用Sliver組件創(chuàng)建自定義滾動(dòng)效果的滾動(dòng)組件,使用場(chǎng)景:
ListView和GridView相互嵌套場(chǎng)景,ListView嵌套GridView時(shí),需要給GridView指定高度,但我們希望高度隨內(nèi)容而變化(不指定),ListView和GridView作為整體滾動(dòng)效果。
一個(gè)頁面頂部是AppBar,然后是GridView,最后是ListView,這3個(gè)區(qū)域以整體來滾動(dòng),AppBar具有吸頂效果。
CustomScrollView就像一個(gè)粘合劑,將多個(gè)組件粘合在一起,具統(tǒng)一的滾動(dòng)效果。
Sliver系列組件有很多,比如SliverList、SliverGrid、SliverFixedExtentList、SliverPadding、SliverAppBar等。
相互嵌套場(chǎng)景
在實(shí)際業(yè)務(wù)場(chǎng)景中經(jīng)常見到這樣的布局,頂部是懸浮導(dǎo)航,接下來網(wǎng)格布局(GridView),然后是列表布局(ListView),滾動(dòng)的時(shí)候做為一個(gè)整體,此場(chǎng)景是無法使用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("講解組合滑動(dòng)"), ); }
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)航懸浮):
實(shí)際項(xiàng)目中頁面頂部是AppBar,然后是GridView,最后是ListView,這3個(gè)區(qū)域以整體來滾動(dòng),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ù)控制其滾動(dòng)方向,用法如下:
CustomScrollView( scrollDirection: Axis.horizontal, reverse: true, ... )
scrollDirection
滾動(dòng)方向,分為垂直和水平方向。
reverse
參數(shù)表示反轉(zhuǎn)滾動(dòng)方向,并不是垂直轉(zhuǎn)為水平,而是垂直方向滾動(dòng)時(shí),默認(rèn)向下滾動(dòng),reverse
設(shè)置false,滾動(dòng)方向改為向上,同理水平滾動(dòng)改為水平向左。
設(shè)置scrollDirection:Axis.horizontal
效果:
primary設(shè)置為true時(shí),不能設(shè)置controller,因?yàn)閜rimarytrue時(shí),controller使用PrimaryScrollController,這種機(jī)制帶來的好處是父組件可以控制子樹中可滾動(dòng)組件的滾動(dòng)行為,例如,Scaffold正是使用這種機(jī)制在iOS中實(shí)現(xiàn)了點(diǎn)擊導(dǎo)航欄回到頂部的功能。
controller
為滾動(dòng)控制器,可以監(jiān)聽滾到的位置,設(shè)置滾動(dòng)的位置等,用法如下:
_scrollController = ScrollController(); //監(jiān)聽滾動(dòng)位置 _scrollController.addListener((){ print('${_scrollController.position}'); }); //滾動(dòng)到指定位置 _scrollController.animateTo(20.0); CustomScrollView( controller: _scrollController, ... )
physics表示可滾動(dòng)組件的物理滾動(dòng)特性,系統(tǒng)提供的ScrollPhysics有:
AlwaysScrollableScrollPhysics:總是可以滑動(dòng)
NeverScrollableScrollPhysics:禁止?jié)L動(dòng)
BouncingScrollPhysics :內(nèi)容超過一屏 上拉有回彈效果
ClampingScrollPhysics :包裹內(nèi)容 不會(huì)有回彈
NestedScrollView
可以在其內(nèi)部嵌套其他滾動(dòng)視圖的組件,其滾動(dòng)位置是固有鏈接的。
在普通的ScrollView中, 如果有一個(gè)Sliver組件容納了一個(gè)TabBarView,它沿相反的方向滾動(dòng)(例如,允許用戶在標(biāo)簽所代表的頁面之間水平滑動(dòng),而列表則垂直滾動(dòng)),則該TabBarView內(nèi)部的任何列表都不會(huì)相互作用 與外部ScrollView。例如,瀏覽內(nèi)部列表以滾動(dòng)到頂部不會(huì)導(dǎo)致外部ScrollView中的SliverAppBar折疊以展開。
滾動(dòng)隱藏AppBar
代碼如下:
_buildNestedScrollView(){ return NestedScrollView( headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){ return [ SliverAppBar(title: Text('導(dǎo)航測(cè)試'),) ]; }, 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 測(cè)試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ù)控制其滾動(dòng)方向,用法如下:', style: TextStyle(fontSize: 18),), Text( '作者 csdn賬號(hào) ', 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滾動(dòng)控件的詳細(xì)內(nèi)容,更多關(guān)于Flutter Widgets滾動(dòng)控件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS中設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間+模擬的方法詳解
這篇文章主要介紹了在iOS中設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間+模擬的方法,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-04-04快速解決低版本Xcode不支持高版本iOS真機(jī)調(diào)試的問題方法
這篇文章主要介紹了快速解決低版本Xcode不支持高版本iOS真機(jī)調(diào)試的問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02iOS應(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-03CAMediaTiming ( 時(shí)間協(xié)議)詳解及實(shí)例代碼
這篇文章主要介紹了CAMediaTiming / 時(shí)間協(xié)議詳解及實(shí)例代碼的相關(guān)資料,這里附有實(shí)例代碼,幫助大家學(xué)習(xí)參考,需要的朋友可以參考下2016-12-12iOS tableView上拉刷新顯示下載進(jìn)度的問題及解決辦法
這篇文章主要介紹了 iOS tableView上拉刷新顯示下載進(jìn)度的問題及解決辦法,需要的朋友可以參考下2017-03-03