Volodymyr Bilovus
Volodymyr Bilovus

Reputation: 781

Flutter Scaffold.of(context).openDrawer() doesn't work

I want to open a drawer after pushing on the custom button in BottomMenu I have trouble with Scaffold.of(context).openDrawer(), it doesn't work. My BottomMenu is a separate widget class. As I understand, it doesn't work because it's a separate context. How can I get the right context? Or perhaps someone knows another solution.

Here my code reproducer:

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(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Drawer'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      bottomNavigationBar: BottomMenu(),
      endDrawer: SizedBox(
        width: double.infinity,
        child: Drawer(
          elevation: 16,
          child: Container(
            color: Colors.black,
            child: ListView(
              padding: EdgeInsets.zero,
              children: <Widget>[
                ListTile(
                    title: Text('Some context here',
                        style: TextStyle(color: Colors.white))),
                ListTile(
                    title: Text('Some context here',
                        style: TextStyle(color: Colors.white))),
              ],
            ),
          ),
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Call Drawer form menu reproducer',
            )
          ],
        ),
      ),
    );
  }
}

class BottomMenu extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15),
      child: Wrap(
        alignment: WrapAlignment.center,
        children: <Widget>[
          Divider(color: Colors.black, height: 1),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 2),
            child: Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  InkWell(
                    borderRadius: new BorderRadius.circular(20.0),
                    customBorder: Border.all(color: Colors.black),
                    child: Container(
                      padding: EdgeInsets.only(
                          left: 3, right: 6, bottom: 15, top: 11),
                      child: Row(
                        children: <Widget>[
                          Icon(Icons.menu),
                          Text('Show menu', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
                        ],
                      ),
                    ),
                    onTap: () {
                      Scaffold.of(context).openDrawer();
                    },
                  ),
                ],
              ),
          ),
        ],
      ),
    );
  }
}

Upvotes: 11

Views: 19080

Answers (12)

Rifat Khan
Rifat Khan

Reputation: 11

I tried with this and worked

Builder(
        builder: (BuildContext context) {
          return IconButton(
            onPressed: () {
              Scaffold.of(context).openEndDrawer();
            },
            icon: const Icon(Icons.menu),
          );
        },
      ),

Upvotes: 0

Lara Amanj
Lara Amanj

Reputation: 15

class MyApp extends StatelessWidget {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text('My App'),
      ),
      body: Center(
        child: Text('This is the body of the app.'),
      ),
      bottomNavigationBar: BottomMenu(scaffoldKey: _scaffoldKey),
    );
  }
}

class BottomMenu extends StatelessWidget {
  final GlobalKey<ScaffoldState> scaffoldKey;

  const BottomMenu({Key? key, required this.scaffoldKey}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      items: const [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: 'Home',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.settings),
          label: 'Settings',
        ),
      ],
      onTap: (index) {
        // Open the drawer when the settings button is tapped.
        if (index == 1) {
          scaffoldKey.currentState?.openDrawer();
        }
      },
    );
  }
}

Upvotes: 0

Abdullah Opadeji
Abdullah Opadeji

Reputation: 236

NOTE: ANY OTHER TYPE OF BUTTON WON'T WORK FOR INVOKING THE DRAWER

Must use IconButton

Example:

AppBar(leading: Builder(builder: (context) {
          return IconButton(
            icon:Text("Open Drawer"), // icon property is of type Widget
            onPressed: () {
              Scaffold.of(context).openDrawer();
            },
          );
        }))

Best of Luck!

Upvotes: 0

THANGSTAR
THANGSTAR

Reputation: 81

you need wrap Builder, like this:

