Flutter And Dart - Drawer Navigation

This article explains how to navigate using drawer in Android and iOS using Flutter as the framework and Dart as the language.

In this post, we will learn how to navigate from one page to another using drawer menu options. As we all know, the app is just a widget in Flutter or a description of the UI portrayed. Each class which we create by extending StatelessWidget class gives us the ability to describe the part of our UI by building a constellation of another widget that describes our User Interface more loosely coupled.
 
So, the example which I will be implementing here is going to have one drawer to the top left of the screen on the app bar, which will have some menus. These menus will open UI respectively on user click. Below are the screenshots for the same,
 
                        
 
Follow the below steps to implement this,
  • Run VSCode and press ctrl+shift+p and run Doctor Command to make sure everything is up and running. See if Doctor command runs without issue.
  • Press ctrl+shift+p and type 'New'. You will see the 'Flutter: New Project' command suggested in search, Like Below:
  • It will ask you to give the name for your project. Name it as "InkWell_drawer_Navigation".
  • Make sure you have your Emulator setup done. Start your emulator.
  • Now, we must make changes in our Main.Dart code file. Find this file in the project which we have just created.
At the end of this file create InkWellDrawer and CustomListTile class and add the following code:
  1. class InkWellDrawer extends StatelessWidget {  
  2.   @override  
  3.   Widget build (BuildContext ctxt) {  
  4.     return new Drawer(  
  5.         child: ListView(  
  6.           children: <Widget>[  
  7.             DrawerHeader(  
  8.               decoration: BoxDecoration(  
  9.                 gradient: LinearGradient(colors: <Color>[  
  10.                   Colors.lightBlue,  
  11.                   Colors.blue  
  12.                 ])  
  13.               ),  
  14.               child: Container(  
  15.                 child: Column(  
  16.                   children: <Widget>[  
  17.                     Material(  
  18.                       borderRadius: BorderRadius.all(Radius.circular(50.0)),  
  19.                       elevation: 10,  
  20.                       child: Padding(padding: EdgeInsets.all(8.0),  
  21.                       child: Image.asset("assets/images/drawerHeader.png", height: 90, width: 90),  
  22.                       ),  
  23.                     ),  
  24.                     Text('Flutter', style: TextStyle(color: Colors.white, fontSize: 25.0),)  
  25.                   ],  
  26.                 ),  
  27.               )),  
  28.             CustomListTile(Icons.person, 'Profile', ()=>{  
  29.               Navigator.pop(ctxt),  
  30.               Navigator.push(ctxt,   
  31.               new MaterialPageRoute(builder: (ctxt) => new HomeView())  
  32.               )  
  33.             }),  
  34.             CustomListTile(Icons.notifications, 'Notification', ()=>{  
  35.               Navigator.pop(ctxt),  
  36.               Navigator.push(ctxt,   
  37.               new MaterialPageRoute(builder: (ctxt) => new NotificationView())  
  38.               )  
  39.             }),  
  40.             CustomListTile(Icons.settings, 'Settings', ()=>{}),  
  41.             CustomListTile(Icons.lock'Log Out', ()=>{}),  
  42.           ],  
  43.   
  44.         ),  
  45.     );  
  46.   }  
  47. }  
  48.   
  49. class CustomListTile extends StatelessWidget{  
  50.   
  51.   final IconData icon;  
  52.   final  String text;  
  53.   final Function onTap;  
  54.   
  55.   CustomListTile(this.icon, this.text, this.onTap);  
  56.   @override  
  57.   Widget build(BuildContext context){  
  58.     //ToDO   
  59.     return Padding(  
  60.       padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0),  
  61.       child:Container(  
  62.         decoration: BoxDecoration(  
  63.           border: Border(bottom: BorderSide(color: Colors.grey.shade400))  
  64.         ),  
  65.       child: InkWell(  
  66.         splashColor: Colors.orangeAccent,  
  67.         onTap: onTap,  
  68.         child: Container(  
  69.           height: 40,  
  70.           child: Row(  
  71.             mainAxisAlignment : MainAxisAlignment.spaceBetween,  
  72.             children: <Widget>[  
  73.             Row(children: <Widget>[  
  74.             Icon(icon),  
  75.             Padding(  
  76.               padding: const EdgeInsets.all(8.0),  
  77.             ),  
  78.             Text(text, style: TextStyle(  
  79.               fontSize: 16  
  80.             ),),  
  81.           ],),  
  82.         Icon(Icons.arrow_right)  
  83.       ],)  
  84.         )   
  85.     ),  
  86.     ),  
  87.     );  
  88.   }  
  89. }  
