aster.cloud aster.cloud
  • /
  • Platforms
    • Public Cloud
    • On-Premise
    • Hybrid Cloud
    • Data
  • Architecture
    • Design
    • Solutions
    • Enterprise
  • Engineering
    • Automation
    • Software Engineering
    • Project Management
    • DevOps
  • Programming
    • Learning
  • Tools
  • About
  • /
  • Platforms
    • Public Cloud
    • On-Premise
    • Hybrid Cloud
    • Data
  • Architecture
    • Design
    • Solutions
    • Enterprise
  • Engineering
    • Automation
    • Software Engineering
    • Project Management
    • DevOps
  • Programming
    • Learning
  • Tools
  • About
aster.cloud aster.cloud
  • /
  • Platforms
    • Public Cloud
    • On-Premise
    • Hybrid Cloud
    • Data
  • Architecture
    • Design
    • Solutions
    • Enterprise
  • Engineering
    • Automation
    • Software Engineering
    • Project Management
    • DevOps
  • Programming
    • Learning
  • Tools
  • About
  • Programming

Composable Functions

  • aster.cloud
  • September 24, 2022
  • 8 minute read

In the previous MAD Skills Compose Basics article, you learned how to think in Compose — you describe your UI in Kotlin as functions. No more XML needed! In this article, we will dive deeper into these functions and how you can build UI with them.

As a reminder, we will be answering your questions on Compose Basics in our live Q&A session on October 13th. Make sure to leave a comment here, on YouTube, or by using the #MADCompose hashtag on Twitter.

To understand how these functions work, let’s see how we can build the single choice question screen of Jetsurvey, one of our Compose samples.


Partner with aster.cloud
for your next big idea.
Let us know here.



From our partners:

CITI.IO :: Business. Institutions. Society. Global Political Economy.
CYBERPOGO.COM :: For the Arts, Sciences, and Technology.
DADAHACKS.COM :: Parenting For The Rest Of Us.
ZEDISTA.COM :: Entertainment. Sports. Culture. Escape.
TAKUMAKU.COM :: For The Hearth And Home.
ASTER.CLOUD :: From The Cloud And Beyond.
LIWAIWAI.COM :: Intelligence, Inside and Outside.
GLOBALCLOUDPLATFORMS.COM :: For The World's Computing Needs.
FIREGULAMAN.COM :: For The Fire In The Belly Of The Coder.
ASTERCASTER.COM :: Supra Astra. Beyond The Stars.
BARTDAY.COM :: Prosperity For Everyone.

You can watch the accompanying video to this article here.

A simple composable function

A single answer in the survey can be written in Compose as a function containing a Row with an Image, Text, and a RadioButton.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SurveyAnswer(answer: Answer) {
    Row {
        Image(answer.image)
        Text(answer.text)
        RadioButton(false, onClick = { /* … */ })
    }
}

To create a UI component in Compose, we must annotate a function with the <strong class="ke iz">@Composable</strong> annotation. This annotation tells the Compose compiler that this function is intended to convert data into UI, so an answer into UI.¹

Functions with this annotation are also called composable functions, or composables for short. These functions are the building blocks of UI in Compose. It’s quick and easy to add this annotation encouraging you to organize your UI into a library of reusable elements.

For example, to implement a list of possible answers to select from, we can define a new function called SingleChoiceQuestion that takes in a list of answers, and then call the SurveyAnswer function that we have just defined.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  Column {
    answers.forEach { answer ->
      SurveyAnswer(answer = answer)
    }
  }
}

SingleChoiceQuestion accepts parameters which allows it to be configured by the app’s logic. In this case, it accepts a list of possible answers so that it can display these options to the UI. Notice that the composable doesn’t return anything (it returns `Unit`), but instead, it emits UI. Specifically, it emits the Column layout composable that’s part of the Compose toolkit which arranges items vertically. Within the column, it emits a SurveyAnswer composable for each answer.

Composables are immutable. You can’t hold a reference to them — like holding a reference to a single answer — and later update its contents. You need to pass any and all information as parameters when you call it.

