Rebuilding The Flutter Counter App In Jetpack Compose

Rebuilding The Flutter Counter App In Jetpack Compose

ยท

6 min read

This article will show how to rebuild the default Flutter counter app using Jetpack Compose.

The article is divided into two sections:

  1. UI, where the app's user interface will be built.

  2. State, where the state of the app's counter will be implemented.

Below is a screenshot of the default Flutter counter app:

162827266517692828 (3).jpeg

This is what we'll rebuild with Jetpack Compose.

The first step is to download a Jetpack Compose supported Android Studio version (at this time of writing, that is the Arctic Fox version).

The next step is to create an empty compose activity by clicking on File > New Project and selecting Empty Compose Activity from the templates shown.

Click on Next.

Then we enter our application name. For this article, we'll use "ComposeCounterApp".

Click on Finish and it should open the ComposeCounterApp project.

1. UI

Open the MainActivity.kt file. It should contain the default Compose Activity code.

We'll remove the code and update the content of MainActivity.kt to this below:

package com.example.composecounterapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import com.example.composecounterapp.ui.theme.ComposeCounterAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeCounterAppTheme {
                ComposeCounter()
            }
        }
    }
}

@Composable
fun ComposeCounter() {
    Scaffold(
        content = {}
    )
}

On running the project on a device, an empty screen is displayed. This is because ComposeCounter only contains a Scaffold composable with empty content.

Next, we'll add the code for the app bar. We'll use the TopAppBar composable and specify the title, the background colour, and the content colour.

Update the ComposeCounter function to this.

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {}
    )
}

This will show the following screen:

162827266517692828.jpeg

Next, we'll add the content of the screen. And for this, we'll pass a Column with two Texts to the Scaffold as content. The column items will be aligned in the center and we'll pass modifiers to ensure the Column fills the screen horizontally and vertically.

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth()
            ) {
                Text(
                    text = "You have pushed this button this many times",
                )
                Text(
                    text = "0",
                    style = MaterialTheme.typography.h4,
                )
            }
        },
    )
}

And now we'll have this showing on our device.

162827266517692828 (1).jpeg

The last thing to add for the UI is the floating action button, which we can specify on the Scaffold by passing a FloatingActionButton composable to the Scaffold as floatingActionButton.

We do that by updating the ComposeCounter composable to the code below:

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth()
            ) {
                Text(
                    text = "You have pushed this button this many times",
                )
                Text(
                    text = "0",
                    style = MaterialTheme.typography.h4,
                )
            }
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {},
                backgroundColor = primaryColor,
                contentColor = Color.White
            ) {
                Icon(Icons.Filled.Add, contentDescription = "Add button")
            }
        },
    )
}

And now, we'll have our complete UI! ๐ŸŽ‰

162827266517692828 (2).jpeg

2. State

Currently, the app displays "0" as the counter state and we need to make sure that clicking the floating action button increments that state.

We need a variable to hold the state of the counter.

From State and Jetpack Compose,

There are three ways to declare a MutableState object in a composable:

  • val mutableState = remember { mutableStateOf(default) }

  • var value by remember { mutableStateOf(default) }

  • val (value, setValue) = remember { mutableStateOf(default) }

We'll be using the second one.

At the top of the ComposeCounter composable, we'll add a variable counter to hold the state of the counter and set its default value to zero.

We'll also use this counter variable in the Text composable instead of "0".

And now the ComposeCounter will have the following code:

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)
    var counter: Int by remember { mutableStateOf(0) }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth()
            ) {
                Text(
                    text = "You have pushed this button this many times",
                )
                Text(
                    text = counter.toString(),
                    style = MaterialTheme.typography.h4,
                )
            }
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                },
                backgroundColor = primaryColor,
                contentColor = Color.White
            ) {
                Icon(Icons.Filled.Add, contentDescription = "Add button")
            }
        },
    )
}

There will be no visible change on the screen for now.

The next thing to do is to increment the counter when the floating action button is clicked.

We can do that in the onClick callback of the FloatingActionButton.

The code will be updated to this:

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)
    var counter: Int by remember { mutableStateOf(0) }


    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth()
            ) {
                Text(
                    text = "You have pushed this button this many times",
                )
                Text(
                    text = counter.toString(),
                    style = MaterialTheme.typography.h4,
                )
            }
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                    counter += 1
                },
                backgroundColor = primaryColor,
                contentColor = Color.White
            ) {
                Icon(Icons.Filled.Add, contentDescription = "Add button")
            }
        },
    )
}

Now the app increments the counter when the floating action button is tapped as seen in the gif below:

162827266517692828.gif

And now the app is complete.

The complete code for the MainActivity.kt is this below:

package com.example.composecounterapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.example.composecounterapp.ui.theme.ComposeCounterAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeCounterAppTheme {
                ComposeCounter()
            }
        }
    }
}

@Composable
fun ComposeCounter() {
    val primaryColor = Color(0xFF2196F3)
    var counter: Int by remember { mutableStateOf(0) }


    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "Compose Home Demo Page") },
                backgroundColor = primaryColor,
                contentColor = Color.White
            )
        },
        content = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth()
            ) {
                Text(
                    text = "You have pushed this button this many times",
                )
                Text(
                    text = counter.toString(),
                    style = MaterialTheme.typography.h4,
                )
            }
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                    counter += 1
                },
                backgroundColor = primaryColor,
                contentColor = Color.White
            ) {
                Icon(Icons.Filled.Add, contentDescription = "Add button")
            }
        },
    )
}

The complete project can be found on Github.