I want to make the drawer opened when a user navigate to the screen. How can I do it?
Widget build(BuildContext context) {
return Scaffold(
appBar: Header(),
drawer: Theme(
child: Drawer(
elevation: 0,
),
),
);
}
You need to create a GlobalKey and use it as the Scaffold's key.
Then you need to use the key to call openDrawer on state initialization.
This is the corresponding code :
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
#override
void initState() {
super.initState();
openDrawer();
}
openDrawer() async {
await Future.delayed(Duration.zero);
_scaffoldKey.currentState.openDrawer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
drawer: Drawer(
),
body: Center()
);
}
Related
Minimal code:
void main() => runApp(MaterialApp(home: MainPage()));
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
final _drawerKey = GlobalKey<DrawerControllerState>();
#override
Widget build(BuildContext context) {
return Drawer(
key: _drawerKey,
child: RaisedButton(
onPressed: () => print(_drawerKey.currentState), // Prints null
child: Text('Show Dialog'),
),
);
}
}
When I press the button, it prints null, so what's the correct way of using the DrawerControllerState?
See, the problem is, you are not using your key correctly. Firstly, in order to get the state of the Drawer, you need to have ScaffoldState not the DrawerControllerState type key.
With the use of ScaffoldState.currentState, you will be getting the data. Also, if you want to see whether your drawer is open or closed. You can use it like this:
ScaffoldState.currentState.isDrawerOpen
There are two ways to do this:
1. Declaring the GlobalKey gloabally for accessing it anywhere
void main() => runApp(MaterialApp(home: MainPage()));
// declare it globally or make your drawer inside the MainPage only
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey, // <-- Use your key here not for drawer
appBar: AppBar(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: RaisedButton(
onPressed: () => print(_scaffoldKey.currentState.isDrawerOpen), // <-- prints true, when open
child: Text('Show Dialog'),
)
);
}
}
2. Make your drawer inside your MainPage only. Easily accessible
class MainPage extends StatelessWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Drawer get _drawer => Drawer(
child: RaisedButton(
onPressed: () => print(_scaffoldKey.currentState.isDrawerOpen), // <-- prints true when opened
child: Text('Show Dialog'),
)
);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey, // <-- Using Key for Scaffold
appBar: AppBar(),
drawer: _drawer
);
}
}
Passing the GlobalKey from MainPage to MyDrawer won't help. You can play with that.
I want to navigate screen and change tab in bottomNavigationBar at the same time.
....
class _MainPageState extends State<MainPage> with TickerProviderStateMixin {
...
Widget selectedAppBar(int index) {
...
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: selectedAppBar(_selectedIndex),
body: SafeArea(
child: _screenOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigation(
onChangeScreen: _onChangeScreen,
selectedIndex: _selectedIndex,
),
);
}
...
}
other page, there is a bottom and when I click it so will be navigation to main screen and also change tab of BottomNavigationBar
....
class _ItemState extends State<ItemPage> {
ItemScreenArguments _screenArguments;
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
_screenArguments = ModalRoute.of(context).settings.arguments;
final item = _screenArguments.item;
return BlocBuilder<CartBloc, CartState>(
builder: (BuildContext context, CartState state) {
...
final cart = (state as LoadCartState);
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
title: Text(item.name),
actions: [
Basket(
count: cart.count,
onTap: () {
// It should navigate main route and fourth section in bottomNavigationBar
Navigator.of(context).popUntil(ModalRoute.withName('/main'))
},
)
]
),
body: ItemScreen(),
...
);
}
);
}
I try that do it is using Bloc or it's bad idea to use Bloc?
May be, using Router MaterialPageRoute better than Bloc or using them together? How to you do it? Can you help to choose better option?
You could send the tab number you want to select by default
Navigate.of(context).pushNamed("/main",arguments:{'tab':3})
You can retrieve this tab number using ModalRoute and set _selectedIndex accordingly
Widget build(BuildContext context) {
final route= ModalRoute.of(context).settings.arguments as Map<String,int>;
var _selectedIndex = route['tab']
return Scaffold(
appBar: selectedAppBar(_selectedIndex),
body: SafeArea(
child: _screenOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigation(
onChangeScreen: _onChangeScreen,
selectedIndex: _selectedIndex,
),
);
}
I want to open side drawer from a button exist in a container not from the AppBar side button. Also this should be open from right to left.
For more information I have added screenshot of my requirement
Here you have a basic sample:
#override
Widget build(BuildContext context) {
return Scaffold(
endDrawer: Drawer(),
appBar: AppBar(),
body: Builder(
builder: (context) {
return Center(
child: RaisedButton(
child: Text("open drawer"),
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
),
);
},
),
);
}
Or you can use GlobalKey of ScaffoldState:
GlobalKey<ScaffoldState> _key = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
endDrawer: Drawer(),
appBar: AppBar(),
body: Center(
child: RaisedButton(
child: Text("open drawer"),
onPressed: () {
_key.currentState.openEndDrawer();
},
),
),
);
}
}
Check this link for more information : https://api.flutter.dev/flutter/material/ScaffoldState/openEndDrawer.html
create a scaffold key then give it your scaffold widget
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
then, to open drawer
_scaffoldKey.currentState.openDrawer()
or
_scaffoldKey.currentState.openEndDrawer()
I have made customized Drawer and AppBar. I want the Drawer to be opened when action widget in the AppBar is tapped. I want to know how to implement this for the custom AppBar
#override
Widget build(BuildContext context) {
return Scaffold(
endDrawer:buildProfileDrawer(),
appBar: setAppBar(),
body: HomeBody()
);
}
//custom widget
Widget setAppBar(){
return AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.account_circle,),
onPressed: () {
//Open the drawer
},
)
],
);
}
//Custom drawer
buildProfileDrawer() {
return Drawer(
//....drawer childs
);
}
You should use GlobalKey in Scaffold, and call openEndDrawer method on it.
GlobalKey<ScaffoldState> _key = GlobalKey(); // add this
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key, // set it here
endDrawer: buildProfileDrawer(),
appBar: setAppBar(),
body: Center(),
);
}
//custom widget
Widget setAppBar() {
return AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.account_circle),
onPressed: () {
_key.currentState.openEndDrawer(); // this opens drawer
},
)
],
);
}
//Custom drawer
buildProfileDrawer() {
return Drawer();
}
Update
GlobalKey<ScaffoldState> _key = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
endDrawer: buildProfileDrawer(),
appBar: setAppBar(_key),
body: Center(),
);
}
Somewhere in some file.
Widget setAppBar(GlobalKey<ScaffoldState> globalKey) {
return AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.account_circle),
onPressed: () {
globalKey.currentState.openEndDrawer();
},
)
],
);
}
Somewhere in some other file
buildProfileDrawer() {
return Drawer();
}
You can use this in actions list:
StatefulBuilder(
builder: (BuildContext context, setState) {
return IconButton(
icon: Icon(Icons.format_align_right),
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
);
},
),
We can use
Scaffold.of(context).openDrawer();
on onPressed inside an IconButton in your CustomAppBar or wherever you want to call the drawer.
In the Scaffold just ensure to provide the drawer: property a Drawer Widget.
If you create an Scafold there is an option for drawer. If you now create this drawer you get automaticly the menu icon on the leading position of the appbar. But i want an other icon there which opens the drawer. I tried to make an iconbutton myself on the leading position but this button can‘t open the drawer even with „Scafold.of(context).openDrawer()“ it can‘t open it.
Is there any option to replace the icon for the drawer button?
Use a Key in your Scaffold and show the drawer by calling myKey.currentState.openDrawer(), here is a working code:
import "package:flutter/material.dart";
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
drawer: new Drawer(),
appBar: new AppBar(
leading: new IconButton(
icon: new Icon(Icons.settings),
onPressed: () => _scaffoldKey.currentState.openDrawer(),
),
),
);
}
}
Alternative to the accepted answer which does not require a GlobalKey:
class _TestState extends State<Test> {
#override
Widget build(BuildContext context) {
return new Scaffold(
drawer: new Drawer(),
appBar: new AppBar(
leading: Builder(
builder: (context) => IconButton(
icon: new Icon(Icons.settings),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
),
);
}
}
Using GlobalKey:
final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key, // Assign the key to Scaffold.
drawer: Drawer(),
floatingActionButton: FloatingActionButton(
onPressed: () => _key.currentState!.openDrawer(), // <-- Opens drawer
),
);
}
Using Builder:
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(),
floatingActionButton: Builder(builder: (context) {
return FloatingActionButton(
onPressed: () => Scaffold.of(context).openDrawer(), // <-- Opens drawer.
);
}),
);
}
you need initialize scaffoldKey
after that,
Open drawer and close drawer
GestureDetector(
onTap: () {
if(scaffoldKey.currentState.isDrawerOpen){
scaffoldKey.currentState.openEndDrawer();
}else{
scaffoldKey.currentState.openDrawer();
}
},
child: LeadingIcon(icon: Icons.menu),//your button
),