Developing a scientific calculator using Jetpack and Kotlin
The scientific calculator app we are building in this tutorial comes with an intuitive and attractive user interface.Additionally,it provides arithmetic functions such as divison and multiplication.
Scientific calculators have a number of uses.For instance,they are majorly used to simplify arithmetic operations in supermarkets,shops,schools and even hospitals.This tutorial outlines step by step development of a scientific calculator using Jetpack and Kotlin in Android Studio.
Setting up project in Android Studio
- To create a scientific calculator from scratch ensure the latest version of Android studio is installed in your computer.Create a new project by choosing File,New,New Project then choose Empty Activity.Write Scientific Calculator in the name field and click Finish button.


Adding ViewModel Dependencies
2.Ensure you add the required View model dependencies in build.gradle(.kts) in the app level module which comprises of:
implementation("androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version")
// LiveData
implementation("androidx.lifecycle:lifecycle-livedata:$lifecycle_version")
// Lifecycles only (without ViewModel or LiveData)
implementation("androidx.lifecycle:lifecycle-runtime:$lifecycle_version")
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version")
// Annotation processor
annotationProcessor("androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version")
// optional - helpers for implementing LifecycleOwner in a Service
implementation("androidx.lifecycle:lifecycle-service:$lifecycle_version")
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")
// optional - ReactiveStreams support for LiveData
implementation("androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version")
// optional - Test helpers for LiveData
testImplementation("androidx.arch.core:core-testing:$arch_version")
// optional - Test helpers for Lifecycle runtime
testImplementation("androidx.lifecycle:lifecycle-runtime-testing:$lifecycle_version")
Try Kodaschool for free
Click below to sign up and get access to free web, android and iOs challenges.
Calculator Components
3.Delete the code for Hello World logic.Next,add a class called CalculatorButton.kt then create a CalculatorButton function with a string symbol,modifiers to control UI changes and onClick lambda to respond to click events.Additionally,add Box composable and pass the modifier as a parameter and style it to achieve circular shape with 90dp .clip(RoundedCornerShape(90.dp)).Inside Box composable add Text composable with symbol, fontsize, color and Theme which is set to dark or light color.
package com.example.scientificcalculator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
@Composable
fun calculatorButton(
symbol: String,
modifier: Modifier = Modifier,
color: Color = Color.White,
textStyle: TextStyle = TextStyle(),
onClick: () -> Unit
){
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.clip(RoundedCornerShape(100.dp))
.background(color)
.clickable {
onClick()
}
.then(modifier)
) {
Text(
text = symbol,
style = textStyle,
fontSize = 36.sp,
color = Color.White
)
}
}
4.Next is creating click events that will aid in clearing all content,deleting a single item,arithmetic options such as multiplication,division,addition and subtraction(x,/,+ and -).Moreover,add a click event that triggers when = sign is pressed and another for the Decimal point.Define these in a new Sealed Class called CalculatorActions.kt.
package com.example.scientificcalculator
sealed class CalculatorActions {
data class Number(val number: Int): CalculatorActions()
object Clear: CalculatorActions()
object Delete: CalculatorActions()
data class Operation(val): CalculatorActions()
object Calculate: CalculatorActions()
object Decimal: CalculatorActions()
}

Sealed Classes
5.Create a sealed class that will have all the operations including multiplication,division,addition, subtraction and operation.Pass the CalculatorOperations class to data class Operation in CalculatorActions.
package com.example.scientificcalculator
import java.text.DateFormatSymbols
sealed class CalculatorOperation (val symbols: String){
object Add: CalculatorOperation("+")
object Subtract: CalculatorOperation("-")
object Multiply: CalculatorOperation("x")
object Divide: CalculatorOperation("/")
}
Passing class into data class
package com.example.scientificcalculator
sealed class CalculatorAction {
data class Number(val number: Int): CalculatorAction()
object Clear: CalculatorAction()
object Delete: CalculatorAction()
data class Operation(val operations: CalculatorOperation): CalculatorAction()
object Calculate: CalculatorAction()
object Decimal: CalculatorAction()
}
Tracking States
6.Add a data class that will allow tracking of states when user enter values in the calculator.Name the data Class CalculatorState.kt

