flutter custom drawer widget with navigationrail vertical labels

How to add NavigtionRail to the Drawer in Flutter

Why use Flutter Drawer with NavigationRail inside

The Flutter Drawer with a NavigationRail inside can be used in a variety of use cases. It allows you to create a custom navigation menu that can be displayed on the left-hand side of the app’s screen. Here are a few potential use cases for the Flutter drawer with a NavigationRail inside:

  1. Navigation Menu: The most common use case for the Flutter drawer with a NavigationRail inside is to create a custom navigation menu for an app. The NavigationRail allows users to easily navigate between different sections of an app, while the drawer provides additional navigation options.
  2. Multi-Level Navigation: The drawer can be used to provide a multi-level navigation menu, where users can drill down into different levels of content. This can be particularly useful for apps that have a large amount of content.
  3. Settings Menu: The Flutter drawer with a NavigationRail can also be used to create a settings menu. The drawer can display a list of settings options, such as changing the app’s theme or language, while the NavigationRail can provide additional options related to the app’s settings.
  4. User Profile Menu: The Flutter drawer with a NavigationRail can be used to create a user profile menu. The drawer can display the user’s profile picture and basic information, while the NavigationRail can provide additional options related to the user’s profile, such as editing their profile or viewing their activity history.
  5. Product Catalog Menu: For an e-commerce app, the Flutter drawer with a NavigationRail inside can be used to display a product catalogue menu. The drawer can display different categories of products, while the NavigationRail can provide additional options related to browsing and searching for products.

How to use a NavigationRail in Flutter

A NavigationRail is a widget that provides a navigation paradigm for navigating through an app’s content using a vertical list of destinations. It is commonly used as a navigation menu within a drawer or as a persistent navigation widget on larger screens.

To use a NavigationRail, you need to create an instance of the widget and specify its properties. The destinations property is a required list of NavigationRailDestination objects, which define each destination in the navigation rail. Each NavigationRailDestination needs to have an icon and a label. The selectedIndex property is used to indicate which destination is currently selected, and the onDestinationSelected property is a callback function that is called when a destination is selected. Within the callback function, you can update the selectedIndex property to reflect the new selection.

Here is a simple example of how to use a NavigationRail:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

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

class MyHomePage extends StatefulWidget {

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>{

  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('flutterassets.com'),
      ),
      body:  Row(
        children: <Widget>[
          NavigationRail(
            destinations: const [
              NavigationRailDestination(
                icon: Icon(Icons.home),
                label: Text('Home'),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.settings),
                label: Text('Settings'),
              ),
            ],
            selectedIndex: _selectedIndex,
            onDestinationSelected: (int index) {
              setState(() {
                _selectedIndex = index;
              });
            },
          ),
          Expanded(
            child: Container(
              padding: EdgeInsets.all(20),
              alignment: Alignment.topCenter,
              child: Text('Selected index: $_selectedIndex'),
            ),
          ),
        ],
      ),
    );
  }
}
flutter navigationrail

In this example, we create a NavigationRail with two destinations: “Home” and “Settings”. We also define an onDestinationSelected callback that updates the _selectedIndex property when a destination is selected. The selected index is then displayed in the body of the scaffold.

Flutter NavigationRail vertical text label

To make a vertical label text in the NavigationRail we can use the RotatedBox widget to rotate the label text by -90 degrees so that it’s presented vertically. We also add padding to the text to make sure it’s centred within the container. Here us updated code:

return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
// drawer: MyDrawer(),
body: Row(
children: <Widget>[
NavigationRail(
labelType: NavigationRailLabelType.all,
selectedLabelTextStyle: TextStyle(
color: Colors.red,
fontSize: 30,
letterSpacing: 1,
decoration: TextDecoration.underline,
decorationThickness: 2.5
),
selectedIconTheme: const IconThemeData(
color: Colors.red,
size: 30
),
unselectedIconTheme: const IconThemeData(
color: Colors.grey,
size: 20
),
unselectedLabelTextStyle: TextStyle(
color: Colors.grey,
fontSize: 20,
letterSpacing: 0.8,
),
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('Home')
),
),
),
NavigationRailDestination(
icon: Icon(Icons.settings),
// icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('Settings')
),
),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('About')
),
),
),
],
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
),
Expanded(
child: Container(
padding: EdgeInsets.all(20),
alignment: Alignment.topCenter,
child: Text('Selected index: $_selectedIndex'),
),
),
],
),
);
flutter navigationrail vertical label and icon

I’ve also added the additional parameters specified in the prompt. The selectedLabelTextStyle property is used to style the text of the currently selected label. We’ve set the font size to 30, the colour to Colors.red, and added an underline using the TextDecoration property. We’ve also set the underline thickness to 2.5 using the decorationThickness property.

Similarly, the unselectedIconTheme and unselectedLabelTextStyle properties are used to style the icons and labels of the unselected destinations. We’ve set the icon size to 20 and the font size of the unselected labels to 20. We’ve also set a letterSpacing value of 0.8 to add some space between the letters of the unselected labels.

