Navigation Flutter: Happiness App – Step 3

What is Navigation in Flutter? It is a routing mechanism. As a result, in a Flutter App, we go from one page to another page.

However, to accomplish this task, we need the Navigator Widget. in addition, we can also go from one page to another page by using the Router Widget.

Then, you may ask, what is the difference? Why not there is only one mechanism?

Because it depends on its complexity. As the Flutter App becomes more elaborate, or there are more pages to go and come back, the Navigation becomes tedious.

Then we need the Router API.

But, for simple App like the Happiness Calculator App that we have been building step by step, the Navigator API is enough.

What is navigation in Flutter

Consider a simple example. Suppose there is a bread on the table.

We place a second bread on top of the first bread. Next, we add the third bread on top of the second bread.

Therefor, there are three breads one above the other.

Now, someone comes and pick up the third bread from the stack of three breads.

What will happen?

The second bread will come on the top.

If we imagine Flutter pages in place of those breads, the same thing happens. The home page from where we start our journey, is our first bread.

As we keep adding pages, they place themselves one above the other.

When we use the imperative API Navigator.push, it takes us from the home page to the second page. On the contrary, when want to come back to the home page again, we use the Navigator.pop method.

Let us see how it looks. The image will say the thousands words.

Navigation Flutter_ Home page of Happiness App
Navigation Flutter_ Home page of Happiness App

This is the home page of our Happiness Calculator App. The App will count the index of Happiness by taking inputs from three factors.

When the user sets the values as we are seeing above, the result will show up in the next page.

Navigation Flutter_ result page
Navigation Flutter_ result page

This Happiness Calculator App works on simple principle. Between the three factors, when greed is too high, and other two factors, gratitude and diligence are low, the happiness index is also low,

However, if a user has low level of greed and high level of gratitude and diligence, then the user becomes a happy person.

What is Navigator.push?

During building this Flutter App, we have learned a few key concepts, such as enum, and ternary operator.

After that, we have learned to use the Flutter Slider Widget and also learned how to customise it.

Similarly, in this section, we will learn the simple navigation where we will go from the home page to the result page.

Let us take a look at the code of home page firstly. Because, we had seen our App as follows before.

Customized Slider Theme that runs across the entire Flutter App
Customised Slider Theme that runs across the entire Flutter App

We have added two more factors below the Slider Widget.

As a result, the code of Homepage has changed a lot.

In addition, the design has changed completely. And, now it looks like below.

Navigation Flutter_ Home page of Happiness App
Navigation Flutter_ Home page of Happiness App

To clarify, it would not happen if we did not change the code of home page.

Let us see the code, first. Then we will discuss how we have added the Navigation mechanism in this page.

import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:happiness_calculator/view/happiness_result.dart';

import '../model/happy_theme.dart';
import '../model/constants.dart';
import '../model/container_color.dart';

class HappinessHomePage extends StatefulWidget {
  const HappinessHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  @override
  State<HappinessHomePage> createState() => _HappinessHomePageState();
}