Notice that since the function is written in Kotlin, we can use the full Kotlin syntax and control flow to produce our UI. Here we’re using forEach to iterate through each answer and call SurveyAnswer to display them. If we want to conditionally display something else, it’s as simple as using an if statement. No View.visibility = View.GONE or View.INVISIBLE needed. With a declarative UI framework like Compose, if you want your UI to look differently depending on the inputs provided, the composable must describe how your UI should look for every possible value of inputs. Using conditional statements like this snippet is how you can achieve this.

Read More  Top Tips For Adopting Android’s Notification Permission
// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  Column {
    if (answers.isEmpty()) {
      Text("There are no answers to choose from!")
    } else {
      answers.forEach { answer ->
        SurveyAnswer(answer = answer)
      }
    }
  }
}

Composables must be fast and free of side-effects. It must behave the same when called multiple times with the same argument, and it should not modify properties or global variables. We say that functions with this property are idempotent. This property is necessary for all composables so that UI can be emitted correctly when functions are reinvoked with new values.

Notice that the parameters provided to the functions entirely control the UI. This is what we mean by transforming state into UI. The logic in the function will guarantee that the UI can never get out of sync. If the list of answers changes, then a new UI is generated from the new list of answers by executing this function again and redrawning the UI if necessary.

This process of regenerating UI when state changes is called recomposition. Since composables are immutable, recomposition is the mechanism for updating UI with new state.

Recomposition and State of composable functions

Recomposition happens when a composable is reinvoked with different function parameters. This happens because State that the function depends on changes.

For example, say the SurveyAnswer composable accepts a parameter isSelected which dictates if the answer is selected or not. Initially, no answer is selected:

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  answers.forEach { answer ->
    SurveyAnswer(
      answer = answer,
      isSelected = false,
    )
  }
}

In the View world, interacting by tapping on one of the answer UI elements would visually toggle it as Views hold their own state. In the Compose world, however, since false is provided to all SurveyAnswer composables, all answers would remain unselected despite user interaction. To make them visually respond to user interaction, the composable needs to be recomposed so that the UI can be regenerated with new state.

To do that, a new variable needs to be introduced which holds the selected answer. Additionally, the variable needs to be a <a class="au la" href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/MutableState" target="_blank" rel="noopener ugc nofollow">MutableState</a>— an observable type that is integrated within the Compose runtime. Any changes to the state will automatically schedule a recomposition for any composables that read it. A new MutableState can be created with the <a class="au la" href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateOf(kotlin.Any,androidx.compose.runtime.SnapshotMutationPolicy)" target="_blank" rel="noopener ugc nofollow">mutableStateOf</a>method like so:

Read More  Coding with Serenade: Hands-Free Voice-Activated Programming
// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  // Initialize selectedAnswer to null since no answer will be selected at first
  var selectedAnswer: MutableState<Answer?> =
    mutableStateOf(null)
  answers.forEach { answer ->
    SurveyAnswer(
      answer = answer,
      isSelected = (selectedAnswer.value == answer),
    )
  }
}

Note in the snippet above that the value of isSelected has been updated to compare the current answer to selectedAnswer. Since selectedAnswer is of type MutableState, we have to use the value property to get the selected answer. When this value changes, Compose will automatically re-execute the SurveyAnswer so that the selected answer is highlighted.

The snippet above won’t quite work, however. The value of selectedAnswer needs to be remembered across recomposition so that it is not overwritten when SingleChoiceQuestion is reinvoked. To fix that, the mutableStateOf call should be performed within a call to remember. This guarantees that the value is remembered, and not reset, when the composable recomposes. To remember values across configuration changes, we can also use rememberSaveable:

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  var selectedAnswer: Answer? by
    rememberSaveable { mutableStateOf(null) }
  answers.forEach { answer ->
    SurveyAnswer(
      answer = answer,
      isSelected = (selectedAnswer == answer),
    )
  }
}

The above code snippet can further be refined by using Kotlin’s delegated property syntax on the selectedAnswer variable. Doing so changes the type from MutableState<Answer?> to simply, Answer?. This syntax is quite nice as we can work directly with the value of the underlying state — no more calls to the value property on a MutableState object:

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  var selectedAnswer: Answer? by
    rememberSaveable { mutableStateOf(null) }
  answers.forEach { answer ->
    SurveyAnswer(
      answer = answer,
      isSelected = (selectedAnswer == answer),
    )
  }
}

