DEV Community

loading...
Cover image for Let's Build a BMI Calculator App with Flutter Part - 2

Let's Build a BMI Calculator App with Flutter Part - 2

codingmonkey profile image Coding Monkey ใƒป7 min read

Hey All๐Ÿ‘‹,

I am writing this post in continuation of the previous post where I started creating a BMI Calculator app. which is going to see something like this

Input Page Result Page

And stopped when our App looked like this

Things To Be Implemented

So as we continue our app development now we need to fill 2 containers to get the weight data and the age of the user.

  • RoundIconButton custom widget created using the RawMaterialButton
  • Routes for page navigation
  • GestureDetector to detect any user interaction with our widget
  • BMI calculation functions

Building App

Add Weight Input Widget

So as we have collected the user height data now let's collect users weight data but this time around we are going to use the button to get data instead of the sliders.

          Expanded(
            child: Row(
              children: [
                Expanded(
                  child: new ReusableCard(
                    colour: kActiveCardColor,
                    cardChild: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'WEIGHT',
                          style: kLabelStyle,
                        ),
                        Text(
                          weight.toString(),
                          style: kNumberTextStyle,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            RoundIconButton(
                              icon: FontAwesomeIcons.minus,
                              onPressed: () {
                                setState(() {
                                  if (weight > 30) weight--;
                                });
                              },
                            ),
                            SizedBox(
                              width: 10.0,
                            ),
                            RoundIconButton(
                              icon: FontAwesomeIcons.plus,
                              onPressed: () {
                                setState(() {
                                  weight++;
                                });
                              },
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
Enter fullscreen mode Exit fullscreen mode
class RoundIconButton extends StatelessWidget {
  RoundIconButton({@required this.icon, @required this.onPressed});
  final IconData icon;
  final Function onPressed;
  @override
  Widget build(BuildContext context) {
    return RawMaterialButton(
      child: Icon(icon),
      onPressed: () {
        onPressed();
      },
      shape: CircleBorder(),
      fillColor: Color(0xFF4C4F5E),
      constraints: BoxConstraints.tightFor(
        width: 56.0,
        height: 56.0,
      ),
      elevation: 6.0,
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

To the Next Expanded widget in the container now we have to add the required widgets to have the text weight then the weight data below it the positive and negative buttons to change the weight value we take the most of the info from the previously created widget such as the text style.

Also, we have created a new class to get those round buttons to increase or decrease the weight. Here we are going to create the button using the RawMaterialButton which is the base widget for all the button widgets in the flutter.

In this widget, we taking 2 data one in the icon for the button and onPressed method which tells us what to do when the user clicks on that button. Also some Styling for the button such as the shape of the button elevation and colour of the button.

Same created custom RoundIconButton can be used for the age container. Also, let's move this class to a new file naming it round_button_icon.

               Expanded(
                  child: new ReusableCard(
                    colour: kActiveCardColor,
                    cardChild: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'AGE',
                          style: kLabelStyle,
                        ),
                        Text(
                          age.toString(),
                          style: kNumberTextStyle,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            RoundIconButton(
                              icon: FontAwesomeIcons.minus,
                              onPressed: () {
                                setState(() {
                                  if (age > 1) age--;
                                });
                              },
                            ),
                            SizedBox(
                              width: 10.0,
                            ),
                            RoundIconButton(
                              icon: FontAwesomeIcons.plus,
                              onPressed: () {
                                setState(() {
                                  age++;
                                });
                              },
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
Enter fullscreen mode Exit fullscreen mode

So this was simple since the only thing we had to change was changing all the place where we weighted age ๐Ÿ˜….

Implement Button To Calculate BMI

So let's create a button upon clicking the button the BMI calculation will be done so let's do it

          Container(
            margin: EdgeInsets.only(top: 10.0),
            color: kBottomContainerColor,
            height: kBottomContainerHeight,
            width: double.infinity,
          )
Enter fullscreen mode Exit fullscreen mode

As we already got many Custom styles implemented so we didn't have to make any changes the global variable to be implemented are

import 'package:flutter/material.dart';

const kBottomContainerHeight = 80.0;
const kBottomContainerColor = Color(0xFFEB1555);
const kActiveCardColor = Color(0xFF1D1E33);
const kInActiveCardColor = Color(0xFF111328);

const kLabelStyle = TextStyle(
  fontSize: 18.0,
  color: Color(0xFF8D8E98),
);

const kNumberTextStyle = TextStyle(
  fontSize: 50.0,
  fontWeight: FontWeight.w900,
);

const kLargeButtonTextStyle = TextStyle(
  fontSize: 25.0,
  fontWeight: FontWeight.bold,
);
Enter fullscreen mode Exit fullscreen mode

Now our input page looks like this

Now it's time to add some logic so that when we click on this button it will take us to result page where we show the result of the BMI calculations

But first, to use the multiple pages first we have to mention the routes we are going to use in the MaterialApp widget under the routes parameter.

MaterialApp(
      theme: ThemeData.dark().copyWith(
        primaryColor: Color(0xFF0A0E21),
        scaffoldBackgroundColor: Color(0xFF0A0E21),
      ),
      home: InputPage(),
      routes: {
        '/result': (context) => ResultPage(),
      },
    );
Enter fullscreen mode Exit fullscreen mode

In the MaterialApp widget app we mention that when we use the /result route then ResultPage() should be loaded.

Now let's use this route by implementing the GestureDetector on the newly created button at the bottom of the app.

          GestureDetector(
            onTap: () {
              Navigator.pushNamed(context, '/result');
            },
            child: Container(
              child: Text(
                'CALCULATE',
                style: kLabelStyle,
              ),
              margin: EdgeInsets.only(top: 10.0),
              color: kBottomContainerColor,
              height: kBottomContainerHeight,
              width: double.infinity,
            ),
Enter fullscreen mode Exit fullscreen mode

As you can see when we click on the button we are passing the route /result which in turns load the ResultPage class for us but we haven't created any ResultPage class so now let's create a basic class with Scaffold widget

import 'package:flutter/material.dart';

class ResultPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('BMI CALCULATOR'),
        ),
        body: Text('Hello'));
  }
}
Enter fullscreen mode Exit fullscreen mode

So as of now when clicks on the Calculate button we will be displayed with hello ๐Ÿ˜Ž in the top left side of the app. Now let's start implementing our result page design.

import 'package:BMI_calculator/reusable_card.dart';
import 'package:BMI_calculator/constant.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../components/bottom_button.dart';

class ResultPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BMI CALCULATOR'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: Container(
              alignment: Alignment.bottomLeft,
              padding: EdgeInsets.all(15.0),
              child: Text(
                'Your result',
                style: kTitleTextStyle,
              ),
            ),
          ),
          Expanded(
            flex: 5,
            child: ReusableCard(
              colour: kActiveCardColor,
              cardChild: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Expanded(
                    child: Center(
                      child: Text('OVERWEIGHT', style: kResultTextStyle),
                    ),
                  ),
                  Expanded(
                    child: Text(
                      '28.4',
                      style: kBMITextStyle,
                    ),
                  ),
                  Expanded(
                    child: Text(
                      'You have more weight than normal body weight. Try exercising more.',
                      textAlign: TextAlign.center,
                      style: kBodyTextStyle,
                    ),
                  ),
                ],
              ),
            ),
          ),
          BottomButton(
            buttonText: 'RE - CALCULATE',
            route: '/',
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

So I have provided the whole code of our result page design since most of our code is repeated which we did for the input page with few changes such as flex value ๐Ÿ˜…(it's been a long post so just want to complete it fast ๐Ÿ˜ฃ sorry).

Time To Create Brain of The App

So as we are done with the design part now it's time to give some brainpower to our app so what we want to do now is just to take the height and weight of the user calculate the BMI value based on that and some interpretation based on the BMI value.

import 'dart:math';

class CalculatorBrain {
  CalculatorBrain({this.height, this.weight});

  final int height;
  final int weight;
  double _bmi;

  String calculateBMI() {
    _bmi = weight / pow(height / 100, 2);
    return _bmi.toStringAsFixed(1);
  }

  String getResult() {
    if (_bmi >= 25) {
      return 'Overweight';
    } else if (_bmi > 18.5) {
      return 'Normal';
    } else {
      return 'Underweight';
    }
  }

  String getInterpretation() {
    if (_bmi >= 25) {
      return 'You have higher than normal body weight. Try exercising more.';
    } else if (_bmi > 18.5) {
      return 'You have a normal body weight. Good job!.';
    } else {
      return 'You have lower than normal body weight. You can eat bit more.';
    }
  }
}      
Enter fullscreen mode Exit fullscreen mode

so now we have created a total of 3 methods one to calculate BMI another to check if your is overweight or underweight and the last one to pass interpretation to the user based on BMI value.

So let's make those change to use these methods in our app

          BottomButton(
            buttonText: 'CALCULATE',
            onTap: () {
              CalculatorBrain calc =
                  CalculatorBrain(height: height, weight: weight);
              Navigator.pushNamed(
                context,
                ResultPage.routeName,
                arguments: ResultPage(
                  bmiResult: calc.calculateBMI(),
                  resultText: calc.getResult(),
                  interpretation: calc.getInterpretation(),
                ),
              );
            },
          ),
Enter fullscreen mode Exit fullscreen mode

Also Now let's use those methods to display our result page.

import 'package:BMI_calculator/reusable_card.dart';
import 'package:BMI_calculator/constant.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../bottom_button.dart';

class ResultPage extends StatelessWidget {
  static const routeName = '/result';
  ResultPage({this.bmiResult, this.resultText, this.interpretation});

  final String bmiResult;
  final String resultText;
  final String interpretation;
  @override
  Widget build(BuildContext context) {
    final ResultPage resultPage = ModalRoute.of(context).settings.arguments;
    return Scaffold(
      appBar: AppBar(
        title: Text('BMI CALCULATOR'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: Container(
              alignment: Alignment.bottomLeft,
              padding: EdgeInsets.all(15.0),
              child: Text(
                'Your result',
                style: kTitleTextStyle,
              ),
            ),
          ),
          Expanded(
            flex: 5,
            child: ReusableCard(
              colour: kActiveCardColor,
              cardChild: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Expanded(
                    child: Center(
                      child: Text(
                        resultPage.resultText,
                        style: kResultTextStyle,
                      ),
                    ),
                  ),
                  Expanded(
                    child: Text(
                      resultPage.bmiResult,
                      style: kBMITextStyle,
                    ),
                  ),
                  Expanded(
                    child: Text(
                      resultPage.interpretation,
                      textAlign: TextAlign.center,
                      style: kBodyTextStyle,
                    ),
                  ),
                ],
              ),
            ),
          ),
          BottomButton(
            buttonText: 'RE - CALCULATE',
            onTap: () {
              Navigator.pushNamed(
                context,
                '/',
              );
            },
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

First, let's create a constructor which takes the BMI result, result text and interpretations. also we are going to implement navigation to input page when use click on the RECALCULATE button.

You can get the complete code from this repo

Thank you

So by Now you also might have the BMI calculator app in your phone. Also, there are so many things that could be added such as using the gender data we are storing and age of the user. Also, we can add more comprehensive interpretation to the user and many more.

It was quite a long post which was divided into 2 parts. If you liked it leave like or comment (Both are fine by me). If you find any mistakes do leave a comment for me so I can correct myself also comments on improving my article are most welcomed. ๐Ÿ˜

Soon I will come with a new post as I continue to learn the flutter by following the Angela Yu courses.

Discussion (0)

pic
Editor guide