Blog Infos
Author
Published
Topics
, , , ,
Published

This post is to see how to render a composable list, more at an educational level than a practical one, since in most cases we can use lazy lists.

I once had to deal with a screen that painted several nested lists dynamically, using generic types, quite complex due to the way it was built and also using the hybrid XML + Jetpack Compose system, so in the end I ended up using a composable list, or at least that was the solution I found by reusing the pieces I had available.

But again, in most cases this approach will be the exception or you will probably come up with other solutions.

Composables list

In this example, the UI will receive three values ​​that will be emitted every second:

private val viewTypeState: MutableState<ViewType?> = mutableStateOf(null)

init {
  emitViewTypes()
}

private fun emitViewTypes() {
  lifecycleScope.launch {
    delay(1_000)
    viewTypeState.value = ViewType.Header
    delay(1_000)
    viewTypeState.value = ViewType.Body
    delay(1_000)
    viewTypeState.value = ViewType.Footer
  }
}

The list will be dynamically created in the UI and displayed like this:

@Composable
private fun RenderViews() {
    val views: MutableList<@Composable () -> Unit> = remember {
        mutableListOf()
    }

    viewTypeState.value?.let { viewType ->
        val view = getView(text = viewType.title)
        views.add(view)

        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier.padding(16.dp),
        ) {
            views.forEach { view ->
                view()
            }
        }
    }
}

@Composable
private fun getView(text: String): @Composable () -> Unit {
    return {
        Text(text = text)
    }
}

That is, the getView() method returns a composable lambda that, when invoked, will display a text view.

And the RenderViews() method goes through the list of views, invoking them item by item, to display the views on the screen.

When running the above code, it looks like the result will be this:

Compose list expected

But it actually shows:

Compose list real

So what’s going on?

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

No results found.

Reference in memory of lambdas

What happens is that every time the lambda is invoked, the same reference is obtained in memory, so:

  1. The Header is emitted, the view with the header is added to the list, and the Header view is displayed.
  2. The Body is emitted, the view with the body is added to the list, and the Body view is displayed twice, since the first item that had the value of Header now has the value of Body.
  3. The Footer is emitted, the view with the Footer is added to the list, and the Footer view is displayed three times, since the first item and the second item that had the value of Body now have the value of Footer.

And so on, the last value emitted is added to the list and affects the previous elements, since the list lambdas have the same reference, so they will always render the same view (Text) with a different value (title):

This could be due to how Kotlin handles different lambda instances depending on the context, in this case the let block, even though the content is different.

Different contexts to have different lambda references

Therefore, a solution could have different contexts to generate different instances. In this case, using the when block:

@Composable
private fun RenderViews() {
    val views: MutableList<@Composable () -> Unit> = remember {
        mutableListOf()
    }

    when (val viewType = viewTypeState.value) {
        ViewType.Body -> {
            val view = getView(text = viewType.title)
            views.add(view)
        }

        ViewType.Footer -> {
            val view = getView(text = viewType.title)
            views.add(view)
        }

        ViewType.Header -> {
            val view = getView(text = viewType.title)
            views.add(view)
        }

        null -> Unit
    }

    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = Modifier.padding(16.dp),
    ) {
        views.forEach { view ->
            view()
        }
    }
}

Thus, calling the getView() method generates different instances:

. . .

I hope you don’t have to do this kind of workaround in your projects, but if you do, at least this can help you face future challenges. 🙂

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
Menu