7. Navigation Drawer

7. Navigation Drawer

Hello, Welcome back. Glad that you’re here.

We're building a Movie App with the best coding practices and tools. In the previous tutorial, we created Movie Tabs.

In this tutorial, we'll create the navigation drawer on the home screen.

Drawer in Scaffold

Open home_screen.dart, and add the drawer:

Scaffold(
  drawer: const NavigationDrawer(),
  body: ///body
)

The drawer is a journey here, so create a new folder drawer in the presentation folder.

Create a new file navigation_drawer.dart:

//1
class NavigationDrawer extends StatelessWidget {
  //2
  const NavigationDrawer();

  @override
  Widget build(BuildContext context) {
    //3
    return Container(
      width: Sizes.dimen_300.w,
      color: Theme.of(context).primaryColor.withOpacity(0.7),
    );
  }
}
  1. A stateless widget
  2. Declare a const constructor so that we can use const on the home screen and improve little performance.
  3. Return a Container with a fixed width of 300 and the primaryColor with 0.7 opacity.

Before running the app, don't forget to add an import statement in home_screen.dart. You can drag from the left side of the screen and see the navigation drawer. Ideally, we've to open the navigation drawer when we tap on the menu icon. As we've used a custom app bar, we'll have to open the navigation drawer explicitly.

Open movie_app_bar.dart, open the drawer on tap of menu icon:

GestureDetector(
  onTap: () {
    //1
    Scaffold.of(context).openDrawer();
  },
  child: Align(
    alignment: Alignment.topLeft,
    child: SvgPicture.asset(
      'assets/svgs/menu.svg',
      height: Sizes.dimen_12.h,
    ),
  ),
)
  1. You'll get the access to the Scaffold above the tree by using Scaffold.of(context), open the drawer by using the reference of the Scaffold.

Now run the app and click on the menu icon.

Blur Effect

In navigation_drawer.dart, add BoxDecoration and remove the color:

decoration: BoxDecoration(
    boxShadow: [
      BoxShadow(
        color: Theme.of(context).primaryColor.withOpacity(0.7),
        blurRadius: 4,
      ),
    ],
),
  1. Use BoxDecoration and add boxShadow with blur radius as 4.

Run the app and see the effect. This gives a nice look to the drawer.

At top in the navigation drawer add the Logo:

//1
SafeArea(
  //2
  child: Column(
    //3
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      //4
      Padding(
        padding: EdgeInsets.only(
          top: Sizes.dimen_8.h,
          bottom: Sizes.dimen_18.h,
          left: Sizes.dimen_8.w,
          right: Sizes.dimen_8.h,
        ),
        //5
        child: Logo(
          height: Sizes.dimen_20.h,
        ),
      ),
    ],
  ),
),
  1. Use SafeArea to have logical space from the top.
  2. For all the items in the vertical direction, use Column.
  3. Use CrossAxisAlignment.start to have every item start from the left.
  4. Add Padding from all directions to have proper space between the list items and the Logo.
  5. Finally, use the Logo widget that we create and use in MovieAppBar. If you remember, I intentionally added height as an argument for this Logo. Provide 20 height.

Run the application and see Logo.

Custom ListItem

NavigationListItem

In the drawer, we show Favorite Movies, Feedback, and About. So, we'll create a common component. Also, this is a journey, so create a new folder in the journey folder.

In journeys/drawer folder, create a new file navigation_list_item.dart:

This widget will show a title and when you tap on it, you'll navigate to a specific screen.

//1
class NavigationListItem extends StatelessWidget {
  //2
  final String title;
  final Function onPressed;

  const NavigationListItem({Key key, @requried this.title, @required this.onPressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    //3
    return GestureDetector(
      onTap: onPressed,
      //4
      child: Container(
        //5
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              color: Theme.of(context).primaryColor,
              blurRadius: 2,
            ),
          ],
        ),
        //6
        child: ListTile(
          title: Text(
            title,
            style: Theme.of(context).textTheme.subtitle1,
          ),
        ),
      ),
    );
  }
}
  1. Create a stateless widget.
  2. Add fields for title and a function onPressed, that will be called when you press the line item.
  3. Use GestureDetector to have this title tappable.
  4. We will use Container because we need to add a similar blurry effect to each line item.
  5. Add BoxShadow in BoxDecoration with blurRadius of 2 and primary color.
  6. Use the ListTile from the flutter framework.

