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

Introducing Jetpack Compose into your existing app

Introducing Jetpack Compose into your existing app

In this talk I am explaining how to introduce Jetpack Compose into your existing app by following those steps:
- Theme
- Components
- Interop (with the old UI Toolkit)
- Full Screens
- Navigation

Alexander Gherschon

July 18, 2022
Tweet

More Decks by Alexander Gherschon

Other Decks in Programming

Transcript

  1. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  2. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  3. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  4. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  5. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  6. THEME - DEFINING @Composable fun IntroducingComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(),

    content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  7. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } }
  8. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } }
  9. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } }
  10. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } }
  11. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } }
  12. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  13. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  14. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  15. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  16. THEME - USAGE class MainActivity : ComponentActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  17. THEME - HOW DOES THIS WORK? class MainActivity : ComponentActivity()

    { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { IntroducingComposeTheme { GreetingScreen() } } } } @Composable fun GreetingScreen() { Surface( modi fi er = Modi fi er. fi llMaxSize(), color = MaterialTheme.colors.primary ) { Text(text = "Hello KotlinTLV!") } }
  18. THEME - MATERIAL THEME @Composable fun IntroducingComposeTheme( darkTheme: Boolean =

    isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
  19. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) }
  20. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same }
  21. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same }
  22. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same }
  23. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same }
  24. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same }
  25. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same } internal val LocalColors = staticCompositionLocalOf { error("LocalColors was not provided") }
  26. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same } internal val LocalColors = staticCompositionLocalOf { error("LocalColors was not provided") }
  27. THEME - MATERIAL THEME @Composable fun MaterialTheme( colors: Colors =

    MaterialTheme.colors, typography: Typography = MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit ) { CompositionLocalProvider( LocalColors provides colors, // more provided compositionLocals content = content ) } object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get() = LocalColors.current val typography: Typography // same val shapes: Shapes // same } internal val LocalColors = staticCompositionLocalOf { error("LocalColors was not provided") }
  28. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  29. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  30. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  31. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  32. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  33. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  34. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  35. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) }
  36. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) } object OZTheme { val isDarkMode: Boolean @Composable get() = LocalIsDarkMode.current val isGeneralMode: Boolean @Composable get() = LocalIsGeneralMode.current val colors: OZColors @Composable get() = LocalOZColors.current val generalColors: OZGeneralColors @Composable get() = LocalOZGeneralColors.current val typography: OZTypography @Composable get() = LocalOZTypography.current val shapes: OZShapes @Composable get() = LocalOZShapes.current }
  37. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) } object OZTheme { val isDarkMode: Boolean @Composable get() = LocalIsDarkMode.current val isGeneralMode: Boolean @Composable get() = LocalIsGeneralMode.current val colors: OZColors @Composable get() = LocalOZColors.current val generalColors: OZGeneralColors @Composable get() = LocalOZGeneralColors.current val typography: OZTypography @Composable get() = LocalOZTypography.current val shapes: OZShapes @Composable get() = LocalOZShapes.current }
  38. THEME - FULLY-CUSTOM THEME @Composable fun OZTheme( isDarkMode: Boolean =

    isSystemInDarkTheme(), isGeneralMode: Boolean = false, content: @Composable () -> Unit, ) { val colors = if (isDarkMode) ozDarkColors else ozLightColors val generalColors = ozGeneralColors val typography = OZTypography() val shapes = OZShapes() CompositionLocalProvider( LocalOZColors provides colors, LocalOZGeneralColors provides generalColors, LocalOZTypography provides typography, LocalOZShapes provides shapes, LocalIsDarkMode provides isDarkMode, LocalIsGeneralMode provides isGeneralMode, content = content ) } object OZTheme { val isDarkMode: Boolean @Composable get() = LocalIsDarkMode.current val isGeneralMode: Boolean @Composable get() = LocalIsGeneralMode.current val colors: OZColors @Composable get() = LocalOZColors.current val generalColors: OZGeneralColors @Composable get() = LocalOZGeneralColors.current val typography: OZTypography @Composable get() = LocalOZTypography.current val shapes: OZShapes @Composable get() = LocalOZShapes.current }
  39. THEME - CONCLUSION • Use Material Theme as your Design

    System if it’s enough for you • Have your designers build your brand aka Design System • Translate Your Design System into your own Fully Custom Theme
  40. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  41. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  42. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  43. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  44. COMPONENTS - STEP PROGRESS BAR enum class StepProgressBarVariant( val completedStepColor:

    Color, val unCompletedStepColor: Color, ) { Regular( completedStepColor = OZTheme.colors.buttonsPrimaryRegular, unCompletedStepColor = OZTheme.colors.systemGrayGray5 ), }
  45. COMPONENTS - STEP PROGRESS BAR enum class StepProgressBarVariant( val completedStepColor:

    Color, val unCompletedStepColor: Color, ) { Regular( completedStepColor = OZTheme.colors.buttonsPrimaryRegular, unCompletedStepColor = OZTheme.colors.systemGrayGray5 ), } Looks good?
  46. COMPONENTS - STEP PROGRESS BAR enum class StepProgressBarVariant( val completedStepColor:

    Color, val unCompletedStepColor: Color, ) { Regular( completedStepColor = OZTheme.colors.buttonsPrimaryRegular, unCompletedStepColor = OZTheme.colors.systemGrayGray5 ), } Looks good, but doesn’t work
  47. COMPONENTS - STEP PROGRESS BAR enum class StepProgressBarVariant( val completedStepColor:

    Color, val unCompletedStepColor: Color, ) { Regular( completedStepColor = OZTheme.colors.buttonsPrimaryRegular, unCompletedStepColor = OZTheme.colors.systemGrayGray5 ), } HOW DO WE FIX THIS?
  48. COMPONENTS - STEP PROGRESS BAR enum class StepProgressBarVariant( val completedStepColor:

    @Composable () -> Color, val unCompletedStepColor: @Composable () -> Color, ) { Regular( completedStepColor = { OZTheme.colors.buttonsPrimaryRegular }, unCompletedStepColor = { OZTheme.colors.systemGrayGray5 } ), }
  49. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  50. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  51. COMPONENTS - STEP PROGRESS BAR @Composable fun StepProgressBar( modi fi

    er: Modi fi er = Modi fi er, variant: StepProgressBarVariant = StepProgressBarVariant.Regular, numberOfSteps: Int, currentStep: Int ) { // some code here }
  52. COMPONENTS - STEP PROGRESS BAR { Row( modi fi er

    = modi fi er .width(230.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { // more code here } }
  53. COMPONENTS - STEP PROGRESS BAR { Row( modi fi er

    = modi fi er .width(230.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { // more code here } }
  54. COMPONENTS - STEP PROGRESS BAR { Row( modi fi er

    = modi fi er .width(230.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { // more code here } }
  55. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  56. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  57. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  58. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  59. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  60. COMPONENTS - STEP PROGRESS BAR for (step in 0 until

    numberOfSteps) { Step( completed = step < currentStep, completedColor = variant.completedStepColor(), unCompletedColor = variant.unCompletedStepColor(), modi fi er = Modi fi er .weight(1F), ) if (step < numberOfSteps - 1) { Spacer(modi fi er = Modi fi er.width(5.dp)) } }
  61. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  62. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  63. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  64. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  65. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  66. COMPONENTS - STEP PROGRESS BAR @Composable fun Step( modi fi

    er: Modi fi er = Modi fi er, completed: Boolean, completedColor: Color, unCompletedColor: Color ) { Divider( modi fi er = modi fi er .width(20.dp) .clip(OZTheme.shapes.cornerRadius50percent), color = if (completed) completedColor else unCompletedColor, thickness = 2.dp ) }
  67. COMPONENTS - STEP PROGRESS BAR @Preview(name = "Step Progress Bar

    / Light Mode", showBackground = true, uiMode = Con fi guration.UI_MODE_NIGHT_NO) @Preview(name = "Step Progress Bar / Dark Mode", showBackground = true, uiMode = Con fi guration.UI_MODE_NIGHT_YES) @Composable fun StepProgressBarPreview() { OZTheme { StepProgressBar(numberOfSteps = 10, currentStep = 2) } } @Preview(name = "Step Progress Bar / General Mode", showBackground = true, uiMode = Con fi guration.UI_MODE_NIGHT_YES) @Composable fun StepProgressBarGeneralPreview() { OZTheme(isGeneralMode = true) { StepProgressBar(numberOfSteps = 6, currentStep = 3) } }
  68. COMPONENTS - TESTING class StepProgressBarViewTest { @get:Rule val paparazzi =

    Paparazzi( theme = "android:Theme.AppCompat", deviceCon fi g = DeviceCon fi g.NEXUS_5, ) // screenshot tests here }
  69. COMPONENTS - TESTING class StepProgressBarViewTest { @get:Rule val paparazzi =

    Paparazzi( theme = "android:Theme.AppCompat", deviceCon fi g = DeviceCon fi g.NEXUS_5, ) // screenshot tests here }
  70. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  71. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  72. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  73. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  74. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  75. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  76. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  77. COMPONENTS - TESTING @Test fun sixStepsTwoCompleted() { paparazzi.snapshot { OZTheme

    { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modi fi er = Modi fi er . fi llMaxSize() .background(backgroundColor), ) { StepProgressBar( modi fi er = Modi fi er.background(componentBackgroundColor), numberOfSteps = 6, currentStep = 2, ) } } } }
  78. COMPONENTS - CONCLUSION • Translate design needs into the right

    parameters like a “Variant” • We can’t call something @Composable inside enums or classes but we can hold a Composable lambda • Use cashapp/paparazzi to screenshot-test your components
  79. INTEROP - USING COMPONENTS <?xml version="1.0" encoding="utf-8"?> <merge ...> <androidx.appcompat.widget.Toolbar

    ...> <androidx.compose.ui.platform.ComposeView android:id="@+id/step_progress_bar" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.appcompat.widget.Toolbar> </merge>
  80. INTEROP - USING COMPONENTS <?xml version="1.0" encoding="utf-8"?> <merge ...> <androidx.appcompat.widget.Toolbar

    ...> <androidx.compose.ui.platform.ComposeView android:id="@+id/step_progress_bar" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.appcompat.widget.Toolbar> </merge>
  81. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  82. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  83. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  84. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  85. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  86. INTEROP - USING COMPONENTS binding.stepProgressBar.setOZContent(forceLtr = false) { Column( verticalArrangement

    = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { StepProgressBar( numberOfSteps = numberOfSteps, currentStep = currentStep ) } }
  87. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  88. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  89. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  90. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  91. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  92. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  93. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  94. INTEROP - USING COMPONENTS fun ComposeView.setOZContent( forceLtr: Boolean = true,

    content: @Composable () -> Unit, ) { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { OZTheme { val direction = if (forceLtr) LayoutDirection.Ltr else LocalLayoutDirection.current CompositionLocalProvider( LocalLayoutDirection provides direction, content = content ) } } }
  95. INTEROP - CONCLUSION • Build your helper functions to make

    it easy to use • Do not forget setViewCompositionStrategy()
  96. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  97. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  98. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  99. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  100. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  101. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  102. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  103. FULL SCREENS - BASE FRAGMENTS abstract class ComposeFragment : Fragment()

    { protected open val isThemeInGeneralMode: Boolean = false override fun onCreateView(in fl ater: LayoutIn fl ater, c: ViewGroup?, sub: Bundle?): View? { return setOZContent(isThemeInGeneralMode) { ComposeContent() } } @Composable protected abstract fun ComposeContent() }
  104. FULL SCREENS - USING BASE FRAGMENT class SomeComposeFragment: ComposeFragment() {

    @Composable override fun ComposeContent() { TestComposeScreen() } }
  105. FULL SCREENS - USING BASE FRAGMENT class SomeComposeFragment: ComposeFragment() {

    @Composable override fun ComposeContent() { TestComposeScreen() } }
  106. FULL SCREENS - USING BASE FRAGMENT class SomeComposeFragment: ComposeFragment() {

    @Composable override fun ComposeContent() { TestComposeScreen() } }
  107. FULL SCREENS - USING BASE FRAGMENT class SomeComposeFragment: ComposeFragment() {

    @Composable override fun ComposeContent() { TestComposeScreen() } }
  108. FULL SCREENS - USING BASE FRAGMENT fun TestComposeScreen(testViewModel: TestMVIViewModel =

    viewModel()) { val uiState by testViewModel.uiState.collectAsState() Column(modi fi er = Modi fi er. fi llMaxSize()) { when (uiState) { TestUiState.Initial -> Text("Initial State") TestUiState.SecondState -> Text("Second State") } } }
  109. FULL SCREENS - USING BASE FRAGMENT fun TestComposeScreen(testViewModel: TestMVIViewModel =

    viewModel()) { val uiState by testViewModel.uiState.collectAsState() Column(modi fi er = Modi fi er. fi llMaxSize()) { when (uiState) { TestUiState.Initial -> Text("Initial State") TestUiState.SecondState -> Text("Second State") } } }
  110. FULL SCREENS - USING BASE FRAGMENT fun TestComposeScreen(testViewModel: TestMVIViewModel =

    viewModel()) { val uiState by testViewModel.uiState.collectAsState() Column(modi fi er = Modi fi er. fi llMaxSize()) { when (uiState) { TestUiState.Initial -> Text("Initial State") TestUiState.SecondState -> Text("Second State") } } }
  111. FULL SCREENS - USING BASE FRAGMENT fun TestComposeScreen(testViewModel: TestMVIViewModel =

    viewModel()) { val uiState by testViewModel.uiState.collectAsState() Column(modi fi er = Modi fi er. fi llMaxSize()) { when (uiState) { TestUiState.Initial -> Text("Initial State") TestUiState.SecondState -> Text("Second State") } } }
  112. FULL SCREENS - USING BASE FRAGMENT fun TestComposeScreen(testViewModel: TestMVIViewModel =

    viewModel()) { val uiState by testViewModel.uiState.collectAsState() Column(modi fi er = Modi fi er. fi llMaxSize()) { when (uiState) { TestUiState.Initial -> Text("Initial State") TestUiState.SecondState -> Text("Second State") } } }
  113. FULL SCREENS - USING BASE FRAGMENT class SomeComposeFragment: ComposeFragment() {

    @Composable override fun ComposeContent() { TestComposeScreen() } }
  114. NAVIGATION • Jetpack Navigation for Compose • Compose-destinations by Rafael

    Costa (1.3k stars on Github) • Voyager by Adiel Café (659 stars on Github)
  115. THANK YOU! Alexander Gherschon 
 Android Tech Lead @ ONE

    ZERO Digital Bank Blog - https://galex.co.il/ Twitter - https://twitter.com/galex Github - https://github.com/galex Linkedin - https://www.linkedin.com/in/agherschon/