Close modal bottom sheet programmatically in flutter
DartFlutterDart Problem Overview
I am displaying a BottomSheet via showModalBottomSheet<Null>()
and inside several widgets with a GestureDetector.
I would like to see the BottomSheet closed not only by touching outside it but also after an onTap event of a GestureDetector inside. However, it seems the GestureDetector is not forwarding the touch event.
So I am wondering, is there a way to trigger the closing of the ModalBottomSheet programmatically or a way to tell the GestureDetector to forward the touch event?
Update (2018-04-12):
Following a code snippet for better understanding. The problem is that the ModalBottomSheet isn't closing when tapping on "Item 1" or "Item 2".
showModalBottomSheet<Null>(context: context, builder: (BuildContext context)
{
return new SingleChildScrollView(child:
new Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
new GestureDetector(onTap: () { doSomething(); }, child:
new Text("Item 1")
),
new GestureDetector(onTap: () { doSomething(); }, child:
new Text("Item 2")
),
]),
);
});
Dart Solutions
Solution 1 - Dart
Closing a ModalBottomSheet programmatically is done via
Navigator.pop(context);
So I just call that pop function inside the onTap callback function of the GestureDetector.
showModalBottomSheet(context: context, builder: (BuildContext context)
{
return SingleChildScrollView(child:
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
GestureDetector(onTap: () {
Navigator.pop(context);
doSomething();
}, child:
Text("Item 1")
),
GestureDetector(onTap: () {
Navigator.pop(context);
doSomething();
}, child:
Text("Item 2")
),
]),
);
});
Solution 2 - Dart
Short answer:
Use any of the below:
Navigator.pop(context);
Navigator.of(context).pop();
Long answer:
Generally there are 2 types of bottom sheet.
-
showModalBottomSheet
=> Like aDialog
, not a part ofScaffold
-
showBottomSheet
=> Part ofScaffold
, this is persistent.
showModalBottomSheet
1. Showing and Hiding This code shows bottom sheet, and hides it when tapping on the FlutterLogo
@override
void initState() {
super.initState();
Future(() {
showModalBottomSheet(
context: context,
builder: (_) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(), // Closing the sheet.
child: FlutterLogo(size: 200),
);
},
);
});
}
Output:
showBottomSheet
2. Showing and Hiding This code shows a button, which will open and close the bottom sheet.
late PersistentBottomSheetController _controller;
GlobalKey<ScaffoldState> _key = GlobalKey();
bool _open = false;
@override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
body: Center(
child: ElevatedButton(
onPressed: () {
if (!_open) {
_controller = _key.currentState!.showBottomSheet(
(_) => SizedBox(
child: FlutterLogo(size: 200),
width: double.maxFinite,
),
);
} else {
_controller.close();
}
setState(() => _open = !_open);
},
child: Text(_open ? "Close" : "Open"),
),
),
);
}
Output:
Solution 3 - Dart
class _FABState extends State<FAB> {
bool isOpen = false;
var bottomSheetController;
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
setState(() {
isOpen = !isOpen;
});
print('tapped on the bottom sheet');
if(isOpen) {
bottomSheetController = showBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (ctx) {
return ClipRRect(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
child: Container(
height: 150,
color: Colors.black,
child: TextField()
),
);
});
bottomSheetController.closed.then((value) {
setState(() {
isOpen = !isOpen;
});
});
} else {
Navigator.of(context).pop();
setState(() {
isOpen = !isOpen;
});
}
},
child: isOpen?Icon(Icons.arrow_downward):Icon(Icons.arrow_upward),
);
}
}
Solution 4 - Dart
Showing and hiding bottom sheet in flutter:
showModalBottomSheet(
context: context,
builder: (bCtx) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container(height: 300, child: Text('Bottom sheet widget'),),
onTap: () {
Navigator.of(context).pop();
},
);
},
);
Solution 5 - Dart
The fix in our code was to wrap our bottom sheet widget like this. The buttons in the bottom sheet still worked:
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: <existing bottom sheet widget>
)
Solution 6 - Dart
If your modal sheet doesn't goes down and gets back to the previous page directly, so you can use {useRootNavigator:true}
, then use a Navigator.pop(context);
from the other function. It will only hide the modal sheet instead of taking to the previous page.
Solution 7 - Dart
If you are using bottom sheet with scaffold key and persistent bottom sheet controller my way is like;
final scaffoldState = GlobalKey<ScaffoldState>();
dynamic controller;
if(controller!=null){
controller as PersistentBottomSheetController).close();
controller=null;
}
override
void dispose() {
if(controller!=null){
controller as PersistentBottomSheetController).close();
}
super.dispose();
}
in this way, for like my widget hierarchy; if user click the bottom navigation bar, user can change the current page, I am closing the current bottom sheet.
if user close the bottom sheet himself, for our dynamic controller, we can close the controller and assign it to null value. so this way, we can know is the bottom sheet exist or not.
Solution 8 - Dart
Once your code has completed the task expected, you can add Navigator.of(context, rootNavigator: true).pop();
at the end so that the modal closes