Persisting AppBar Drawer across all Pages Flutter

rmtmckenzie is very correct.

Although if you are curious about the multi scaffold solution, this can be more elegant than you think.

To share a drawer between all pages we could add a builder in our MaterialApp instance. This will instantiate a Scaffold under Navigator but above all routes.

MaterialApp(
  title: 'Flutter Demo',
  builder: (context, child) {
    return Scaffold(
      drawer: MyDrawer(),
      body: child,
    );
  },
  home: MyHome()
)

Inside your page, you can instantiate another Scaffold without restriction as you'd usually do.

You can then show the shared drawer by doing the following in any widget under MaterialApp :

final ScaffoldState scaffoldState = context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();

Code which you can extract into a nice helper :

class RootScaffold {
  static openDrawer(BuildContext context) {
    final ScaffoldState scaffoldState =
        context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
    scaffoldState.openDrawer();
  }
}

Then reuse using RootScaffold.openDrawer(context)


There are a few different options for this. The most basic is hopefully something you've already done, but I'll list it anyways:

1: Create a class for your drawer

Your widget should be its own stateful or stateless widget. This way, you just have to instantiate it each time.

class MyDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Drawer(...);
  }
}

And then when using it in each page:

Scaffold(
  drawer: MyDrawer(...),
  ...
)

I hope you're already doing this; if not you should be. A class's build function shouldn't be too large or it can lead to poor performance and harder to maintain code; splitting things into logical units will help you in the long run.

2: Create a class for your scaffold

If having to include the same drawer in a scaffold for each page is still too much code, you can instead use a class that encapsulates your scaffold. It would essentially take inputs for each of the scaffold inputs you actually use.

class MyScaffold extends StatelessWidget {

  final Widget body;

  MyScaffold({this.body});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
     body: body,
     drawer: MyDrawer(...),
    );
  }
}

And then instead of using Scaffold in your code, use MyScaffold (but please name it something better =D).

3: Multi level scaffold

I'm only including this way of doing it to be complete, and I don't recommend it. That being said, there are certain things you can't get to work within flutter's normal workflow that you could do by doing this - for example if you want a custom animation for when the user taps on different items in the drawer.

Basically, what you'd do in this case is to have a Scaffold outside of your MaterialApp or Navigator (which I believe would also mean you'd have to have another Navigator outside that, but I'm not 100% sure). You would have the scaffold that's outside your navigation show the drawer while the other one (on each page within the navigation) would do whatever else you need it to do. There's a few caveats - you'd have to make sure you get the right scaffold (i.e. Scaffold.of(context) by itself wouldn't cut it - you'd have to get the context of the first scaffold and use it to find the higher-level one), and you'd probably need to pass a GlobalKey (of the lower-level scaffold) to the Drawer so that it could actually change pages within it.

As I said, I don't recommend this approach, so I'm not going to go into any more detail than that but rather leave it as an exercise for the reader if they want to go down that rabbit hole!

Tags:

Dart

Flutter