package com.example.scientificcalculator
data class CalculatorState(
var no1: String ="",
var no2: String ="",
var operations: CalculatorOperation?=null,
)
7.Create a viewModel that will ensure that states are not lost even if the screen is rotated.
package com.example.scientificcalculator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
class CalculatorViewModel:ViewModel() {
var state by mutableStateOf(CalculatorState())
private set
}
8.Define the UI by styling the Items in the Layout in MainActivity and ensure that any custom colors are correctly defined in color.kt file in ui.theme folder
Color Definition
package com.example.scientificcalculator.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val MediumGray = Color(0xFF2E2E2E)
val LightGray = Color(0xFF818181)
val Orange = Color
MainActivity code with styles for Calculator Layout
package com.example.scientificcalculator
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.LightGray
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.scientificcalculator.ui.theme.MediumGray
import com.example.scientificcalculator.ui.theme.Orange
import com.example.scientificcalculator.ui.theme.ScientificCalculatorTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ScientificCalculatorTheme {val viewModel = viewModel<CalculatorViewModel>()
val state = viewModel.state
val buttonSpacing = 8.dp
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.DarkGray)
.padding(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter),
verticalArrangement = Arrangement.spacedBy(buttonSpacing),
) {
Text(
text = state.number1 + (state.operation?.symbol ?: "") + state.number2,
textAlign = TextAlign.End,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 32.dp),
fontWeight = FontWeight.Light,
fontSize = 80.sp,
color = Color.White,
maxLines = 2
)
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
CalculatorButton(
symbol = "AC",
color = LightGray,
modifier = Modifier
.aspectRatio(2f)
.weight(2f)
) {
viewModel.onAction(CalculatorAction.Clear)
}
CalculatorButton(
symbol = "Del",
color = LightGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Delete)
}
CalculatorButton(
symbol = "/",
color = Orange,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Operation(CalculatorOperation.Divide))
}
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
CalculatorButton(
symbol = "7",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(7))
}
CalculatorButton(
symbol = "8",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(8))
}
CalculatorButton(
symbol = "9",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(9))
}
CalculatorButton(
symbol = "x",
color = Orange,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Operation(CalculatorOperation.Multiply))
}
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
CalculatorButton(
symbol = "4",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(4))
}
CalculatorButton(
symbol = "5",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(5))
}
CalculatorButton(
symbol = "6",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(6))
}
CalculatorButton(
symbol = "-",
color = Orange,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Operation(CalculatorOperation.Subtract))
}
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
CalculatorButton(
symbol = "1",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(1))
}
CalculatorButton(
symbol = "2",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(2))
}
CalculatorButton(
symbol = "3",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Number(3))
}
CalculatorButton(
symbol = "+",
color = Orange,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Operation(CalculatorOperation.Add))
}
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
CalculatorButton(
symbol = "0",
color = MediumGray,
modifier = Modifier
.aspectRatio(2f)
.weight(2f)
) {
viewModel.onAction(CalculatorAction.Number(0))
}
CalculatorButton(
symbol = ".",
color = MediumGray,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Decimal)
}
CalculatorButton(
symbol = "=",
color = Orange,
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
) {
viewModel.onAction(CalculatorAction.Calculate)
}
}
}
}
}
}
}
}
9.Finally add logic that will trigger the calculations in the CalculatorViewModel
package com.example.scientificcalculator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
class CalculatorViewModel: ViewModel() {
var state by mutableStateOf(CalculatorState())
fun onAction(action: CalculatorAction) {
when(action) {
is CalculatorAction.Number -> enterNumber(action.number)
is CalculatorAction.Delete -> delete()
is CalculatorAction.Clear -> state = CalculatorState()
is CalculatorAction.Operation -> enterOperation(action.operation)
is CalculatorAction.Decimal -> enterDecimal()
is CalculatorAction.Calculate -> calculate()
}
}
private fun enterOperation(operation: CalculatorOperation) {
if(state.number1.isNotBlank()) {
state = state.copy(operation = operation)
}
}
private fun calculate() {
val number1 = state.number1.toDoubleOrNull()
val number2 = state.number2.toDoubleOrNull()
if(number1 != null && number2 != null) {
val result = when(state.operation) {
is CalculatorOperation.Add -> number1 + number2
is CalculatorOperation.Subtract -> number1 - number2
is CalculatorOperation.Multiply -> number1 * number2
is CalculatorOperation.Divide -> number1 / number2
null -> return
}
state = state.copy(
number1 = result.toString().take(15),
number2 = "",
operation = null
)
}
}
private fun delete() {
when {
state.number2.isNotBlank() -> state = state.copy(
number2 = state.number2.dropLast(1)
)
state.operation != null -> state = state.copy(
operation = null
)
state.number1.isNotBlank() -> state = state.copy(
number1 = state.number1.dropLast(1)
)
}
}
private fun enterDecimal() {
if(state.operation == null && !state.number1.contains(".") && state.number1.isNotBlank()) {
state = state.copy(
number1 = state.number1 + "."
)
return
} else if(!state.number2.contains(".") && state.number2.isNotBlank()) {
state = state.copy(
number2 = state.number2 + "."
)
}
}
private fun enterNumber(number: Int) {
if(state.operation == null) {
if(state.number1.length >= MAX_NUM_LENGTH) {
return
}
state = state.copy(
number1 = state.number1 + number
)
return
}
if(state.number2.length >= MAX_NUM_LENGTH) {
return
}
state = state.copy(
number2 = state.number2 + number
)
}
companion object {
private const val MAX_NUM_LENGTH = 8
}
}

Please use the knowledge gathered in this tutorial,to build a scientific calculator for practice.This will enhance your understanding on concepts such as composables and lambdas in the context of Jetpack and Kotlin.