這一篇的篇幅估計很多,請先買好瓜子汽水前排坐好盒卸,開車了..
NestedScrollView是一個復(fù)雜的組件骗爆,它跟Sliver系列是一伙的,最下層是個CustomScrollView世落。
銀色系列的東東很多淮腾,我們下面來一一介紹一下。
是銀組件的老祖宗屉佳,全部的銀都放在這個里面谷朝。
2.SliverList ,它是一個顯示兒童線性列表的條子武花。
3.SliverFixedExtentList 圆凰,它是一個更有效的條子,顯示沿著滾動軸具有相同范圍的子項的線性列表体箕。比SliverList多一個就是相同的行高专钉。這樣性能會更好
4.SliverPrototypeExtentList SliverPrototypeExtentList將其子項排列在沿著主軸的一條線上,從零偏移開始累铅,沒有間隙跃须。每個子項的約束程度與沿主軸的prototypeItem和沿橫軸的SliverConstraints.crossAxisExtent的程度相同。
5.SliverGrid 娃兽,它是一個顯示2D兒童陣列的條子菇民。可以設(shè)置每行的個數(shù)的網(wǎng)格
6.SliverPadding ,這是一條在另一條棉條周圍增加空白的條子第练。
7.SliverPersistentHeader 條子滾動到視口前緣時尺寸不同的條子阔馋。這是SliverAppBar用于縮小/增長效果的布局基元。
非常好用的組件娇掏,SliverAppBar就是用這個實現(xiàn)的呕寝。這個組件的特點是可以創(chuàng)建出隨著滑動變化的可以已固定的元素,大家經(jīng)常用的什么吸頂組件可以用這個很方便的構(gòu)建婴梧,后面我會使用這個寫一個自定義效果的SliverAppbar下梢。
8.SliverAppBar ,它是一個顯示標(biāo)題的條子志秃,可以在滾動視圖滾動時展開和浮動怔球。
9.SliverToBoxAdapter 當(dāng)你想把一個非銀的控件放在CustomScrollview里面的時候,你需要用這個包裹一下浮还。
10.SliverSafeArea 通過足夠的填充來插入另一條條子以防止操作系統(tǒng)入侵的條子竟坛。例如,這將使條子縮進(jìn)足以避開屏幕頂部的狀態(tài)欄钧舌。為了防止各種邊界的越界担汤,比如說越過頂部的狀態(tài)欄
11.SliverFillRemaining調(diào)整 其子項的大小以在十字軸中填充視口并填充主軸中視口中的剩余空間。使用這個它會填充完剩余視里面的全部空間
12.SliverOverlapAbsorber 洼冻,** SliverOverlapAbsorberHandle**這個上面2個是官方專門為了解決我們今天主角** NestedScrollView**中固定組件對身體里面Scroll狀態(tài)影響的崭歧,但官方做的不夠完美。
看源碼是一件好玩的事情撞牢,大家跟我一起來吧率碾。
flutterpackagesflutterlibsrcwidgetsnested_scroll_view.dart
首先我們看看第一個問題,從官方文檔中的樣品可以看到NestedScrollView
DefaultTabController(
length:_tabs.length屋彪,//這是制表符的數(shù)量所宰。
child:NestedScrollView(
headerSliv??erBuilder :( BuildContext context,bool innerBoxIsScrolled){
//這些是在“外部”滾動視圖中顯示的條子畜挥。
return <Widget> [
SliverOverlapAbsorber(
//此小部件采用SliverAppBar的重疊行為仔粥,
//并將其重定向到下面的SliverOverlapInjector。如果是
//缺少蟹但,那么嵌套的“內(nèi)部”滾動視圖是可能的
//下面最終在SliverAppBar下面甚至在內(nèi)部
//滾動視圖認(rèn)為它尚未滾動躯泰。
//如果只構(gòu)建“headerSliv??erBuilder”,則不需要這樣做
//與下一個條子不重疊的小部件华糖。
handle:NestedScrollView.sliverOverlapAbsorberHandleFor(context)麦向,
孩子:SliverAppBar(
title:const Text('Books'),//這是應(yīng)用欄中的標(biāo)題客叉。
固定的:真的磕蛇,
expandedHeight:150.0景描,
//“forceElevated”屬性導(dǎo)致SliverAppBar顯示
// 一個影子⌒闫玻“innerBoxIsScrolled”參數(shù)為true時為true
//內(nèi)部滾動視圖滾動超出其“零”點,即
//當(dāng)它似乎在SliverAppBar下方滾動時向族。
//如果沒有這個呵燕,就會出現(xiàn)陰影出現(xiàn)的情況
//或者不恰當(dāng)?shù)爻霈F(xiàn),因為SliverAppBar是
//實際上并沒有意識到內(nèi)在的準(zhǔn)確位置
//滾動視圖件相。
forceElevated:innerBoxIsScrolled再扭,
底部:TabBar(
//這些是放在標(biāo)簽欄中每個標(biāo)簽中的小部件。
tabs:_tabs.map((String name)=> Tab(text:name))夜矗。toList()泛范,
)
)
)
]。
}紊撕,
body:TabBarView(
//這些是選項卡視圖下方的選項卡視圖的內(nèi)容罢荡。
children:_tabs.map((String name){
返回SafeArea(
頂部:假,
bottom:false对扶,
孩子:建造者(
//需要此Builder來提供“內(nèi)部”的BuildContext
// NestedScrollView区赵,以便sliverOverlapAbsorberHandleFor()可以
//找到NestedScrollView。
builder:(BuildContext context){
返回CustomScrollView(
//應(yīng)該留下“控制器”和“主要”成員
//取消設(shè)置浪南,以便NestedScrollView可以控制它
//內(nèi)部滾動視圖笼才。
//如果設(shè)置了“controller”屬性,則滾動
//視圖不會與NestedScrollView相關(guān)聯(lián)络凿。
// PageStorageKey對于此ScrollView應(yīng)該是唯一的;
//它允許列表記住它的滾動位置
//標(biāo)簽視圖不在屏幕上骡送。
key:PageStorageKey <String>(name),
條:<Widget> [
SliverOverlapInjector(
//這是上面SliverOverlapAbsorber的另一面絮记。
handle:NestedScrollView.sliverOverlapAbsorberHandleFor(context)摔踱,
)
SliverPadding(
padding:const EdgeInsets.all(8.0),
//在此示例中到千,內(nèi)部滾動視圖具有
//固定高度列表項昌渤,因此使用
// SliverFixedExtentList。但是憔四,人們可以使用任何一個
//這里的sliver小部件膀息,例如SliverList或SliverGrid。
條子:SliverFixedExtentList(
//此示例中的項目固定為48像素
//高 這符合Material Design規(guī)范
// ListTile小部件
itemExtent:48.0了赵,
代表:SliverChildBuilderDelegate(
(BuildContext context潜支,int index){
//為每個孩子調(diào)用此構(gòu)建器。
//在這個例子中柿汛,我們只為每個列表項編號冗酿。
return ListTile(
title:Text('Item $ index')埠对,
);
},
// SliverChildBuilderDelegate的childCount
//指定此內(nèi)部列表的子項數(shù)
// 具有裁替。在此示例中项玛,每個選項卡都有一個列表
//正好30項,但這是任意的弱判。
childCount:30襟沮,
)
)
)
]
);
},
)
);
})昌腰。toList()开伏,
)
)
)
復(fù)制代碼
可以看到官方用一個SliverOverlapAbsorber包裹了SliverAppbar,在下面身體里面遭商,每一個列表的上面都加了個SliverOverlapInjector固灵。實際效果就是SliverOverlapInjector的高度就等于SliverAppbar的Pinned的高度。如果不加入這些代碼劫流,當(dāng)body里面的列表滾動到SliverAppbar下方的時候..依然可以繼續(xù)向上滾動徒仓,也就是說身體的滾動最上面點為0懂讯,而不是SliverAppbar的固??定高度。
為什么會出現(xiàn)這種情況呢?這要從Sliver的老祖宗CustomScrollView說起來姊途⌒聿迹可能很多人發(fā)現(xiàn)聂使,這些Sliver小部件(可以滾動的那種)沒有ScrollController這個東西(CustomScrollview和NestedScrollView除外)趣席。其實當(dāng)你把Sliver Widgets(可以滾動的那種)放到CustomScrollView里面的時候?qū)⒂蒀ustomScrollView來統(tǒng)一處理各種Sliver Widgets(可以滾動的那種),每個Sliver Widgets(可以滾動的那種)都會附加各自的ScrollPosition根穷。比如說第一個列表滾動到頭了姜骡,第2個列表就會開始處理對應(yīng)的的scrollPosition,將出現(xiàn)在檢視區(qū)里面的元素渲染出來屿良。
在我們的主角NestedScrollView當(dāng)中圈澈,有2個ScrollController。
class _NestedScrollController擴(kuò)展ScrollController {
_NestedScrollController(
this.coordinator尘惧,{
double initialScrollOffset = 0.0康栈,
字符串debugLabel,
復(fù)制代碼
一個是內(nèi)部喷橙,一個外部啥么。外部負(fù)責(zé)headerSliv??erBuilder里面的滾動小部件內(nèi)部是負(fù)責(zé)身體里面??的滾動小部件當(dāng)外滾動到底了之后,就會看看內(nèi)里面是否有能滾動的東東贰逾,開始滾動悬荣。
為了解決1問題,我們這里需要來處理外這個ScrollController里面控制的_NestedScrollPosition疙剑,問題1在于氯迂,當(dāng)頭部里面有多個釘扎的插件的時候践叠,我們外能滾動的程度。應(yīng)該要去減掉這個固定的總的高度嚼蚀。這樣當(dāng)滾動到固定的組件下方的時候禁灼。我們就會開始滾動內(nèi)。
在_NestedScrollPosition里面
// _NestedScrollPosition由a的內(nèi)部和外部視口使用
// NestedScrollView轿曙。它跟蹤用于那些視口的偏移量匾二,并且知道
//關(guān)于_NestedScrollCoordinator,以便在觸發(fā)活動時
//這個班級拳芙,他們可以推遲或受到協(xié)調(diào)員的影響。
class _NestedScrollPosition擴(kuò)展了ScrollPosition
實現(xiàn)ScrollActivityDelegate {
_NestedScrollPosition({
@required ScrollPhysics物理皮璧,
@required ScrollContext上下文舟扎,
double initialPixels = 0.0,
ScrollPosition oldPosition悴务,
字符串debugLabel睹限,
@required this.coordinator,
}):super(
復(fù)制代碼
我重寫了applyContentDimensions方法
@override
bool applyContentDimensions(double minScrollExtent讯檐,double maxScrollExtent){
if(debugLabel =='outer'&&
coordinator.pinnedHeaderSliv??erHeightBuilder羡疗!= null){
maxScrollExtent =
maxScrollExtent - coordinator.pinnedHeaderSliv??erHeightBuilder();
maxScrollExtent = math.max(0.0,maxScrollExtent);
}
return super.applyContentDimensions(minScrollExtent别洪,maxScrollExtent);
}
復(fù)制代碼
pinnedHeaderSliv??erHeightBuilder是我從最外層傳遞進(jìn)來的用于獲取當(dāng)時Pinned為真的全部Sliver header的高度..在這里把外部最大的滾動范圍減去了Pinned的總的高度叨恨,這樣我們就完美解決了問題。 1
示例代碼
在我的demo里面.pinned的高度由status bar + appbar + 1個或者2個tabbar組成挖垛。這里為什么要用個功能而不是直接傳遞個算好的高度呢痒钝?因為在我的案例里面這個pinned的高度是會改變的。
var tabBarHeight = primaryTabBar.preferredSize.height;
var pinnedHeaderHeight =
// statusBa height
statusBarHeight +
//在標(biāo)題中固定SliverAppBar高度
kToolbarHeight +
//在標(biāo)題中固定標(biāo)簽欄高度
(primaryTC.index == 0痢毒?tabBarHeight * 2:tabBarHeight);
返回NestedScrollViewRefreshIndicator(
onRefresh:onRefresh送矩,
child:extended.NestedScrollView(
headerSliv??erBuilder:(c,f){
return _buildSliverHeader(primaryTabBar);
}哪替,
//
pinnedHeaderSliv??erHeightBuilder :(){
return pinnedHeaderHeight;
}栋荸,
復(fù)制代碼
最后放上Github extended_nested_scroll_view,如果你有更好的方式解決這個問題或者有什么不明白的地方凭舶,都請告訴我晌块,由衷感謝。
過兩天我把flutter大綱給大家弄出來