Flutter change Scaffold drawer icon - flutter

I would like to change the default Scaffold drawer icon (hamburger icon).
I tried to add a key to the Scaffold widget and a leading icon, but it didn't work form me:
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
/* ... */
leading: IconButton(
icon: Icon(Icons.person),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
},
),
),
drawer: Drawer(
/* ... */
),
/* ... */
);
}
}
How can I change the drawer icon?

I accomplished this by adding a leading icon wrapped by a Builder widget (the key was not needed):
return Scaffold(
appBar: AppBar(
/*...*/
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.person),
onPressed: () { Scaffold.of(context).openDrawer(); },
);
},
),
/*...*/
);

Related

Flutter Navigator v2.0 how to pop a child nested route

I've been trying flutter new declarative Navigator v2.0, following this
johnpryan example, I've decide to change FlatButton pop() in the BookDetailsScreen:
class BookDetailsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
children: [
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Back'),
),
],
}
To the parent router AppBar() in the _AppShellState:
class _AppShellState extends State<AppShell> {
InnerRouterDelegate _routerDelegate;
ChildBackButtonDispatcher _backButtonDispatcher;
#override
Widget build(BuildContext context) {
var appState = widget.appState;
_backButtonDispatcher.takePriority();
return Scaffold(
appBar: appState.selectedBook != null
? AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => _routerDelegate.popRoute(),
),
actions: [
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () => null,
)
],
)
: AppBar(),
body: Router(
routerDelegate: _routerDelegate,
backButtonDispatcher: _backButtonDispatcher,
),
Fullcode
My question is, I'm using _routerDelegate.popRoute() it's working, but I not sure if it is right way to do it?
PS: If someone have a more complex example using Navigator v2.0, I'm new to Flutter and need to know best practices to how separate and organize my code, how to add more routes for instance an Edit and login screen? working with more objects like users, books, authors and etc...
I have a tweak to show the back button, kinda naturally, when the BookDetailsScreen is presented.
In the _AppShellState class
#override
Widget build(BuildContext context) {
...
return Scaffold(
// <- Comment out this appBar
// appBar: AppBar(
// leading: appState.selectedBook != null
// ? IconButton(
// icon: Icon(Icons.arrow_back),
// onPressed: () => _routerDelegate.popRoute(),
// )
// : null,
// ),
...
// Keep the rest part the same
);
}
Add the appBar property into Screens Scaffold in the build method, like this below. Have the appBar on both BooksListScreen and BookDetailsScreen,
class BooksListScreen extends StatelessWidget {
...
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(), // <- This is the key
...
);
}
}
Hope this makes sense.

Page Route with new file dart

How to use MaterialPageRoute with a new file dart?
My codes:
main.dart
import 'package:flutter/material.dart';
import 'package:hello_world/settings_page.dart';
void main() => runApp(Belajar());
class Belajar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
leading: Icon(Icons.android),
title: Text('Hello World'),
actions: [
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SettingsPage();
},
),
);
},
)
],
),
),
);
}
}
settings_page.dart
import 'package:flutter/material.dart';
class SettingsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
I tried the code above. But, give me an error:
Navigator operation requested with a context that does not include a Navigator.
What wrong with my code?
you are missing just one thing
MaterialApp(
home:Builder(
//use this context to push new route
builder:(context){
return Scaffold(
appBar://..
);
}
)
)
here builder will introduce a new context which will contain the navigator introduced by material app
First create a class for example runFunction()
and you can use these code for going to that screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => runFunction(),
),
);
an example of runFuction
class _recipeSearchState extends State<recipeSearch> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Text("Text"),
],
),
),
);
}
}

Open side drawer from custom button exist in a container

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

"How to open scaffold drawer from the custom AppBar ?"

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.

Open drawer on clicking AppBar

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