Reputation: 1415
i'm new to flutter and I want to create a web app with drawer and couple of screens.
here is my main
function and root of apps ui:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Tapchi Admin Panel',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme
.of(context)
.textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
home: const DashboardScreen()
);
}
}
and here is my DashboardScreen
:
class DashboardScreen extends StatelessWidget {
const DashboardScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
);
}
}
and here is my SideMenu
:
class SideMenu extends StatelessWidget {
const SideMenu({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: [
const DrawerHeader(child: Icon(Icons.android)),
SideMenuItem(
title: 'dashboard',
leadingIcon: Icons.dashboard,
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DashboardScreen()));
}),
SideMenuItem(
title: 'users',
leadingIcon: Icons.person,
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserScreen()));
}),
],
),
);
}
}
my problem is when i navigate into DashboardScreen
i lose AppBar
and Drawer
but I want to have them for entire application!.
in android we could solve this problem by using NavHost
.
how can I have one Drawer
for my whole app.
by the way i'm developing a webApp
Upvotes: 1
Views: 2498
Reputation: 378
We had a massive web project where this was a must and quite some research was done. We found a few approaches and indeed you can create 2 navigators, however, there are already features to support this without any workaround. What we used was GoRouter which has not only normal routes (called GoRoutes) but also ShellRoutes where the nested routes. With this implementation, you will go to a different route inside the shellRoute without changing whatever parent widget code is inside the shellRoute - a drawer or anything else. If you want to change it, one approach is to have other shell route with a different top level widget (or without any - image a page with no drawer al tall). Or you could simply conditionally change the widget on the first shellRoute to show for example an empty container when you want to remove the drawer.
Here is a code sample using shellRoute for this purpose:
static GoRouter mainRouter = GoRouter(
initialLocation: "/main",
routes: [
ShellRoute(
navigatorKey: mainNavigatorKey,
builder: (context, state, child) {
return Cookies(
child: HomeCorePage(
child: child,
),
);
},
routes: [
GoRoute(
name: "main",
path: "/main",
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
name: "postDetails",
path: "postDetails",
builder: (context, state) =>
PostDetailsPage(postId: state.params['id'] ?? ""),
),
GoRoute(
name: "chat",
path: "chat",
builder: (context, state) => ChatPage(),
),
//...........
],
),
],
),
],
);
Last notes: GoRoute is a wrapper around the Flutter Navigator 2.0 - So you will be using the last and more flexible Navigator on your project. GoRouter has adopted by flutter and is officially recommended int theFlutter Favorite program. And last, in my experience it is super intuitive and very scalable/flexible. It also seems very mature as we search and found several issues on github that were closed and were related to older versions.
Hope it helps. Cheers
Upvotes: 1
Reputation: 2136
Ok, I managed to do that using two MaterialApp
widgets and a global navigatorKey
variable. Here is an example:
import 'package:flutter/material.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Sample();
}
}
class Sample extends StatelessWidget {
const Sample({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
drawer: SideMenu(),
// use new MaterialApp to push new (sub)screens on top of that area and preserve the same drawer
body: MaterialApp(
navigatorKey: navigatorKey,
home: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(child: Text('Home screen')),
),
);
}
}
class SideMenu extends Drawer {
const SideMenu({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
const DrawerHeader(child: Icon(Icons.android)),
SideMenuItem(
title: 'dashboard',
leadingIcon: Icons.dashboard,
press: () {
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => const DashboardScreen()));
}),
SideMenuItem(
title: 'users',
leadingIcon: Icons.person,
press: () {
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => const UserScreen()));
}),
],
),
);
}
}
class SideMenuItem extends StatelessWidget {
final String title;
final IconData leadingIcon;
final Function() press;
const SideMenuItem({
super.key,
required this.title,
required this.leadingIcon,
required this.press,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(leadingIcon),
title: Text(title),
onTap: press,
);
}
}
class DashboardScreen extends StatelessWidget {
const DashboardScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.red,
child: Center(child: Text('Dashboard screen')),
),
);
}
}
class UserScreen extends StatelessWidget {
const UserScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: Center(child: Text('User screen')),
),
);
}
}
Upvotes: 1