With our newly introduced state, we can pass a lambda function for the onAnswerSelected parameter so we can perform an action when the user makes a selection. In the definition of this lambda, we can set the value of selectedAnswer to the new one:

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun SingleChoiceQuestion(answers: List<Answer>) {
  var selectedAnswer: Answer? by
    rememberSaveable { mutableStateOf(null) }
  answers.forEach { answer ->
    SurveyAnswer(
      answer = answer,
      isSelected = (selectedAnswer == answer),
      onAnswerSelected = { answer -> selectedAnswer = answer }
    )
  }
}

If you recall from the previous article, events are the mechanism for how State is updated. Here, the event onAnswerSelected would be invoked when the user interacts by tapping an answer.

Read More  A New Look For The Red Pin On Maps JavaScript, Android And iOS

The Compose runtime automatically keeps track of where state reads occur so that it can intelligently recompose composables that depend on that state. As a result, you don’t need to explicitly observe State, or manually update the UI.

Behaviors and properties of composables

There are some other behavioral properties of composable functions you should also be aware of. Because of these behaviors, it’s important that your composable functions are side-effect free, and behave the same when called multiple times with the same argument.

  1. Composables can execute in any order

Looking at the snippet below, you might assume that the code runs sequentially. But this isn’t necessarily true. Compose can recognize that some UI elements are higher priority than others, and so those elements might be drawn first. Say for example you have code that draws three screens in a tab layout. You might assume that StartScreen is executed first, however, these executions can happen in any order.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun ButtonRow() {
  MyFancyNavigation {
    StartScreen()
    MiddleScreen()
    EndScreen()
  }
}

2. Composable functions can run in parallel

Composables can run in parallel thereby taking advantage of multiple cores which improves the rendering performance of a screen. In the code snippet below, the code runs side-effect free and transforms the input list to UI.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun ListComposable(myList: List<String>) {
  Row(horizontalArrangement = Arrangement.SpaceBetween) {
    Column {
      for (item in myList) {
        Text("Item: $item")
      }
    }
    Text("Count: ${myList.size}")
  }
}

However, if the function writes to a local variable, such as in the snippet below, the code is no longer considered side-effect free. Doing something similar to the code below may result in odd behaviors in your UI.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun ListWithBug(myList: List<String>) {
    var items = 0
    Row(horizontalArrangement = Arrangement.SpaceBetween) {
      Column {
        for (item in myList) {
          Text("Item: $item")
          items++ // Avoid! Side-effect of the column recomposing.
        }
      }
      Text("Count: $items")
    }
}

3. Recomposition skip as much as possible

Compose does its best to recompose only portions of that UI that need to be updated. If a composable doesn’t use the state that triggered recomposition, then it will be skipped. In the snippet, if the name string changes, the Header and Footer composables will not be re-executed since it does not depend on that state.

// Copyright 2022 Google LLC.	
// SPDX-License-Identifier: Apache-2.0

@Composable
fun GreetingScreen(name: String) {
  Column {
    Header()
    Greeting(name = name)
    Footer()
  }
}

4. Recomposition is optimistic

Recomposition is optimistic — this means that Compose expects to finish recomposition before the parameters change again. If a parameter does change before recomposition finishes, Compose might cancel the recomposition and restart it with the new parameter.

5. Composable functions might run frequently

Lastly, composable functions might run frequently. This might be the case if your composable function contains an animation that needs to be executed for every frame. This is why it’s important to make sure that your composable functions are fast to avoid dropped frames.

If you need to perform long-running operation, don’t do it in the composable function, Instead perform it off the UI thread and only use the result in the composable.

Summary

We covered a lot! To summarize:

  • You can create composable functions by using the @Composable annotation
  • It’s quick and easy to create composables, encouraging you to organize your UI into a library of reusable components.
  • Composables can and should accept parameters to configure their behavior
  • MutableState, remember and rememberSaveable can be used to store a component’s state and have Compose automatically track and recompose changes
  • Composables should be side-effect free

We also learned about some interesting properties of composables. Composables can:

  • Execute in any order
  • Run in parallel
  • Be skipped
  • Run frequently

The Compose toolkit provides a lot of foundational and powerful composables that help you build beautiful apps. That’s what we’ll cover next. If you want to jump ahead, check out the following resources:

  • Basic layouts in Compose
  • Jetpack Compose theming
  • Theming in Compose
  • Compose samples

