Flutter 130: 圖解 DraggableScrollableSheet 可手勢(shì)滑動(dòng)的菜單欄

????小菜發(fā)現(xiàn)在長(zhǎng)期未登陸小米應(yīng)用市場(chǎng)時(shí),再次登陸會(huì)有可滑動(dòng)的半屏底部菜單,供用戶(hù)方便下載和推廣砍艾;而在 Flutter 中這個(gè)半屏底部菜單并不是一個(gè)簡(jiǎn)單的 BottomSheet 完成的,可以通過(guò) DraggableScrollableSheet 根據(jù)手勢(shì)操作滑動(dòng)固定位的菜單欄完成;小菜簡(jiǎn)單學(xué)習(xí)一下抄伍;

DraggableScrollableSheet

源碼分析

const DraggableScrollableSheet({
    Key key,
    this.initialChildSize = 0.5,    // 初始比例
    this.minChildSize = 0.25,       // 最小比例
    this.maxChildSize = 1.0,        // 最大比例
    this.expand = true,             // 是否填充滿(mǎn)
    @required this.builder,
})

@overridep
Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        _extent.availablePixels = widget.maxChildSize * constraints.biggest.height;
        final Widget sheet = FractionallySizedBox(
          heightFactor: _extent.currentExtent,
          child: widget.builder(context, _scrollController),
          alignment: Alignment.bottomCenter,
        );
        return widget.expand ? SizedBox.expand(child: sheet) : sheet;
      },
    );
}

????簡(jiǎn)單分析源碼 DraggableScrollableSheet 作為一個(gè)有狀態(tài)的 StatefulWidget 小組件,通過(guò) FractionallySizedBox 以父 Widget 為基數(shù)管宵,可設(shè)置寬高比例的容器構(gòu)建子內(nèi)容截珍;

案例嘗試

1. builder

????ScrollableWidgetBuilder 構(gòu)造器作為必選字段,用于在 DraggableScrollableSheet 中顯示可滑動(dòng)的子內(nèi)容箩朴;其中返回內(nèi)容需為可滑動(dòng)的 ScrollableWidget笛臣,例如 ListView / GridView / SingleChildScrollView 等;

_listWid(controller) => SingleChildScrollView(
    controller: controller,
    child: Column(children: [
      Container(
          height: 5.0, width: 25.0,
          decoration: BoxDecoration(color: Colors.blue.withOpacity(0.8), borderRadius: BorderRadius.all(Radius.circular(16.0))),
          margin: EdgeInsets.symmetric(vertical: 12.0)),
      Padding(
          padding: EdgeInsets.symmetric(horizontal: 12.0),
          child: GridView.builder(
              physics: ScrollPhysics(),
              primary: false, shrinkWrap: true,
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 5, mainAxisSpacing: 8.0, crossAxisSpacing: 12.0, childAspectRatio: 0.7),
              itemCount: 12,
              itemBuilder: (context, index) => GestureDetector(
                  child: Column(children: <Widget>[
                    PhysicalModel(
                        color: Colors.transparent, shape: BoxShape.circle,
                        clipBehavior: Clip.antiAlias, child: Image.asset('images/icon_hzw01.jpg')),
                    SizedBox(height: 4), Text('海賊王')
                  ]),
                  onTap: () {}))),
      ListView.builder(
          physics: NeverScrollableScrollPhysics(),
          shrinkWrap: true,
          itemCount: 15,
          itemBuilder: (BuildContext context, index) => ListTile(title: Text('Current Item = ${(index + 1)}')))
    ]));

2. initialChildSize

????initialChildSize 用于顯示初始子 Widgets 所占父 Widget 比例隧饼;同時(shí)沈堡,若返回的子 Widget 未提供 ScrollController,則 DraggableScrollableSheet 不會(huì)隨手勢(shì)進(jìn)行滑動(dòng)燕雁,小菜理解為 initialChildSize = minChildSize = maxChildSize诞丽;

_sheetWid02() => DraggableScrollableSheet(
    initialChildSize: 0.66,
    builder: (BuildContext context, ScrollController scrollController) =>
        Container(
            decoration: BoxDecoration(
                color: Colors.grey.withOpacity(0.4),
                borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))),
            child: _listWid(null)));