class _HappinessHomePageState extends State<HappinessHomePage> {
  ContainerColor? selectedContainer;
  int greed = 20;
  int gratitude = 10;
  int dilligence = 20;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: HappyTheme.shrineBrown900,
      appBar: AppBar(
        title: Text(
          widget.title,
          style: HappyTheme.appbarStyle,
        ),
        backgroundColor: HappyTheme.shrineBrown600,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: [
                expandGender(
                  ContainerColor.first,
                  'Male',
                  Icons.male,
                ),
                expandGender(
                  ContainerColor.second,
                  'Female',
                  Icons.female,
                ),
              ],
            ),
            Expanded(
              child: Container(
                margin: const EdgeInsets.all(5.0),
                alignment: Alignment.center,
                color: HappyTheme.shrineBrown600,
                width: double.infinity,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Container(
                      padding: const EdgeInsets.only(
                        top: 5.0,
                        bottom: 2.0,
                      ),
                      child: Text(
                        'GREED',
                        style: HappyTheme.appbarStyle,
                      ),
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.baseline,
                      textBaseline: TextBaseline.alphabetic,
                      children: [
                        Text(
                          greed.toString(),
                          style: HappyTheme.greedStyle,
                        ),
                      ],
                    ),
                    SliderTheme(
                      data: SliderTheme.of(context).copyWith(
                        inactiveTrackColor: HappyTheme.shrinePink50,
                        activeTrackColor: HappyTheme.shrineBackgroundWhite,
                        thumbColor: HappyTheme.shrineErrorRed,
                        overlayColor: HappyTheme.shrinePink50,
                        thumbShape: const RoundSliderThumbShape(
                          enabledThumbRadius: 15.0,
                        ),
                        overlayShape: const RoundSliderOverlayShape(
                          overlayRadius: 35.0,
                        ),
                      ),
                      child: Slider(
                        value: greed.toDouble(),
                        min: 20.0,
                        max: 100.0,
                        activeColor: activeColor,
                        inactiveColor: Colors.black26,
                        onChanged: (double newValue) {
                          setState(() {
                            greed = newValue.round();
                          });
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                expandGratitude(),
                expandDilligence(),
              ],
            ),
          ],
        ),
      ),
      bottomNavigationBar: Container(
        width: double.infinity,
        height: 60.0,
        color: HappyTheme.activeCoor,
        child: TextButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const HappinessResult(),
              ),
            );
          },
          child: Text(
            'CALCULATE',
            style: HappyTheme.appbarStyle,
          ),
        ),
      ),
    );
  }

  Expanded expandDilligence() {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.all(5.0),
        child: Container(
          alignment: Alignment.center,
          width: double.infinity,
          height: 100.0,
          color: HappyTheme.inactiveCoor,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                'DILLIGENCE',
                style: TextStyle(
                  fontSize: 20.0,
                  color: Colors.white,
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                mainAxisSize: MainAxisSize.min,
                children: [
                  FloatingActionButton(
                    heroTag: 'btn3',
                    onPressed: () {
                      setState(() {
                        dilligence--;
                      });
                    },
                    child: const Icon(
                      Icons.minimize,
                    ),
                  ),
                  Container(
                    padding: const EdgeInsets.only(
                      left: 10.0,
                      right: 10.0,
                    ),
                    child: Text(
                      dilligence.toString(),
                      style: HappyTheme.dilligenceStyle,
                    ),
                  ),
                  FloatingActionButton(
                    heroTag: 'btn4',
                    onPressed: () {
                      setState(() {
                        dilligence++;
                      });
                    },
                    child: const Icon(
                      Icons.add,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  Expanded expandGratitude() {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.all(5.0),
        child: Container(
          alignment: Alignment.center,
          width: double.infinity,
          height: 100.0,
          color: HappyTheme.inactiveCoor,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                'GRATITUDE',
                style: TextStyle(
                  fontSize: 20.0,
                  color: Colors.white,
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                mainAxisSize: MainAxisSize.min,
                children: [
                  FloatingActionButton(
                    heroTag: 'btn1',
                    onPressed: () {
                      setState(() {
                        gratitude--;
                      });
                    },
                    child: const Icon(
                      Icons.minimize,
                    ),
                  ),
                  Container(
                    padding: const EdgeInsets.only(
                      left: 10.0,
                      right: 10.0,
                    ),
                    child: Text(
                      gratitude.toString(),
                      style: HappyTheme.dilligenceStyle,
                    ),
                  ),
                  FloatingActionButton(
                    heroTag: 'btn2',
                    onPressed: () {
                      setState(() {
                        gratitude++;
                      });
                    },
                    child: const Icon(
                      Icons.add,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  Expanded expandGender(
      ContainerColor? containerColor, String gender, IconData genderIcon) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.all(5.0),
        child: GestureDetector(
          onTap: () {
            setState(() {
              selectedContainer = containerColor;
            });
          },
          child: Container(
            alignment: Alignment.center,
            color: selectedContainer == containerColor
                ? HappyTheme.activeCoor
                : HappyTheme.inactiveCoor,
            width: double.infinity,
            height: 100.0,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(
                  genderIcon,
                  size: 80.0,
                  color: Colors.white,
                ),
                Text(
                  gender,
                  style: HappyTheme.genderStyle,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Now, we can either make any value lower, or make it higher. Meanwhile, we can press the “CALCULATE” button, and it takes us to the result page.

In the bottom navigation bar property, in a Text Button we have used the Navigator.push method.

bottomNavigationBar: Container(
        width: double.infinity,
        height: 60.0,
        color: HappyTheme.activeCoor,
        child: TextButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const HappinessResult(),
              ),
            );
          },
          child: Text(
            'CALCULATE',
            style: HappyTheme.appbarStyle,
          ),
        ),
      ),

Certainly, the Happiness result page displays a dummy result. Because we have only finished the design part. Besides, we make an easy navigation defining the route.

As we see in the above code, Navigator.push method passes two parameters.

The first parameter is the context. And the second parameter is the MaterialPageRoute class constructor.

In this constructor the builder property expects that the context will return the second page.

What is Navigtor.pop?

Let us go back to the previous examples. The Navigator.push method basically puts one page over the other.

However, the Navigator.pop method does the opposite. It takes out the page from the top.

As a result, the page below will again emerge.

This is the most basic routing mechanism where we go to one page, and come back to the home page.

Here is the result page code that will explain how this routing mechanism works.

import 'package:flutter/material.dart';
import 'package:happiness_calculator/model/happy_theme.dart';

class HappinessResult extends StatelessWidget {
  const HappinessResult({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: HappyTheme.shrineBrown900,
      appBar: AppBar(
        backgroundColor: HappyTheme.shrineBrown600,
        title: Text(
          'How Happy You Are!',
          style: HappyTheme.appbarStyle,
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.all(18.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              margin: const EdgeInsets.all(5.0),
              child: Text(
                'Result',
                style: HappyTheme.resultStyle,
              ),
            ),
            Container(
              width: double.infinity,
              padding: const EdgeInsets.only(
                top: 10.0,
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    '10',
                    style: HappyTheme.happinessIndexStyle,
                  ),
                  Text(
                    'You are very unhappy. Reduce greed, increase gratitude and dilligence',
                    style: HappyTheme.happinessResultStyle,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: Container(
        width: double.infinity,
        height: 60.0,
        color: HappyTheme.activeCoor,
        child: TextButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text(
            'RE-CALCULATE',
            style: HappyTheme.appbarStyle,
          ),
        ),
      ),
    );
  }
}

In the same vein, we use the bottom navigation bar, and a Text Button.

In addition, we define the routing mechanism by using Navigator.pop method.

onPressed: () {
            Navigator.pop(context);
          },

As we press the bottom navigation bar button, It picks up the result page. Therefore, the home page emerges from the below.

If you want to clone this step, please visit this branch of the GitHub repository.

Certainly, this routing mechanism is simple. Moreover, it has not sent any data from the home page to the result page.

But we can do that.

In fact, in the next section we will pass the data from home page to the result page.

And based on that data, we will calculate the Happiness Index.

So stay tuned.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter

Comments

3 responses to “Navigation Flutter: Happiness App – Step 3”

  1. […] Further, we have absorbed the basic route mechanism. […]

  2. […] In our previous Flutter App, “Happiness Calculator”, we have seen how we can use simple Navigation. […]

  3. […] How does the Flutter navigation work? We have discussed in a previous lesson. […]

Leave a Reply