Flutter: How to open Drawer programmatically

FlutterDartFlutter Layout

Flutter Problem Overview


I want to open Drawer programmatically not by sliding it, how to disable that sliding functionality (touch functionality of Drawer)

Flutter Solutions


Solution 1 - Flutter

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

Solution 2 - Flutter

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

  1. 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.

Solution 3 - Flutter

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

Solution 4 - Flutter

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

Solution 5 - Flutter

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

Solution 6 - Flutter

You have to wrap your widgets in a new BuildContext, because most likely, the context you are using is an outer context, and is not aware of the Scaffold.

drawer: Builder(
  builder: (BuildContext internalContext) {
    return _drawer(internalContext);
  },
),

Check out the full code

class SampleAppPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: _appBar(context),
        drawer: new Builder(
          builder: (BuildContext internalContext) {
            return _drawer(internalContext);
          },
        ),
        body: new Builder(
          builder: (BuildContext internalContext) {
            return _body(internalContext);
          },
        ),
      ),
    );
  }

  Widget _appBar(BuildContext context) {
    return new AppBar(
      title: new Text('Drawer example'),
    );
  }

  Widget _drawer(BuildContext context) {
    return new Center(
      child: new RaisedButton(
        child: new Text('Close drawer'),
        onPressed: () => Navigator.of(context).pop(),
      ),
    );
  }

  Widget _body(BuildContext context) {
    return new Column(
      children: <Widget>[
        new RaisedButton(
          child: new Text('Open via Scaffold context'),
          onPressed: () => Scaffold.of(context).openDrawer(),
        ),
      ],
    );
  }
}

Solution 7 - Flutter

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkrishnajiView Question on Stackoverflow
Solution 1 - FlutterCopsOnRoadView Answer on Stackoverflow
Solution 2 - FluttergiulpView Answer on Stackoverflow
Solution 3 - FlutterGagandeep GambhirView Answer on Stackoverflow
Solution 4 - FlutterKrolawView Answer on Stackoverflow
Solution 5 - FlutterHardik KumbhaniView Answer on Stackoverflow
Solution 6 - FlutterParesh MangukiyaView Answer on Stackoverflow
Solution 7 - FlutterHaseeb PavarattyView Answer on Stackoverflow