Flutter Getting Started: Tutorial 3 Navigation – DEVELOPPARADISE
17/07/2018

Flutter Getting Started: Tutorial 3 Navigation

Introduction

In this article I would discuss Flutter Navigation, Navigation or movement among pages is important business requirement of any application, for example almost every application required login page or option page, where various options could be set based on user input.

In simpler term, Its mean leaving one page and loading new page. Based on code, page may persist in memory or simply deleted from stack. So at most care should be taken by Developer/Architect while developing application, what should be kept in memory and what shouldn’t.

Background

If you chance to read my article Flutter Getting Started: Tutorial 2 – StateFulWidget , we will build on same application, let me summarize what I have done in that article, I have provided a TextField which is similar to TextBox in C#, and on press of save button its get added to list which also present same page. 

Now we destruct that application and create two pages out of it, One which will display the list of cities added and other page provide options for adding the cities. So this would be final result once application is completed.

Add City new UI design

Flutter Getting Started: Tutorial 3 Navigation
Here as mentioned above, form portion going into AddDataPage file and Listview which kept list of data, added by user would become part of home page. I marked two new addition with yellow square, one for invoking AddDataPage and other for going back to HomePage

Aim of Tutorial

As mentioned earlier in this article, I would be building application based on my previous article, I would be creating two pages application and in end I would be adding additional pages to showcase removing of multiple pages.

  • There would be two pages
    • Page #1: Homepage would be containing ListView, where all the user input city would be displayed
    • Page #2: AddDataPage would contain TextField from where you can add data to list ( its contain TextField, Cancel and Add RaisedButton )
  • Task#1 Basic Navigation, when we navigate between pages using known page object
  • Task#2 Sending data using Navigation object
  • Task#3 Using named route for navigation instead of page object
  • Bonus Task#4 additional page would be added to show navigation stack, and how pages persist between navigation

Overall, application would contain two pages, one would show all the text entered by user and other page would provide form to enter data.

