Flutter Drawer is not working properly in other pages - flutter

I have a drawer class in common for 3 pages. Here my problem is if i open any list item page in drawer while in main screen its working fine. if i goto some page(say page2 screen) other than main screen, now if i open any list item in drawer it opens the screen but problem is if i press back button it goes to main screen instead of page2 screen.
Drawer class:
class CustomDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.deepOrangeAccent.withOpacity(0.9), //This will change the drawer background to blue.//other styles
),
child: Drawer(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left:8.0),
child: ListView(
shrinkWrap: true,
children: <Widget>[
ListTile(
leading: Icon(Icons.event_note,color: Colors.white),
title: Text("Menu",style: TextStyle(color:Colors.white),),
onTap: () {
Navigator.push (
context, MaterialPageRoute(builder(BuildContextcontext)=> Menu()));
},
),
ListTile(
leading: Icon(Icons.person,color: Colors.white),
title: Text("My Profile",style: TextStyle(color:Colors.white),),
onTap: () {
Navigator.push (
context, MaterialPageRoute(builder: (BuildContext context) => MyProfile()));
},
),
],
),
)
],
),
),
);
}
}

Related

How to open Drawer on clicking Icon inside an AppBar in Flutter?

I have my files as follows:
homepage.dart
return Scaffold{
appBar: CustomAppBar(),
drawer: CustomDrawer(),
}
CustomAppBar.dart
class HomePageAppBar extends StatelessWidget with PreferredSizeWidget {
return AppBar{
leading: InkWell(
onTap: () {
// Need to open drawer when clicked
},
child: FaIcon(
FontAwesomeIcons.bars,
color: Theme.of(context).iconTheme.color,
size: Theme.of(context).iconTheme.size,
),
),
}
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
CustomDrawer.dart
return Drawer{
}
Now since my AppBar and Drawer are in separate files, I cannot use Scaffold.of(context).openDrawer() to open the drawer. I tried setting a GlobalKey to my Drawer but was now able to access it from my CustomAppBar file.
I tried converting the CustomAppBar.dart file to a Scaffold so that it contains my appbar and my drawer and used Scaffold.of(context).openDrawer(). This caused the drawer to open and display only in the appbar area (Probably because of using PreferredSizeWidget).
How can I make the drawer open on clicking the AppBar's icon given that they are placed in separate files? I want to keep my AppBar and Drawer in separate files.
See the documentation https://api.flutter.dev/flutter/material/showModalBottomSheet.html
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
);
},
Declare globalkey outside the class (Global) or create a static memeber
GlobalKey<ScaffoldState> _globalkey = new GlobalKey<ScaffoldState>();
opend Drawer using globalkey.
_globalkey.currentState?.openDrawer();

How to keep the hamburger icon in my flutter app after I go another stateful widget?

So I have a drawer and I can open it with the hamburger icon on top-left corner. If I change the screen to another page, this hamburger icon become a back button (<--). How to make this into a hamburger icon again? (So no back button but open the drawer on every screen)
In App Bar of next page put
leading: BurgerIcon(),
The Burger icon is from having a Drawer on your Scaffold.
If your next page/route has a Scaffold with a drawer: specified, you'll get the "burger" icon button again.
Without a drawer: specified on Scaffold Flutter will default to the back arrow to go to the previous route.
Here's an example:
import 'package:flutter/material.dart';
class DrawerDirectoryPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Drawer Directories'),
),
body: Center(
child: InkWell(
child: Text('Page One. Click to go to Page Two.'),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PageTwo())
),
),
),
drawer: MyDrawerDirectory(),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Burger Time!'),
),
body: Center(
child: Text('Page TWO /flex'),
),
drawer: MyDrawerDirectory(), // ← Drawer Directory a.k.a. burger icon
);
}
}
class MyDrawerDirectory extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: [
ListTile(title: Text('Page One'), onTap: () => _navPush(context, DrawerDirectoryPage())),
ListTile(title: Text('Page Two'), onTap: () => _navPush(context, PageTwo()))
],
),
);
}
Future<dynamic> _navPush(BuildContext context, Widget page) {
return Navigator.push(context, MaterialPageRoute(
builder: (context) => page,
));
}
}
In the AppBar widget, you can specify a leading widget. After you specify a leading widget (in your case, an Icon Button), you should set AppBar's automaticallyImplyLeading = false.
If you use the methods Navigator.of(context).push or Navigator.of(context).pushNamed you will also experience this issue.
You can use other method to navigate between screens so it doesn't show the hamburger button
Put automaticallyImplyLeading: false in appBar of next page then set leading icon like this:
automaticallyImplyLeading: false,
leading: Padding(
padding: const EdgeInsets.only(right: 20.0),
child: GestureDetector(
onTap: () {
if (scaffoldKey.currentState.isDrawerOpen) {
Navigator.of(context).pop();
} else {
scaffoldKey.currentState.openDrawer();
}
},
child: Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Icon(
Icons.menu,
size: 30,
color: appColor,
),
),
),
),

