flutter setstate rebuild only one child - flutter

in flutter I need that when I call setstate, it only rebuilds a widget
I put 2 children in a stack, I need that when a button is pressed, only the second one is rebuilt.
bool popup = false;
Scaffold(
appBar: AppBar(
title: const Text('TEST'),
actions: <Widget>[
IconButton( // + BUTTON
icon: Icon(Icons.add),
onPressed: () {
setState(() {
popup = true;
});
},
),
IconButton( // - BUTTON
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
popup = false;
});
),
],
),
body: SafeArea(
child: Stack(
children: <Widget>[
Container( // FIRST WIDGET
key: ValueKey(1),
child: Text("Random - "+new Random().nextInt(20).toString())
),
popup ? Center(child: Text("abc")) : Text("") , // SECOND WIDGET
],
),
),
);
I expect that when I press the "+" button only the second widget will be re-built, but now it will rebuild all the contents of the stack.
thank you all.

From the official docs we can read:
"When setState() is called on a State, all descendent widgets rebuild. Therefore, localize the setState() call to the part of the subtree whose UI actually needs to change. Avoid calling setState() high up in the tree if the change is contained to a small part of the tree."
My suggestion, and I use it most of the times, is separate the widget that you want to rebuild in a new StatefulWidget. This way the setState only will be rebuild that widget.
class MyAppBar extends StatefulWidget
...
class _MyAppBarState extends State<MyAppBar> {
bool popup = false;
#override
Widget build(BuildContext context) {
return AppBar(
title: const Text('TEST'),
actions: <Widget>[
IconButton( // + BUTTON
icon: Icon(Icons.add),
onPressed: () {
setState(() {
popup = true;
});
},
),
IconButton( // - BUTTON
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
popup = false;
});
),
],
),
}
Then call it in your Scaffold:
Scaffold(
appBar: MyAppBar(),
Other method I can suggest is using ValueNotifier or notifyListeners(). Please read this page Avoid rebuilding all the widgets repetitively. It is well explained.

Another option is to use ValueListenableBuilder:
class _MyHomePageState extends State<MyHomePage> {
final ValueNotifier<bool> popup = ValueNotifier<bool>(false);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TEST'),
actions: <Widget>[
IconButton(
// + BUTTON
icon: Icon(Icons.add),
onPressed: () {
popup.value = true;
}),
IconButton(
// - BUTTON
icon: Icon(Icons.remove),
onPressed: () {
popup.value = false;
})
],
),
body: Center(
child: ValueListenableBuilder<bool>(
valueListenable: popup,
builder: (context, value, _) {
return Stack(
children: [
Text("Random - " + new Random().nextInt(20).toString()),
popup.value ? Center(child: Text("abc")) : Text(""),
],
);
}),
),
);
}
}

You can use StreamBuilder:
StreamController<bool> popup = StreamController<bool>();
#override
void dispose() {
popup.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TEST'),
actions: <Widget>[
IconButton( // + BUTTON
icon: Icon(Icons.add),
onPressed: () => popup.add(true),
),
IconButton( // - BUTTON
icon: Icon(Icons.remove),
onPressed: () => popup.add(false),
),
],
),
body: SafeArea(
child: Stack(
children: <Widget>[
Container( // FIRST WIDGET
key: ValueKey(1),
child: Text("Random - "+new Random().nextInt(20).toString())
),
StreamBuilder<bool>(
stream: popup.stream,
initialData: false,
builder: (cxt, snapshot) {
return snapshot.data ? Center(child: Text("abc")) : Text("");
},
)
],
),
),
);
}

Remove the setState from the widget you don't want to be changed. And only use setState for the ones you need to rebuild
Or you can consider using inheritedModel widget

Here is the example from where you can learn how to build an Inherited model widget to update only specific widgets rather than the whole widgets.
https://medium.com/flutter-community/flutter-state-management-setstate-fn-is-the-easiest-and-the-most-powerful-44703c97f035

Related

How to display SnackBar in Flutter?

I want to display a SnackBar in my Flutter app. I have read the docs and copyed it:
The body of my scaffold:
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Osztályok"),
leading: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: IconButton(
icon: Icon(Icons.exit_to_app, color: Colors.white70),
onPressed: () {
authService.signOut();
authService.loggedIn = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GoogleSignUp()));
})),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 5.0),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.add_circle_outline,
color: Colors.white70),
onPressed: () {
createPopup(context);
}),
// IconButton(
// icon: Icon(Icons.search, color: Colors.black38),
// onPressed: null),
],
)),
],
),
The SnackBarPage class:
class SnackBarPage extends StatelessWidget {
void jelszopress(TextEditingController jelszoController, BuildContext context) async{
var jelszo;
DocumentReference docRef =
Firestore.instance.collection('classrooms').document(globals.getid());
await docRef.get().then((value) => jelszo= (value.data['Jelszo']) );
if (jelszo == jelszoController.text.toString()){
Navigator.push(context,
MaterialPageRoute(builder: (context) => InClassRoom()));
}
else{
Navigator.pop(context);
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
}
}
Future<String> jelszoba(BuildContext context) {
TextEditingController jelszoController = TextEditingController();
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add meg a jelszót'),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: TextField(
controller: jelszoController,
decoration: InputDecoration(hintText: "Jelszó")
)
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text('Mehet'),
onPressed: () {
jelszopress(jelszoController, context);
},
)]);
}
);
}
var nevek;
var IDS;
SnackBarPage(this.nevek, this.IDS);
#override
Widget build(BuildContext context){
return ListView.builder(
itemCount: nevek.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
globals.setid(IDS[index]);
jelszoba(context);
},
title: Text(nevek[index]),
),
);
},
) ;
}
}
But my cody doesn't display the SnackBar. I tried the solution of this question: How to properly display a Snackbar in Flutter? but adding a Builder widget didn't help.
"Scaffold.of(context)" has been deprecated, will return null. Now use "ScaffoldMessenger.of(context)". As per Flutter documentation.
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () { },
),
));
},
child: const Text('SHOW SNACK'),
),
),
);
}
NOTE: Make sure your main.dart overrided build() function should return "MaterialApp" as a widget, such as:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Must be MaterialApp widget for ScaffoldMessenger support.
return MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyDashboard(),
);
}
}
So based on the error, it would seem that the context passed in Snackbar.of() is not the correct context. This would make sense based on 1 & 2; and summary copied below:
Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget.build or State.build function. (And similarly, the parent of any children for RenderObjectWidgets.)
In particular, this means that within a build method, the build context of the widget of the build method is not the same as the build context of the widgets returned by that build method.
So this means that the build context you are passing in jelszoba(context) function is not the build context you need and is actually the build context of the widget that is instantiating the Scaffold.
So How to Fix:
To fix this wrap your Card widget in your SnackbarPage in a Builder widget and pass the context from it, to the jelszoba(context) method.
An example from 1 I post below:
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
You can normally use snack bar in the Bottom Navigation bar in this way. However, if you want to show it in the body, then just copy the code from Builder and paste it in the body of the scaffold.
Scaffold(bottomNavigationBar: Builder(builder: (context) => Container(child: Row(children: <Widget>[
Icon(Icons.add_alarm), Icon(Icons.map), IconButton(icon: Icon(Icons.bookmark),
onPressed:() {
Scaffold.of(context).showSnackBar(mySnackBar);
final mySnackBar = SnackBar(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.white, duration: Duration(seconds: 1),
content: Text(
'Article has been removed from bookmarks',
),);
}
),
],
),
),
),
);
Note: In the behaviour property of SnackBar, you can just leave it empty. But the problem with that is "If you have Curved Navigation Bar or you have a floating action button above the bottom navigation bar, then the snackbar will lift these icons (or FAB ) and will affect the UI". That's why SnackBar.floating is more preferred as it is more capatible with the UI.
But you can check and see on your own which suits you the best.

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

I am not able to access the `Set` list from the other file even after importing the file in Flutter

I have an empty list in which the items are added when the items are selected. But I'm not able to access the list in a different file which is of type Set to avoid duplicates. Also the list has two types of items, images and text(inside the children of a Row). The list is in another file and except this list everything can be accessed. I don't know why this is happening, can anyone help?
My code:
final Set saved = Set(); //This thing is not getting accessed
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
// backgroundColor: Colors.indigo,
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
// value: _saved.contains(context), // changed
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// _saved.add(context); // changed
// } else{ // changed
// _saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text(nameOfSite[index]),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}
The part where I want it to be accessed:
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text(
// 'Add Your Favorite Sites Here!❤',
// style: TextStyle(color: Colors.white),
// ),
// Container(
// child: Icon(Icons.favorite, size: 150, color: Colors.blue[100]),
// ),
SizedBox(height: 250),
FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
},
child: Icon(Icons.add),
foregroundColor: Colors.blue,
),
],
);
}
}
I don't think it's a problem of access to the Set from the FavoriteList widget.
You have already created a way to get the latest value of saved Set from the FavoriteList, which is below:
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
)
So, this way you will get the latest value of saved when the FavoriteList Widget
is poped out.
You can use the following approach to catch the new value, make change in SecondPage's code,
From :
FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return FavoriteList();
},
),
);
},
child: Icon(Icons.add),
foregroundColor: Colors.blue,
),
To:
FloatingActionButton(
onPressed: () async {
Set newSet = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return FavoriteList();
},
),
);
print('newSet: $newSet');
},
child: Icon(Icons.add),
foregroundColor: Colors.blue,
),
The newSet value is the latest values that are result of the FavoriteList Widget.
It can be Used to update the UI in SecondPage. Just create a list in SecondPageStateList favorites = List();`. Use this list to populate your UI however you like.
Now, when you catch the saved Set in FloatingActionButton just do Following:
setState((){
favoriteList = newSet.toList();
});
Note:
However, I would like to suggest that if you are providing access to something from multiple widgets, make sure to keep the accessed value in the widget on top of all widgets.
If you need more help, let me know in the comments.

Flutter: How to open Drawer programmatically

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

Show back button instead of drawer button - flutter

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