Got any questions? Leave a comment below or use the #MADCompose hashtag on Twitter and we will address your questions in our upcoming live Q&A for the series on October 13. Stay tuned!

¹ Not all functions annotated with @Composable return UI. For example, functions that make a call to remember don’t return UI, however, they do produce a node in the Compose UI tree.

By Chris Arriola
Source Medium


For enquiries, product placements, sponsorships, and collaborations, connect with us at [email protected]. We'd love to hear from you!

Our humans need coffee too! Your support is highly appreciated, thank you!

aster.cloud

Related Topics
  • Android
  • Composables
  • Compose
  • Medium
You May Also Like
View Post
  • Architecture
  • Data
  • Engineering
  • People
  • Programming
  • Software Engineering
  • Technology
  • Work & Jobs

Predictions: Top 25 Careers Likely In High Demand In The Future

  • June 6, 2023
View Post
  • Programming
  • Software Engineering
  • Technology

Build a Python App to Alert You When Asteroids Are Close to Earth

  • May 22, 2023
View Post
  • Programming

Illuminating Interactions: Visual State In Jetpack Compose

  • May 20, 2023
View Post
  • Computing
  • Data
  • Programming
  • Software
  • Software Engineering

The Top 10 Data Interchange Or Data Exchange Format Used Today

  • May 11, 2023
View Post
  • Architecture
  • Programming
  • Public Cloud

From Receipts To Riches: Save Money W/ Google Cloud & Supermarket Bills – Part 1

  • May 8, 2023
View Post
  • Programming
  • Public Cloud

3 New Ways To Authorize Users To Your Private Workloads On Cloud Run

  • May 4, 2023
View Post
  • Programming
  • Public Cloud

Buffer HTTP Requests With Cloud Tasks

  • May 4, 2023
View Post
  • Programming
  • Public Cloud
  • Software
  • Software Engineering

Learn About Google Cloud’s Updated Renderer For The Maps SDK For Android

  • May 4, 2023

Stay Connected!
LATEST
  • college-of-cardinals-2025 1
    The Definitive Who’s Who of the 2025 Papal Conclave
    • May 7, 2025
  • conclave-poster-black-smoke 2
    The World Is Revalidating Itself
    • May 6, 2025
  • 3
    Conclave: How A New Pope Is Chosen
    • April 25, 2025
  • Getting things done makes her feel amazing 4
    Nurturing Minds in the Digital Revolution
    • April 25, 2025
  • 5
    AI is automating our jobs – but values need to change if we are to be liberated by it
    • April 17, 2025
  • 6
    Canonical Releases Ubuntu 25.04 Plucky Puffin
    • April 17, 2025
  • 7
    United States Army Enterprise Cloud Management Agency Expands its Oracle Defense Cloud Services
    • April 15, 2025
  • 8
    Tokyo Electron and IBM Renew Collaboration for Advanced Semiconductor Technology
    • April 2, 2025
  • 9
    IBM Accelerates Momentum in the as a Service Space with Growing Portfolio of Tools Simplifying Infrastructure Management
    • March 27, 2025
  • 10
    Tariffs, Trump, and Other Things That Start With T – They’re Not The Problem, It’s How We Use Them
    • March 25, 2025
about
Hello World!

We are aster.cloud. We’re created by programmers for programmers.

Our site aims to provide guides, programming tips, reviews, and interesting materials for tech people and those who want to learn in general.

We would like to hear from you.

If you have any feedback, enquiries, or sponsorship request, kindly reach out to us at:

[email protected]
Most Popular
  • 1
    IBM contributes key open-source projects to Linux Foundation to advance AI community participation
    • March 22, 2025
  • 2
    Co-op mode: New partners driving the future of gaming with AI
    • March 22, 2025
  • 3
    Mitsubishi Motors Canada Launches AI-Powered “Intelligent Companion” to Transform the 2025 Outlander Buying Experience
    • March 10, 2025
  • PiPiPi 4
    The Unexpected Pi-Fect Deals This March 14
    • March 13, 2025
  • Nintendo Switch Deals on Amazon 5
    10 Physical Nintendo Switch Game Deals on MAR10 Day!
    • March 9, 2025
  • /
  • Technology
  • Tools
  • About
  • Contact Us

Input your search keywords and press Enter.