Comeback to navigation_drawer.dart, and add NavigationListItem below the first child in the Column.

//1
NavigationListItem(
  title: 'Favorite Movies',
  onPressed: () {},
),
//2
NavigationListItem(
  title: 'Feedback',
  onPressed: () {},
),
NavigationListItem(
  title: 'About',
  onPressed: () {},
),
  1. Use NavigationListItem for Favorite Movies and keep the onPressed as an empty method right now.
  2. Similarly add Feedback and About list item as well.

Now, run the application. You'll see three items below the logo.

NavigationExpandedListItem

I have decided to give language selection the drawer itself. So to show a dropdown on tap of the tile, we have to open a list of sub-items. When you tap on the Language tile, you'll show English and Spanish as 2 options. To make a tile to be expanded, we have ExpansionTile that takes in a title and list of sub-list items.

In drawer folder, create another file navigation_expanded_list_item.dart.

class NavigationExpandedListItem extends StatelessWidget {
  final String title;
  //1
  final List<String> children;
  final Function onPressed;

  const NavigationExpandedListItem({
    Key key,
    @required this.title,
    @required this.children,
    @required this.onPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        boxShadow: [
          BoxShadow(
            color: Theme.of(context).primaryColor,
            blurRadius: 2,
          ),
        ],
      ),
      //2
      child: ExpansionTile(
        title: Text(
          title,
          style: Theme.of(context).textTheme.subtitle1,
        ),
        //3
        children: <Widget>[
          for (int i = 0; i < children.length; i++)
            NavigationListItem(
              title: children[i],
              onPressed: () {},
            ),
        ],
      ),
    );
  }
}
  1. This is very similar to NavigationListItem, except that now it also has children as one of the required parameters.
  2. Instead of ListTile, use ExpansionTile.
  3. children passed in the NavigationExpandedListItem is a list of strings and that used in ExpansionTile accepts List of Widgets. So, using the for loop iterate through the list of strings and create a NavigationListItem.

Use this expanded list tile in the navigation_drawer.dart below the Favorite Movies line item.

NavigationExpandedListItem(
  title: 'Language',
  //1
  children: [
    'English',
    'Spanish',
  ],
  onPressed: (index) {},
),
  1. Add two languages as of now.

Now run the application and tap on the language line item. You'll see the dropdown of languages.

The sub-list in language should've little padding from the left as well because it should look like a nested item in language.

NavigationSubListItem

To do that let's create another line item.

In the navigation_list_item.dart file, create another stateless widget:

class NavigationSubListItem extends StatelessWidget {
  final String title;
  final Function onPressed;

  const NavigationSubListItem({Key key, this.title, this.onPressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed,
      child: Container(
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              color: Theme.of(context).primaryColor,
              blurRadius: 2,
            ),
          ],
        ),
        child: ListTile(
          //1
          contentPadding: EdgeInsets.symmetric(horizontal: 32.w),
          title: Text(
            title,
            style: Theme.of(context).textTheme.subtitle1,
          ),
        ),
      ),
    );
  }
}
  1. Exactly clone of this, just add contentPadding.

In NavigationExpandedListItem, instead of NavigationListItem use NavigationSubListItem.

Run the application now and tap on the language list item now. Notice the color of the arrow that changes its direction on collapse and expanded state.

It doesn't match our app theme. So go in app.dart and add accentColor in ThemeData:

accentColor: AppColor.royalBlue,

Now run the app for the final time. This time, the color matches our theme.

By this, we've added NavigationDrawer.

This was all about creating NavigationDrawer with a blur effect. See you in the next part of the series.

Did you find this article valuable?

Support Prateek Sharma by becoming a sponsor. Any amount is appreciated!