Close modal bottom sheet programmatically in flutter

DartFlutter

Dart 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.

  1. showModalBottomSheet => Like a Dialog, not a part of Scaffold

  2. showBottomSheet => Part of Scaffold, this is persistent.


1. Showing and Hiding showModalBottomSheet

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:

enter image description here


2. Showing and Hiding showBottomSheet

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:

enter image description here

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),
    );
  }
}

Video Representing the bottom sheet

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonasHView Question on Stackoverflow
Solution 1 - DartJonasHView Answer on Stackoverflow
Solution 2 - DartCopsOnRoadView Answer on Stackoverflow
Solution 3 - DartAashar WahlaView Answer on Stackoverflow
Solution 4 - DartHimanshu MahajanView Answer on Stackoverflow
Solution 5 - DartParesh MangukiyaView Answer on Stackoverflow
Solution 6 - Dartneon97View Answer on Stackoverflow
Solution 7 - DartleyleksevenView Answer on Stackoverflow
Solution 8 - DartDatapalaceView Answer on Stackoverflow