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

It's Compose O'Clock on Android (DevWeek Europe 2021)

It's Compose O'Clock on Android (DevWeek Europe 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.

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

April 27, 2021
Tweet

Transcript

  1. Márton Braun zsmb.co zsmb13 It’s Compose O'Clock on Android

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2022 Jetpack Compose announced React React Native Flutter Flutter 2 SwiftUI SwiftUI “2.0”
  18. 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”
  19. 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
  20. 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
  21. 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
  22. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 2008 2009 2010 2011 2007
  23. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 2008 2009 2010 2011 2007
  24. 2013 2014 2015 2016 2017 2018 2019 2020 2021 2012

    2022 2008 2009 2010 2011 2007
  25. Common traits

  26. Common traits › Declarative

  27. Common traits › Declarative › Component-based

  28. Common traits › Declarative › Component-based › UI is a

    function of state
  29. Common traits › Declarative › Component-based › UI is a

    function of state › Mostly blue
  30. Common traits › Declarative › Component-based › UI is a

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

    function of state › Mostly blue › Quick iteration, fast previews › Multiplatform ambitions
  32. Jetpack Compose

  33. Jetpack Compose › Built with Composable functions › Eliminates the

    need to sync UI state
  34. Jetpack Compose › Built with Composable functions › Eliminates the

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

    need to sync UI state › Unbundled › Not tied to OS versions › All Kotlin › Awesome!
  36. 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
  37. Let’s get ticking

  38. A single number @Composable fun Number(value: Int) { }

  39. A single number @Composable fun Number(value: Int) { Text( text

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

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

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

    = value.toString(), fontSize = 20.sp, modifier = Modifier ) } 3 Number(3) .size(40.dp)
  43. 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
  44. 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
  45. 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
  46. 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) {
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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) } }
  58. 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) } }
  59. 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) } }
  60. 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))
  61. 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
  62. 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, ) {
  63. 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)
  64. 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))
  65. 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)
  66. 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)
  67. 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)
  68. 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)
  69. 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)
  70. 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, ) } }
  71. 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, ) } }
  72. 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
  73. 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 = )
  74. Animated

  75. Polished

  76. 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/
  77. zsmb13 zsmb.co/talks

  78. zsmb.co/talks zsmb13 Márton Braun It's Compose O'Clock on Android ›

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