Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making fixed app-wide menu instead of Drawer on tablets in Flutter

My application has a lot of routes and almost every route uses Scaffold with the same Drawer menu to navigate inside the app (my own CustomDrawer widget). As for devices with big screen, I want to always show the menu on the left side in layout, instead of using Drawer (it works like this in Gmail app. I attached a pic). In other words, I need to make a responsive layout with fixed menu.

example

What I've tried:

  • I know that you can use LayoutBuilder to learn constraints size;
  • Making same layout inside every route will work, but it's bad solution because each route will build menu for itself (scroll position will be different, there will be many states for many menus etc.). I need one app-wide menu for many different routes, but it's impossible to make layout on top routes;
  • Squashing all routes into one route with changing state of main content will take a lot of refactoring and doesn't sound good at all.

In React the app layout would look something like this:

    <App>
      <Menu />
      <main className="content">
        <Switch>
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
          <Route path="/" component={Home} />
        </Switch>
      </main>
    </App>

But I have no idea how to make something like this in Flutter.

tl;dr: To make UI responsive for big screens, I want to show fixed menu instead of Drawer. One menu for whole app, not for every route.

like image 275
Igor Kharakhordin Avatar asked Nov 15 '19 12:11

Igor Kharakhordin


1 Answers

The trick was to use a nested navigator. If viewport's width is big, I put a menu in a row with content, otherwise pass a menu to Scaffold as drawer parameter.

portrait landscape

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,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  final routes = List.generate(20, (i) => 'test $i');

  final navigatorKey = GlobalKey<NavigatorState>();

  bool isMenuFixed(BuildContext context) {
    return MediaQuery.of(context).size.width > 500;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    final menu = Container(
      color: theme.canvasColor,
      child: SafeArea(
        right: false,
        child: Drawer(
          elevation: 0,
          child: ListView(
            children: <Widget>[
              for (final s in routes)
                ListTile(
                  title: Text(s),
                  onTap: () {
                    // Using navigator key, because the widget is above nested navigator  
                    navigatorKey.currentState.pushNamedAndRemoveUntil(s, (r) => false);

                    // navigatorKey.currentState.pushNamed(s);
                  },
                ),
            ],
          ),
        )
      )
    );

    return Row(
      children: <Widget>[
        if (isMenuFixed(context))
          menu,
        Expanded(
          child: Navigator(
            key: navigatorKey,
            initialRoute: '/',
            onGenerateRoute: (settings) {
              return MaterialPageRoute(
                builder: (context) {
                  return Scaffold(
                    appBar: AppBar(
                      title: Text(settings.name),
                    ),
                    body: SafeArea(
                      child: Text(settings.name),
                    ),
                    drawer: isMenuFixed(context) ? null : menu,
                  );
                },
                settings: settings
              );
            },
          ),
        ),
      ],
    );
  }
}
like image 191
Igor Kharakhordin Avatar answered Nov 03 '22 13:11

Igor Kharakhordin