How can I create a sidebar menu in Flutter such that selecting a menu changes the main panels widget?

I am trying to create an application with a sidebar menu in Flutter but don't know how to change the main panels content when one of the menu items is selected.
Here is the basic main screen
class MainScreen extends StatelessWidget {
static const routeName = '/mainScreen';
final leftSection = new Container(
child: new Sidebar(),
);
final middleSection = new Expanded(
child: new DetailPage1(),
);
final rightSection = new Container(
child: new Sidebar(),
);
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
child: new Row(
children: <Widget>[leftSection, middleSection, rightSection],
),
),
);
}
}
Here is the Sidebar
class Sidebar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text(
'Side menu',
style: TextStyle(color: Colors.white, fontSize: 25),
),
decoration: BoxDecoration(
color: Colors.green,
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage('assets/images/cover.jpg'))),
),
ListTile(
leading: Icon(Icons.input),
title: Text('Welcome'),
onTap: () => {},
),
ListTile(
leading: Icon(Icons.verified_user),
title: Text('Profile'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.border_color),
title: Text('Feedback'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text('Logout'),
onTap: () => {Navigator.of(context).pop()},
),
],
),
);
}
}
How can I wire things up such that the MainScreen middleSection contents are changed ?
I am thinking of creating some sort of global variable that can have a variable set that MainScreen can observe and then call a function to replace the middleSection's container child.
Does Flutter have some design patterns for doing this ? Is this even the right approach and if so then:
How should global variables be created in Flutter ?
How can I observe changes on global variables in Flutter ?
I am familiar with SwiftUI and Combine and ObservableObject and so on - is there a similar construct in Flutter or Dart that can be used to achieve the same thing ?
Thanks
Renavigate to main page with pushReplacement and pass data which you wanna change in main screen:
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext
context) => MainScreen(
// pass your data to be changed on main screen
)));
}

How to show popup menu on any icon in Flutter?

I want a popup menu or some kind of slide screen with options to come when i click on an icon in the app bar, however i dont want to use PopMenuButton as i dont want to use that icon. How can I do this?
My code
return new Scaffold(
appBar: new AppBar(
title: new Text("Home"),
leading: IconButton(
icon: Icon(
Icons.dehaze,
color: Colors.black,
),
onPressed: () {
// do something
},
),
),
body: new Center(...),
);
#Denise, you don't need to manually create a button and assign action for drawer menu. You can simply use drawer in Scaffold with Drawer widget like so,
class MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Test'),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
body: Padding(
padding: EdgeInsets.all(20.0),
child: Center(
child: Column(
children: <Widget>[
Text('')
],
)
)
),
)
);
}
}
And if you wanna use different icon,
class MyAppState extends State<MyApp> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Test'),
leading: new IconButton(
icon: new Icon(Icons.dehaze),
onPressed: () => _scaffoldKey.currentState.openDrawer()),
),
drawer: Drawer(......
Hope this helps.
If the icon is the problem in PopMenuButton. You can change it by assigning icon attribute in PopMenuButton.
PopupMenuButton<Choice>(
onSelected: _select,
icon:Icon(
Icons.dehaze,
color: Colors.black,
),
itemBuilder: (BuildContext context) {
return choices.skip(2).map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Text(choice.title),
);
}).toList();
https://flutter.dev/docs/catalog/samples/basic-app-bar

Show Flutter's SnackBar above a visible Drawer menu?

I have a Scaffold with a simple Drawer in which I show a menu where a user can press a button. When this button is pressed I want to display a SnackBar, but the SnackBar is always displayed behind the Drawer. Is there some way to show it in front of the Drawer?
The drawer's code looks like:
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.lock_open),
title: Text('Click Me'),
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
'Test.',
)));
},
),
],
),
);
}
}
and it is used directly in the Scaffold:
return Scaffold(
drawer: MyDrawer(),
[...]
They said Drawer must be on the top of UI under MaterialDesign rules. But if you wanna such behaviour too much you can write own SnackBar.
static void showSnackBarAsBottomSheet(BuildContext context, String message)
{
showModalBottomSheet<void>(
context: context,
barrierColor: const Color.fromRGBO(0, 0, 0, 0),
builder: (BuildContext context) {
Future.delayed(const Duration(seconds: 5), () {
try {
Navigator.pop(context);
} on Exception {}
});
return Container(
color: Colors.grey.shade800,
padding: const EdgeInsets.all(12),
child: Wrap(children: [
Text(
message,
style:
GoogleFonts.robotoCondensed().copyWith(color: Colors.white),
)
]));
},
);
}
I've solved it by adding one more Scaffold inside Drawer and making its background transparent:
return Scaffold(
drawer: Scaffold(
backgroundColor: Colors.transparent,
body: MyDrawer(),
[...]