Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Recomposition Roundup: All You Need to Know

Recomposition Roundup: All You Need to Know

Jetpack Compose, a modern UI toolkit for Android, offers a declarative approach to UI development. However, understanding and optimizing recompositions is crucial for delivering smooth and efficient user experiences. This technical session explores common pitfalls developers encounter when working with recompositions in Jetpack Compose.

We'll explore the impact of unstable classes, including external classes and lambdas, on unintended recompositions. These classes can often lead to unnecessary recompositions, impacting performance. Additionally, we'll discuss performance challenges associated with LazyList, columns, continuously changing state, and the role of modifiers in affecting recomposition behavior.

Through practical examples and code demonstrations, attendees will learn techniques to:

Identify and resolve recomposition issues
Optimize UI performance
Write efficient and stable Jetpack Compose code

By the end of this session, you'll have a solid understanding of recompositions and how to avoid common pitfalls, enabling you to create high-quality and performant Jetpack Compose applications.

Harun Wangereka

November 23, 2024
Tweet

More Decks by Harun Wangereka

Other Decks in Programming

Transcript

  1. Jetpack Compose is a modern UI toolkit for Android, offers

    a declarative approach to UI development
  2. Calling composable functions again with new data causes the function

    to be recomposed--the widgets emitted by the function are redrawn, if necessary, with new data. The Compose framework can intelligently recompose only the components that changed.
  3. @Composable fun ColumnKeys() { Screen { var movies by remember

    { mutableStateOf(buildMovies(4)) } Scaffold( topBar = { TopBar("Column Keys") { RefreshAction { movies = movies.shuffled() } } } ) { padding -> Column( modifier = Modifier.padding(padding) ) { for (movie in movies) { key(movie.id) { MovieItem(movie = movie) } } } } } } Column Keys
  4. @Composable fun DeferRead() { Screen { Scaffold( topBar = {

    TopBar("Defer Read") } ) { padding -> var sliderValue by remember { mutableFloatStateOf(0f) } Column( modifier = Modifier .padding(padding) .padding(horizontal = 16.dp) ) { Slider( value = sliderValue, onValueChange = { sliderValue = it } ) RedBall(offset = { sliderValue * 200 }) } } } } Deferred Reads
  5. @Composable fun RedBall(offset: () -> Float) { Box( modifier =

    Modifier .offset { IntOffset(offset().toInt(), 0) } .size(56.dp) .clip(CircleShape) .background(Color.Red) ) } Deferred Read
  6. LazyColumn( contentPadding = padding ) { items(movies, key = {

    it.id }) { MovieItem(movie = it) } } Lazy List Keys
  7. Screen { Scaffold( topBar = { TopBar("Stable State") } )

    { padding -> val contact = remember { Contact("John Doe") } var selected by remember { mutableStateOf(false) } Row( modifier = Modifier .fillMaxSize() .padding(padding), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { ContactDetail(contact) ToggleHeart( checked = selected, onCheckedChange = { selected = it } ) } } } Stable State
  8. @Composable fun DerivedState() { val scope = rememberCoroutineScope() val lazyState

    = rememberLazyListState() val showScrollToTop by remember { derivedStateOf { lazyState.firstVisibleItemIndex > 0 }} Screen { Scaffold( topBar = { TopBar("Derived State") { if (showScrollToTop) { TopBarAction( imageVector = Icons.Default.KeyboardArrowUp, contentDescription = "Scroll to top", onClick = { scope.launch { lazyState.scrollToItem(0)}} ) } } } ) { padding -> LazyColumn( state = lazyState, contentPadding = padding ) { items(buildMovies(100), key = { it.id }) { MovieItem(movie = it) } } } } }
  9. Provide unique keys, sp that Compose can efficiently update only

    the necessary items, minimizing the number of recompositions and improving performance. LazyList Keys Delay the reading of a state value until the layout or drawing phase, rather than during the composition phase Deferred read Efficiently derive state from other state values. Derived state use keys within a Column to optimize recomposition performance when dealing with dynamic lists Columns Keys Use of @Stable ensures that recomposition is optimized by avoiding unnecessary updates. Stable State In summary…
  10. Composable functions that depend on results of other composables Don’t

    create depandable In compose functions because they can run in pararrel Avoid any side effects Move any logic from composables to ViewModels, Presenters etc Move any logic out Things to keep in mind