Flutter Drawer below AppBar - flutter

I've implemented a Drawer in my Flutter app.
Closed Drawer:
Opened Drawer:
As you can see, the Drawer is on top of the Appbar. Before I started the app on Flutter, we had a native Android app with a Drawer that used to look like this:
Closed Drawer:
Opened Drawer:
Here is my code:
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return _buildDrawer(context);
}
}
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new ListView(
children: <Widget>[
_buildDrawerItem(context, EnumDrawerItem.PROJECT_SELECTION, Icons.home, Colors.transparent),
new Divider(height: 20.0),
_buildDrawerItem(context, EnumDrawerItem.TASK_LIST, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.GUIDED_TASKS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.PHOTOS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.DOCUMENTS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.LOG_OUT, Icons.home, const Color(0x85bf0202)),
new Divider(),
],
),
);
}
Widget _buildDrawerItem(BuildContext context, EnumDrawerItem drawerItem, IconData iconData, Color color) {
return Container(
color: color,
child: new Padding(
padding: new EdgeInsets.all(7.0),
child: new Row(
children: <Widget>[
new Icon(iconData),
new Container(
margin: new EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
child: new Text(
drawerItem.toString(),
style: styleDrawerItem,
),
),
],
),
),
);
}
I know this is the standard Material Design style, but the client wants it as it was before.
Would it be possible to implemented it as in the 2 last screenshots? Do you have any idea?

Wrap your main Scaffold in another Scaffold and use the drawer of child Scaffold also make sure to set automaticallyImplyLeading to false so you don't get back icon in the AppBar
UPDATE :
i don't recommend this way because of this issue
return Scaffold(
primary: true,
appBar: AppBar(
title: Text("Parent Scaffold"),
automaticallyImplyLeading: false,
),
body: Scaffold(
drawer: Drawer(),
),
);
Final Result :

I use the key in scaffold and references in leading in scaffold principal how in the example
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
return Scaffold(
appBar: AppBar(
title: Text('Draw'),
leading: IconButton(
icon: Icon(Icons.dehaze),
onPressed: () {
if (_scaffoldKey.currentState.isDrawerOpen == false) {
_scaffoldKey.currentState.openDrawer();
} else {
_scaffoldKey.currentState.openEndDrawer();
}
})),
body: Scaffold(
key: _scaffoldKey,
drawer: Drawer(),
body: Center(
child: Text('Drawer'),
),
),
);

Try this one:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var statusBarHeight = MediaQuery.of(context).padding.top;
var appBarHeight = kToolbarHeight; //this value comes from constants.dart and equals to 56.0
return Scaffold(
drawerScrimColor: Colors.transparent,
appBar: AppBar(),
drawer: Container(
padding: EdgeInsets.only(top: statusBarHeight+ appBarHeight + 1),//adding one pixel for appbar shadow
width: MediaQuery.of(context).size.width,
child: Drawer(),//write your drawer code
),
body: AnyBody(), //add your body
bottomNavigationBar: AnyNavigationBar(), //add your navigation bar
);
}
}

Simple and to the point:
drawer: Padding(
padding: const EdgeInsets.fromLTRB(0, 80, 0, 0),
child: Drawer(),

Related

Appbar's title not working on setstate in flutter

appBar: AppBar(
elevation: 0.0,
title: Text(gazung),
backgroundColor: Color(0xffDBDDE0),
),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Color(0xff9CA2B0),
),
child: Text('2021-02 개정 기준'),
),
ListTile(
tileColor: Color(0xffDBDDE0),
title: Text('2021-02 개정 기준'),
onTap: (){
setState(() {
gazung = 'changedaapbartext';
});
I output the title of the app bar as a variable called gazung and setstate in the drawer to change it, but it is not applied to the title of the appbar.
You can try with this code, i did not get any issue.
class ApBarTest extends StatefulWidget {
const ApBarTest({Key? key}) : super(key: key);
#override
State<ApBarTest> createState() => _ApBarTestState();
}
class _ApBarTestState extends State<ApBarTest> {
String gazung = 'Title';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
title: Text(gazung),
backgroundColor: Color(0xffDBDDE0),
),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Color(0xff9CA2B0),
),
child: Text('2021-02 개정 기준'),
),
ListTile(
tileColor: Color(0xffDBDDE0),
title: Text('2021-02 개정 기준'),
onTap: () {
setState(() {
gazung = 'changedaapbartext';
});
})
],
),
),
);
}
}

How can I add endDrawer outside of appbar in Flutter?

I want to add a Drawer to the icon button but it's outside of appbar
here in this code, I tried to implement it by watching some tutorial but its not working for me maybe cuz I used endDraw anyone have idea how to do it?
here is my code
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
endDrawer: Drawer2() ,
appBar: AppBar(
backgroundColor: MyColor.backgroud_primary_color,
leading: Icon(
Icons.chevron_left_rounded,
color: MyColor.icon_color,
),
centerTitle: true,
title: Container(
width: 100,
height: 100,
child: Image(
image: AssetImage("assets/logo.png"),
fit: BoxFit.contain,
),
),
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: IconButton(
onPressed: () => _scaffoldKey.currentState!.openDrawer(),
icon: Icon(Icons.sort),
iconSize: 30,
color: MyColor.icon_color,
),
)
],
),
Drawer2() is a custom drawer that I have made I want to open the end drawer when I click on Icon button is there any way?
import 'package:flutter/material.dart';
class DemoScreen extends StatelessWidget {
const DemoScreen({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo Screen"),),
endDrawer: Drawer2(),
body: Center(
child: Text("data")
),
);
}
}
class Drawer2 extends StatelessWidget {
const Drawer2({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer( // Custom widget must be return Drawer
child: ListTile( //Implement any design on child property
title: Text("Demo tile"),
),
);
}
}
Use:
Scaffold.of(context).openEndDrawer()
and if you want to disable the drag behavior set this in Scaffold:
drawerEnableOpenDragGesture: false,
To open the drawer you need to use _scaffoldKey.currentState!.openEndDrawer(),
based on endDrawer docs, here
So, your code should be:
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: IconButton(
onPressed: () => _scaffoldKey.currentState!.openEndDrawer(),
icon: Icon(Icons.sort),
iconSize: 30,
color: MyColor.icon_color,
),
)
],

