????小菜發(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)源: 阿策小和尚