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

It's Compose O'Clock on Android (DevFest Live 2021)

It's Compose O'Clock on Android (DevFest Live 2021)

Jetpack Compose is changing how UI is being built on Android. This new toolkit is replacing the now 10-year-old, somewhat tedious View system with a declarative, functional approach, and promises to simplify and speed up UI development.

This is happening at the same time as Flutter continuously gaining popularity, and iOS getting its own declarative UI APIs with SwiftUI.

This session explains what Jetpack Compose is, and then shows a quick example of interesting UI with it.

Márton Braun

June 03, 2021
Tweet

More Decks by Márton Braun

Other Decks in Technology

Transcript

  1. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter
  2. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter
  3. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2
  4. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2
  5. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2 SwiftUI
  6. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2 SwiftUI
  7. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  8. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  9. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  10. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  11. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  12. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 Jetpack Compose alpha Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  13. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 Jetpack Compose alpha Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0” Jetpack Compose beta
  14. Jetpack Compose alpha Jetpack Compose announced React React Native Flutter

    Flutter 2 SwiftUI SwiftUI “2.0” Jetpack Compose beta 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012 2022
  15. Jetpack Compose alpha Jetpack Compose announced React React Native Flutter

    Flutter 2 SwiftUI SwiftUI “2.0” Jetpack Compose beta 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012 2022
  16. Common traits › Declarative › Component-based › UI is a

    function of state › Mostly blue › Quick iteration, fast previews
  17. Common traits › Declarative › Component-based › UI is a

    function of state › Mostly blue › Quick iteration, fast previews › Multiplatform ambitions
  18. Jetpack Compose › Built with Composable functions › Eliminates the

    need to sync UI state › Unbundled › Not tied to OS versions
  19. Jetpack Compose › Built with Composable functions › Eliminates the

    need to sync UI state › Unbundled › Not tied to OS versions › All Kotlin › Awesome!
  20. Jetpack Compose › Built with Composable functions › Eliminates the

    need to sync UI state › Unbundled › Not tied to OS versions › All Kotlin › Awesome! › Two-way interoperability › Works with existing UI code
  21. A single number @Composable fun Number(value: Int) { Text( text

    = value.toString(), fontSize = 20.sp, modifier = Modifier.size(40.dp) ) }
  22. A single number @Composable fun Number(value: Int) { Text( text

    = value.toString(), fontSize = 20.sp, modifier = Modifier.size(40.dp) ) }
  23. A single number @Composable fun Number(value: Int) { Text( text

    = value.toString(), fontSize = 20.sp, modifier = Modifier.size(40.dp) ) } Number(3)
  24. A single number @Composable fun Number(value: Int) { Text( text

    = value.toString(), fontSize = 20.sp, modifier = Modifier ) } 3 Number(3) .size(40.dp)
  25. A single number @Composable fun Number(value: Int) { Text( text

    = value.toString(), fontSize = 20.sp, modifier = Modifier .background(Color.Black) ) } Number(3) .size(40.dp) 3
  26. A single number text = value.toString(), fontSize = 20.sp, )

    } Number(3) modifier = Modifier .size(40.dp) .background(Color.Black) Text( @Composable fun Number(value: Int) { 3
  27. A single number Box( ) { text = value.toString(), fontSize

    = 20.sp, ) } } Number(3) modifier = Modifier .size(40.dp) .background(Color.Black) Text( @Composable fun Number(value: Int) { 3
  28. A single number Box( contentAlignment = Alignment.Center, ) { text

    = value.toString(), fontSize = 20.sp, ) } } Number(3) 3 modifier = Modifier .size(40.dp) .background(Color.Black) Text( @Composable fun Number(value: Int) {
  29. backgroundColor ) { Text( text = value.toString(), fontSize = 20.sp,

    Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background( A single number Number(3) Color.Black @Composable fun Number(value: Int) { ) } } ) 3
  30. A single number Number(3) @Composable fun Number(value: Int, active: Boolean)

    { Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background( ) val backgroundColor = if (active) { MaterialTheme.colors.primary } else { MaterialTheme.colors.primaryVariant } ) } } ) { Text( text = value.toString(), fontSize = 20.sp, backgroundColor color = Color.White, 3
  31. A single number Column { Number(3, active = true) Number(7,

    active = false) } @Composable fun Number(value: Int, active: Boolean) { val backgroundColor = if (active) { MaterialTheme.colors.primary } else { MaterialTheme.colors.primaryVariant } Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background(backgroundColor) ) { Text( text = value.toString(), fontSize = 20.sp, color = Color.White, ) } } 3 7
  32. A single number Column { Number(3, active = true) Number(7,

    active = false) } @Composable fun Number(value: Int, active: Boolean) { val backgroundColor = if (active) { MaterialTheme.colors.primary } else { MaterialTheme.colors.primaryVariant } Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background(backgroundColor) ) { Text( text = value.toString(), fontSize = 20.sp, color = Color.White, ) } } 3 7
  33. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } 3 7
  34. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } 3 7
  35. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } 3 7
  36. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } 3 7
  37. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } NumberColumn(range = 0..9, current = 5) 3 7
  38. A column of digits @Composable fun NumberColumn( range: IntRange, current:

    Int, ) { Column( Modifier .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } NumberColumn(range = 0..9, current = 5) 2 0 1 4 3 6 5 8 7 6
  39. A complete clock @Composable fun Clock(time: Time) { Row( modifier

    = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { NumberColumn(0..2, time.hours / 10) NumberColumn(0..9, time.hours % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.minutes / 10) NumberColumn(0..9, time.minutes % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.seconds / 10) NumberColumn(0..9, time.seconds % 10) } }
  40. A complete clock @Composable fun Clock(time: Time) { Row( modifier

    = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { NumberColumn(0..2, time.hours / 10) NumberColumn(0..9, time.hours % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.minutes / 10) NumberColumn(0..9, time.minutes % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.seconds / 10) NumberColumn(0..9, time.seconds % 10) } }
  41. A complete clock @Composable fun Clock(time: Time) { Row( modifier

    = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { NumberColumn(0..2, time.hours / 10) NumberColumn(0..9, time.hours % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.minutes / 10) NumberColumn(0..9, time.minutes % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.seconds / 10) NumberColumn(0..9, time.seconds % 10) } }
  42. A complete clock @Composable fun Clock(time: Time) { Row( modifier

    = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { NumberColumn(0..2, time.hours / 10) NumberColumn(0..9, time.hours % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.minutes / 10) NumberColumn(0..9, time.minutes % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.seconds / 10) NumberColumn(0..9, time.seconds % 10) } } Clock(Time(14, 15, 59))
  43. A complete clock @Composable fun Clock(time: Time) { Row( modifier

    = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { NumberColumn(0..2, time.hours / 10) NumberColumn(0..9, time.hours % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.minutes / 10) NumberColumn(0..9, time.minutes % 10) Spacer(Modifier.size(16.dp)) NumberColumn(0..5, time.seconds / 10) NumberColumn(0..9, time.seconds % 10) } } Clock(Time(14, 15, 59)) 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 5 2 0 1 4 3 5 0 1 2
  44. Aligning digits 2 0 1 4 3 6 5 8

    7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 5 2 0 1 4 3 5 0 1 2 Clock(Time(14, 15, 59)) ) { range.forEach { num -> Number(num, num == current) } } } Column( Modifier.clip(RoundedCornerShape(percent = 25)) @Composable fun NumberColumn( range: IntRange, current: Int, ) {
  45. Aligning digits 2 0 1 4 3 6 5 8

    7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 5 2 0 1 4 3 5 0 1 2 Clock(Time(14, 15, 59)) ) { range.forEach { num -> Number(num, num == current) } } } Column( Modifier .clip(RoundedCornerShape(percent = 25)) @Composable fun NumberColumn( range: IntRange, current: Int, ) { val mid = (range.last - range.first) / 2f val offset = 40.dp * (mid - current) .offset(y = offset)
  46. Aligning digits @Composable fun NumberColumn( range: IntRange, current: Int, )

    { val mid = (range.last - range.first) / 2f val offset = 40.dp * (mid - current) Column( Modifier .offset(y = offset) .clip(RoundedCornerShape(percent = 25)) ) { range.forEach { num -> Number(num, num == current) } } } 2 0 1 4 3 6 5 8 7 6 2 0 1 4 3 6 5 8 7 6 2 4 3 6 5 8 7 6 2 0 1 4 3 5 2 0 1 4 3 5 0 1 2 Clock(Time(14, 15, 59))
  47. Driving the clock fun currentTime(): Time { val cal =

    Calendar.getInstance() return Time( hours = cal.get(Calendar.HOUR_OF_DAY), minutes = cal.get(Calendar.MINUTE), seconds = cal.get(Calendar.SECOND), ) } var time by remember { mutableStateOf(currentTime()) } LaunchedEffect(0) { while (true) { time = currentTime() delay(1000) } } Clock(time)
  48. Driving the clock fun currentTime(): Time { val cal =

    Calendar.getInstance() return Time( hours = cal.get(Calendar.HOUR_OF_DAY), minutes = cal.get(Calendar.MINUTE), seconds = cal.get(Calendar.SECOND), ) } var time by remember { mutableStateOf(currentTime()) } LaunchedEffect(0) { while (true) { time = currentTime() delay(1000) } } Clock(time)
  49. Driving the clock fun currentTime(): Time { val cal =

    Calendar.getInstance() return Time( hours = cal.get(Calendar.HOUR_OF_DAY), minutes = cal.get(Calendar.MINUTE), seconds = cal.get(Calendar.SECOND), ) } var time by remember { mutableStateOf(currentTime()) } LaunchedEffect(0) { while (true) { time = currentTime() delay(1000) } } Clock(time)
  50. Driving the clock fun currentTime(): Time { val cal =

    Calendar.getInstance() return Time( hours = cal.get(Calendar.HOUR_OF_DAY), minutes = cal.get(Calendar.MINUTE), seconds = cal.get(Calendar.SECOND), ) } var time by remember { mutableStateOf(currentTime()) } LaunchedEffect(0) { while (true) { time = currentTime() delay(1000) } } Clock(time)
  51. Driving the clock fun currentTime(): Time { val cal =

    Calendar.getInstance() return Time( hours = cal.get(Calendar.HOUR_OF_DAY), minutes = cal.get(Calendar.MINUTE), seconds = cal.get(Calendar.SECOND), ) } var time by remember { mutableStateOf(currentTime()) } LaunchedEffect(0) { while (true) { time = currentTime() delay(1000) } } Clock(time)
  52. Animations @Composable fun Number(value: Int, active: Boolean) { val backgroundColor

    = if (active) MaterialTheme.colors.primary else MaterialTheme.colors.primaryVariant Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background(backgroundColor), ) { Text( text = value.toString(), fontSize = 20.sp, color = Color.White, ) } }
  53. Animations @Composable fun Number(value: Int, active: Boolean) { val backgroundColor

    by animateColorAsState( if (active) MaterialTheme.colors.primary else MaterialTheme.colors.primaryVariant ) Box( contentAlignment = Alignment.Center, modifier = Modifier .size(40.dp) .background(backgroundColor), ) { Text( text = value.toString(), fontSize = 20.sp, color = Color.White, ) } }
  54. Animations = Column( Modifier .offset(y = offset) .clip(RoundedCornerShape(percent = 25))

    ) { range.forEach { num -> Number(num, num == current) } } } @Composable fun NumberColumn( range: IntRange, current: Int, ) { 40.dp * (mid - current) val mid = (range.last - range.first) / 2f val offset
  55. Animations Column( Modifier .offset(y = offset) .clip(RoundedCornerShape(percent = 25)) )

    { range.forEach { num -> Number(num, num == current) } } } @Composable fun NumberColumn( range: IntRange, current: Int, ) { 40.dp * (mid - current) val mid = (range.last - range.first) / 2f val offset by animateDpAsState( targetValue = )
  56. Resources • Compose O’Clock  https://zsmb.co/compose-o-clock/ • Source code on

    GitHub  https://github.com/zsmb13/ComposeClock • Jetpack Compose First Impressions & Learning Resources  https://getstream.io/blog/jetpack-compose-impressions-resources/
  57. zsmb.co/talks zsmb13 Márton Braun It's Compose O'Clock on Android ›

    Declarative, component-based › Quick iteration › Future of Android UI development