As title. It since that we can detect the drawer is opened, but is this possible to check it is closed or not? Thanks.
6 Answers
I have added this feature in Flutter 2.0.0. Make sure you are using Flutter SDK version >= 2.0.0 to use this.
Simply use a callback in Scaffold
return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      drawer: NavDrawer(),
      onDrawerChanged: (isOpen) {
        // write your callback implementation here
        print('drawer callback isOpen=$isOpen');
      },
      endDrawer: NavDrawerEnd(),
      onEndDrawerChanged: (isOpen) {
        // write your callback implementation here
        print('end drawer callback isOpen=$isOpen');
      },
      body:
      ...
Pull request merged in 2.0.0: https://github.com/flutter/flutter/pull/67249
Happy coding!
 
    
    - 7,127
- 2
- 51
- 63
- 
                    1
- 
                    Perfect! I needed this. I simply added `onDrawerChanged: (_) => setState(() {}),` since I just needed the page to update - it worked like a charm. – JaredEzz Aug 04 '23 at 19:45
Declare a GlobalKey to reference your drawer:
GlobalKey _drawerKey = GlobalKey();
Put the key in your Drawer:
 drawer: Drawer(
                  key: _drawerKey,
Check if your drawer is visible:
 final RenderBox box = _drawerKey.currentContext?.findRenderObject();
 if (box != null){
    //is visible
 } else {
   //not visible  
} 
 
    
    - 93,875
- 20
- 236
- 194
- 
                    This won't work if I want to change the drawer button's look with its `onPressed`, because at that time the Drawer will still be null. – kakyo Jul 27 '20 at 09:43
You can copy paste run full code below 
You can wrap Drawer with a StatefulWidget and put callback in initState() and dispose() 
initState() will call widget.callback(true); means open 
dispose() will call widget.callback(false); means close 
Slide also work in this case 
code snippet
drawer: CustomDrawer(
        callback: (isOpen) {
          print("isOpen ${isOpen}");
          WidgetsBinding.instance.addPostFrameCallback((_) {
            setState(() {
              _isDrawerOpen = isOpen;
            });
          });
        },
...
class CustomDrawer extends StatefulWidget {
  CustomDrawer({
    Key key,
    this.elevation = 16.0,
    this.child,
    this.semanticLabel,
    this.callback,
  })  : assert(elevation != null && elevation >= 0.0),
        super(key: key);
  final double elevation;
  final Widget child;
  final String semanticLabel;
  final DrawerCallback callback;
  @override
  _CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
  @override
  void initState() {
    if (widget.callback != null) {
      widget.callback(true);
    }
    super.initState();
  }
  @override
  void dispose() {
    if (widget.callback != null) {
      widget.callback(false);
    }
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Drawer(
        key: widget.key,
        elevation: widget.elevation,
        semanticLabel: widget.semanticLabel,
        child: widget.child);
  }
}
working demo
full code
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  bool _isDrawerOpen = false;
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      drawer: CustomDrawer(
        callback: (isOpen) {
          print("isOpen ${isOpen}");
          WidgetsBinding.instance.addPostFrameCallback((_) {
            setState(() {
              _isDrawerOpen = isOpen;
            });
          });
        },
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>[
            DrawerHeader(
              child: Text('Drawer Header'),
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
            ),
            ListTile(
              title: Text('Item 1'),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => SecondRoute()),
                );
              },
            ),
            ListTile(
              title: Text('Item 2'),
              onTap: () {
                // Update the state of the app.
                // ...
              },
            ),
          ],
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Align(
              alignment: Alignment.centerRight,
              child: Text(
                _isDrawerOpen.toString(),
              ),
            ),
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
class CustomDrawer extends StatefulWidget {
  CustomDrawer({
    Key key,
    this.elevation = 16.0,
    this.child,
    this.semanticLabel,
    this.callback,
  })  : assert(elevation != null && elevation >= 0.0),
        super(key: key);
  final double elevation;
  final Widget child;
  final String semanticLabel;
  final DrawerCallback callback;
  @override
  _CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
  @override
  void initState() {
    if (widget.callback != null) {
      widget.callback(true);
    }
    super.initState();
  }
  @override
  void dispose() {
    if (widget.callback != null) {
      widget.callback(false);
    }
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Drawer(
        key: widget.key,
        elevation: widget.elevation,
        semanticLabel: widget.semanticLabel,
        child: widget.child);
  }
}
class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("route test"),
        ),
        body: Text("SecondRoute"));
  }
}
 
    
    - 51,087
- 5
- 102
- 120
You can simply use onDrawerChanged for detecting if the drawer is opened or closed in the Scaffold widget.
Property :
{void Function(bool)? onDrawerChanged} Type: void Function(bool)? Optional callback that is called when the Scaffold.drawer is opened or closed.
Example :
@override Widget build(BuildContext context) {
return Scaffold(
  onDrawerChanged:(val){
    if(val){
      setState(() {
        //foo bar;
      });
    }else{
      setState(() {
        //foo bar;
      });
    }
},     
  drawer: Drawer(        
      child: Container(
      )
  ));
}
 
    
    - 274
- 4
- 8
When you click a Drawer Item where you will navigate to a new screen, there in the Navigator.push(..) call, you can add a .then(..) clause, and then know when the Drawer item Screen has been popped.  
Here is the ListTile for a Drawer item which makes the Navigator.push(..) call when clicked , and the the associated .then(..) callback block:
     ListTile(
          title: Text('About App'),
          onTap: () {
            Navigator.push(
              _ctxt,
              MaterialPageRoute(builder: (context) => AboutScreen()),
            ).then(
              (value) {
                print('Drawer callback for About selection');
                if (_onReadyCallback != null) {
                  _onReadyCallback();
                }
              },
            );
          }),
_onReadyCallback() represents a Function param you can pass in.
I found this is approach - of leveraging the .then() callback from a .push() call - to be a very useful concept to understand with Flutter in general.
Big thanks to the main 2 answers here: Force Flutter navigator to reload state when popping
Here's the complete Drawer code:
Drawer drawer = Drawer(
  child: ListView(
    padding: EdgeInsets.zero,
    children: <Widget>[
      DrawerHeader(
        decoration: BoxDecoration(
          color: Color(0xFF7FAD5F),
        ),
        child: Text(App.NAME_MENU),
      ),
      ListTile(
          title: Text('About App'),
          onTap: () {
            Navigator.push(
              _ctxt,
              MaterialPageRoute(builder: (context) => AboutScreen()),
            ).then(
              (value) {
                print('Drawer callback for About selection');
                if (_onReadyCallback != null) {
                  _onReadyCallback();
                }
              },
            );
          }),
    ],
  ),
);
 
    
    - 11,284
- 8
- 90
- 137
I would recommend that you use this package : https://pub.dev/packages/visibility_detector.
Afterwards you should assign a GlobalKey, like _drawerKey for instance, to the Drawer widget, after which you would be able to detect when the drawer is closed like this:
VisibilityDetector(
              key: _drawerKey,
              child: Container(),
              onVisibilityChanged: (info) {
                if (info.visibleFraction == 0.0) {
                  // drawer not visible.
                }
              },
            )
 
    
    - 266
- 5
- 11
 
    