Flutter擴(kuò)展NestedScrollView(一)固定頭引起的bug解決

這一篇的篇幅估計很多,請先買好瓜子汽水前排坐好盒卸,開車了..

NestedScrollView是一個復(fù)雜的組件骗爆,它跟Sliver系列是一伙的,最下層是個CustomScrollView世落。

銀色系列的東東很多淮腾,我們下面來一一介紹一下。

1.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大綱給大家弄出來

想學(xué)習(xí)更多Android知識库快,或者獲取相關(guān)資料請加入Android技術(shù)開發(fā)交流2群:935654177摸袁。本群可免費獲取Gradle,RxJava义屏,小程序靠汁,Hybrid蜂大,移動架構(gòu),NDK蝶怔,React Native奶浦,性能優(yōu)化等技術(shù)教程!

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末踢星,一起剝皮案震驚了整個濱河市澳叉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沐悦,老刑警劉巖成洗,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藏否,居然都是意外死亡瓶殃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門副签,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遥椿,“玉大人,你說我怎么就攤上這事淆储」诔。” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵本砰,是天一觀的道長碴裙。 經(jīng)常有香客問我,道長灌具,這世上最難降的妖魔是什么青团? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮咖楣,結(jié)果婚禮上督笆,老公的妹妹穿的比我還像新娘。我一直安慰自己诱贿,他們只是感情好娃肿,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著珠十,像睡著了一般料扰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焙蹭,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天晒杈,我揣著相機(jī)與錄音,去河邊找鬼孔厉。 笑死拯钻,一個胖子當(dāng)著我的面吹牛帖努,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粪般,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼拼余,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亩歹?” 一聲冷哼從身側(cè)響起匙监,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎小作,沒想到半個月后亭姥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡顾稀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年致份,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片础拨。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绍载,靈堂內(nèi)的尸體忽然破棺而出诡宗,到底是詐尸還是另有隱情,我是刑警寧澤击儡,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布塔沃,位于F島的核電站,受9級特大地震影響阳谍,放射性物質(zhì)發(fā)生泄漏蛀柴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一矫夯、第九天 我趴在偏房一處隱蔽的房頂上張望鸽疾。 院中可真熱鬧,春花似錦训貌、人聲如沸制肮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豺鼻。三九已至,卻和暖如春款慨,著一層夾襖步出監(jiān)牢的瞬間儒飒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工檩奠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留桩了,地道東北人附帽。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像圣猎,于是被迫代替她去往敵國和親士葫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容