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.
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.
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.
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.
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()));
}
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With