How to Add Navigation Drawer to Flutter App


In this post, I am going to show how you can implement Navigation Drawer in your flutter project.
To add a drawer, in your build function ad drawer as follows
return Scaffold(
     appBar: AppBar(
        title: Text(widget.title),
    ),
    drawer: Drawer(
        child: ListView(
            children: <Widget>[]
        ),
    ),
    body: Container(),
);
The first item to the ListView is DrawerHeader.

DrawerHeader(
  child: Text("Header", style: TextStyle(color: Colors.white),),
  decoration: BoxDecoration(
    color: Colors.blue
  ),
),

This is very basic, let’s try to replace DrawerHeader with user details for that we use UserAccountsDrawerHeader:

UserAccountsDrawerHeader(
  accountEmail: Text("john.doe@example.com"),
  accountName: Text("John Doe"),
  currentAccountPicture: ClipRRect(
    borderRadius: BorderRadius.circular(110),
    child: Image.asset("images/user1.jpg", fit: BoxFit.cover,),
  ),
  otherAccountsPictures: <Widget>[
    ClipRRect(
      borderRadius: BorderRadius.circular(110),
      child: Image.asset("images/user2.jpg", fit: BoxFit.cover,),
    ),
    ClipRRect(
      borderRadius: BorderRadius.circular(110),
      child: Image.asset("images/user3.jpg", fit: BoxFit.cover,),
    )
  ],
  decoration: BoxDecoration(
    color: Colors.blue
  ),
),

For the image assets to be located by the app, create a images directory and add the images. Then edit pubspec.yaml file so that the images can be located

assets:
  - images/

Let’s add some menu’s to the ListView, for example

ListTile(
  leading: Icon(Icons.assessment),
  title: Text("Assessment"),
  onTap: (){
    // change the content
    //do the operation here...
    //close the drawer
    Navigator.pop(context);
  },
),
ListTile(
  leading: Icon(Icons.school),
  title: Text("School"),
  onTap: (){
    // change the content
    //do the operation here...
    //close the drawer
    Navigator.pop(context);
  },
),

Define a new variable _selectedFragment:

Widget _selectedFragment = AssesmentFragment();

The variable stores the fragment to display and use the variable to display the content, so the body part changes to:

body: Container(
  padding: EdgeInsets.all(8),
  child: _selectedFragment,
)

Now for menu click in the navigation drawer, we update the fragment

ListTile(
  leading: Icon(Icons.assessment),
  title: Text("Assessment"),
  onTap: (){
    setState(() {
      _selectedFragment = AssesmentFragment();
    });
    Navigator.pop(context);
  },
),
ListTile(
  leading: Icon(Icons.school),
  title: Text("School"),
  onTap: (){
    setState(() {
      _selectedFragment = SchoolFragment();
    });
    Navigator.pop(context);
  },
),

The code for AssesmentFragment is:

import 'package:flutter/material.dart';

class AssesmentFragment extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Text('Assessment Fragment',);
  }
}

The code for SchoolFragment is:

import 'package:flutter/material.dart';

class SchoolFragment extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Text('School Fragment',);
  }
}

Let’s add a menu in the navigation drawer, which instead of updating the screen as a fragment navigates to a separate screen.

ListTile(
  leading: Icon(Icons.announcement),
  title: Text("Announcement"),
  onTap: (){
    Navigator.pop(context);
    Navigator.push(context, MaterialPageRoute(builder: (context)=> NewScreen()));
  },
)

The screen to which the menu click navigates to is:

import 'package:flutter/material.dart';

class NewScreen extends StatefulWidget {
  final String slug;

  _NewScreenState createState() => _NewScreenState();

  NewScreen({ this.slug});
}

class _NewScreenState extends State<NewScreen> {
  String _pageContent = "New screen content";
  String _pageTitle = "NewScreen";

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_pageTitle),
      ),
      body: Container(
        padding: EdgeInsets.all(8),
        child: Column(
          children: <Widget>[
            Container(
                child:Text(
                    _pageContent
                )
            ),
          ],
        ),
      ),
    );
  }
}

We still have one problem, we see grey color in the statusbar area of the navigation drawer which can be fixed by adding the following lines to the build function before return function:

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(
    statusBarColor: Colors.blueAccent));

The final main.dart file looks as follows:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:navigation_drawer_example/NewScreen.dart';
import 'fragments/AssesmentFragment.dart';
import 'fragments/SchoolFragment.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Navigation Drawer Example'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  Widget _selectedFragment = AssesmentFragment();

  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(
        statusBarColor: Colors.blueAccent
    ));
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      drawer: Drawer(
        child: ListView(
          children: <Widget>[
          UserAccountsDrawerHeader(
            accountEmail: Text("john.doe@example.com"),
            accountName: Text("John Doe"),
            currentAccountPicture: ClipRRect(
              borderRadius: BorderRadius.circular(110),
              child: Image.asset("images/user1.jpg", fit: BoxFit.cover,),
            ),
            otherAccountsPictures: <Widget>[
              ClipRRect(
                borderRadius: BorderRadius.circular(110),
                child: Image.asset("images/user2.jpg", fit: BoxFit.cover,),
              ),
              ClipRRect(
                borderRadius: BorderRadius.circular(110),
                child: Image.asset("images/user3.jpg", fit: BoxFit.cover,),
              )
            ],
            decoration: BoxDecoration(
              color: Colors.blue
            ),
          ),
            ListTile(
              leading: Icon(Icons.assessment),
              title: Text("Assessment"),
              onTap: (){
                setState(() {
                  _selectedFragment = AssesmentFragment();
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: Icon(Icons.school),
              title: Text("School"),
              onTap: (){
                setState(() {
                  _selectedFragment = SchoolFragment();
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: Icon(Icons.announcement),
              title: Text("Announcement"),
              onTap: (){
                Navigator.pop(context);
                Navigator.push(context, MaterialPageRoute(builder: (context)=> NewScreen()));
              },
            )
          ],
        ),
      ),
      body: Container(
        padding: EdgeInsets.all(8),
        child: _selectedFragment,
      )
    );
  }
}

Full code can be downloaded from github.

Image Used in the app:
https://unsplash.com/photos/ROJFuWCsfmA
https://unsplash.com/photos/hh3ViD0r0Rc
https://unsplash.com/photos/qmsP-aRLWsc

Comments

Popular posts from this blog

Automate file upload in Selenium IDE

How To Install and Configure Nextcloud