Jetpack Compose: Getting Started

Aug 1 2023 · Kotlin 1.8.10, Android 13, Android Studio Flamingo

Part 3: Jetpack Controls

17. State Management in Jetpack Compose

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 16. Display Lists Using Lazy Layouts Next episode: 18. Understanding Recomposition

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

So far, we have been able to draw out different items on the UI. However, we have not been able to interact with these items.

Add State to our SearchBar

Let us add a MutableState variable to our SearchBar Composable. This variable will be used to store the value of the TextField.

@ExperimentalMaterial3Api
@Composable
fun SearchBar() {
    var search by remember { mutableStateOf("") }

    Card(
        modifier = Modifier
            .fillMaxWidth()
            .height(75.dp)
            .padding(end = 16.dp, start = 16.dp, top = 10.dp),
        shape = RoundedCornerShape(100.dp),
    ) {

        TextField(
            modifier = Modifier.fillMaxSize(),
            value = search,
            onValueChange = { search = it },
            leadingIcon = { Image(painter = painterResource(id = R.drawable.ic_search), contentDescription = "search bar") },
            trailingIcon = { Image(painter = painterResource(id = R.drawable.ic_filter), contentDescription = "filter") },
            placeholder = {
                Text(
                    text = "Search for food ...",
                    modifier = Modifier
                        .fillMaxHeight(),
                    textAlign = TextAlign.Center,
                )
            },
            colors = TextFieldDefaults.textFieldColors(
                focusedIndicatorColor = Color.Transparent,
                unfocusedIndicatorColor = Color.Transparent,
            ),
        )
    }
}

Add HomeSection

@Composable
fun HomeSection() {
    Surface(modifier = Modifier) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(top = 16.dp)
                .padding(horizontal = 8.dp),
        ) {
            // Categories header
            Row(
                modifier = Modifier
                    .fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
            ) {
                Text(text = "Food Category")
                Row(modifier = Modifier, verticalAlignment = Alignment.CenterVertically) {
                    Text(
                        text = "See more",
                        color = MaterialTheme.colorScheme.primary,
                        fontWeight = FontWeight.Bold,
                    )

                    Spacer(modifier = Modifier.width(10.dp))

                    Image(
                        painter = painterResource(id = R.drawable.ic_right),
                        contentDescription = "Right",
                        colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.primary),
                    )
                }
            }

            // Categories list
            LazyRow(
                modifier = Modifier
                    .padding(top = 8.dp)
                    .fillMaxWidth(),
            ) {
                items(getCategories()) {
                        item ->
                    FoodCategoryItem(icon = item.image, category = item.name)
                }
            }

            Spacer(modifier = Modifier
                .fillMaxWidth()
                .height(32.dp))

            Row(
                modifier = Modifier
                    .fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween) {

                Text(text = "Food For You")

                Row(modifier = Modifier
                    .width(40.dp)
                    .align(Alignment.CenterVertically),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.SpaceBetween) {

                    Surface(modifier = Modifier
                        .size(10.dp),
                    shape = CircleShape,
                    color = MaterialTheme.colorScheme.primary) {

                    }

                    Surface(modifier = Modifier
                        .size(10.dp),
                        shape = CircleShape,
                        color = MaterialTheme.colorScheme.onBackground) {

                    }

                    Surface(modifier = Modifier
                        .size(10.dp),
                        shape = CircleShape,
                        color = MaterialTheme.colorScheme.onBackground) {

                    }
                }

            }

            Spacer(modifier = Modifier
                .fillMaxWidth()
                .height(8.dp))

           // For You - Food Items
            LazyRow(
                modifier = Modifier
                    .fillMaxWidth(),
                contentPadding = PaddingValues(5.dp)
            ){

                itemsIndexed(getFood()){ index, item ->
                    FoodItem(id = index ,food = item)
                }

            }


        }
    }
}

Add PriceBar

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PriceBar(
    price: Double? = 0.0,
    originalPrice: Double = 0.0
) {

    var qty by remember { mutableStateOf(1) }

    Box(modifier = Modifier
        .height(60.dp)
        .fillMaxWidth()){

        Row(modifier = Modifier) {

            Text(
                text = "$${ price?:originalPrice }",
                style = MaterialTheme.typography.displaySmall,
                color = MaterialTheme.colorScheme.primary)

            if (price != null){

                Text(
                    modifier = Modifier.padding(start = 5.dp),
                    text = "$${originalPrice}",
                    fontWeight = FontWeight.Medium,
                    textDecoration = TextDecoration.LineThrough
                )

            }
        }


        Box(
            modifier = Modifier
                .width(150.dp)
                .fillMaxHeight()
                .background(
                    color = MaterialTheme.colorScheme.tertiary,
                    shape = RoundedCornerShape(35.dp)
                )
                .clip(RoundedCornerShape(35.dp))
                .align(Alignment.CenterEnd),
        ) {

            Row(
                modifier = Modifier
                    .fillMaxSize(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                
                FloatingActionButton(onClick = { qty = if (qty > 0) --qty else 0  },
                    modifier = Modifier
                        .padding(start = 5.dp)
                        .size(45.dp),
                    shape = CircleShape,
                    containerColor = MaterialTheme.colorScheme.primary,
                    contentColor = MaterialTheme.colorScheme.onPrimary) {

                    Image(
                        painter = painterResource(id = R.drawable.ic_remove),
                        contentDescription = "Add")
                }

                TextField(
                    value = "$qty",
                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number,),
                    onValueChange = { },
                    modifier = Modifier
                        .width(45.dp),
                    colors = TextFieldDefaults.textFieldColors(
                        textColor = MaterialTheme.colorScheme.onTertiary,
                        containerColor = MaterialTheme.colorScheme.tertiary)
                )
                
                FloatingActionButton(onClick = { qty++ },
                    modifier = Modifier
                        .padding(end = 5.dp)
                        .size(45.dp),
                    shape = CircleShape,
                    containerColor = MaterialTheme.colorScheme.primary,
                    contentColor = MaterialTheme.colorScheme.onPrimary) {

                    Image(
                        painter = painterResource(id = R.drawable.ic_add),
                        contentDescription = "Add")

                }

            }

        }

    }

}

State across Configuration Changes

remember helps you to retain state during recompositions, but it doesn’t help you to retain state across configuration changes. For example, if you rotate the device, the state will be lost. To retain state across configuration changes, you can use rememberSaveable.

@Composable
fun Counter() {
    var count by rememberSaveable { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("I've been clicked $count times")
    }
}