Search code examples
flutter

Flutter: Show Modal Bottom Sheet over/replacing Bottom Navigation Bar (without barrier)


I'm trying to replicate some functionality similar to Google Maps for my own Flutter application. Also map based, I'd like the user to be able to select a point of interest and have the option to navigate to said location.

When they hit 'navigate', I want a bottom sheet to appear in place (or on top of) the existing Bottom Navigation Bar that I have for tab-based navigation.

showModalBottomSheet() does this, however it has a barrier which prevents the user from interacting with the parent view. I need it without the barrier.

showBottomSheet() doesn't have the barrier but it also doesn't get placed on top of the Bottom Navigation Bar.

Is there some solution in order to have this functionality? I am thinking to add the Bottom Navigation Bar to a Container that will show/hide based on whether the user is navigating or not, but I feel it's quite a hacked solution and was hoping for something more elegant.

I've attached a couple of images below:

  • The first screenshot shows Google Maps' standard view on iOS that has the Bottom Navigation Bar.
  • The second screenshot shows the 'pre-navigation' view which is a Bottom Sheet which is placed on top of (or in place of) the Bottom Navigation Bar. This is the view that I'm trying to replicate.

This is the main view with the Bottom Navigation Bar at the bottom This is the 'pre-navigation' view that shows a bottom sheet without / on top of the Bottom Navigation Bar


Solution

  • The solution I've found would be to wrap the Scaffold widget containing your bottomNavigationBar into another Scaffold. The idea would be to display your modal inside the parent Scaffold so it would be on top of the bottomNavigationBar inside the child Scaffold.

    class _MyWidgetState extends State<MyWidget> {
      bool _showModal = false;
      final _scaffoldKey = GlobalKey<ScaffoldState>();
    
      PersistentBottomSheetController _bottomSheetController;
    
      void _showOrHide(bool show) {
        _showModal = show;
        if (_showModal) {
          _bottomSheetController = _scaffoldKey.currentState.showBottomSheet(
            (_) => Container(
              height: MediaQuery.of(context).size.height / 4,
              width: MediaQuery.of(context).size.width,
              child: Text('This is a BottomSheet'),
            ),
            backgroundColor: Colors.white,
          );
        } else {
          if (_bottomSheetController != null) _bottomSheetController.close();
          _bottomSheetController = null;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          key: _scaffoldKey,
          backgroundColor: Colors.white,
          body: Scaffold(
            bottomNavigationBar: BottomNavigationBar(
              items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: 'Home',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.business),
                  label: 'Business',
                ),
              ],
            ),
            body: Center(
              child: ElevatedButton(
                child: Text('Show/Hide Modal Bottom Sheet'),
                onPressed: () => _showOrHide(!_showModal),
              ),
            ),
          ),
        );
      }
    }
    

    In this example I am keeping a reference to my parent ScaffoldState through _scaffoldKey so I can call my showBottomSheet on the correct Scaffold.

    Try the full code on DartPad

    Screenshot:

    enter image description here enter image description here