SingleChildScrollView 源碼定義如下:
專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)麻山免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千余家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
需要注意的是, 通常 SingleChildScrollView 只應(yīng)在期望的內(nèi)容不會(huì)超過屏幕太多時(shí)使用 ,這是因?yàn)?SingleChildScrollView 不支持基于 Sliver 的延遲加載模型,所以如果預(yù)計(jì)視口可能包含超出屏幕尺寸太多的內(nèi)容時(shí),那么使用 SingleChildScrollView 將會(huì)非常昂貴(性能差),此時(shí)應(yīng)該使用一些支持Sliver延遲加載的可滾動(dòng)組件,如 ListView 。
示例1
下面是一個(gè)將大寫字母 A-Z 沿垂直方向顯示的例子,由于垂直方向空間會(huì)超過屏幕視口高度,所以我們使用SingleChildScrollView:
示例2
示例3 - 橫向滾動(dòng)
ListView的基礎(chǔ)創(chuàng)建使用有三種方式:
通過默認(rèn)構(gòu)造函數(shù)來創(chuàng)建列表,應(yīng)用場景 = 短列表
這種方式創(chuàng)建的列表存在一個(gè)問題:對(duì)于那些長列表或者需要較昂貴渲染開銷的子組件,即使還沒有出現(xiàn)在屏幕中但仍然會(huì)被ListView所創(chuàng)建,這將是一項(xiàng)較大的開銷,使用不當(dāng)可能引起性能問題甚至卡頓。
長列表
列表子項(xiàng)之間需要分割線
ListView的進(jìn)階使用主要包括:下拉刷新 上拉加載
在Flutter中,ListView結(jié)合RefreshIndicator組件實(shí)現(xiàn)下拉刷新
通過包裹一層RefreshIndicator,自定義onRefresh回調(diào)方法實(shí)現(xiàn)
方式有兩種:
通過ListView.controller屬性可以判斷ListView是否滑動(dòng)到了底部,再進(jìn)行上拉加載
NotificationListener是一個(gè)Widget,可監(jiān)聽子Widget發(fā)出的Notification
ListView在滑動(dòng)時(shí)中會(huì)發(fā)出ScrollNotification類型的通知,可通過監(jiān)聽該通知得到ListView的滑動(dòng)狀態(tài),判斷是否滑動(dòng)到了底部,從而進(jìn)行上拉加載
NotificationListener有一個(gè)onNotification屬性,定義了監(jiān)聽的回調(diào)方法,通過它來處理加載更多邏輯
不定期分享關(guān)于 安卓開發(fā) 的干貨,追求 短、平、快 ,但 卻不缺深度 。
Flutter 中有兩種布局模型:
基于 RenderBox 的盒模型布局。
基于 Sliver ( RenderSliver ) 按需加載列表布局。
通常可滾動(dòng)組件的子組件可能會(huì)非常多、占用的總高度也會(huì)非常大;如果要一次性將子組件全部構(gòu)建出將會(huì)非常昂貴!為此,F(xiàn)lutter中提出一個(gè)Sliver(中文為“薄片”的意思)概念,Sliver 可以包含一個(gè)或多個(gè)子組件。Sliver 的主要作用是配合:加載子組件并確定每一個(gè)子組件的布局和繪制信息,如果 Sliver 可以包含多個(gè)子組件時(shí),通常會(huì)實(shí)現(xiàn)按需加載模型。
只有當(dāng) Sliver 出現(xiàn)在視口中時(shí)才會(huì)去構(gòu)建它,這種模型也稱為“基于Sliver的列表按需加載模型”??蓾L動(dòng)組件中有很多都支持基于Sliver的按需加載模型,如 ListView 、 GridView ,但是也有不支持該模型的,如 SingleChildScrollView 。
Flutter 中的可滾動(dòng)主要由三個(gè)角色組成: Scrollable 、 Viewport 和 Sliver :
具體布局過程:
比如有一個(gè) ListView,大小撐滿屏幕,假設(shè)它有 100 個(gè)列表項(xiàng)(都是RenderBox)且每個(gè)列表項(xiàng)高度相同,結(jié)構(gòu)如圖6-1所示:
圖中白色區(qū)域?yàn)樵O(shè)備屏幕,也是 Scrollable 、 Viewport 和 Sliver 所占用的空間,三者所占用的空間重合,父子關(guān)系為:Sliver 父組件為 Viewport,Viewport的 父組件為 Scrollable 。注意ListView 中只有一個(gè) Sliver,在 Sliver 中實(shí)現(xiàn)了子組件的按需加載。
其中頂部和底部灰色的區(qū)域?yàn)?cacheExtent,它表示預(yù)渲染的高度,需要注意這是在可視區(qū)域之外,如果 RenderBox 進(jìn)入這個(gè)區(qū)域內(nèi),即使它還未顯示在屏幕上,也是要先進(jìn)行構(gòu)建的,預(yù)渲染是為了后面進(jìn)入 Viewport 的時(shí)候更絲滑。cacheExtent 的默認(rèn)值是 250,在構(gòu)建可滾動(dòng)列表時(shí)我們可以指定這個(gè)值,這個(gè)值最終會(huì)傳給 Viewport。
用于處理滑動(dòng)手勢,確定滑動(dòng)偏移,滑動(dòng)偏移變化時(shí)構(gòu)建 Viewport,我們看一下其關(guān)鍵的屬性:
在可滾動(dòng)組件的坐標(biāo)描述中,通常將滾動(dòng)方向稱為主軸,非滾動(dòng)方向稱為縱軸。由于可滾動(dòng)組件的默認(rèn)方向一般都是沿垂直方向,所以默認(rèn)情況下主軸就是指垂直方向,水平方向同理。
Viewport 比較簡單,用于渲染當(dāng)前視口中需要顯示 Sliver。
需要注意的是:
Sliver 主要作用是對(duì)子組件進(jìn)行構(gòu)建和布局,比如 ListView 的 Sliver 需要實(shí)現(xiàn)子組件(列表項(xiàng))按需加載功能,只有當(dāng)列表項(xiàng)進(jìn)入預(yù)渲染區(qū)域時(shí)才會(huì)去對(duì)它進(jìn)行構(gòu)建和布局、渲染。
Sliver 對(duì)應(yīng)的渲染對(duì)象類型是 RenderSliver,RenderSliver 和 RenderBox 的相同點(diǎn)是都繼承自 RenderObject 類,不同點(diǎn)是在布局的時(shí)候約束信息不同。RenderBox 在布局時(shí)父組件傳遞給它的約束信息對(duì)應(yīng)的是 BoxConstraints ,只包含最大寬高的約束;而 RenderSliver 在布局時(shí)父組件(列表)傳遞給它的約束是對(duì)應(yīng)的是 SliverConstraints 。關(guān)于 Sliver 的布局協(xié)議,我們將在本章最后一節(jié)中介紹。
幾乎所有的可滾動(dòng)組件在構(gòu)造時(shí)都能指定 scrollDirection (滑動(dòng)的主軸)、 reverse (滑動(dòng)方向是否反向)、 controller 、 physics 、 cacheExtent ,這些屬性最終會(huì)透傳給對(duì)應(yīng)的 Scrollable 和 Viewport,這些屬性我們可以認(rèn)為是可滾動(dòng)組件的通用屬性,后續(xù)再介紹具體的可滾動(dòng)組件時(shí)將不再贅述。
可滾動(dòng)組件都有一個(gè) controller 屬性,通過該屬性我們可以指定一個(gè) ScrollController 來控制可滾動(dòng)組件的滾動(dòng),比如可以通過ScrollController來同步多個(gè)組件的滑動(dòng)聯(lián)動(dòng)。由于 ScrollController 是需要結(jié)合可滾動(dòng)組件一起工作,所以本章中,我們會(huì)在介紹完 ListView 后詳細(xì)介紹 ScrollController。
Scrollbar是一個(gè)Material風(fēng)格的滾動(dòng)指示器(滾動(dòng)條),如果要給可滾動(dòng)組件添加滾動(dòng)條,只需將Scrollbar作為可滾動(dòng)組件的任意一個(gè)父級(jí)組件即可,如:
Scrollbar 和 CupertinoScrollbar 都是通過監(jiān)聽滾動(dòng)通知來確定滾動(dòng)條位置的。關(guān)于的滾動(dòng)通知的詳細(xì)內(nèi)容我們將在本章最后一節(jié)中專門介紹。
CupertinoScrollbar是 iOS 風(fēng)格的滾動(dòng)條,如果你使用的是Scrollbar,那么在iOS平臺(tái)它會(huì)自動(dòng)切換為CupertinoScrollbar
對(duì)于滾動(dòng)的視圖,我們經(jīng)常需要監(jiān)聽它的一些滾動(dòng)事件,在監(jiān)聽到的時(shí)候去做對(duì)應(yīng)的一些事情。
比如視圖滾動(dòng)到底部時(shí),我們可能希望做上拉加載更多;
比如滾動(dòng)到一定位置時(shí)顯示一個(gè)回到頂部的按鈕,點(diǎn)擊回到頂部的按鈕,回到頂部;
比如監(jiān)聽滾動(dòng)什么時(shí)候開始,什么時(shí)候結(jié)束;
在Flutter中監(jiān)聽滾動(dòng)相關(guān)的內(nèi)容由兩部分組成:ScrollController和ScrollNotification。
ScrollController
在Flutter中,Widget并不是最終渲染到屏幕上的元素(真正渲染的是RenderObject),因此通常這種監(jiān)聽事件以及相關(guān)的信息并不能直接從Widget中獲取,而是必須通過對(duì)應(yīng)的Widget的Controller來實(shí)現(xiàn)。
ListView、GridView的組件控制器是ScrollController,我們可以通過它來獲取視圖的滾動(dòng)信息,并且可以調(diào)用里面的方法來更新視圖的滾動(dòng)位置。
另外,通常情況下,我們會(huì)根據(jù)滾動(dòng)的位置來改變一些Widget的狀態(tài)信息,所以ScrollController通常會(huì)和StatefulWidget一起來使用,并且會(huì)在其中控制它的初始化、監(jiān)聽、銷毀等事件。
我們來做一個(gè)案例,當(dāng)滾動(dòng)到1000位置的時(shí)候,顯示一個(gè)回到頂部的按鈕:
jumpTo(double offset)、animateTo(double offset,...):這兩個(gè)方法用于跳轉(zhuǎn)到指定的位置,它們不同之處在于,后者在跳轉(zhuǎn)時(shí)會(huì)執(zhí)行一個(gè)動(dòng)畫,而前者不會(huì)。
ScrollController間接繼承自Listenable,我們可以根據(jù)ScrollController來監(jiān)聽滾動(dòng)事件。
要實(shí)現(xiàn)上面的效果,每個(gè)種類的標(biāo)簽橫向滾動(dòng),實(shí)現(xiàn)的方式,最外層的大分類標(biāo)簽一個(gè)ListView,每個(gè)分類的標(biāo)簽也是ListView 設(shè)置橫向滾動(dòng)結(jié)合Wrap組件就能實(shí)現(xiàn)。
注意: 滾動(dòng)組件添加: physics: ClampingScrollPhysics() 可以處理IOS系統(tǒng)的物理滾動(dòng)的效果(即橡皮筋效果)
ListView 是最常用的可滾動(dòng)組件之一,可以沿一個(gè)方向線性排布所有子組件,并且它也支持基于Sliver的延遲構(gòu)建模型
默認(rèn)構(gòu)造函數(shù):
ListView.builder:
ListView.separated:
ListView.separated 可以在生成的列表項(xiàng)之間添加一個(gè)分割組件,它比 ListView.builder 多了一個(gè) separatorBuilder 參數(shù),該參數(shù)是一個(gè)分割組件生成器。
RefreshIndicator 下拉刷新:
RefreshIndicator 是 Material 風(fēng)格的下拉刷新組件。
CupertinoSliverRefreshControl 下拉刷新:
CupertinoSliverRefreshControl 是 ios 風(fēng)格的下拉刷新控件。
上拉加載的功能,需要用到 ScrollController + ListView組件: