Is it possible to detect when a Drawer is open so that we can run some routine to update its content?
A typical use case I have would be to display the number of fol
I think one simple solution is to override the leading
property of your AppBar
so you can have access when the menu icon is pressed an run your API calls based on that.
Yet I may have misunderstood your question because with the use case you provided, you usually need to manage it in a way that you can listen to any change which will update the value automatically so I am not sure what are you trying to trigger when the drawer is open.
Anyway here is the example.
class DrawerExample extends StatefulWidget {
@override
_DrawerExampleState createState() => new _DrawerExampleState();
}
class _DrawerExampleState extends State<DrawerExample> {
GlobalKey<ScaffoldState> _key = new GlobalKey<ScaffoldState>();
int _counter =0;
_handleDrawer(){
_key.currentState.openDrawer();
setState(() {
///DO MY API CALLS
_counter++;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: _key,
appBar: new AppBar(
title: new Text("Drawer Example"),
centerTitle: true,
leading: new IconButton(icon: new Icon(
Icons.menu
),onPressed:_handleDrawer,),
),
drawer: new Drawer(
child: new Center(
child: new Text(_counter.toString(),style: Theme.of(context).textTheme.display1,),
),
),
);
}
}
Unfortunately, at the moment there is no readymade solution.
You can use the dirty hack for this: to observe the visible position of the Drawer.
For example, I used this approach to synchronise the animation of the icon on the button and the location of the Drawer box.
The code that solves this problem you can see below:
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class DrawerListener extends StatefulWidget {
final Widget child;
final ValueChanged<FractionalOffset> onPositionChange;
DrawerListener({
@required this.child,
this.onPositionChange,
});
@override
_DrawerListenerState createState() => _DrawerListenerState();
}
class _DrawerListenerState extends State<DrawerListener> {
GlobalKey _drawerKey = GlobalKey();
int taskID;
Offset currentOffset;
@override
void initState() {
super.initState();
_postTask();
}
_postTask() {
taskID = SchedulerBinding.instance.scheduleFrameCallback((_) {
if (widget.onPositionChange != null) {
final RenderBox box = _drawerKey.currentContext?.findRenderObject();
if (box != null) {
Offset newOffset = box.globalToLocal(Offset.zero);
if (newOffset != currentOffset) {
currentOffset = newOffset;
widget.onPositionChange(
FractionalOffset.fromOffsetAndRect(
currentOffset,
Rect.fromLTRB(0, 0, box.size.width, box.size.height),
),
);
}
}
}
_postTask();
});
}
@override
void dispose() {
SchedulerBinding.instance.cancelFrameCallbackWithId(taskID);
if (widget.onPositionChange != null) {
widget.onPositionChange(FractionalOffset(1.0, 0));
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
key: _drawerKey,
child: widget.child,
);
}
}
If you are only interested in the final events of opening or closing the box, it is enough to call the callbacks in initState
and dispose
functions.
initState()
when open drawer by any action.dispose()
when close drawer by any action.class MyDrawer extends StatefulWidget {
@override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
@override
void initState() {
super.initState();
print("open");
}
@override
void dispose() {
print("close");
super.dispose();
}
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Text("test1"),
Text("test2"),
Text("test3"),
],
),
);
}
}
If you are altering state with these functions to rebuild drawer items, you may encounter the error: Unhandled Exception: setState() or markNeedsBuild() called during build
.
This can be handled by using the following two functions in initState() source
Option 1
WidgetsBinding.instance.addPostFrameCallback((_){
// Add Your Code here.
});
Option 2
SchedulerBinding.instance.addPostFrameCallback((_) {
// add your code here.
});
Full Example of Option 1
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// Your Code Here
});
}
Best solution
ScaffoldState
has a useful method isDrawerOpen
which provides the status of open/close.
Example: Here on the back press, it first checks if the drawer is open, if yes then first it will close before exit.
/// create a key for the scaffold in order to access it later.
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(context) {
return WillPopScope(
child: Scaffold(
// assign key (important)
key: _scaffoldKey,
drawer: SideNavigation(),
onWillPop: () async {
// drawer is open then first close it
if (_scaffoldKey.currentState.isDrawerOpen) {
Navigator.of(context).pop();
return false;
}
// we can now close the app.
return true;
});}