AppBar(
                leading: Builder(
                  builder: (context) {
                    return IconButton(
                      icon: Icon(Icons.abc_outlined),
                      onPressed: () {
                        Scaffold.of(context).openDrawer();
                      },
                    );
                  }
                ),
//...//

Its working for me!

Upvotes: 2

Shakil Awan
Shakil Awan

Reputation: 1

Assign Drawer to drawer property in scaffold. Wrap your specific Widget/Button(where you want to open drawer on its click method) with Builder. Use below method on click property: enter image description here Scaffold.of(context).openDrawer();

Upvotes: 0

Matthew Trent
Matthew Trent

Reputation: 3264

The Problem

This issue can occur when you do not use the correct BuildContext when calling Scaffold.of(context).openDrawer() (or openEndDrawer()).

Easiest Solution

Simply wrap whatever calls openDrawer() (or openEndDrawer()) with a Builder widget. This will give it a working context.

Minimal Working Example

// your build method
@override
Widget build(BuildContext context) {
  return Scaffold(
    floatingActionButton: Builder(builder: (context) { // this uses the new context to open the drawer properly provided by the Builder
      return FloatingActionButton(onPressed: (() => Scaffold.of(context).openDrawer())); 
    }),
    drawer: const Drawer(
      child: Text("MY DRAWER"),
    ),
  );
}

Upvotes: 5

chaitanya Harde
chaitanya Harde

Reputation: 376

Scaffold.of(context).openEndDrawer()

Upvotes: 4

loonix
loonix

Reputation: 490

If you have the appbar widget with an action button to launch the drawer and the drawer is never pushed please remember that you need to define after appbar: ... the endDrawer: YOURAppDrawerWIDGET(), or else using the Scaffold.of(context).openEndDrawer() will not work.

Scaffold(
    appBar: AppBar(title: Text(_title)),
    endDrawer: AppDrawer(), // <-- this is required or else it will not know what is opening
    body: SingleChildScrollView(
     ///...

Upvotes: -1

Hoon
Hoon

Reputation: 687

In my case, this worked.

return Scaffold(
      key: _scaffoldKey,
      endDrawerEnableOpenDragGesture: false,  // This!
      appBar: AppBar(
        iconTheme: IconThemeData(color: Colors.white),
        leading: IconButton(
          icon: Icon(Icons.menu, size: 36),
          onPressed: () => _scaffoldKey.currentState.openDrawer(),  // And this!
        ),
      ),
      drawer: DrawerHome(),
      ....

and _scaffoldKey must be initialized as,

final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

under the class.

Upvotes: 21

live-love
live-love

Reputation: 52386

Similar problem here. Clicked on button and nothing happened. The problem is I was using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.

Here is how I solved it:

// body: Column(
        //   children: <Widget>[
        //     Row(
        //       children: <Widget>[        
        //         IconButton(
        //           icon: Icon(Icons.filter_list),
        //           onPressed: () => Scaffold.of(context).openEndDrawer(), (wrong context)
        //         ),
        //       ],
        //     ),
        //   ],
        // )

To:

body: Builder(
            builder: (context) => Column(
                  children: <Widget>[
                    Row(
                      children: <Widget>[
                        IconButton(
                          icon: Icon(Icons.filter_list),
                          onPressed: () => Scaffold.of(context).openEndDrawer(),
                        ),
                      ],
                    ),
                  ],
                )),
      ),

Upvotes: 1

Shojaeddin
Shojaeddin

Reputation: 2073

My problem solved that instead of

Scaffold.of(context).openEndDrawer()

I give key to Scaffold and then I call by state like below

_scaffoldkey.currentState.openEndDrawer()

It solved my problem I hope It also works for you

Upvotes: 8

cegas
cegas

Reputation: 3091

The problem is that you specified endDrawer on Scaffold yet you're calling Scaffold.of(context).openDrawer().

openDrawer() documentation states:

If the scaffold has a non-null Scaffold.drawer, this function will cause the drawer to begin its entrance animation.

Since your drawer is null, nothing happens.

In contrast, openEndDrawer() informs us:

If the scaffold has a non-null Scaffold.endDrawer, this function will cause the end side drawer to begin its entrance animation.

Since your endDrawer is not null you should use openEndDrawer() method. Alternatively, if you don't care which side the drawer slides in from, you can use drawer instead of endDrawer when building Scaffold.

Upvotes: 11

Related Questions