1. 基本介绍
BottomSheet 是一种常见的上拉框,个人感觉 showModalBottomSheet 更为常用一点。
2. 示例代码
代码下载地址。如果对你有帮助的话记得给个关注,代码会根据我的 Flutter 专题不断更新。
3. 属性介绍
BottomSheet 属性 | 介绍 |
---|---|
animationController | 动画控制器 |
enableDrag | 是否可以拖动,默认为 true |
onDragStart | 开始拖拽回调,没有找到具体使用场景,后续更新 |
onDragEnd | 结束拖拽回调,没有找到具体使用场景,后续更新 |
backgroundColor | 背景色 |
elevation | 阴影高度 |
shape | 形状 BorderShape |
clipBehavior | 剪切方式 |
onClosing | 关闭回调函数 |
builder | 构建函数 |
4. BottomSheet 详解
BottomSheet 作为组件直接使用的时候比较少,比如配合 Scaffold 的子属性使用,可以理解为展示在屏幕下方的一个组件。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State {
@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"),
);
},
);
}
}
5. showModalBottomSheet 详解
showModalBottomSheet 是一个直接调起 BottomSheet 的 api,使用频率较高。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State {
@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),),
);
},
);
},
);
}
}
6. showBottomSheet 详解
showBottomSheet 对新手可能不太友好,它的实际调用是 Scaffold.of(context).showBottomSheet,.of(context) 方法在当前同一层级是拿不到 Scaffold Widget 的,所以会报错,需要在封装一层 class 进行使用。
6.1 常规报错
══╡ 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. (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. (package:FMStudyApp/Widgets/Material_components/bottomsheet.dart:53:9)
#4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#5 _InkResponseState.build. (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. (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. (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. (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 单独用一个 class 封装一层即可。
import 'package:flutter/material.dart';
class FMBottomSheetVC extends StatefulWidget{
@override
FMBottomSheetState createState() => FMBottomSheetState();
}
class FMBottomSheetState extends State {
@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);
7. 技术小结
上拉框是一个很常用的控件,其实除了唤起,其他属性和该组件并没有太大关系,考验的还是各种组件搭配使用的基本功。