3. minChildSize & maxChildSize

????minChildSize & maxChildSize 分別對(duì)應(yīng)子 Widgets 占整體的最大最小比例,其中 initialChildSize 需要在兩者之間拐格;

_sheetWid03() => DraggableScrollableSheet(
    initialChildSize: 0.6,
    minChildSize: 0.3,
    maxChildSize: 0.9,
    expand: true,
    builder: (BuildContext context, ScrollController scrollController) =>
        Container(
            decoration: BoxDecoration(
                color: Colors.grey.withOpacity(0.4),
                borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))),
            child: _listWid(scrollController)));

4. expand

????expand 用于是否填充滿(mǎn)父 Widget僧免,若 DraggableScrollableSheet 外層固定高度則不影響;若外層未對(duì)高度進(jìn)行固定捏浊,expand 作用于是否填充滿(mǎn)父 Widget懂衩;構(gòu)造的源碼也是通過(guò) SizedBox.expand 對(duì)父 Widget 進(jìn)行填充判斷的;

return widget.expand ? SizedBox.expand(child: sheet) : sheet;

小擴(kuò)展

????之前在分析 DraggableScrollableSheet 時(shí)其源碼采用了 FractionallySizedBox 比例容器,小菜簡(jiǎn)單了解一下浊洞,其源碼非常簡(jiǎn)單牵敷,通過(guò)設(shè)置 heightFactor & widthFactor 占父 Widget 的比例即可,通過(guò) alignment 設(shè)置所在父 widget 的對(duì)齊方式法希;

SizedBox.expand(child: _sizedBox())

_sizedBox() => FractionallySizedBox(
    heightFactor: 0.5,
    widthFactor: 0.5,
    alignment: Alignment.center,
    child: Container(
        color: Colors.deepOrange.withOpacity(0.4),
        child: ListView.builder(
            itemCount: 15,
            itemBuilder: (BuildContext context, index) => ListTile(title: Text('Current Item = ${(index + 1)}')))));

????案例源碼


????小菜對(duì) DraggableScrollableSheet 的手勢(shì)滑動(dòng)過(guò)程還不夠熟悉枷餐,之后會(huì)對(duì)手勢(shì)進(jìn)行進(jìn)一步學(xué)習(xí);如有錯(cuò)誤苫亦,請(qǐng)多多指導(dǎo)毛肋!

來(lái)源: 阿策小和尚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屋剑,隨后出現(xiàn)的幾起案子润匙,更是在濱河造成了極大的恐慌,老刑警劉巖唉匾,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁桃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡肄鸽,警方通過(guò)查閱死者的電腦和手機(jī)卫病,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)典徘,“玉大人蟀苛,你說(shuō)我怎么就攤上這事〈澹” “怎么了帜平?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)梅鹦。 經(jīng)常有香客問(wèn)我裆甩,道長(zhǎng),這世上最難降的妖魔是什么齐唆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任嗤栓,我火速辦了婚禮,結(jié)果婚禮上箍邮,老公的妹妹穿的比我還像新娘茉帅。我一直安慰自己,他們只是感情好锭弊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布堪澎。 她就那樣靜靜地躺著,像睡著了一般味滞。 火紅的嫁衣襯著肌膚如雪樱蛤。 梳的紋絲不亂的頭發(fā)上钮呀,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音昨凡,去河邊找鬼爽醋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛土匀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播形用,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼就轧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了田度?” 一聲冷哼從身側(cè)響起妒御,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镇饺,沒(méi)想到半個(gè)月后乎莉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奸笤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年惋啃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片监右。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡边灭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出健盒,到底是詐尸還是另有隱情绒瘦,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布扣癣,位于F島的核電站惰帽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏父虑。R本人自食惡果不足惜该酗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望士嚎。 院中可真熱鬧垂涯,春花似錦、人聲如沸航邢。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至册招,卻和暖如春岔激,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背是掰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留江兢,地道東北人杉允。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子案疲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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