Building a calculator app with React Native and Tailwind CSS
Published: 2023-07-18 07:11:01

Recently, I completed a React Native course, and here I am writing about my first project - a calculator app. 😎

I decided to start with small projects before going into complex mobile applications. So, expect more tutorials about building advanced or complex mobile applications from me.

![I've got this!](https://media.giphy.com/media/WrmAysy2Zb2Z2qhVkS/giphy.gif)

Therefore, in this tutorial, I'll walk you through how to build a mobile calculator app with React Native and Tailwind CSS.

> _💡PS: This tutorial assumes you've React Native and an emulator (or device) installed on your computer._


## What is React Native?
[React Native](https://reactnative.dev) is an open-source React framework that enables you to create native applications for both IOS and Android with JavaScript code. Although in this tutorial, we'll build the application with Expo.

[Expo](https://docs.expo.dev) saves us from the complex configurations required to create a native application with the React Native CLI, making it the easiest and fastest way to build and publish React Native apps.

>**💡 Dear prospective employers! I'm actively seeking remote opportunities as a Junior Frontend Engineer (Intern) or Technical Writer where I can thrive, learn, and grow. Here is my [portfolio and resume](https://dave-asaolu.vercel.app).**

## Project Set up
Create a new Expo application by running the code snippet below.

```bash
npx create-expo-app calculator-app
```

Add Nativewind and Tailwind CSS as the project's dependencies.

```bash
yarn add nativewind
yarn add --dev tailwindcss
```
> 💡 [NativeWind](https://www.nativewind.dev) uses Tailwind CSS as its scripting language, therefore enabling us to write the same styles with Tailwind CSS for both Android and iOS React Native apps.

Run `npx tailwindcss init` to create a `tailwind.config.js` file and update the `tailwind.config.js` file as done below.

```js
module.exports = {
	content: [
		"./App.{js,jsx,ts,tsx}",
		"./<custom directory>/**/*.{js,jsx,ts,tsx}",
	],
	theme: {
		extend: {},
	},
	plugins: [],
};
```

Finally, add the Babel plugin to the `babel.config.js`.

```js
// babel.config.js
module.exports = function (api) {
	api.cache(true);
	return {
		presets: ["babel-preset-expo"],
		plugins: ["nativewind/babel"],
	};
};
```
Congratulations!🎉 You've successfully configured Tailwind CSS. You can now style the application with Tailwind CSS.

## Building the application user interface
In this section, you'll learn how to build the user interface for the application.

![Application UI interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02b39fhq2x6tskimgf3d.png)

From the image above, we have a large display and a group of buttons. Next, create a component that returns a row of buttons containing numbers and an operation.

![Button Row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t00kw6buct5c4mmkrwl1.png)

```js
import { Pressable, Text, View } from "react-native";
import React from "react";

const ButtonGroup = ({
	first,
	second,
	third,
	fourth,
	handleNumberPress,
	handleOperationPress,
}) => {
	return (
		<View className='flex-row items-center w-full space-x-3 justify-center px-10 mb-2'>
			<Pressable
				className=' bg-white py-4 rounded-xl shadow-md w-1/4'
				onPress={() => handleNumberPress(first)}
			>
				<Text className='text-3xl text-gray-600 font-semibold text-center'>
					{first}
				</Text>
			</Pressable>
			<Pressable
				className=' bg-white py-4   rounded-xl shadow-md w-1/4'
				onPress={() => handleNumberPress(second)}
			>
				<Text className='text-3xl text-gray-600 font-semibold text-center'>
					{second}
				</Text>
			</Pressable>
			<Pressable
				className=' bg-white py-4 rounded-xl shadow-md w-1/4'
				onPress={() => handleNumberPress(third)}
			>
				<Text className='text-3xl text-gray-600 font-semibold text-center'>
					{third}
				</Text>
			</Pressable>
			<Pressable
				className='bg-blue-600 py-4  rounded-xl shadow-md w-1/4'
				onPress={() => handleOperationPress(fourth)}
			>
				<Text className='text-3xl text-white font-semibold text-center'>
					{fourth}
				</Text>
			</Pressable>
		</View>
	);
};

export default ButtonGroup;
```

The code snippet above accepts each value of the button and the function to be executed when you press the buttons. The last button within the component will contain an operation; that's why there is another function - `handleOperationPress` for its action.

Next, update the `App.js` file to render the UI component below.
```js
return (
	<SafeAreaView className='flex-1 items-center'>
		<View className='flex-1 w-full bg-blue-50 rounded-xl p-4 mb-4 items-end justify-end'>
			<Text className={`${firstNumber.length <= 7 ? "text-8xl" : "text-6xl"}`}>
				{display()}
			</Text>
		</View>

		<View className='w-full rounded-xl py-4'>
			{/* --- button container ---*/}
		</View>
	</SafeAreaView>
);
```
The code snippet above renders the calculator's display screen.

Render the buttons using the code snippet below.

```js
<View className='w-full rounded-xl py-4'>
	<View className='flex-row items-center w-full space-x-3 justify-center px-10 mb-2'>
		<Pressable
			className='bg-gray-600 py-4   rounded-xl shadow-md w-1/4'
			onPress={() => clearScreen()}
		>
			<Text className='text-3xl text-white font-semibold text-center'>C</Text>
		</Pressable>
		<Pressable
			className='bg-gray-600 py-4   rounded-xl shadow-md w-1/4'
			onPress={() => changeSignFunction()}
		>
			<Text className='text-3xl text-white font-semibold text-center'>+/-</Text>
		</Pressable>
		<Pressable
			className='bg-gray-600 py-4 rounded-xl shadow-md w-1/4'
			onPress={() => percentageFunction()}
		>
			<Text className='text-3xl text-white font-semibold text-center'>%</Text>
		</Pressable>
		<Pressable
			className='bg-blue-600 py-4   rounded-xl shadow-md w-1/4'
			onPress={() => handleOperationPress("÷")}
		>
			<Text className='text-3xl text-white font-semibold text-center'>÷</Text>
		</Pressable>
	</View>
	<ButtonGroup
		first='7'
		second='8'
		third='9'
		fourth='x'
		handleNumberPress={handleNumberPress}
		handleOperationPress={handleOperationPress}
	/>
	<ButtonGroup
		first='4'
		second='5'
		third='6'
		fourth='-'
		handleNumberPress={handleNumberPress}
		handleOperationPress={handleOperationPress}
	/>
	<ButtonGroup
		first='1'
		second='2'
		third='3'
		fourth='+'
		handleNumberPress={handleNumberPress}
		handleOperationPress={handleOperationPress}
	/>
	<View className='flex-row items-center w-full space-x-3 justify-center px-10 mb-2'>
		<Pressable
			className='bg-white py-4   rounded-xl shadow-md w-1/4'
			onPress={() => handleNumberPress(".")}
		>
			<Text className='text-3xl text-gray-600 font-semibold text-center'>
				.
			</Text>
		</Pressable>
		<Pressable
			className='py-4   rounded-xl shadow-md w-1/4'
			onPress={() => handleNumberPress("0")}
		>
			<Text className='text-3xl text-gray-600 font-semibold text-center'>
				0
			</Text>
		</Pressable>
		<Pressable
			className='bg-white py-4 rounded-xl items-center justify-center shadow-md w-1/4'
			onPress={() => deleteFunction()}
		>
			<Feather name='delete' size={24} color='black' />
		</Pressable>
		<Pressable
			className='bg-blue-600 py-4 rounded-xl shadow-md w-1/4'
			onPress={() => getResult()}
		>
			<Text className='text-3xl text-white font-semibold text-center'>=</Text>
		</Pressable>
	</View>
</View>
```

The code snippet renders the buttons on the screen. I didn't use the `ButtonGroup` component for the top and bottom row because its functions and colours are different from others.

## Creating the button functionalities
First, you need to create three different states that hold the button values and the operation.

```js
import { StatusBar } from "expo-status-bar";
import { Pressable, SafeAreaView, Text, View } from "react-native";
import ButtonGroup from "./components/ButtonGroup";
import { Feather } from "@expo/vector-icons";
import { useState } from "react";

export default function App() {
	const [firstNumber, setFirstNumber] = useState("");
	const [secondNumber, setSecondNumber] = useState("");
	const [operation, setOperation] = useState("");

	return <div>{/* --- UI components ---*/}</div>;
}
```
The code snippet above shows that the user can input at least two numbers into the calculator and an operation, except for the percent operation.

Create a `display` function that shows the user's input on the screen.

```js
const display = () => {
	if (!secondNumber && !firstNumber) {
		return "0";
	}
	if (!secondNumber) {
		return `${firstNumber}${operation}`;
	} else {
		return `${secondNumber}`;
	}
};
```

The function above checks if the user has not entered a number, then returns "0"; otherwise it returns the right number on the screen.

Add a function that runs when a user clicks on the operation buttons.

```js
const handleOperationPress = (value) => {
	if (
		firstNumber[firstNumber.length - 1] === "x" ||
		firstNumber[firstNumber.length - 1] === "+" ||
		firstNumber[firstNumber.length - 1] === "-" ||
		firstNumber[firstNumber.length - 1] === "%" ||
		firstNumber[firstNumber.length - 1] === "÷" ||
		operation !== ""
	) {
		return;
	}
	setOperation(value);
};
```

The function above checks if the last input is not an operation before updating the `operation` state.

Create another function that is executed every time the user presses a number.
```js
const handleNumberPress = (value) => {
	if (!operation && firstNumber.length < 10) {
		if (value !== ".") {
			setFirstNumber(firstNumber + value);
		} else {
			if (firstNumber.includes(".")) {
				return;
			} else {
				setFirstNumber(firstNumber + value);
			}
		}
	}
	if (operation && secondNumber.length < 10) {
		if (value !== ".") {
			setSecondNumber(secondNumber + value);
		} else {
			if (secondNumber.includes(".")) {
				return;
			} else {
				setSecondNumber(secondNumber + value);
			}
		}
	}
};
```
The function above checks if the user has entered an operation before setting the `firstNumber` and `secondNumber` state values. 
When a user press a number button, the calculator sets its value to the `firstNumber` variable. Any subsequent number after an operation sign is set as the `secondNumber`.

Create the equals-to function as shown below.

```js
const getResult = () => {
	switch (operation) {
		case "+":
			clearScreen();
			setFirstNumber(parseFloat(firstNumber) + parseFloat(secondNumber));
			setOperation("");
			setSecondNumber("");
			break;
		case "-":
			clearScreen();
			setFirstNumber(parseFloat(firstNumber) - parseFloat(secondNumber));
			setOperation("");
			setSecondNumber("");
			break;
		case "x":
			clearScreen();
			setFirstNumber(parseFloat(firstNumber) * parseFloat(secondNumber));
			setOperation("");
			setSecondNumber("");
			break;
		case "÷":
			clearScreen();
			const value = parseInt(firstNumber) / parseInt(secondNumber);
			if (value !== Math.round(value) && value !== Math.trunc(value)) {
				setFirstNumber(value.toFixed(5));
			} else {
				setFirstNumber(value);
			}
			setOperation("");
			setSecondNumber("");
			break;
		default:
			clearScreen();
			break;
	}
};
```
The function above accepts the values of the `firstNumber` and the `secondNumber` states and performs the right operation on the values.

When a user clicks on the percentage function, it returns the value in percentage.

```js
const percentageFunction = () => {
	if (!secondNumber) {
		return setFirstNumber(parseFloat(firstNumber) / 100);
	}
};
```

Add a function that enables users to clear the screen or remove the recently entered value.

```js
//👇🏻 clears the screen
const clearScreen = () => {
	setFirstNumber("");
	setSecondNumber("");
	setOperation("");
};

//👇🏻 removes the recently entered value
const deleteFunction = () => {
	if (operation) {
		return setSecondNumber(secondNumber.slice(0, -1));
	}
	return setFirstNumber(firstNumber.toString().slice(0, -1));
};
```

Finally, create the `changeSignFunction` function to enable users to enter negative and positive values into the calculator. The function checks the sign on a number and toggles it.

```js
const changeSignFunction = () => {
	if (operation) {
		if (secondNumber.startsWith("-")) {
			return setSecondNumber(secondNumber.replace("-", "+"));
		}
		if (secondNumber.startsWith("+")) {
			return setSecondNumber(secondNumber.replace("+", "-"));
		}
		return setSecondNumber(`-${secondNumber}`);
	} else {
		if (firstNumber.toString().startsWith("-")) {
			return setFirstNumber(firstNumber.toString().replace("-", "+"));
		}
		if (firstNumber.toString().startsWith("+")) {
			return setFirstNumber(firstNumber.toString().replace("+", "-"));
		}
		return setFirstNumber(`-${firstNumber}`);
	}
};
```

## Conclusion
As I stated earlier, this is a beginner React Native project; hopefully, it helps you get started with React Native or build your first mobile application.

You can get the source code on [GitHub](https://github.com/dha-stix/reactnative-calculator) and try out a demo of the project [here](https://expo.dev/@dhastix).

Thank you for reading! 🎉

### Open to work🙂
Did you enjoy this article or need an experienced Technical Writer / React Developer for a remote, full-time, or contract-based role? Feel free to contact me.
[GitHub](https://github.com/dha-stix) ||  [LinkedIn](https://www.linkedin.com/in/david-asaolu234/) ||  [Twitter](https://twitter.com/dayvid_JS)

[![Buy David a coffee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cinwy8wfhl6ra51t6ml5.png)](https://www.buymeacoffee.com/asaoludavid)
<img src="https://media.giphy.com/media/fVtcfEXWQJQUbsF1sH/giphy.gif" alt="Thank You" width="auto" height="auto"/>