Flutter Beginners’ Guide: Crafting Your First BMI Calculator App |Part 1
Welcome to the exciting world of Flutter development! In this article, I’ll take you through my journey of creating a BMI Calculator app using Flutter. Whether you’re a newcomer to Flutter or have some prior experience, I invite you to join me as we delve into the process together. Let’s dive in and uncover the valuable insights we can gain along the way!
To create a new Flutter project, you can use the flutter create
command followed by the name of your project. Here's how you can do it:
flutter create bmi_calculator
Open bmi_calculator project folder in vscode. Since we are creating from scratch, delete all the content of main.dart
Before getting into coding , if you need to know about the files and folder structure read this article. https://www.geeksforgeeks.org/flutter-file-structure/
Let’s kickstart our Flutter project by establishing the entry point with the main
function. In Flutter, the main
function acts as the driver function, initiating the execution of our application. Within the main
function, we utilize the runApp
function to attach a widget to the screen, serving as the root widget of our application. While runApp
requires a widget as its argument, we'll pass MyApp
, which we'll create shortly. Although it may initially display an error due to the absence of the MyApp
widget, we'll promptly address this in the subsequent steps of our development process. Let's proceed to define MyApp
and bring our BMI Calculator app to life!
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
Now, let’s proceed to create our root widget, MyApp
. In Flutter, most custom widgets are implemented as classes that extend either StatelessWidget
or StatefulWidget
. For MyApp
, we'll create a class that extends StatelessWidget
. Within this class, we'll define a build()
method, which encapsulates the UI components to be displayed on the screen.
In this implementation, we’ll return a MaterialApp
widget, an integral part of Flutter that leverages Material Design principles for the application's user interface. Additionally, we'll set the home
property of MaterialApp
to HomePage
, the subsequent widget we'll create. This straightforward approach establishes HomePage
as the initial page displayed when the application is launched. Let's proceed to define MyApp
and lay the foundation for our BMI Calculator app.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomePage(),);
}
}
Now, let’s proceed to create our home page. Begin by creating a new file named home_page.dart
in the same location as main.dart
. Alternatively, you can choose to define the HomePage
widget within the same file as main.dart
, depending on your project structure preferences.
Within home_page.dart
, we'll define a class named HomePage
that extends StatefulWidget
. Unlike MyApp
, HomePage
requires state management, indicating that we may need to rebuild the page to display different UI elements at various points in time.
The HomePage
widget consists of two classes: HomePage
, representing the widget itself, and _HomePageState
, representing the state of HomePage
. The build()
method, responsible for defining the UI components, resides within the state class. Let's proceed to define HomePage
and its corresponding state to further advance our BMI Calculator app development.
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
Ensure home_page.dart
is imported into main.dart
if HomePage
is defined in a separate file.
import 'package:bmi_calculator/home_page.dart';
Now, let’s run our app using the flutter run
command.
flutter run
Upon running the app, you’ll be prompted to select a device from the list of connected devices. Depending on the platform you’re targeting, choose the appropriate device. For simplicity, let’s proceed with Chrome. This will open a Chrome window with no content displayed yet. However, there’s no need to worry as we’re about to create the user interface (UI) for our app. Let’s move forward and design the UI components for our BMI Calculator app.
After initiating the app, you’ll notice a message in the terminal, typically highlighted in red, indicating that any changes made to the UI will require a refresh to reflect them on your device (in this case, the browser). To trigger a refresh, simply press the ‘r’ or ‘R’ key in the terminal. This quick shortcut ensures that any modifications you make to the UI are promptly updated and visible on your device. Let’s keep this in mind as we proceed with designing and refining our BMI Calculator app’s user interface.
User Interface
Now lets discuss about the minimal user interface required for our bmi calculator. What all be need ?Lets list down.
We need two UIs , one to take inputs and the other to show the outputs.
Input Ui
1. User input field for height
2. User input field for Weight
3. A button to initiate calculation
Output Ui
- the BMI value
State
We created the HomePage as StatefulWidget and mentioned that a state will be associated with it. What is the state here ?
Lets put it this way, what is thing that decides which UI should be rendered on the screen ? Yes , its the BMI value.
If we have BMI value → display the BMI value (output ui) ,
otherwise → ui to collect the details to calculate the bmi value(input ui).
Fields and Methods
Lets now list down what are the methods and fields
Fields and datatypes
bmiValue : double → to store bmi value
heightController, weightController : TextEditingController → to access the value in corresponding text field
Methods
calculateBMI() → to calculate the bmi and update the ui
checkAnother() → to reset the values and check again
buildInputUi() → to build the input ui
buildOutputUi() → to build the output ui
Lets Start Coding
Now we have idea on the UIs, State and associated fields and methods to build out application.
Lets define the fields inside the _HomePageState,
class _HomePageState extends State<HomePage> {
...
...
double? bmiValue;
TextEditingController heightController = TextEditingController();
TextEditingController weightController = TextEditingController();
...
...
}
Now lets define the methods one by one
buildInputUi()
the input ui requirement can be build with a Column ,TextFields for height and weight and a MaterailButton.To keep the UI simple and functional, we’ll implement only the essential components necessary for the BMI calculation functionality.
Widget buildInputUi() {
return Column(
children: [
TextField(
controller: heightController,
decoration: InputDecoration(hintText: "Height in cm"),
),
TextField(
controller: weightController,
decoration: InputDecoration(hintText: 'Weight in Kg'),
),
MaterialButton(
color: Colors.greenAccent,
onPressed: calculateBMI,
child: Text("Calculate BMI"),
)
],
);
}
Column → takes a list of widgets as children and render it one below the other
TextField → allows user to input values and allows us to attach a controller to it, the user input value can be accessed with that controller. The decoration property allows us to modify the appearance of the text field with hint, label, border etc
MaterialButton → display a button and have onPressed property where we can give the function to be executed when user clickes on the button. The text or icons to be displayed on the button can be passes as child. Here, its calculateBMI() , we will create that function later
buildOutputUi()
Output ui is nothing different from input ui. We use a Column, Text to display the value and a MaterialButton to reset and check another.
Widget buildOutputUi() {
return Column(
children: [
Text("$bmiValue"),
MaterialButton(
color: Colors.greenAccent,
onPressed: checkAnother,
child: Text("Check Another"),
)
],
);
}
calculateBMI()
When calculating BMI, we use the formula: weight (kg) / [height (m)]². Since the values accessed from the text editing controller are strings, we parse them into double data types. It’s important to note the “!” operator, indicating to Flutter that we’re asserting the non-nullability of the value returned by double.parse()
. While proper exception handling should be included, we'll defer this for now.
Later on, we'll revisit and refine this function as needed.
void calculateBMI() {
//BMI Formula: weight (kg) / [height (m)]^2
double h = double.tryParse(heightController.text)!/100;
double w = double.tryParse(weightController.text)!;
bmiValue = w / (h*h);
print("BMI : $bmiValue");
}
checkAnother()
For resetting the UI and values, we clear all the values the fields.
void checkAnother() {
heightController.text = "";
weightController.text = "";
bmiValue = null;
}
Now that we’ve defined all the necessary fields and methods, it’s time to run the app. Before doing so, let’s add the body to our scaffold to display the input UI.
Widget build(BuildContext context) {
return Scaffold(
body: buildInputUi(),
);
}
Now, run the app using flutter run
. Input the height and weight to calculate the BMI. Although the UI won't visibly change, you can observe the calculated BMI value printed in the terminal. This confirms that the functionality is working correctly, even though the UI remains static for now.
Let’s address this step by step. Firstly, we need to adjust the Scaffold to render buildOutputUi
if bmiValue
contains a valid BMI value. Otherwise, we'll render buildInputUi
. We can implement this logic using the conditional operator to dynamically determine which UI to display based on the presence of the BMI value. Let's proceed with implementing this logic.
@override
Widget build(BuildContext context) {
return Scaffold(
body: bmiValue==null?buildInputUi():buildOutputUi(),
);
}
After making this adjustment, reload the app. Initially, you should see the input UI. Then, assign a value to bmiValue
(line no. 13 in the code snippet provided) and reload the app again. This time, you'll observe the output UI displayed. Once you've tested the functionality, remember to undo the change made to bmiValue
to maintain the original state of the app.
so lets list down the observations.
- Initially, the app displays the input UI.
- Upon invoking
calculateBMI()
, the BMI is calculated andbmiValue
is updated, but the UI remains unchanged. - When assigning an initial value to
bmiValue
, the output UI is rendered.
The missing piece here is that although bmiValue
is updated, the UI doesn't reflect this change. This is because we need to inform Flutter to rebuild the UI once the state changes. We accomplish this using the setState((){})
function, which notifies the Flutter framework that the state has changed and triggers a rebuild of the UI by calling the build()
method again. Let's update the methods to incorporate the use of setState()
.
void calculateBMI() {
//bmi = Formula: weight (kg) / [height (m)]^2
double h = double.tryParse(heightController.text)! / 100;
double w = double.tryParse(weightController.text)!;
setState(() {
bmiValue = w / (h * h);
});
}
void checkAnother() {
setState(() {
heightController.text = "";
weightController.text = "";
bmiValue = null;
});
}
There you have it! Our basic BMI calculator is now complete. While it’s functional, there’s still room for improvement. The UI could be enhanced, and exception handling needs to be implemented. These aspects will be addressed in the next stage.
If you’re interested in taking our BMI calculator to the next level, follow the part 2 here
Source code : https://github.com/AbhijithKonnayil/bmi_calculator
you can access Part 1 of the tutorial in the part-1
branch.
If you found this tutorial helpful and enjoyed building your first BMI calculator app with Flutter, please consider giving it an applause on Medium and starring the repository on GitHub. Your support encourages me to create more content to assist you on your Flutter development journey. Thank you for your support, and happy coding!