Using the code

  1. Let start by creating new flutter project in Android Studio, Give it name Flutter3_navigation. In case you don’t know how to create flutter project, please read here 
  2. Create a package in lib directory by name pages and add two DART files
    1. adddatapage.dart : Add AddDataPage class which extend Statelesswidget
    2. homepage.dart :- Add HomePage class which extend StateFulWidget (if you don’t know about StateFulWidget, read here), also add _HomePageState which extends state<HomePage>
  3. Delete all code from main.dart, the entry point of Mobile Application, and replace with this code
    import 'package:flutter/material.dart';
    import 'package:flutter3_navigation/pages/homepage.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(        
            primarySwatch: Colors.blue
          ),
          home: new HomePage(title: 'Flutter 3 : Navigation'),
        );
      }
    }

    Explanation of Code

    • Here MyApp is our entry class for mobile application, here we returning MaterialApp object. This will provide skeleton for our app.
    • I set home object to HomePage object, which mean it would be loaded when application start running
  4. Put following code in HomePage.dart, following control have been added
    import 'package:flutter/material.dart';
    import 'package:flutter3_navigation/pages/adddatapage.dart';
    
    class HomePage extends StatefulWidget {
      final String title;
      HomePage({Key key, this.title}) : super(key: key);
    
      @override
      _HomePageState createState() => new _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(
              widget.title,
              style: new TextStyle(fontSize: 16.0),
            ),
            actions: <Widget>[
              new IconButton(
                  icon: new Icon(Icons.add),
                  onPressed: () {
                    print("Add button clicked");
                    
                  })
            ],
          ),
        );
      }}
    1. Here we create page Scaffold and create AppBar inside it
    2. Here is new addition in App Bar, I have added IconButton with display of Icons.add, This is placeholder for navigating to AddDataPage, currently we are not doing anything
      Flutter Getting Started: Tutorial 3 Navigation
  5. Now before I show you code for AddDataPage.dart, let me give you small overview of Navigator Class, we generally use following function (definition taken from Flutter website)
    1. Navigator.push – Push the given route onto the navigator that most tightly encloses the given context.
    2. Navigator.pop – Pop the top-most route off the navigator that most tightly encloses the given context.
    3. Navigator.pushNamed – Push a named route onto the navigator that most tightly encloses the given context. we are going to use it in Task#3
    4. Navigator.pushNamedAndRemoveUntil – Push the route with the given name onto the navigator that most tightly encloses the given context, and then remove all the previous routes until the predicate returns true. We are going to use in Task#4
  6. Now add function  void _onAddCityButtonPressed(BuildContext context) in HomePage.dart file and call that from  Add IconButton 
    void _onAddCityButtonPressed(BuildContext context)  {
      Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => AddDataPage())
      ) }

    Explanation of code

    • As mentioned in step 5, I am using Navigator.Push to push new page on stack, here we need to use current BuildContext and Push AddDataPage, when person click on + button in AppBar
  7. Now its turn of AddDataPage.dart, Add following code 
    import 'package:flutter/material.dart';
    
    class AddDataPage extends StatelessWidget {
      final TextEditingController _CityNameController = new TextEditingController();
    
      @override
      Widget build(BuildContext context) {
    
        return new Scaffold(
            appBar: new AppBar(
              title: new Text(
                "Add City",
                style: new TextStyle(fontSize: 16.0),
              ),
            ),
            body: new SafeArea(
              child: new Container(
                child: new Column(children: <Widget>[
                  new TextField(
                    decoration: new InputDecoration(
                      labelText: "City Name",
                    ),
                    controller: _CityNameController,
                  ),
                  new ButtonBar(
                    alignment: MainAxisAlignment.end,
                    children: <Widget>[
                      new RaisedButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: new Text("Cancel"),
                      ),
                      new RaisedButton(
                        onPressed: () {
                          Navigator.pop(context,_CityNameController.text);
                        },
                        child: new Text("Add"),
                      )
                    ],
                  )
                ]),
              ),
            ));
      }
    }

    Explanation of code

    • Here in this page, I have added a TextField, a ButtonBar with two button Cancel (which will pop the current page) and Add (Which will return whatever data entered in TextField)
    • For returning data when unloading page, you can use 2nd parameter with whatever data you want to return, In our case, CityName.
      Flutter Getting Started: Tutorial 3 Navigation

    Here we come to end of Task#1

  8. Now we are sending the data, But we haven’t write code to catch whatever coming from the other page, the answer lie in Future class, its type of object which keep track object coming in future and let you catch it linked function. I would be writing full-fledged article using async function and Future in Dart language. Its similar to Observable/ Promises in Angular. Here how we going to catch CityName sent by AddDataPage to HomePage, here is modified function look like
    void _onAddCityButtonPressed(BuildContext context)  {
      Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => AddDataPage())
      )
      .then((Object result){
        print("result from addpage ${result.toString()}");
      });
    
    }

    Explanation of code

    • then function is part of Future class, this function will be anchor to page you navigate to, and bring you data whatever sent from there.
    • There are various way to achieve this (gathering of data), I am showing only one way here, as its not fruitful to tell them before letting you know how async works in DART.
  9. Now I modified code to include ListView in the  HomePage and adding data received from AddDataPage, all explaination of below code is available in my previous article here.
    final List<Text> _lstCities = new List<Text>();
    
      Widget _getListViewFromBuilder() {
        return ListView.builder(
          itemCount: _lstCities.length,
          shrinkWrap: true,
          padding: const EdgeInsets.all(16.0),
          itemBuilder: getListItems,
        );
      }
    
    // Call back function, will called for each item in the
      Widget getListItems(BuildContext context, int index) {
        return _lstCities[index];
      }
    
      // this function would called when add city button pressed
    
      void _onAddCityButtonPressed(BuildContext context) {
        Navigator
            .push(context, MaterialPageRoute(builder: (context) => AddDataPage()))
            .then((Object result) {
          if (result == null) return;
          setState(() {
            _lstCities.add(new Text(
              "${_lstCities.length + 1} ${result.toString()}",
              textAlign: TextAlign.justify,
              style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 24.0),
            ));
          });
        });
      }

    Flutter Getting Started: Tutorial 3 Navigation
    Here we come to end of Task#2

  10. Now problem with above code, the routing is fixed, i.e. since we are navigating through providing new object for page we want load, this create problem if we want to have dynamic routes and avoid references of pages in different project ( you need to import class file, before providing page object)
  11. Now you have to modify the Main.dart with following changes, home property need to commented out, as we are relying completely on routing now,
    //home: new HomePage(title: 'Flutter 3 : Navigation'),
    routes: {
      '/' : (BuildContext build)=> new HomePage(title: 'Flutter 3 : Navigation'),
      '/adddata':(BuildContext build)=> new AddDataPage(),
    },
      initialRoute: "/"

    Explanation of code

    • Here I commented out home property, as we no longer using it.
    • Add route property, which is map of String as key and WidgetBuilder as value, as going forward we need string key to identify route we need to create
    • Now instead of Navigator.push in HomePage, use Navigator.pushNamed
      /*
       Task # 1 and 2
      
        Navigator
        .push(context, MaterialPageRoute(builder: (context) => AddDataPage()))
           */
      /*
       Task 3 and 4
       */
           Navigator.pushNamed(context,'/adddata' )
           .then((Object result) {
    • In above since we want to navigate to AddDataPage, we use it’s String key to navigate.

    Here we come to end of Task#3

  12. Now for bonus part, here we would be adding a new page by name EndPage and I would include bottom bar in both AddDataPage and EndPage.
    1. AddDataPage bottom bar would contain forward navigation to end, which will use Navigator.pushNamed to reach EndPage 
    2. EndPage bottom bar would have one home button, which navigate to home page using pushNamedAndRemoveUntil and backward button to move HomePage
  13. Now modified AddDataPage looks like, Inside page container add bottomNavigationBar and new function _getBottomNavigationBarWidget()
    bottomNavigationBar: _getbottomNavigationBarWidget(context),

    New function needed to added at the bottom of class, don’t forget to route definition for EndPage in main.dart, as mentioned in step 11

    Widget _getbottomNavigationBarWidget(BuildContext context) {
      return Container(
        color: Colors.black87,
    
        child: new ButtonBar(
          mainAxisSize: MainAxisSize.max,
          alignment: MainAxisAlignment.end,
          children: <Widget>[
            new IconButton(icon: new Icon(Icons.arrow_forward,size: 24.0,color: Colors.white,), onPressed: (){
               Navigator.pushNamed(context, "/endpage");
            })
          ],
        ),
      );
    }

    Here we added an IconButton in bottombar, on pressing the button, we navigate to endpage
    Flutter Getting Started: Tutorial 3 Navigation

  14. Add new dart file in pages folder by endpage.dart, and add EndPage class to it, since we are not managing any state inside it, we can safely derived it from StatelessWidget, code would look like this
    import 'package:flutter/material.dart';
    
    class EndPage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
    
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(
              "End Page",
              style: new TextStyle(fontSize: 16.0),
            ),
          ),
          body: new Center(
            child: new Text("Welcome to End Page",style: new TextStyle(fontSize: 24.0,fontWeight: FontWeight.bold),),
          ),
          bottomNavigationBar: _getbottomNavigationBarWidget(context),
        );
      }
    
      Widget _getbottomNavigationBarWidget(BuildContext context) {
        return Container(
          color: Colors.black87,
    
          child: new ButtonBar(
            mainAxisSize: MainAxisSize.max,
            alignment: MainAxisAlignment.end,
            children: <Widget>[
              new IconButton(icon: new Icon(Icons.arrow_back,size: 24.0,color: Colors.white,), onPressed: (){
                Navigator.pop(context);
              }),
              new IconButton(icon: new Icon(Icons.home,size: 24.0,color: Colors.white,), onPressed: (){
    
                Navigator.pushNamedAndRemoveUntil(context, "/", (Route<dynamic> route){ return false;});
              })
            ],
          ),
        );
      }
    }

    Here we have two button in BottomNavigationBar, on pressing backward button, we pop current page and move to AddDataPage. On pressing Home button, we remove all pages till we show HomePage
    Flutter Getting Started: Tutorial 3 Navigation
    Here we come to end of Task#4

  15. Here a gif file of final output
    Flutter Getting Started: Tutorial 3 Navigation

Though there are lot of things left, like Page Animation during transition etc. Hopefully I have covered all basic maneuvering in this article. Please don’t hesitate to ask any question or comment.

Points of Interest

Please go through these articles. It might give you a headway, where the wind is actually flowing:

  1. Flutter — 5 reasons why you may love it
  2. What’s Revolutionary about Flutter
  3. Why Flutter Uses Dart
  4. Github : https://github.com/thatsalok/FlutterExample/tree/master/flutter3_navigation

Flutter Tutorial

  1. Flutter Getting Started: Tutorial 1 Basics
  2. Flutter Getting Started: Tutorial 2 – StateFulWidget

Dart Tutorial

  1. DART2 Prima Plus – Tutorial 1
  2. DART2 Prima Plus – Tutorial 2 – LIST

History

  • 12-July-2018: First version