1. 基本介紹
BottomSheet 是一種常見的上拉框,個人感覺 showModalBottomSheet 更為常用一點。
2. 示例代碼
代碼下載地址。如果對你有幫助的話記得給個關(guān)注,代碼會根據(jù)我的 Flutter 專題不斷更新。
3. 屬性介紹
| BottomSheet 屬性 | 介紹 |
|---|---|
| animationController | 動畫控制器 |
| enableDrag | 是否可以拖動,默認(rèn)為 true |
| onDragStart | 開始拖拽回調(diào),沒有找到具體使用場景,后續(xù)更新 |
| onDragEnd | 結(jié)束拖拽回調(diào),沒有找到具體使用場景,后續(xù)更新 |
| backgroundColor | 背景色 |
| elevation | 陰影高度 |
| shape | 形狀 BorderShape |
| clipBehavior | 剪切方式 |
| onClosing | 關(guān)閉回調(diào)函數(shù) |
| builder | 構(gòu)建函數(shù) |
4. BottomSheet 詳解
BottomSheet 作為組件直接使用的時候比較少,比如配合 Scaffold 的子屬性使用,可以理解為展示在屏幕下方的一個組件。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State <FMBottomSheetVC>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return _scaffold(context);
}
Scaffold _scaffold(context){
return Scaffold(
appBar: AppBar(title: Text("BottomSheet"),),
body: Center(
// child: BottomSheetBtn(),
),
bottomSheet: _bottomSheet(context),
floatingActionButton: FloatingActionButton(
child: Text("返回"),
onPressed: (){
Navigator.pop(context);
},
),
);
}
BottomSheet _bottomSheet(context){
return BottomSheet(
onClosing: (){
print("closed");
},
builder: (context){
return Container(
height: 300,
color: Colors.yellow,
alignment: Alignment.centerLeft,
child: Text("BottomSheet In Scaffold"),
);
},
);
}
}

BottomSheet.png
5. showModalBottomSheet 詳解
showModalBottomSheet 是一個直接調(diào)起 BottomSheet 的 api,使用頻率較高。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State <FMBottomSheetVC>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return _scaffold(context);
}
Scaffold _scaffold(context){
return Scaffold(
appBar: AppBar(title: Text("BottomSheet"),),
body: Center(
child: _raisedButton(context),
),
// bottomSheet: _bottomSheet(context),
// floatingActionButton: FloatingActionButton(
// child: Text("返回"),
// onPressed: (){
// Navigator.pop(context);
// },
// ),
);
}
BottomSheet _bottomSheet(context){
return BottomSheet(
onClosing: (){
print("closed");
},
builder: (context){
return Container(
height: 300,
color: Colors.yellow,
alignment: Alignment.centerLeft,
child: Text("BottomSheet In Scaffold"),
);
},
);
}
RaisedButton _raisedButton(context){
return RaisedButton(
child: Text("showModalBottomSheet"),
onPressed: (){
showModalBottomSheet(
context: context,
builder: (context){
return Container(
width: 414,
height: 300,
color: Colors.red,
alignment: Alignment.centerLeft,
child: Text("showModalBottomSheet", style: TextStyle(color: Colors.white),),
);
},
);
},
);
}
}

