How can I add endDrawer outside of appbar in Flutter? - 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,
),
)
],

Related

Flutter remove tooltip text from drawer

I am trying to remove the "Open Navigation Menu" text that appears when you hover over the menu icon of a drawer. Attached is my code:
class MyMobileBody extends StatelessWidget {
const MyMobileBody({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final currentWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: EdgeInsets.fromLTRB(16.0, 0, 0, 0),
child: IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
icon: Image.asset("assets/Logo/Logo-DarkMode-Transparent.png"),
iconSize: 32,
padding: EdgeInsets.zero,
onPressed: () {
// Navigator.of(context)
// .pushNamedAndRemoveUntil('/', (route) => false);
// Navigator.pushNamed(context, '/');
}),
),
),
endDrawer: mobileDrawer(),
body: const Center(
child: Text(
"COMING SOON!",
style: TextStyle(
fontSize: 70,
color: Colors.white,
),
textAlign: TextAlign.center,
),
));
}
}
I tried to manually add the button as suggested in other questions however I want the logo on the left and the drawer on the right and adding it manually would always cause the 2 to overlap. I also tried using:
#override
String get openAppDrawerTooltip => 'Open navigation menu';
To override the text however that didn't change anything, Possibly because I may have put it in the wrong place.
I simply want to remove the text and have nothing display when hovering on the option.
Thanks!
You can try wrap your page with TooltipVisibility
example:
MaterialApp(
home: TooltipVisibility(
visible: false,
child: Scaffold(
appBar: AppBar(),
drawer: Drawer(),
body: Container(),
),
),
);
you just need to remove tooltip from app bar . Please check below code
AppBar(
leading: Padding(
padding: EdgeInsets.fromLTRB(16.0, 0, 0, 0),
child: IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
icon: Icon(Icons.menu)/*Image.asset("assets/Logo/Logo-DarkMode-Transparent.png")*/,
iconSize: 32,
padding: EdgeInsets.zero,
onPressed: () {
// Navigator.of(context)
// .pushNamedAndRemoveUntil('/', (route) => false);
// Navigator.pushNamed(context, '/');
},
//Note : comment this line
// tooltip: 'Something else',
),
),
),
There's another way to open drawer by using ScaffoldState.
you can add new IconButton and call drawer when user press the button.
class MyMobileBody extends StatelessWidget {
MyMobileBody({Key? key}) : super(key: key);
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
final currentWidth = MediaQuery.of(context).size.width;
return Scaffold(
key: _scaffoldKey, // dont forget to set the scaffoldKey
endDrawer: const mobileDrawer(), // open drawer on the right side
// drawer: const mobileDrawer(), // if you want to open drawer on left side
appBar: AppBar(
leading: IconButton(
onPressed: () {},
icon: const Icon(Icons.abc),
),
actions: [
IconButton(
onPressed: () {
_scaffoldKey.currentState?.openEndDrawer();
// _scaffoldKey.currentState?.openDrawer(); // if you want to open drawer on left side
},
icon: const Icon(Icons.menu))
],
),
);
}
}
the tooltip will not appear anymore when you hover.
but in case you want to add tooltip, you can wrap the iconButton with Tooltip widget.

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 to change appBar size in Scaffold [duplicate]