Flutter NavigationRail vertical label without icon

To make the icon not visible in the NavigationRail widget, you can replace the icon parameter with an empty SizedBox widget by setting icon: SizedBox.shrink(),. Here’s how you can modify the code:

NavigationRailDestination(
icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('Home')
),
),
),

The above code specifies that the icon should be an empty SizedBox, effectively making the icon invisible.

The RotatedBox widget is used to rotate the text by -90 degrees (a quarter turn to the left) so that it is displayed vertically instead of horizontally. The quarterTurns property of RotatedBox specifies how many quarter turns (90-degree increments) the child should be rotated by. A negative value is used here to turn the text to the left.

Custom Flutter Drawer widget with NavigationRail

The code below creates a custom Flutter Drawer with a NavigationRail that allows the user to select between three different views. The Drawer is made up of two columns: the NavigationRail in the first column and the selected content in the second column. The selected content is updated based on the selected index of the NavigationRail. I called the file my_drawer.dart.

import 'package:flutter/material.dart';

class MyDrawer extends StatefulWidget {
const MyDrawer({Key? key}) : super(key: key);

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

class _MyDrawerState extends State<MyDrawer> {
int _selectedIndex = 0;

@override
Widget build(BuildContext context) {
return Drawer(
child: Row(
children: [
Expanded(
flex: 1,
child: NavigationRail(
backgroundColor: Colors.grey.shade200,
elevation: 12,
labelType: NavigationRailLabelType.selected,
selectedIndex: _selectedIndex,
selectedIconTheme: const IconThemeData(
color: Colors.red,
),
selectedLabelTextStyle: TextStyle(color: Colors.red),
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
leading: Container(
margin: const EdgeInsets.only(bottom: 40.0),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.0, color: Colors.grey),
)
),
child: IconButton(
icon: Icon(Icons.info_outline_rounded),
onPressed: (){print('Info Pressed');},
),
),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.only(top: 40.0),
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 1.0, color: Colors.grey),
)
),
child: IconButton(
icon: Icon(Icons.logout, color: Colors.red,),
onPressed: (){},
),
),
),
),
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.favorite_border),
label: Text('Favorites'),
),
NavigationRailDestination(
icon: Icon(Icons.history),
label: Text('History'),
),
NavigationRailDestination(
icon: Icon(Icons.list_alt),
label: Text('List'),
),
],
),
),
Expanded(
flex: 3,
child: selectedMenu(_selectedIndex),
),
],
),
);
}

Widget selectedMenu(index){
switch (index) {
case 0:
return _buildFavoritesListView();
case 1:
return _buildHistoryListView();
case 2:
return _buildListView();
default:
return _buildFavoritesListView();
}
}

Widget _buildFavoritesListView() {
return ListView(
children: [
ListTile(
title: Text('Favorite Book'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Favorite Song'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Favorite Movie'),
onTap: (){
//go somewhere or do something
},
),
],
);
}

Widget _buildHistoryListView() {
return ListView(
children: [
ListTile(
title: Text('My History'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Your History'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Our History'),
onTap: (){
//go somewhere or do something
},
),
],
);
}

Widget _buildListView() {
return ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('List item $index'),
);
},
);
}
}
flutter custom drawer widget with navigationrail

In the above code the build method of _MyDrawerState returns a Row with two children: a NavigationRail widget and a selectedMenu widget. The NavigationRail is in the first column and has three NavigationRailDestination elements: “Favorites”, “History”, and “List”. Each NavigationRailDestination element has an icon and a label. The selectedMenu widget is in the second column and displays the appropriate content based on the selected index of the NavigationRail

The NavigationRail has several properties that customize its appearance and behaviour. The backgroundColor and elevation properties control the background colour and elevation of the NavigationRail. The labelType property determines how the label is displayed when selected. The selectedIndex property specifies the initially selected index.

The selectedIconTheme and selectedLabelTextStyle properties control the appearance of the selected NavigationRailDestination. The onDestinationSelected property is a callback that updates the selected index when a NavigationRailDestination is selected.

The leading property of the NavigationRail displays an IconButton with an “info” icon, while the trailing property displays an IconButton with a “logout” icon. These icons are wrapped in a Container with a BoxDecoration that adds a border to the bottom or top of the NavigationRail.

The selectedMenu method returns the appropriate content based on the selected index of the NavigationRail. It uses a switch statement to determine which content to display. There are three possible options: _buildFavoritesListView(), _buildHistoryListView(), and _buildListView(). If the selected index is not recognized, _buildFavoritesListView() is returned as a default.

Each _build*ListView() method returns a ListView widget with a list of ListTile widgets. The ListTile widgets have a Text widget as their title property and an onTap property that can be used to trigger an action when the ListTile is tapped. These methods are used to build the content displayed in the drawer based on the selected index of the NavigationRail.

Use the Custom Drawer widget in Flutter