showModalBottomSheet.gif
6. showBottomSheet 詳解
showBottomSheet 對新手可能不太友好,它的實際調(diào)用是 Scaffold.of(context).showBottomSheet,.of(context) 方法在當(dāng)前同一層級是拿不到 Scaffold Widget 的,所以會報錯,需要在封裝一層 class 進(jìn)行使用。
6.1 常規(guī)報錯
══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
No Scaffold widget found.
FMBottomSheetVC widgets require a Scaffold widget ancestor.
The specific widget that could not find a Scaffold ancestor was:
FMBottomSheetVC
The ancestors of this widget were:
...
Semantics
Builder
RepaintBoundary-[GlobalKey#26d4e]
IgnorePointer
AnimatedBuilder
...
Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of
your application widget tree.
When the exception was thrown, this was the stack:
#0 debugCheckHasScaffold.<anonymous closure> (package:flutter/src/material/debug.dart:114:7)
#1 debugCheckHasScaffold (package:flutter/src/material/debug.dart:125:4)
#2 showBottomSheet (package:flutter/src/material/bottom_sheet.dart:725:10)
#3 FMBottomSheetState._raisedButton.<anonymous closure> (package:FMStudyApp/Widgets/Material_components/bottomsheet.dart:53:9)
#4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
#6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
#7 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
#8 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
#9 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:219:7)
#10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:477:9)
#11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:78:12)
#12 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:124:9)
#13 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
#14 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:122:18)
#15 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:108:7)
#16 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:220:19)
#17 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
#18 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
#19 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
#20 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
#24 _invoke1 (dart:ui/hooks.dart:267:10)
#25 _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)
(elided 3 frames from dart:async)
Handler: "onTap"
Recognizer:
TapGestureRecognizer#1116e
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
No Scaffold widget found.
FMBottomSheetVC widgets require a Scaffold widget ancestor.
The specific widget that could not find a Scaffold ancestor was: FMBottomSheetVC
dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#da548]]
state: FMBottomSheetState#0bb6b
The ancestors of this widget were:
: MaterialApp
state: _MaterialAppState#e550a
: MyApp
...
Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of your application widget tree.
When the exception was thrown, this was the stack:
#0 debugCheckHasScaffold.<anonymous closure> (package:flutter/src/material/debug.dart:114:7)
#1 debugCheckHasScaffold (package:flutter/src/material/debug.dart:125:4)
#2 showBottomSheet (package:flutter/src/material/bottom_sheet.dart:725:10)
#3 FMBottomSheetState._raisedButton.<anonymous closure> (package:FMStudyApp/Widgets/Material_components/bottomsheet.dart:53:9)
#4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#1116e
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(214.5, 497.0)
finalLocalPosition: Offset(106.5, 17.0)
button: 1
sent tap down
════════════════════════════════════════════════════════════════════════════════════════════════════
No Scaffold widget found.
Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of
your application widget tree.
6.2 解決方案
將 RaisedButton 單獨(dú)用一個 class 封裝一層即可。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State <FMBottomSheetVC>{
@override
Widget build(BuildContext context) {
// TODO: implement build
// return _materialApp(context);
return _scaffold(context);
}
Scaffold _scaffold(context){
return Scaffold(
appBar: AppBar(title: Text("BottomSheet"),),
body: Center(
// child: _raisedButton(context),
child: BottomSheetBtn(),
),
// bottomSheet: _bottomSheet(context),
// floatingActionButton: FloatingActionButton(
// child: Text("返回"),
// onPressed: (){
// Navigator.pop(context);
// },
// ),
);
}
BottomSheet _bottomSheet(context){
return BottomSheet(
onClosing: (){
print("closed");
},
builder: (context){
return Container(
height: 300,
color: Colors.yellow,
alignment: Alignment.centerLeft,
child: Text("BottomSheet In Scaffold"),
);
},
);
}
RaisedButton _raisedButton(context){
return RaisedButton(
child: Text("showModalBottomSheet"),
onPressed: (){
showBottomSheet(
context: context,
builder: (context){
return Container(
width: 414,
height: 300,
color: Colors.red,
alignment: Alignment.centerLeft,
child: Text("showModalBottomSheet", style: TextStyle(color: Colors.white),),
);
},
);
},
);
}
}
class BottomSheetBtn extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return RaisedButton(
child: Text("showBottomSheet"),
onPressed: (){
showBottomSheet(
context: context,
builder: (context){
return Container(
width: 414,
height: 300,
color: Colors.red,
alignment: Alignment.centerLeft,
child: Text("showBottomSheet", style: TextStyle(color: Colors.white),),
);
},
);
},
);
}
}
可以用以下方法收起上拉框
Navigator.pop(context);

showBottomSheet.gif
7. 技術(shù)小結(jié)
上拉框是一個很常用的控件,其實除了喚起,其他屬性和該組件并沒有太大關(guān)系,考驗的還是各種組件搭配使用的基本功。