Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - showing a PopupMenuButton in BottomNavigationBar

I am trying to show a menu when a navigation bar item is clicked. This was my attempt:

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 3,
        child: Scaffold(
            appBar: MyAppBar(
              title: "Home",
              context: context,
            ),
            bottomNavigationBar: BottomNavigationBar(
              items: [
                BottomNavigationBarItem(
                    icon: new Icon(Icons.home), title: Text('Home')),
                BottomNavigationBarItem(
                    icon: new Icon(Icons.book), title: Text('Second')),
                BottomNavigationBarItem(
                    icon: new PopupMenuButton(
                      icon: Icon(Icons.add),
                      itemBuilder: (_) => <PopupMenuItem<String>>[
                            new PopupMenuItem<String>(
                                child: const Text('test1'), value: 'test1'),
                            new PopupMenuItem<String>(
                                child: const Text('test2'), value: 'test2'),
                          ],
                    ),
                    title: Text('more')),
              ],
              currentIndex: 0,
            ),
            body: new Container()));
  }

I encountered two problems. First one is the display of the NavigationBarItem. There is a padding between the icon the title that I could not remove (even by adding padding: EdgeInsets.all(0.0)) (as the picture below shows). And the second is that I need to click exactly on the icon for the menu to appear. enter image description here enter image description here

I tried calling showMenu directly (the method that PopupMenuButton calls) when a BottomNavigationBarItem of index=2 (for example) is clicked. But it was tricky how to determine the location of origin where the menu should appear from.

like image 740
M20 Avatar asked Nov 16 '18 12:11

M20


People also ask

How do I show navigation bar in flutter?

In Flutter application, we usually set the bottom navigation bar in conjunction with the scaffold widget. Scaffold widget provides a Scaffold. bottomNavigationBar argument to set the bottom navigation bar. It is to note that only adding BottomNavigationBar will not display the navigation items.

How do I set the popup menu position in flutter?

position property Null safetyoffset is used to change the position of the popup menu relative to the position set by this parameter. When not set, the position defaults to PopupMenuPosition. over which makes the popup menu appear directly over the button that was used to create it.


2 Answers

Here's an attempt that uses the showMenu directly and calling the function buttonMenuPosition to get the position for the menu. It's fairly fragile, but you can change the location of the button to the middle for example with bar.size.center instead of bar.size.bottomRight. With some MATH and by manually manipulating Offset objects (if/when you have more than 3 items), you can change the location to have the menu on one that isn't the center or at the end).

RelativeRect buttonMenuPosition(BuildContext c) {
    final RenderBox bar = c.findRenderObject();
    final RenderBox overlay = Overlay.of(c).context.findRenderObject();
    final RelativeRect position = RelativeRect.fromRect(
      Rect.fromPoints(
        bar.localToGlobal(bar.size.bottomRight(Offset.zero), ancestor: overlay),
        bar.localToGlobal(bar.size.bottomRight(Offset.zero), ancestor: overlay),
      ),
      Offset.zero & overlay.size,
    );
    return position;
  }


  @override
  Widget build(BuildContext context) {

    final key = GlobalKey<State<BottomNavigationBar>>();

    return DefaultTabController(
        length: 3,
        child: Scaffold(
            appBar: AppBar(
              title: Text("Home"),
            ),
            bottomNavigationBar: BottomNavigationBar(
              key: key,
              items: [
                const BottomNavigationBarItem(
                    icon: Icon(Icons.home), title: Text('Home')),
                const BottomNavigationBarItem(
                    icon: Icon(Icons.book), title: Text('Second')),
                const BottomNavigationBarItem(
                    icon: Icon(Icons.add), title: Text('more')),
              ],
              currentIndex: 0,
              onTap: (index) async {
                final position = buttonMenuPosition(key.currentContext);
                if (index == 2) {
                  final result = await showMenu(
                    context: context,
                    position: position,
                    items: <PopupMenuItem<String>>[
                      const PopupMenuItem<String>(
                          child: Text('test1'), value: 'test1'),
                      const PopupMenuItem<String>(
                          child: Text('test2'), value: 'test2'),
                    ],
                  );
                }
              },
            ),
            body: Container()));
  }
like image 107
Ringil Avatar answered Sep 28 '22 17:09

Ringil


Here's my attempt at it:

@override
  Widget build(BuildContext context) {
    return Material(
        child:  DefaultTabController(
        length: 3,
        child: Scaffold(
            appBar: AppBar(
              title: Text("Home"),
            ),
            bottomNavigationBar: BottomNavigationBar(
              items: [
                BottomNavigationBarItem(
                    icon: new Icon(Icons.home), title: Text('Home')),
                BottomNavigationBarItem(
                    icon: new Icon(Icons.book), title: Text('Second')),
                BottomNavigationBarItem(
                    icon: new Icon(Icons.add),
                    title: Text('More')),
              ],
              currentIndex: 0,
              onTap: (int index) async {
                if(index == 2){
                  await showMenu<String>(
                    context: context,
                    position: RelativeRect.fromLTRB(1000.0, 1000.0, 0.0, 0.0),
                    items: <PopupMenuItem<String>>[
                      new PopupMenuItem<String>(
                                child: const Text('test1'), value: 'test1'),
                            new PopupMenuItem<String>(
                                child: const Text('test2'), value: 'test2'),
                    ],
                    elevation: 8.0,
                  );
                }
              },
            ),
            body: new Container())));
  }

Basically using the showMenu method as you said except I've put the values for the RelativeRect as 1000.0 so that it'll be in the bottom right of any device. You could mess around with these values to get a position more right above the icon but I think having it like this works well:

enter image description here

like image 45
soupjake Avatar answered Sep 28 '22 17:09

soupjake