A simple Calculator using Flutter
Ever wanted to create your own apps? Application development is a rising popularity field that helps in developing coding skills. Mobile app development is becoming easier with flutter framework provided by google. To kick start your app development skills, we will learn to build a simple calculator app using flutter. Let’s get started.
Creating a new flutter project
Open up android studio(or visual studio) to create a new flutter project(If you don’t have flutter installed then follow steps in this video and proceed). Choose Start a new flutter project option
Then a new pop up window comes where we will select flutter application and hit the next button
Next give your application a name and then again hit next.
Once the above steps are done, the package name comes up. This is the unique id by which the application will be known once published. Then hit on finish to create your flutter project.
Once that is done we will have our main.dart file loaded, along with the necessary files, with the default flutter start project, which is a counter app.
Fetching the dependencies
Flutter provides various packages for simplifying the app building process. Here, we make use of such a package. We will be using math_expressions flutter package. This package helps in solving mathematical expressions in flutter. We can incorporate the package into our app by providing the package name with version in the pubspec.yaml in our flutter project, that is the configuration file.
In order to do that write down the following code in pubspec.yaml and then run pub get command
dependencies:
math_expression:^2.3.0
The version number need not be necessarily specified. If it is not specified the latest compatible version is fetched.
Next import the package into required dart file
import 'package:math_expressions/math_expressions.dart';
On successfully importing the package we can proceed with our build.
Building the app
In the default main.dart file we will be removing the unnecessary code and keep the necessary code only. Here we will be using a Material App() widget as the root widget under the runApp of the main function as shown
import 'package:flutter/material.dart';
import 'package:simple_calc/simple_calc_ui.dart';
void main() {
runApp(
MaterialApp(
home: SimpleCalc(),//YourWidgetName
debugShowCheckedModeBanner: false,
),
);
}
Now we will create a new dart file for creating our app from File>New>Dart File
We will now create a Stateful widget to build our app. The shortcut to create one is to simply type stful and we will get a skeletal code of the Stateful widget. Replace the YourWidgetName with your own custom names.
class YourWidgetName extends StatefulWidget {
@override
_YourWidgetNameState createState() => _YourWidgetNameState();
}
class _YourWidgetNameState extends State<YourWidgetName> {
@override
Widget build(BuildContext context) {
return Container();
}
}
The UI is quite simple with an app bar and a white and teal theme. First we will make the app bar. In order to do this, we create a Scaffold() widget wrapped in a SafeArea() widget.
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(),
);
}
The Scaffold() widget has properties like appBar, body, floating action button etc. So here we will be making use of the appBar and body properties. The appBar property is where we provide the title and other actions in our app. The app bar is quite simple with a background colour and a text.
In AppBar() widget we provide the background color, title and center title properties as below
Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
title: Text(
"Simple Calculator",
style: TextStyle(
color: Colors.white,
),
),
centerTitle: true,
)
),
Apart from the appBar and body property we will be using the resizeToAvoidBottomInset property of Scaffold() as well. This is to ensure that the components we build in our app does not resize and cause errors when the keyboard pops up.
Scaffold(
resizeToAvoidBottomInset: false,
),
Next we come to the body property of Scaffold(). The body consists of 3 text input fields where we input the numbers and operation to do, 2 buttons to calculate and clear the result and finally an area to display the answer.
We will be arranging elements in a Column fashion so we will be using Column() widget with equal spaced children widgets
Scaffold(
body:Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
],
) ,
),
Inside the children property we will give a list of widgets. The first widget we will give is a row of text input fields as shown below
In order for controlling the state of the text input fields we will be providing controllers for each field (In this case 3 controller). Apart from that we will be also assigning a variable to store the results. In addition we will be making the UI responsive by means of MediaQuery class.
class _SimpleCalcState extends State<SimpleCalc> {
TextEditingController num1 = TextEditingController();
TextEditingController num2 = TextEditingController();
TextEditingController operator = TextEditingController();
String ans = '';
@override
Widget build(BuildContext context) {
double w = MediaQuery.of(context).size.width;
double h = MediaQuery.of(context).size.height;
double txt = MediaQuery.textScaleFactorOf(context);
return SafeArea(
);
We create the text input fields within containers to provide definite width. The 1st and 2nd number input fields are similar and differ only in the hint text property. The operator input field is also similar and differs in the hint text and keyboard type values.
//1st child of Column() widget
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [//1st number
Container(
padding: EdgeInsets.all(10 * txt),
width: w * 0.35,
child: TextFormField(
cursorColor: Colors.teal,
controller: num1,
onChanged: (val) {
setState(() {
num1.text = val;
num1.selection = TextSelection.fromPosition(
TextPosition(offset: num1.text.length),
);
});
},
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: '1st number',
hintStyle: TextStyle(
color: Colors.grey.shade400,
),
),
),
),//Operator
Container(
padding: EdgeInsets.all(10 * txt),
width: w * 0.25,
child: TextFormField(
textAlign: TextAlign.center,
cursorColor: Colors.teal,
controller: operator,
onChanged: (val) {
setState(() {
operator.text = val;
operator.selection = TextSelection.fromPosition(
TextPosition(offset: operator.text.length),
);
});
},
decoration: InputDecoration(
hintText: '(+,-, etc)',
hintStyle: TextStyle(
color: Colors.grey.shade400,
),
),
),
),//2nd number
Container(
padding: EdgeInsets.all(10 * txt),
width: w * 0.35,
child: TextFormField(
cursorColor: Colors.teal,
controller: num2,
keyboardType: TextInputType.number,
onChanged: (val) {
setState(() {
num2.text = val;
num2.selection = TextSelection.fromPosition(
TextPosition(offset: num2.text.length),
);
});
},
decoration: InputDecoration(
hintText: '2nd number',
hintStyle: TextStyle(
color: Colors.grey.shade400,
)),
),
),
],
),
The onChange property that takes in a function is where the value of the text input field changes. It takes in a new value every time the user changes the current value. Here inside onChange property we call the predefined setState function of Stateful widget, which rebuilds the UI with the new values. Here we update the values of the controller with the new values, so it is easier to frame the equations.
Next we provide an area for displaying the calculated result. For this we will be using Container() widget as the second child of Column() widget.
//2nd child of Column() widget
Container(
width: double.infinity,
height: h * 0.4,
child: Center(
child: Text(
ans == '' ? 'Your Answer will be displayed here' : ans,
style: TextStyle(
fontSize: ans == '' ? 16 * txt : 25 * txt,
color: ans == '' ? Colors.grey.shade400 : Colors.teal,
),
),
),
),
Finally we have the calculate and clear buttons.
As the names suggest these buttons are for calculating the result and clearing the text input fields along with the result. Both the buttons are arranged in a Row fashion. So, the final child of the Column() widget will be a Row() widget.
//3rd child of Column() widget
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [//Calculate button
Container(
width: w * 0.4,
height: h * 0.08,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.teal,
),
onPressed: () {
ans = calc();
setState(() {});
},
child: Text(
'CALCULATE',
),
),
),//Clear button
Container(
width: w * 0.4,
height: h * 0.08,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.teal,
),
onPressed: () {
num1.clear();
operator.clear();
num2.clear();
ans = '';
setState(() {});
},
child: Text(
'CLEAR',
),
),
),
],
)
The clear button basically clears the values in the text fields. This is made easy by using the predefined clear function of the text editing controllers and then calling setState() thereby rebuilding the UI with the new set of values.
The answer or result value is stored in the “ans” variable. The result is computed with help of a function
String calc() {
Parser p = Parser();
Expression e = p.parse('${num1.text + operator.text + num2.text}');
ContextModel cm = ContextModel();
num ans = e.evaluate(EvaluationType.REAL, cm);
return ans.toString().length > 10
? ans.toStringAsPrecision(3)
: ans.toString();
}
In the function we create an instance of the Parser class to convert the string values to the desired numerical datatype. Next the expression is formed using the data in the text editing controllers. An instance of the context model is also created which keeps track of all the known variables and functions. With the help of these values the result is calculated using predefined evaluate function of Expression object. The final value is then returned as a String for ease of displaying.
And that’s all. We have completed building our simple calculator. The built app can do basic computations like addition, subtraction, multiplication, division etc. With the help of math_expressions package we can also do more complex calculations like finding solutions of higher order equations, trignometric equations etc. Make sure you give it a try and share your results in the comments.
P.S. You can obtain the complete code from my github profile and a complete tutorial video on youtube. Make sure to check it out as well :)