To use the custom drawer I presented above, you need to import the file where the MyDrawer class is defined in your Flutter project.

import 'my_drawer.dart';

Then, you can use the MyDrawer widget inside your scaffold widget’s drawer property. For example, if you have a scaffold widget like this:

Scaffold(
  appBar: AppBar(
    title: Text('flutterassets.com'),
  ),
  drawer: MyDrawer(),
  body: Center(
    child: Text('Hello, world!'),
  ),
)

The MyDrawer widget will be used as the drawer of your scaffold. When the user taps on the drawer icon on the app bar, the custom drawer with the navigation rail will slide in from the left.

By default, the drawer will show the list of items under the ‘Favorites’ tab, but the user can switch between the ‘Favorites’, ‘History’, and ‘List’ tabs using the navigation rail on the left. The content displayed on the right side of the drawer will change based on the selected tab.

For example, if the user selects the ‘History’ tab, the drawer will display a list of history items, and if they select the ‘List’ tab, the drawer will display a list of generic list items.

You can modify the contents of the MyDrawer widget as needed to customize the look and feel and behaviour of the drawer for your specific use case.

Custom Flutter Drawer widget with NavigationRail and vertical labels

In the code below, the NavigationRail has been modified to have vertical labels instead of icons, and the icons have been removed. The labelType property is changed to NavigationRailLabelType.all to display the labels for all destinations. The selectedLabelTextStyle and unselectedLabelTextStyle properties are added to modify the text styling for selected and unselected items.

import 'package:flutter/material.dart';

class MyDrawer extends StatefulWidget {
const MyDrawer({Key? key}) : super(key: key);

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

class _MyDrawerState extends State<MyDrawer> {
int _selectedIndex = 0;

@override
Widget build(BuildContext context) {
return Drawer(
child: Row(
children: [
Expanded(
flex: 1,
child: NavigationRail(
backgroundColor: Colors.grey.shade200,
elevation: 12,
selectedIndex: _selectedIndex,
labelType: NavigationRailLabelType.all,
selectedLabelTextStyle: TextStyle(
color: Colors.blue,
fontSize: 30,
letterSpacing: 1,
decorationThickness: 2.5
),
unselectedLabelTextStyle: TextStyle(
color: Colors.grey,
fontSize: 20,
letterSpacing: 0.8,
),
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
leading: Container(
margin: const EdgeInsets.only(bottom: 40.0),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.0, color: Colors.grey),
)
),
child: IconButton(
icon: Icon(Icons.info_outline_rounded),
onPressed: (){print('Info Pressed');},
),
),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.only(top: 40.0),
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 1.0, color: Colors.grey),
)
),
child: IconButton(
icon: Icon(Icons.logout, color: Colors.red,),
onPressed: (){},
),
),
),
),
destinations: const [
NavigationRailDestination(
icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('Favorites')
),
),
),
NavigationRailDestination(
icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('History')
),
),
),
NavigationRailDestination(
icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.all(8.0),
child: RotatedBox(
quarterTurns: -1,
child: Text('List')
),
),
),
],
),
),
Expanded(
flex: 3,
child: selectedMenu(_selectedIndex),
),
],
),
);
}

Widget selectedMenu(index){
switch (index) {
case 0:
return _buildFavoritesListView();
case 1:
return _buildHistoryListView();
case 2:
return _buildListView();
default:
return _buildFavoritesListView();
}
}

Widget _buildFavoritesListView() {
return ListView(
children: [
ListTile(
title: Text('Favorite Book'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Favorite Song'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Favorite Movie'),
onTap: (){
//go somewhere or do something
},
),
],
);
}

Widget _buildHistoryListView() {
return ListView(
children: [
ListTile(
title: Text('My History'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Your History'),
onTap: (){
//go somewhere or do something
},
),
ListTile(
title: Text('Our History'),
onTap: (){
//go somewhere or do something
},
),
],
);
}

Widget _buildListView() {
return ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('List item $index'),
);
},
);
}
}
flutter custom drawer widget with navigationrail vertical labels

Here are the main changes made to the second code:

  1. NavigationRailLabelType.all was used instead of NavigationRailLabelType.selected in the above code. This change made all the labels in the NavigationRail visible, not just the selected one.
  2. The labels in the NavigationRail were no longer wrapped with the Icon widget as in the previous code. Instead, a Text widget was used to display the label text vertically. To achieve this, the RotatedBox widget was wrapped around the Text widget. The angle property of the RotatedBox widget was set to -90 degrees to rotate the Text widget vertically.
  3. The selectedIconTheme property was removed in the above code since the icons were not visible anymore.
  4. The selectedLabelTextStyle property was modified in the above code to give the selected label a blue colour with a font size of 30. The decorationThickness property was also set to 2.5 to make the text appear bolder.
  5. The unselectedLabelTextStyle property was also modified in the above code to give the unselected labels a grey colour with a font size of 20 and a letterSpacing of 0.8.