onPressed action On Custom Leading Images Appbar in flutter

Is it possible we can add onPressed action on the logo and start another activity?
I am creating a simple flutter app where I have used AppBar and in leading icon I have used a custom logo. I am not sure how to perform onPressed method so that it starts another activity. Anyone please help me here. Below is my app bar code.
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
"assets/images/logo.png",
),
),
title: Text('Safe Outs Business'),
),
body: Center(
child: Text('Admin HomePage'),
),
);
}
}
Click here to see a sample Image of the layout I am trying to build in flutter
You can embed your logo inside a GestureDetector:
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () => print('TAPPED!'),
child: Image.asset(
"assets/images/logo.png",
),
),
),
title: Text('Safe Outs Business'),
),
body: Center(
child: Text('Admin HomePage'),
),
);

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

Flutter Scaffold.of(context).openDrawer() doesn't work

I want to open a drawer after pushing on the custom button in BottomMenu I have trouble with Scaffold.of(context).openDrawer(), it doesn't work. My BottomMenu is a separate widget class. As I understand, it doesn't work because it's a separate context. How can I get the right context? Or perhaps someone knows another solution.
Here my code reproducer:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Drawer'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: BottomMenu(),
endDrawer: SizedBox(
width: double.infinity,
child: Drawer(
elevation: 16,
child: Container(
color: Colors.black,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
],
),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Call Drawer form menu reproducer',
)
],
),
),
);
}
}
class BottomMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Wrap(
alignment: WrapAlignment.center,
children: <Widget>[
Divider(color: Colors.black, height: 1),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
borderRadius: new BorderRadius.circular(20.0),
customBorder: Border.all(color: Colors.black),
child: Container(
padding: EdgeInsets.only(
left: 3, right: 6, bottom: 15, top: 11),
child: Row(
children: <Widget>[
Icon(Icons.menu),
Text('Show menu', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
],
),
),
onTap: () {
Scaffold.of(context).openDrawer();
},
),
],
),
),
],
),
);
}
}
In my case, this worked.
return Scaffold(
key: _scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This!
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
leading: IconButton(
icon: Icon(Icons.menu, size: 36),
onPressed: () => _scaffoldKey.currentState.openDrawer(), // And this!
),
),
drawer: DrawerHome(),
....
and _scaffoldKey must be initialized as,
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
under the class.
The problem is that you specified endDrawer on Scaffold yet you're calling Scaffold.of(context).openDrawer().
openDrawer() documentation states:
If the scaffold has a non-null Scaffold.drawer, this function will cause the drawer to begin its entrance animation.
Since your drawer is null, nothing happens.
In contrast, openEndDrawer() informs us:
If the scaffold has a non-null Scaffold.endDrawer, this function will cause the end side drawer to begin its entrance animation.
Since your endDrawer is not null you should use openEndDrawer() method. Alternatively, if you don't care which side the drawer slides in from, you can use drawer instead of endDrawer when building Scaffold.
My problem solved that instead of
Scaffold.of(context).openEndDrawer()
I give key to Scaffold and then I call by state like below
_scaffoldkey.currentState.openEndDrawer()
It solved my problem I hope It also works for you
Scaffold.of(context).openEndDrawer()
The Problem
This issue can occur when you do not use the correct BuildContext when calling Scaffold.of(context).openDrawer() (or openEndDrawer()).
Easiest Solution
Simply wrap whatever calls openDrawer() (or openEndDrawer()) with a Builder widget. This will give it a working context.
Minimal Working Example
// your build method
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Builder(builder: (context) { // this uses the new context to open the drawer properly provided by the Builder
return FloatingActionButton(onPressed: (() => Scaffold.of(context).openDrawer()));
}),
drawer: const Drawer(
child: Text("MY DRAWER"),
),
);
}
Similar problem here. Clicked on button and nothing happened. The problem is I was using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.
Here is how I solved it:
// body: Column(
// children: <Widget>[
// Row(
// children: <Widget>[
// IconButton(
// icon: Icon(Icons.filter_list),
// onPressed: () => Scaffold.of(context).openEndDrawer(), (wrong context)
// ),
// ],
// ),
// ],
// )
To:
body: Builder(
builder: (context) => Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () => Scaffold.of(context).openEndDrawer(),
),
],
),
],
)),
),
Assign Drawer to drawer property in scaffold. Wrap your specific Widget/Button(where you want to open drawer on its click method) with Builder. Use below method on click property:
enter image description here
Scaffold.of(context).openDrawer();
If you have the appbar widget with an action button to launch the drawer and the drawer is never pushed please remember that you need to define after appbar: ... the endDrawer: YOURAppDrawerWIDGET(), or else using the Scaffold.of(context).openEndDrawer() will not work.
Scaffold(
appBar: AppBar(title: Text(_title)),
endDrawer: AppDrawer(), // <-- this is required or else it will not know what is opening
body: SingleChildScrollView(
///...