Reputation: 89
I am new to Flutter and trying to build a responsive web app. So far, the code I have working is when the screen shrinks in width to a certain size, the navigation bar and its items get crunched into a menu icon button (Flutter's IconButton). This works. What doesn't work is when I click the IconButton, the new navigation bar doesn't pop up, even though the console shows I'm clicking on it. DrawerItem() is just a Text Widget wrapped in a Container. After the couple sections of code, you can see the console spitting out a response. I do not get any errors on screen or in the console when I adjust the screen size or when I click the IconButton. I've also tried making the MNavigationBar a stateful widget and adding setState to the onPressed attribute, nothing changes from the current issue and the same thing happens.
class MNavigationBar extends StatelessWidget {
const MNavigationBar({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 80,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
Icons.menu,
),
onPressed: () {
NavigationDrawer();
print('Menu Icon pressed');
},
),
NavBarLogo(),
],
),
);
}
}
class NavigationDrawer extends StatelessWidget {
const NavigationDrawer({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 260,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 16,
),
],
),
child: Column(
children: <Widget>[
NavigationDrawerHeader(),
DrawerItem('Home'),
DrawerItem('Events'),
NavBarMenuDropdown(),
DrawerItem('Store'),
DrawerItem('Partners'),
DrawerItem('About Us'),
],
),
);
}
}
Performing hot restart... 148ms
Restarted application in 149ms.
Menu Icon pressed
Menu Icon pressed
Upvotes: 0
Views: 2239
Reputation: 2186
There are better ways to do this in 2024 using go_router.
Use StatefulShellRoute.indexedStack
to define the Screens and then use navigationShell
to build body, maintain current index and to navigate between screens navigationShell.goBranch(index)
in all different size screens.
Use BottomNavigationBar
for Mobile, TabBar
for Browser/Desktop, and NavigationRail
for iPad/Tablet.
With this approach you just define the screens in one single spot and render differently in different screen sizes.
// ResponsiveNavigationBar's body
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).size.width < 500) {
// Mobile phones
return Scaffold(
body: widget.navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: widget.phoneBarButtons,
onTap: (int index) => widget.navigationShell.goBranch(index),
currentIndex: widget.navigationShell.currentIndex,
),
);
} else if (MediaQuery.of(context).size.width > 1024) {
_tabController.index = widget.navigationShell.currentIndex;
// Web Browser / Desktop
return Scaffold(
body: Column(
children: [
TabBar(
tabs: widget.webBarButtons,
onTap: (int index) => widget.navigationShell.goBranch(index),
controller: _tabController,
tabAlignment: TabAlignment.center,
),
Expanded(child: widget.navigationShell),
],
),
);
} else {
// iPads / Tablets
return Scaffold(
body: Row(
children: [
NavigationRail(
destinations: widget.tabletBarButtons,
onDestinationSelected: (int index) =>
widget.navigationShell.goBranch(index),
labelType: NavigationRailLabelType.all,
selectedIndex: widget.navigationShell.currentIndex,
),
Expanded(child: widget.navigationShell),
],
),
);
}
}
In the Go router, use builder to pass the navigationShell to your root view
GoRouter router(BuildContext context) {
List<RouteBase> routes = [
StatefulShellRoute.indexedStack(
branches: [
StatefulShellBranch(
routes: [
GoRoute(
path: '/${HomeView.path}',
builder: (_, __) => const HomeView(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/${Profile.path}',
builder: (_, __) => const Profile(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/${Settings.path}',
builder: (_, __) => const Settings(),
),
],
),
],
builder: (_, __, navigationShell) => ResponsiveNavigationBar(
navigationShell: navigationShell,
barButtons: [
BarItem(icon: const Icon(Icons.home), label: 'Home'),
BarItem(icon: const Icon(Icons.person), label: 'Profile'),
BarItem(icon: const Icon(Icons.settings), label: 'Settings')
],
),
),
];
return GoRouter(
initialLocation: '/',
routes: routes,
);
}
class BarItem {
final Widget icon;
final String label;
BarItem({required this.icon, required this.label});
}
This article has detailed breakdown if you want Adaptive Navigation in Flutter: Bottom for Mobile, Side for Tablet, Top for Web with GoRouter
Upvotes: 0
Reputation: 89
Thank you, Afridi Kayal! I did exactly what you said and it works great! See the answer in the code below...
In my main Scaffold widget, I added this code and also added logic with a ResponsiveBuilder
return ResponsiveBuilder(
builder: (context, sizingInformation) => Scaffold(
drawerEnableOpenDragGesture: false,
drawer: sizingInformation.deviceScreenType == DeviceScreenType.Mobile
? NavigationDrawer()
: null,
In my MNavigationBar, I modified this.
onPressed: () {
Scaffold.of(context).openDrawer();
print('Menu Icon pressed');
},
Upvotes: 1