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

2022 Compose Camp Pathway 1-2: State and Theming in Jetpack Compose

Jaewoong
October 26, 2022

2022 Compose Camp Pathway 1-2: State and Theming in Jetpack Compose

Jaewoong

October 26, 2022
Tweet

More Decks by Jaewoong

Other Decks in Programming

Transcript

  1. This work is licensed under the Apache 2.0 License State

    and Theming in Jetpack Compose Pathway 1-2
  2. This work is licensed under the Apache 2.0 License Codelab

    Guidelines • Clone the project • Checkout to a new branch
  3. This work is licensed under the Apache 2.0 License Compose

    Phases Which UI to draw Where to place UI. Measurement and placement How it renders
  4. This work is licensed under the Apache 2.0 License @Composable

    private fun WellnessTaskItem(..) { Text() Checkbox() IconButton() } Recomposition
  5. This work is licensed under the Apache 2.0 License Recomposition

    @Composable private fun WellnessTaskItem(..) { Text() Checkbox() IconButton() } Update UI (Click the Checkbox) @Composable private fun WellnessTaskItem(..) { Text() Checkbox() IconButton() } Recomposition
  6. This work is licensed under the Apache 2.0 License Recomposition

    @Composable private fun WellnessTaskItem(..) { Text() Checkbox() IconButton() } Smart Recomposition Update UI (Click the Checkbox) @Composable private fun WellnessTaskItem(..) { Text() Checkbox() IconButton() } Skip Skip Recomposition
  7. This work is licensed under the Apache 2.0 License Recomposition

    and Performance https://getstream.io/blog/jetpack-compose-guidelines/ https://youtu.be/EOQB8PTLkpY
  8. This work is licensed under the Apache 2.0 License What's

    the State? State An interface that has a value property during the execution of a Composable function. MutableState A mutable value holder where reads to the value property during the execution of a Composable function. Any changes to value will schedule recomposition of any composable functions that read value.
  9. This work is licensed under the Apache 2.0 License State

    and Recomposition Composition Display UI User Event Update State @Composable
  10. This work is licensed under the Apache 2.0 License State

    and Recomposition Composition Display UI User Event Update State @Composable
  11. This work is licensed under the Apache 2.0 License State

    and Recomposition Composition Display UI User Event Update State @Composable
  12. This work is licensed under the Apache 2.0 License State

    and Recomposition Composition Updated UI User Event Update State Recomposition @Composable
  13. This work is licensed under the Apache 2.0 License What's

    the State? Value Holder A value holder where reads to the value property during the execution of a Composable function. Recomposition Trigger When the value property is written to and changed, recomposition of any subscribed Recompose scopes will be scheduled. Observable The current Recompose scope will observe states and be subscribed to changes of that value.
  14. This work is licensed under the Apache 2.0 License State

    in Composable @Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { // Changes to count are now tracked by Compose val count: MutableState<Int> = mutableStateOf(0) Text("You've had ${count.value} glasses.") Button(onClick = { count.value++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } }
  15. This work is licensed under the Apache 2.0 License @Composable

    fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { // Changes to count are now tracked by Compose val count: MutableState<Int> = mutableStateOf(0) Text("You've had ${count.value} glasses.") Button(onClick = { count.value++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } } State in Composable Compilation error!
  16. This work is licensed under the Apache 2.0 License State

    in Composable @Composable fun WaterCounter(modifier: Modifier = Modifier) { val count: MutableState<Int> = mutableStateOf(0) .. } Initialize states
  17. This work is licensed under the Apache 2.0 License @Composable

    fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { var count by remember { mutableStateOf(0) } Text("You've had $count glasses.") Button(onClick = { count++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } } State and Remember Remember Composable functions can use the remember API to store an object in memory.
  18. This work is licensed under the Apache 2.0 License RemeberSaveable

    @Composable fun WaterCounter(modifier: Modifier = Modifier) { val count: MutableState<Int> by rememberSaveable { mutableStateOf(0) } .. } RememberSaveable It behaves similarly to remember, but the stored value will survive the activity or process recreation using the saved instance state mechanism. • Configuration changes • Switch between dark and light mode • Change language settings
  19. This work is licensed under the Apache 2.0 License Remember

    and Recomposition @Composable fun ContactList( contacts: List<Contact>, comparator: Comparator<Contact> ) { LazyColumn(Modifier) { items(contacts.sortedWith(comparator)) { contact -> // ... } } }
  20. This work is licensed under the Apache 2.0 License Remember

    and Recomposition @Composable fun ContactList( contacts: List<Contact>, comparator: Comparator<Contact> ) { LazyColumn(Modifier) { // DON'T DO THIS items(contacts.sortedWith(comparator)) { contact -> // ... } } }
  21. This work is licensed under the Apache 2.0 License Remember

    and Recomposition @Composable fun ContactList( contacts: List<Contact>, comparator: Comparator<Contact>, ) { val sortedContacts = remember(contacts, sortComparator) { contacts.sortedWith(sortComparator) } LazyColumn { items(sortedContacts) { // ... } …
  22. This work is licensed under the Apache 2.0 License Stateful

    vs Stateless @Composable fun WaterCounter(modifier: Modifier = Modifier) { val count: MutableState<Int> by remember { mutableStateOf(0) } .. } Stateful
  23. This work is licensed under the Apache 2.0 License Stateful

    vs Stateless @Composable fun WaterCounter(modifier: Modifier = Modifier) { val count: MutableState<Int> by remember { mutableStateOf(0) } .. } @Composable fun WaterCounter( modifier: Modifier = Modifier, count: Int = 0 ) { ... } Stateful Stateless
  24. This work is licensed under the Apache 2.0 License Why

    Stateless? Single source of truth By moving state instead of duplicating it, we're ensuring there's only one source of truth. This helps avoid bugs. Decoupled The state for the stateless Composable may be stored anywhere. It increases the reusability of the stateless Composable. Encapsulated Only stateful composables will be able to modify their state. It's completely internal.
  25. This work is licensed under the Apache 2.0 License State

    Hoisting @Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { var count by remember { mutableStateOf(0) } Text("You've had $count glasses.") Button(onClick = { count++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } }
  26. This work is licensed under the Apache 2.0 License State

    Hoisting @Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { var count by remember { mutableStateOf(0) } Text("You've had $count glasses.") Button(onClick = { count++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } } State Read state Write state
  27. This work is licensed under the Apache 2.0 License State

    Hoisting @Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { var count by remember { mutableStateOf(0) } Text("You've had $count glasses.") Button(onClick = { count++ }, Modifier.padding(top = 8.dp)) { Text("Add one") } } } @Composable fun StatelessCounter( count: Int, onIncrement: () -> Unit, modifier: Modifier = Modifier ) { Column(modifier = modifier.padding(16.dp)) { Text("You've had $count glasses.") Button(onClick = onIncrement, Modifier.padding(top = 8.dp)) { Text("Add one") } } }
  28. This work is licensed under the Apache 2.0 License State

    Hoisting @Composable fun StatefulCounter() { var waterCount by remember { mutableStateOf(0) } StatelessCounter(waterCount, { waterCount++ }) } State State @Composable fun StatelessCounter(count: Int, onIncrement: () -> Unit) { Text("You've had $count glasses.") Button(onClick = onIncrement, Modifier.padding(top = 8.dp)) { .. } Stateful Stateless
  29. This work is licensed under the Apache 2.0 License CompositionLocal

    Composable A Composable B Composable C @Composable fun ComposableA() { ComposableB(Color.White) } @Composable fun ComposableB(color: Color) { ComposableC(Color.White) } @Composable fun ComposableC(color: Color) { .. }
  30. This work is licensed under the Apache 2.0 License CompositionLocal

    Composable A Composable B Composable C @Composable fun ComposableA() { ComposableB(Color.White) } @Composable fun ComposableB(color: Color) { ComposableC(Color.White) } @Composable fun ComposableC(color: Color) { .. }
  31. This work is licensed under the Apache 2.0 License CompositionLocal

    Composable A Composable B Composable C @Composable fun ComposableA() { ComposableB(Color.White) } @Composable fun ComposableB(color: Color) { ComposableC(Color.White) } @Composable fun ComposableC(color: Color) { .. } Dependency
  32. This work is licensed under the Apache 2.0 License CompositionLocal

    Composable A Composable B Composable C @Composable fun ComposableA() { CompositionLocalProvider(LocalColor provides color) { .. } @Composable fun ComposableB() { ComposableC() } @Composable fun ComposableC() { val color = LocalColor.current }
  33. This work is licensed under the Apache 2.0 License CompositionLocal

    https://developer.android.com/jetpack/compose/compositionlocal val ColorCompositionLocal = staticCompositionLocalOf<Color> { error("No Color provided") } CompositionLocalProvider(ColorCompositionLocal provides color) { MyComposableFunction(...) } @Composable fun MyComposableFunction() { val color = ColorCompositionLocal.current }
  34. This work is licensed under the Apache 2.0 License Material

    Design in Compose Color Typography Shape
  35. This work is licensed under the Apache 2.0 License MaterialTheme

    @Composable fun JetnewsTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = myColorScheme, shapes = MyShapes, typography = MyTypography, content = content ) }
  36. This work is licensed under the Apache 2.0 License MaterialTheme

    @Composable fun MaterialTheme(..) { CompositionLocalProvider( LocalColorScheme provides rememberedColorScheme, LocalIndication provides rippleIndication, LocalRippleTheme provides MaterialRippleTheme, LocalShapes provides shapes, LocalTextSelectionColors provides selectionColors, LocalTypography provides typography, ) { ProvideTextStyle(value = typography.bodyLarge, content = content) } }
  37. This work is licensed under the Apache 2.0 License MaterialTheme

    @Composable fun MyMaterialTheme(..) { val isDarkTheme = isSystemInDarkTheme() val colorScheme = if (isDarkTheme) { darkColorSceme } else { lightColorSceme } MaterialTheme(colorScheme = colorScheme) { MyScreen() } }