These above classes will add Drawer to our application. In this, we are using the InkWell class to make it look more attractive. So, if we look at our InkWellDrawer class then you will see that we have CustomListTile Class object which takes three parameters, first is Type of Icon. Second is Text for the menu and third is Action for an event onTab.
 
Inside this CustomListTile class, we have implemented our menu.
 
We have a rectangular area which responds to our touch on the UI element. So, Splash is the grayed animated background which gets enable when we touch our UI Element, especially it happens with buttons.
 
Now, we will add our two views: ProfileView and NotificationView. Add these views in our main.dart class,
  1. class NotificationView extends StatelessWidget {  
  2.   @override  
  3.   Widget build(BuildContext context) {  
  4.     return Scaffold(  
  5.       drawer: InkWellDrawer(),  
  6.       appBar: new AppBar(title: new Text("Notifications"),),  
  7.       body: ListView(  
  8.         children: <Widget>[  
  9.           Stack(  
  10.             children: <Widget>[  
  11.               Padding(  
  12.                 padding: const EdgeInsets.only(bottom: 30.0),  
  13.                 child: ClipPath(  
  14.                   clipper: ClippingClass(),  
  15.                   child: Container(  
  16.                     height: 130.0,  
  17.                     decoration: BoxDecoration(color: Colors.blue),  
  18.                   ),  
  19.                 ),  
  20.               ),  
  21.               Positioned.fill(  
  22.                 child: Align(  
  23.                   alignment: Alignment.bottomCenter,  
  24.                   child: Container(  
  25.                     height: 90.0,  
  26.                     width: 90.0,  
  27.                     decoration: BoxDecoration(  
  28.                       image: new DecorationImage(  
  29.                         image: new AssetImage("assets/images/Notification.jpg"),  
  30.                       ) ,  
  31.                       shape: BoxShape.circle,  
  32.                       border: Border.all(  
  33.                         color: Colors.white,  
  34.                         width: 5.0,  
  35.                       ),  
  36.                     ),  
  37.                   ),  
  38.                 ),  
  39.               )  
  40.             ],  
  41.           ),  
  42.           Container(  
  43.             padding: const EdgeInsets.only(top: 32.0),  
  44.             child: Column(  
  45.               children: <Widget>[  
  46.                 Text('Hello Bhupesh!!!'),  
  47.                 Padding(  
  48.                   padding:  
  49.                       const EdgeInsets.only(top: 22.0, left: 42.0, right: 42.0),  
  50.                   child: Center(child: Text('Welcome to your Notification View!!!')),  
  51.                 )  
  52.               ],  
  53.             ),  
  54.           ),  
  55.         ],  
  56.       ),  
  57.     );  
  58.   }  
  59. }  
  60. class ProfileView extends StatelessWidget {  
  61.   @override  
  62.   Widget build(BuildContext context) {  
  63.     return Scaffold(  
  64.       drawer: InkWellDrawer(),  
  65.       appBar: new AppBar(title: new Text("Profile"),),  
  66.       body: ListView(  
  67.         children: <Widget>[  
  68.           Stack(  
  69.             children: <Widget>[  
  70.               Padding(  
  71.                 padding: const EdgeInsets.only(bottom: 30.0),  
  72.                 child: ClipPath(  
  73.                   clipper: ClippingClass(),  
  74.                   child: Container(  
  75.                     height: 130.0,  
  76.                     decoration: BoxDecoration(color: Colors.blue),  
  77.                   ),  
  78.                 ),  
  79.               ),  
  80.               Positioned.fill(  
  81.                 child: Align(  
  82.                   alignment: Alignment.bottomCenter,  
  83.                   child: Container(  
  84.                     height: 90.0,  
  85.                     width: 90.0,  
  86.                     decoration: BoxDecoration(  
  87.                       image: new DecorationImage(  
  88.                         image: new AssetImage("assets/images/bhupesh.jpg"),  
  89.                       ) ,  
  90.                       shape: BoxShape.circle,  
  91.                       border: Border.all(  
  92.                         color: Colors.white,  
  93.                         width: 5.0,  
  94.                       ),  
  95.                     ),  
  96.                   ),  
  97.                 ),  
  98.               )  
  99.             ],  
  100.           ),  
  101.           Container(  
  102.             padding: const EdgeInsets.only(top: 32.0),  
  103.             child: Column(  
  104.               children: <Widget>[  
  105.                 Text('Hello Bhupesh!!!'),  
  106.                 Padding(  
  107.                   padding:  
  108.                       const EdgeInsets.only(top: 22.0, left: 42.0, right: 42.0),  
  109.                   child: Center(child: Text('Welcome to your Profile, Although, you do not have much to say in your profile apart from your cool image!!!')),  
  110.                 )  
  111.               ],  
  112.             ),  
  113.           ),  
  114.         ],  
  115.       ),  
  116.     );  
  117.   }  
  118. }  
  119. class ClippingClass extends CustomClipper<Path> {  
  120.   @override  
  121.   Path getClip(Size size) {  
  122.     var path = Path();  
  123.     path.lineTo(0.0, size.height - 80);  
  124.     path.quadraticBezierTo(  
  125.       size.width / 4,  
  126.       size.height,  
  127.       size.width / 2,  
  128.       size.height,  
  129.     );  
  130.     path.quadraticBezierTo(  
  131.       size.width - (size.width / 4),  
  132.       size.height,  
  133.       size.width,  
  134.       size.height - 80,  
  135.     );  
  136.     path.lineTo(size.width, 0.0);  
  137.     path.close();  
  138.     return path;  
  139.   }  
  140.   
  141.   @override  
  142.   bool shouldReclip(CustomClipper<Path> oldClipper) => false;  
  143. }  