How can I simply set the height of the AppBar in Flutter?
The title of the bar should be staying centered vertically (in that AppBar).
You can use PreferredSize:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example',
home: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0), // here the desired height
child: AppBar(
// ...
)
),
body: // ...
)
);
}
}
Use toolbarHeight:
There's no longer a need to use PreferredSize. Use toolbarHeight with flexibleSpace.
AppBar(
toolbarHeight: 120, // Set this height
flexibleSpace: Container(
color: Colors.orange,
child: Column(
children: [
Text('1'),
Text('2'),
Text('3'),
Text('4'),
],
),
),
)
You can use PreferredSize and flexibleSpace for it:
appBar: PreferredSize(
preferredSize: Size.fromHeight(100.0),
child: AppBar(
automaticallyImplyLeading: false, // hides leading widget
flexibleSpace: SomeWidget(),
)
),
This way you can keep the elevation of AppBar for keeping its shadow visible and have custom height, which is what I was just looking for. You do have to set the spacing in SomeWidget, though.
The easiest way is to use toolbarHeight property in your AppBar
Example :
AppBar(
title: Text('Flutter is great'),
toolbarHeight: 100,
),
You can add flexibleSpace property in your appBar for more flexibility
Output:
For more controls , Use the PreferedSize widget to create your own appBar
Example :
appBar: PreferredSize(
preferredSize: Size(100, 80), //width and height
// The size the AppBar would prefer if there were no other constraints.
child: SafeArea(
child: Container(
height: 100,
color: Colors.red,
child: Center(child: Text('Fluter is great')),
),
),
),
Don't forget to use a SafeArea widget if you don't have a safeArea
Output :
At the time of writing this, I was not aware of PreferredSize. Cinn's answer is better to achieve this.
You can create your own custom widget with a custom height:
import "package:flutter/material.dart";
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Column(children : <Widget>[new CustomAppBar("Custom App Bar"), new Container()],);
}
}
class CustomAppBar extends StatelessWidget {
final String title;
final double barHeight = 50.0; // change this for different heights
CustomAppBar(this.title);
#override
Widget build(BuildContext context) {
final double statusbarHeight = MediaQuery
.of(context)
.padding
.top;
return new Container(
padding: new EdgeInsets.only(top: statusbarHeight),
height: statusbarHeight + barHeight,
child: new Center(
child: new Text(
title,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
);
}
}
In addition to #Cinn's answer, you can define a class like this
class MyAppBar extends AppBar with PreferredSizeWidget {
#override
get preferredSize => Size.fromHeight(50);
MyAppBar({Key key, Widget title}) : super(
key: key,
title: title,
// maybe other AppBar properties
);
}
or this way
class MyAppBar extends PreferredSize {
MyAppBar({Key key, Widget title}) : super(
key: key,
preferredSize: Size.fromHeight(50),
child: AppBar(
title: title,
// maybe other AppBar properties
),
);
}
and then use it instead of standard one
You can simply use toolbarHeight, as follows:
appBar: AppBar(
toolbarHeight: 70.0, // add this line
centerTitle: true, // add this line
title: Text('your title'),
),
but if you have any actions the code above doesn't work as you want
you can use this code
AppBar(
centerTitle: true,
title: Padding(
padding: const EdgeInsets.all(16.0),
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Chats', style: TextStyle(color:Colors.black),),
Icon(Icons.add, color: Colors.black,),
],
),
Align(
alignment: Alignment.centerRight,
child: Icon(Icons.add, color: Colors.black,),
),
],
),
),
)
Cinn's answer is great, but there's one thing wrong with it.
The PreferredSize widget will start immediately at the top of the screen, without accounting for the status bar, so some of its height will be shadowed by the status bar's height. This also accounts for the side notches.
The solution: Wrap the preferredSize's child with a SafeArea
appBar: PreferredSize(
//Here is the preferred height.
preferredSize: Size.fromHeight(50.0),
child: SafeArea(
child: AppBar(
flexibleSpace: ...
),
),
),
If you don't wanna use the flexibleSpace property, then there's no need for all that, because the other properties of the AppBar will account for the status bar automatically.
simply use toolbar height ...
AppBar(
title: Text("NASHIR'S APP"),
toolbarHeight: 100,
),
You can use the toolbarHeight property of Appbar, it does exactly what you want.
class AppBarWidget extends StatelessWidget with PreferredSizeWidget {
final String title;
const AppBarWidget({Key? key, required this.title}) : super(key: key);
#override`enter code here`
Widget build(BuildContext context) {
return AppBar(
title: Text(title),
centerTitle: true,
elevation: 0,
actions: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: IconButton(
icon: const FaIcon(FontAwesomeIcons.language),
onPressed: () {},
),
),
],
);
}
#override
Size get preferredSize => const Size.fromHeight(40);// change
}
You can use PreferredSize, by this use can set multiple children widget inside their
appBar: PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, 75),
child: Column(children: [
AppBar(
centerTitle: true,
toolbarHeight: 74,
backgroundColor: Colors.white,
elevation: 0,
title: Column(
children: [
Text(
viewModel.headingText,
style: sfDisplay16500Text,
),
SizedBox(
height: 8.0,
),
Text(
viewModel.url.toString(),
style: sfDisplay10400LightBlackText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
],
),
),
]),
),
or just directly use toolbarHeight property for only increase hight of appBar.
appBar: AppBar(
title: Text('AppBar Texr'),
toolbarHeight: 200.0, // double
),
Extend AppBar class and override preferredSize
class AppBarCustom extends AppBar {
#override
Size get preferredSize => Size.fromHeight(100);
}
then use it as you would use AppBar class
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBarCustom(),
body:
),
);
}
}
This is simplest and easiest way to change appbar height without changing original theme.
class AppBarSectionView extends StatefulWidget implements PreferredSizeWidget {
const AppBarSectionView({Key? key}) : super(key: key);
#override
_AppBarSectionViewState createState() => _AppBarSectionViewState();
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight + 20);
}
class _AppBarSectionViewState extends State<AppBarSectionView> {
#override
Widget build(BuildContext context) {
return AppBar(
toolbarHeight: widget.preferredSize.height ,
backgroundColor: Colors.red,
leading: const Icon(Icons.arrow_back_ios_rounded),
title: const Text("This Is Title"),
);
}
}
If you are in Visual Code, Ctrl + Click on AppBar function.
Widget demoPage() {
AppBar appBar = AppBar(
title: Text('Demo'),
);
return Scaffold(
appBar: appBar,
body: /*
page body
*/,
);
}
And edit this piece.
app_bar.dart will open and you can find
preferredSize = new Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
Difference of height!

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(
///...

Flutter Drawer below AppBar

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(),