The drawer has this default three horizontal bars as default icon but I want to change it to something else.
I have checked the possible options under the Drawer(), but no property seems to be attached to that.
This should work.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text('hi'),
leading: IconButton(
icon: Icon(Icons.accessible),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
);
From the docs ->
{Widget leading} Type: Widget
A widget to display before the [title].
If this is null and [automaticallyImplyLeading] is set to true, the [AppBar] will imply an appropriate widget. For example, if the [AppBar] is in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this widget with an [IconButton] that opens the drawer (using [Icons.menu]). If there's no [Drawer] and the parent [Navigator] can go back, the [AppBar] will use a [BackButton] that calls [Navigator.maybePop].
The following code shows how the drawer button could be manually specified instead of relying on [automaticallyImplyLeading]:
import 'package:flutter/material.dart';
Widget build(context) {
return AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
},
),
);
}
The [Builder] is used in this example to ensure that the context refers to that part of the subtree. That way this code snippet can be used even inside the very code that is creating the [Scaffold] (in which case, without the [Builder], the context wouldn't be able to see the [Scaffold], since it would refer to an ancestor of that widget).
appBar: AppBar(
leading: Builder(
builder: (context) => IconButton(
icon: Icon(Icons.menu_rounded),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
title: Text(
"Track your Shipment",
),
),
You can open a drawer with a custom button like this too.
create this scaffold key.
var scaffoldKey = GlobalKey<ScaffoldState>();
now added a scaffolled in your state class like this
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
drawer: Drawer(
child: Text('create drawer widget tree here'),
),
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'appName',
style: Theme.of(context).textTheme.headline2,
),
leading: IconButton(
onPressed: () {
scaffoldKey.currentState?.openDrawer();
},
icon: Image.asset(
'assets/images/menu.png',
fit: BoxFit.cover,
),
),
),
body: Container(),
);
}
Lets say you have: index.dart (where you want to use the appbar), drawer.dart (your drawer or navigation menu) and appbar.dart (your appbar)
you can do this in drawer:
Widget drawer(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
Container(
...
)
);
then your appbar.dart:
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: Colors.white,
leading: InkWell(
onTap: () => Scaffold.of(context).openDrawer(),
child: Image.asset("assets/images/imgAppBar.png"),
),
title: Container(...
then your index.dart:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
drawer: drawer(context),
appBar: CustomAppBar(),
...
this is just a simple one. You can use IconButton in case you want to use an Icon etc.
You need to create the Global key of type ScaffoldKey the use that to open the drawer and change the icon too:
Widget build(BuildContext context) {
var scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title:Text('hi'),
leading: IconButton(
icon: Icon(Icons.accessible),
onPressed: () => scafoldKey.currentState.openDrawer(),
),
),
);
Actually, i tried the answer by cmd_prompter and it didn't work for me.
The better approach is described here
My working code is here:
return DefaultTabController(
key: Key("homePage"),
length: 2,
child: Scaffold(
endDrawer: Drawer(
),
appBar: AppBar(
leading: BackButton(
onPressed: () {
},
),
title: Text(profile.selectedCity!),
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: baseUnit(3)),
child: Builder(
builder: (context) => IconButton(
icon: Icon(Icons.account_circle),
onPressed: () => Scaffold.of(context).openEndDrawer(),
)
)
)
It worked fine for me - especially this part regarding using Builder. This is important - otherwise it was not working for me.
class HomeOne extends StatefulWidget { const HomeOne({Key? key}) : super(key: key);
#override State createState() =>HomeOneState(); }
var scaffoldKey = GlobalKey();
class HomeOneState extends State { #override Widget build(BuildContext context) { var theme = Theme.of(context); return Directionality( textDirection: TextDirection.rtl, child: Scaffold( key: scaffoldKey, drawerEnableOpenDragGesture: true, // drawerScrimColor: Colors.red, appBar: AppBar( leading: IconButton( onPressed: () => scaffoldKey.currentState?.openDrawer(), icon: const Icon( Icons.add, color: Colors.red, )), ),
AppBar(
leading: IconButton(
onPressed: () {
// Code
},
icon: Icon(Icons.arrow_back),
),
),
To only change the icon color, it's easier to do by adding an iconTheme to the AppBar:
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(),
appBar: AppBar(
title: Text("Navigation Drawer"),
iconTheme: IconThemeData(color: Colors.green),
),
);
}
Related
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,
),
)
],
I am just learning Flutter and got stuck on this error:
No Scaffold widget found.
Home widgets require a Scaffold widget ancestor.
The specific widget that could not find a Scaffold ancestor was: Home
The ancestors of this widget were
but as you can see from my code I do have a Scaffold and I played around adding it wherever I can but I didn't work for.
What can be the reason to what I've done or didn't notice there?
import 'package:firebase_redux_app/services/firebase.auth.dart';
import 'package:flutter/material.dart';
// import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_redux_app/services/firestore.dart';
import 'package:provider/provider.dart';
import 'package:firebase_redux_app/screens/home/brewList.dart';
import 'package:firebase_redux_app/models/brew.dart';
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
void _showSettingsPanel() {
showBottomSheet(
context: context,
builder: (context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 60.0),
child: Text('bottom sheet'),
);
});
}
return StreamProvider<List<Brew>>.value(
value: DBFirestore().brews,
child: Scaffold(
backgroundColor: Colors.brown[50],
appBar: AppBar(
title: Text('Brew Crew'),
backgroundColor: Colors.brown[400],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
await _auth.signOut();
},
icon: Icon(Icons.person),
label: Text('Log Out')),
FlatButton.icon(
icon: Icon(Icons.settings),
label: Text('settings'),
onPressed: () => _showSettingsPanel(),
)
],
),
body: BrewList(),
),
);
}
}
This error is caused because of the scope of your _showSettingsPanel method
There are 2 things you can do
1.
Make the _showSettingsPanel a method in the Home class and allow it take the context as a parameter. Hence, wrap your settings FlatButton in a Builder and pass the context to the _showSettingsPanel method.
Like this
class Home extends StatelessWidget {
void _showSettingsPanel(context) {
showBottomSheet(
context: context,
builder: (context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 60.0),
child: Text('bottom sheet'),
);
});
}
#override
Widget build(BuildContext context) {
return StreamProvider<List<Brew>>(
value: DBFireStore().brews
child: Scaffold(
backgroundColor: Colors.brown[50],
appBar: AppBar(
title: Text('Brew Crew'),
backgroundColor: Colors.brown[400],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
},
icon: Icon(Icons.person),
label: Text('Log Out')),
Builder(
builder: (context) {
return FlatButton.icon(
icon: Icon(Icons.settings),
label: Text('settings'),
onPressed: () => _showSettingsPanel(context),
);
}
)
],
),
body: BrewList(),
),
);
}
}
2.
Wrap the Home widget in a Scaffold wherever you use it, instead of using just Home
Like this
Scaffold(body: Home())
now this prb has different solutions. U gotta use sccafoldKey
final _scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
_scaffoldKey.currentState!.showBottomSheet(
(context) => const AddTaskScreen());
},
icon: const Icon(Icons.add))
],
),
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
I want to open Drawer programmatically not by sliding it, how to disable that sliding functionality (touch functionality of Drawer)
Null safe code
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.
);
}),
);
}
If you want to disable opening the Drawer using a drag gesture, you can set
Scaffold(
drawerEnableOpenDragGesture: false
// above code ...
)
To disable the slide to open functionality you can set the property drawerEnableOpenDragGesture on Scaffold to false.
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(
home: Scaffold(
// this to prevent the default sliding behaviour
drawerEnableOpenDragGesture: false,
drawer: Drawer(),
appBar: AppBar(
leading: Builder(builder: (context) => // Ensure Scaffold is in context
IconButton(
icon: Icon(Icons.menu),
onPressed: () => Scaffold.of(context).openDrawer()
),
),
)
)
);
}
}
To open the drawer programmatically using Scaffold.of(context) you'll have to ensure (thanks Krolaw !) that the context inside which the call is made is aware of the Scaffold.
A clean way to do it is to wrap the button in a builder.
I've edited the answer to include a minimal full working example.
Scaffold is a widget that implements material design principles, so be aware that to be able to call this method, you'll need to import 'package:flutter/material.dart'; and your widget needs to have a MaterialApp as ancestor.
Codepen demo
As with many Flutter things, there are other solutions to ensure Scaffold is in context.
Error messages are IMO among the best features of flutter framework, allow me to humbly suggest to always read them thoroughly and to explore the documentation they point at.
For instance, this is part of the error message that one gets if calling openDrawer outside of a proper context:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
https://api.flutter.dev/flutter/material/Scaffold/of.html
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
Calling Scaffold.of doesn't work because the context doesn't contain the Scaffold. Some solutions above have ignored this, others have used GlobalKey. I believe the cleanest solution is wrapping the button in a Builder:
Scaffold(
drawerEnableOpenDragGesture: false, // Prevent user sliding open
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text("Some Title"),
actions: [
Builder(builder: (context) => // Ensure Scaffold is in context
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Scaffold.of(context).openDrawer()
)),
],
),
// TODO ...
)
Here is another example of opening the drawer programmatically from a hamburger icon and without the Appbar:-
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
var scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
key: scaffoldKey,
drawer: new Drawer(
child: new ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
//Do some stuff here
//Closing programmatically - very less practical use
scaffoldKey.currentState.openEndDrawer();
},
)
],
),
),
body: Stack(
children: <Widget>[
new Center(
child: new Column(
children: <Widget>[],
)),
Positioned(
left: 10,
top: 20,
child: IconButton(
icon: Icon(Icons.menu),
onPressed: () => scaffoldKey.currentState.openDrawer(),
),
),
],
),
),
);
}
}
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
"Infilon Technologies",
style:
TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.w600),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {
if (_scaffoldKey.currentState.isEndDrawerOpen) {
_scaffoldKey.currentState.openDrawer();
} else {
_scaffoldKey.currentState.openEndDrawer();
}
},
),
],
),
If you are using endDrawer (right to left) in Scaffold, you should use:
Scaffold.of(context).openEndDrawer();
If you are using drawer (left to right) in Scaffold, you should use:
Scaffold.of(context).openDrawer();
You can use this perfect method to open drawer
its Worked with null safty module above flutter 2.12
class DashBoardScreen extends StatefulWidget {
final String? screen;
const DashBoardScreen(this.screen, {super.key});
#override
State<DashBoardScreen> createState() => _DashBoardScreenState();
}
class _DashBoardScreenState extends State<DashBoardScreen> {
DashBoardScreenController controller =
Get.put(getIt<DashBoardScreenController>());
#override
Widget build(BuildContext context) {
controller.scaffoldKey = GlobalKey<ScaffoldState>();
return Obx(() => Scaffold(
key: controller.scaffoldKey,
onDrawerChanged: (isOpened) {
if (!isOpened) {
setState(() {});
}
},
appBar: AppBar(
title: const Text("Test drawer App"),
actions: const [
const Padding(
padding: EdgeInsets.only(right: 20),
child: Icon(Icons.search))
],
leading: UnconstrainedBox(
child: GestureDetector(
onTap: () {
controller.scaffoldKey.currentState!.openDrawer();
},
child: const AbsorbPointer(
absorbing: true,
child: SizedBox(
height: 50,
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
"https://cdn.pixabay.com/photo/2014/07/09/10/04/man-388104_960_720.jpg",
)),
),
),
),
),
),
drawerEdgeDragWidth:
kIsWeb ? MediaQuery.of(context).size.width * 0.2 : null,
drawer: Drawer(
key: controller.scaffoldKey,
child: ListView(
children: [
DropdownMenuItem(onTap: () {}, child: const Text("Add Anime"))
],
)),
body: widget.screen == StringVariables.ADD_ANIME
? AddAnimeFragment(widget.screen!)
: Container(),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Add Anime',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Favourite',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
currentIndex: controller.bottomSheetIndex.value,
selectedItemColor: ColorName.primaryColor,
onTap: (s) {
controller.bottomSheetIndex.value = s;
},
),
));
}
}
Simply flow these steps
create a variable in class like this
var scaffoldKey = GlobalKey<ScaffoldState>();
then use this key in your scaffold like this
Scaffold(
key: scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
leading: IconButton(
onPressed: () {
scaffoldKey.currentState?.openDrawer();
},
icon: Icon(
Icons.menu,
color: ExtraColors.PRIMARY_800,
)),
title: Text(
'${AppStrings.appName}',
),
centerTitle: true,
),
)
I have a Flutter project which uses material design, that as I go through routes the appbar will show the backbutton. Recently, I just implemented a drawer in my project, and the drawer icon overrides the back icon. I essentially want to undo this, showing the back button, for certain screens, and show the menu button for other screens, almost like when I define the drawer having a showIcon: false property? I understand this post is a similar question, but no code is shown for the question or the solution... My drawer looks like this:
return Scaffold(
//appbar is here
appBar: AppBar(
title: Text("Title"),
),
drawer: drawer,
body: _buildBody(),
);
And I define drawer here:
var drawer = Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
//My listTiles and UserAccountsDrawerHeader are removed for simplicity
],
),
);
Thanks for any help.
Short answer:
AppBar(
leading: IconButton(
onPressed: () {}, // Handle your on tap here.
icon: Icon(Icons.arrow_back_ios),
),
)
Screenshot:
Full code:
void main() => runApp(MaterialApp(home: MyPage()));
class MyPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => Page1())),
child: Text("Go to Drawer Page"),
),
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => Page2())),
child: Text("Go to Back button Page"),
),
],
),
),
);
}
}
// This has drawer
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: Drawer(),
);
}
}
// This has back button and drawer
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
),
drawer: Drawer(),
);
}
}
AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () { Navigator.pop(context); },
);
},
),
)