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

    View Slide

  2. Speaker
    skydoves
    @github_skydoves
    Android Developer Advocate @ Stream
    엄재웅 (Jaewoong Eum)

    View Slide

  3. View Slide

  4. Codelab Guidelines

    View Slide

  5. This work is licensed under the Apache 2.0 License
    https://github.com/gdgand/ComposeCamp2022
    Codelab Guidelines
    Fork the project on GitHub

    View Slide

  6. This work is licensed under the Apache 2.0 License
    Codelab Guidelines
    ● Clone the project
    ● Checkout to a new branch

    View Slide

  7. Compose Phases

    View Slide

  8. 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

    View Slide

  9. This work is licensed under the Apache 2.0 License
    Recomposition
    Update UI
    (Click the Checkbox)

    View Slide

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

    View Slide

  11. 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

    View Slide

  12. 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

    View Slide

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

    View Slide

  14. State in Compose

    View Slide

  15. 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.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. 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.

    View Slide

  21. 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 = mutableStateOf(0)
    Text("You've had ${count.value} glasses.")
    Button(onClick = { count.value++ }, Modifier.padding(top = 8.dp)) {
    Text("Add one")
    }
    }
    }

    View Slide

  22. 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 = 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!

    View Slide

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

    View Slide

  24. 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.

    View Slide

  25. This work is licensed under the Apache 2.0 License
    RemeberSaveable
    @Composable
    fun WaterCounter(modifier: Modifier = Modifier) {
    val count: MutableState 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. 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.

    View Slide

  32. 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")
    }
    }
    }

    View Slide

  33. 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

    View Slide

  34. 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")
    }
    }
    }

    View Slide

  35. 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

    View Slide

  36. CompositionLocal

    View Slide

  37. 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) {
    ..
    }

    View Slide

  38. 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) {
    ..
    }

    View Slide

  39. 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

    View Slide

  40. 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
    }

    View Slide

  41. This work is licensed under the Apache 2.0 License
    CompositionLocal
    By Elye

    View Slide

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

    View Slide

  43. Compose Theming

    View Slide

  44. This work is licensed under the Apache 2.0 License
    Material Design

    View Slide

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

    View Slide

  46. 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
    )
    }

    View Slide

  47. 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)
    }
    }

    View Slide

  48. 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()
    }
    }

    View Slide

  49. Thank you!
    This work is licensed under the Apache 2.0 License

    View Slide