NotificationView and ProfileView are the stateless classes which we have used to accomplish our views for profile and notification. We could have had more generic code because both classes are using the same theme for UI but for more clarity, I didn’t make it more generic so you could understand the code and implement the same and then make it more generic.
 
Both classes are using the same Drawer which we have assigned using InkwellDrawer() function to drawer object. InkWell is our class which we have used earlier for implementing our drawer. It is good to have drawer object to be the same drawer to make drawer available in all views. Otherwise, it wouldn’t be available to all views and we will find hard to navigate because we must go back to the home page and then we needed to click on the menu in the drawer.
 
For Clipper, we have one more class for customizing our clipper which we are doing in ClippingClass.
 
Once you have implemented all these classes then go to the main build method and write the below code,
  1. @override  
  2.   Widget build(BuildContext context) {  
  3.     // This method is rerun every time setState is called, for instance as done  
  4.     // by the _incrementCounter method above.  
  5.     //  
  6.     // The Flutter framework has been optimized to make rerunning build methods  
  7.     // fast, so that you can just rebuild anything that needs updating rather  
  8.     // than having to individually change instances of widgets.  
  9.     return Scaffold(  
  10.       appBar: AppBar(  
  11.         // Here we take the value from the MyHomePage object that was created by  
  12.         // the App.build method, and use it to set our appbar title.  
  13.         title: Text(widget.title),  
  14.       ),  
  15.       drawer: InkWellDrawer(),  
  16.       body: Center(  
  17.         // Center is a layout widget. It takes a single child and positions it  
  18.         // in the middle of the parent.  
  19.         child: Column(  
  20.           // Column is also layout widget. It takes a list of children and  
  21.           // arranges them vertically. By default, it sizes itself to fit its  
  22.           // children horizontally, and tries to be as tall as its parent.  
  23.           //  
  24.           // Invoke "debug painting" (press "p" in the console, choose the  
  25.           // "Toggle Debug Paint" action from the Flutter Inspector in Android  
  26.           // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)  
  27.           // to see the wireframe for each widget.  
  28.           //  
  29.           // Column has various properties to control how it sizes itself and  
  30.           // how it positions its children. Here we use mainAxisAlignment to  
  31.           // center the children vertically; the main axis here is the vertical  
  32.           // axis because Columns are vertical (the cross axis would be  
  33.           // horizontal).  
  34.           mainAxisAlignment: MainAxisAlignment.center,  
  35.           children: <Widget>[  
  36.             Text(  
  37.               'You have pushed the button this many times:',  
  38.             ),  
  39.             Text(  
  40.               '$_counter',  
  41.               style: Theme.of(context).textTheme.display1,  
  42.             ),  
  43.           ],  
  44.         ),  
  45.       ),  
  46.       floatingActionButton: FloatingActionButton(  
  47.         onPressed: _incrementCounter,  
  48.         tooltip: 'Increment',  
  49.         child: Icon(Icons.add),  
  50.       ), // This trailing comma makes auto-formatting nicer for build methods.  
  51.     );  
  52.   }  
  53. }  
I didn't remove the counter logic code on the home page which comes by default when we create Flutter project. You can remove that to have nothing on your home page or you can keep that. You will notice that we have used our custom drawer for the drawer object. Once you have done these changes, launch your emulator. Press ctrl+shift+p and type Flutter: Launch Emulator. Make sure you have one. Now press F5 to run the application. You should be able to see the drawer which we implemented and you should be able to navigate to the profile and navigation views.