Slide 1

Slide 1 text

Anton Shilov Composing a Design System 1

Slide 2

Slide 2 text

Bumble Badoo 2

Slide 3

Slide 3 text

Private and con fi dential ~1 M LoC ~500 0 UI-tests ~10 0 UI-components 3

Slide 4

Slide 4 text

Private and con fi dential 1. Design syste m • Token s • Component s • Pattern s 2. Android View interoperabilit y 3. Testin g 4. Integration 4

Slide 5

Slide 5 text

Private and con fi dential Jetpack Compose — declarative UI framework for Kotlin 5

Slide 6

Slide 6 text

Private and con fi dential 6 @Composable fun HelloCompose() { Column{ Text(text = "Hi there!") Text(text = "Welcome to the talk") Button(onClick = {}) { Text(text = "Let's start!") } } }

Slide 7

Slide 7 text

Private and con fi dential 7 @Composable fun HelloCompose() { Column{ Text(text = "Hi there!") Text(text = "Welcome to the talk") Button(onClick = {}) { Text(text = "Let's start!") } } }

Slide 8

Slide 8 text

Private and con fi dential 8 @Composable fun HelloCompose() { Column{ Text(text = "Hi there!") Text(text = "Welcome to the talk") Button(onClick = {}) { Text(text = "Let's start!") } } }

Slide 9

Slide 9 text

Private and con fi dential 9 @Composable fun HelloCompose() { Column{ Text(text = "Hi there!") Text(text = "Welcome to the talk") Button(onClick = {}) { Text(text = "Let's start!") } } }

Slide 10

Slide 10 text

Private and con fi dential • Future of Android U I • Faster developmen t • Easy integration with our architecture — MV I • Our own declarative UI framewor k • Easy to implement design systems Why Compose? 10

Slide 11

Slide 11 text

Private and con fi dential Goal 🎯 Use Compose once the release is stable 11

Slide 12

Slide 12 text

Private and con fi dential • How to create a design system ? • How to integrate with the current UI ? • How to test ? • How to integrate within the business logic? Questions 12

Slide 13

Slide 13 text

Design System 13

Slide 14

Slide 14 text

Private and con fi dential Design system — set of rules and tools for UI design 14

Slide 15

Slide 15 text

Private and con fi dential 15 Text styles Text styles Text styles Text styles

Slide 16

Slide 16 text

Private and con fi dential Dimensions 16 Colors Design tokens

Slide 17

Slide 17 text

Private and con fi dential Icons 17

Slide 18

Slide 18 text

Private and con fi dential Components 18

Slide 19

Slide 19 text

Private and con fi dential Patterns 19

Slide 20

Slide 20 text

Private and con fi dential 20

Slide 21

Slide 21 text

Private and con fi dential 21 MatchPhotos Text - H1 Button - Filled - White Text - P1 CtaBox ic badge-feature-match

Slide 22

Slide 22 text

Private and con fi dential data class TextStyle( val color: Color = Color.Unspecified, val fontSize: TextUnit = TextUnit.Inherit, val fontWeight: FontWeight? = null, val fontStyle: FontStyle? = null, val fontSynthesis: FontSynthesis? = null, val fontFamily: FontFamily? = null, val fontFeatureSettings: String? = null, val letterSpacing: TextUnit = TextUnit.Inherit, val baselineShift: BaselineShift? = null, val textGeometricTransform: TextGeometricTransform? = null, val localeList: LocaleList? = null, val background: Color = Color.Unspecified, val textDecoration: TextDecoration? = null, val shadow: Shadow? = null, val textAlign: TextAlign? = null, val textDirection: TextDirection? = null, val lineHeight: TextUnit = TextUnit.Inherit, val textIndent: TextIndent? = null ) 22

Slide 23

Slide 23 text

Private and con fi dential 23 H1 = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 24.sp, lineHeight = 28.sp )

Slide 24

Slide 24 text

Private and con fi dential 24 data class Typography( val H1: TextStyle, val H2: TextStyle, val Action: TextStyle, val Title: TextStyle, val P1: TextStyle, val P2: TextStyle, val P3: TextStyle )

Slide 25

Slide 25 text

Private and con fi dential 25 colorResource(id = R.color.black) Color(0xFF783bf9)

Slide 26

Slide 26 text

Private and con fi dential 26 data class Palette( val primary: Color, val matchPhotosBorder: Color, //. . . )

Slide 27

Slide 27 text

Private and con fi dential 27 val sizeDp = 16.dp val sizeSp = 16.sp dimensionResource(id = R.dimen.size_lg)

Slide 28

Slide 28 text

Private and con fi dential 28 inline val Int.dp: Dp get() = Dp(value = this)

Slide 29

Slide 29 text

Private and con fi dential 29 data class DimensionsTokens( val tokenMatchPhotosPhotoSize: Dp, val tokenButtonHeight: Dp, val tokenButtonIconSize: Dp, val tokenButtonIconTextSpacing: Dp, val tokenButtonDisabledOpacity: Float, val tokenButtonStrokeBorderWidth: Dp, val tokenMatchPhotosBorderWidth: Dp, //. . . )

Slide 30

Slide 30 text

Icons 30 ImageVector ImageBitmap Drawable

Slide 31

Slide 31 text

Private and con fi dential 31 imageResource(id = R.drawable.photo) vectorResource(id = R.drawable.ic_match)

Slide 32

Slide 32 text

Private and con fi dential 32 val Icons.Filled.TestVector: ImageVector get() = materialIcon(name = "Filled.TestVector") { materialPath(fillAlpha = 0.8f) { moveTo(20.0f, 10.0f) lineToRelative(0.0f, 10.0f) lineToRelative(-10.0f, 0.0f) close() } group { materialPath(pathFillType = EvenOdd) { moveTo(0.0f, 10.0f) lineToRelative(-10.0f, 0.0f) close() } } }

Slide 33

Slide 33 text

Styles + Tokens = Theme 33

Slide 34

Slide 34 text

Private and con fi dential 34 App Scaffol d Toolba r Tex t Conten t Hear t

Slide 35

Slide 35 text

Private and con fi dential 35 App Scaffol d Toolba r Tex t Conten t Hear t App Scaffol d Toolba r Tex t Conten t Hear t

Slide 36

Slide 36 text

Private and con fi dential 36 val LocalHeartColor = compositionLocalOf() @Composable fun HeartsScreen() { Providers( LocalHeartColor provides Color.Red ) { LocalHeartColor.current // I can access to the color her } }

Slide 37

Slide 37 text

Private and con fi dential 37 val LocalHeartColor = compositionLocalOf() @Composable fun HeartsScreen() { Providers( LocalHeartColor provides Color.Red ) { LocalHeartColor.current // I can access to the color her } }

Slide 38

Slide 38 text

Private and con fi dential 38 val LocalHeartColor = compositionLocalOf() @Composable fun HeartsScreen() { Providers( LocalHeartColor provides Color.Red ) { LocalHeartColor.current // I can access to the color her } }

Slide 39

Slide 39 text

Private and con fi dential 39 val LocalHeartColor = compositionLocalOf() @Composable fun HeartsScreen() { Providers( LocalHeartColor provides Color.Red ) { LocalHeartColor.current // I can access to the color her } }

Slide 40

Slide 40 text

Private and con fi dential 40 Key Value LocalHeartColor Color.Red LocalContext Context LocalLifecycleOwner LifecycleOwner

Slide 41

Slide 41 text

Private and con fi dential 41 val LocalTypography = staticCompositionLocalOf() @Composable val TextStyles: Typography get() = LocalTypography.current TextStyles.H1 // LocalTypography.current.H1

Slide 42

Slide 42 text

Private and con fi dential 42 val LocalTypography = staticCompositionLocalOf() @Composable val TextStyles: Typography get() = LocalTypography.current TextStyles.H1 // LocalTypography.current.H1

Slide 43

Slide 43 text

Private and con fi dential 43 val LocalTypography = staticCompositionLocalOf() @Composable val TextStyles: Typography get() = LocalTypography.current TextStyles.H1 // LocalTypography.current.H1

Slide 44

Slide 44 text

Private and con fi dential 44 val LocalTypography = staticCompositionLocalOf() @Composable val TextStyles: Typography get() = LocalTypography.current TextStyles.H1 // LocalTypography.current.H1

Slide 45

Slide 45 text

Private and con fi dential 45 @Composable fun Theme( typography: Typography, colors: Palette, dimensions: DimensionsTokens, content: @Composable () -> Unit ) { Providers( LocalTypography provides typography, LocalColors provides colors, LocalDimensions provides dimensions, ) { content() } }

Slide 46

Slide 46 text

Private and con fi dential 46 @Composable fun Theme( typography: Typography, colors: Palette, dimensions: DimensionsTokens, content: @Composable () -> Unit ) { Providers( LocalTypography provides typography, LocalColors provides colors, LocalDimensions provides dimensions, ) { content() } }

Slide 47

Slide 47 text

Private and con fi dential 47 @Composable fun Theme( typography: Typography, colors: Palette, dimensions: DimensionsTokens, content: @Composable () -> Unit ) { Providers( LocalTypography provides typography, LocalColors provides colors, LocalDimensions provides dimensions, ) { content() // TextStyles.H1 } }

Slide 48

Slide 48 text

Private and con fi dential Composition Local is not a DI framework! 48

Slide 49

Slide 49 text

Private and con fi dential 1. Design syste m • Token s • Component s • Pattern s 2. Android View intero p 3. Testin g 4. Integration 49

Slide 50

Slide 50 text

Private and con fi dential 50 MatchPhotos Button - Filled - White

Slide 51

Slide 51 text

Button 51

Slide 52

Slide 52 text

Private and con fi dential 52 @Composable fun MaterialButton() { Button( onClick = {}, content = { Text(text = "Hello Compose”) } ) }

Slide 53

Slide 53 text

Private and con fi dential 53 @Composable fun SlotApi() { Button( onClick = {}, backgroundColor = Color.Red ) { Button( onClick = {}, backgroundColor = Color.Green ) { Button( onClick = {}, backgroundColor = Color.Blue ) { Text(text = "We need to go deeper") } } } }

Slide 54

Slide 54 text

Private and con fi dential 54

Slide 55

Slide 55 text

Private and con fi dential 55 Filled Stroke Transparent

Slide 56

Slide 56 text

Private and con fi dential 56 @Composable fun Button( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, color: Color = Colors.primary, contentColor: Color = Color.White, icon: ImageVector? = null, enabled: Boolean = true )

Slide 57

Slide 57 text

Private and con fi dential 57 @Composable fun Button( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, color: Color = Colors.primary, contentColor: Color = Color.White, icon: ImageVector? = null, enabled: Boolean = true )

Slide 58

Slide 58 text

Private and con fi dential 58 @Composable fun Button( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, color: Color = Colors.primary, contentColor: Color = Color.White, icon: ImageVector? = null, enabled: Boolean = true )

Slide 59

Slide 59 text

Private and con fi dential 59 @Composable fun Button( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, color: Color = Colors.primary, contentColor: Color = Color.White, icon: ImageVector? = null, enabled: Boolean = true )

Slide 60

Slide 60 text

Private and con fi dential 60 @Composable private fun ButtonSkeleton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RoundedCornerShape(Dimensions.tokenButtonCornerRadius), border: BorderStroke? = null, backgroundColor: Color, contentColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues( start = Dimensions.tokenButtonPaddingHorizontal, end = Dimensions.tokenButtonPaddingHorizontal, top = Dimensions.tokenButtonPaddingVertical, bottom = Dimensions.tokenButtonPaddingVertical ), height: Dp = Dimensions.tokenButtonHeight, icon: ImageVector?, )

Slide 61

Slide 61 text

Private and con fi dential 61 @Composable private fun ButtonSkeleton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RoundedCornerShape(Dimensions.tokenButtonCornerRadius), border: BorderStroke? = null, backgroundColor: Color, contentColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues( start = Dimensions.tokenButtonPaddingHorizontal, end = Dimensions.tokenButtonPaddingHorizontal, top = Dimensions.tokenButtonPaddingVertical, bottom = Dimensions.tokenButtonPaddingVertical ), height: Dp = Dimensions.tokenButtonHeight, icon: ImageVector? )

Slide 62

Slide 62 text

Private and con fi dential 62 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t

Slide 63

Slide 63 text

Private and con fi dential 63 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t - Public API

Slide 64

Slide 64 text

Private and con fi dential 64 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t - private generic button

Slide 65

Slide 65 text

Private and con fi dential 65 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t - background & clicks

Slide 66

Slide 66 text

Private and con fi dential 66 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t - content layout

Slide 67

Slide 67 text

Private and con fi dential 67 FilledButton ButtonSkeleto n Surfac e Ro w Ico n Tex t - content

Slide 68

Slide 68 text

Private and con fi dential 68 Surface( shape = shape, color = backgroundColor, contentColor = contentColor, border = border, modifier = modifier.clickable( onClick = onClick, enabled = enabled ).alpha( if (enabled) 1f else Dimensions.tokenButtonDisabledOpacity ) )

Slide 69

Slide 69 text

Private and con fi dential 69 Surface( shape = shape, color = backgroundColor, contentColor = contentColor, border = border, modifier = modifier.clickable( onClick = onClick, enabled = enabled ).alpha( if (enabled) 1f else Dimensions.tokenButtonDisabledOpacity ) )

Slide 70

Slide 70 text

Private and con fi dential 70 Surface( shape = shape, color = backgroundColor, contentColor = contentColor, border = border, modifier = modifier.clickable( onClick = onClick, enabled = enabled ).alpha( if (enabled) 1f else Dimensions.tokenButtonDisabledOpacity ) )

Slide 71

Slide 71 text

Private and con fi dential 71 Surface( shape = shape, color = backgroundColor, contentColor = contentColor, border = border, modifier = modifier.clickable( onClick = onClick, enabled = enabled ).alpha( if (enabled) 1f else Dimensions.tokenButtonDisabledOpacity ) )

Slide 72

Slide 72 text

Private and con fi dential 72 Row( Modifier .defaultMinSizeConstraints(minHeight = height) .padding(contentPadding), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, )

Slide 73

Slide 73 text

Private and con fi dential 73 if (icon != null) { Icon( modifier = Modifier.size(Dimensions.tokenButtonIconSize), imageVector = icon, tint = contentColor ) Spacer(modifier = Modifier.width(Dimensions.tokenButtonIconTextSpacing)) } Text( text = text, style = TextStyles.Action, color = contentColor, maxLines = 1, overflow = TextOverflow.Ellipsis )

Slide 74

Slide 74 text

Private and con fi dential 74 if (icon != null) { Icon( modifier = Modifier.size(Dimensions.tokenButtonIconSize), imageVector = icon, tint = contentColor ) Spacer(modifier = Modifier.width(Dimensions.tokenButtonIconTextSpacing)) } Text( text = text, style = TextStyles.Action, color = contentColor, maxLines = 1, overflow = TextOverflow.Ellipsis )

Slide 75

Slide 75 text

Private and con fi dential 75 if (icon != null) { Icon( modifier = Modifier.size(Dimensions.tokenButtonIconSize), imageVector = icon, tint = contentColor ) Spacer(modifier = Modifier.width(Dimensions.tokenButtonIconTextSpacing)) } Text( text = text, style = TextStyles.Action, color = contentColor, maxLines = 1, overflow = TextOverflow.Ellipsis )

Slide 76

Slide 76 text

Private and con fi dential 76 @Composable fun Button( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, color: Color = Colors.primary, contentColor: Color = Color.White, icon: ImageVector? = null, enabled: Boolean = true ) { ButtonSkeleton( onClick = onClick, modifier = modifier, backgroundColor = color, contentColor = contentColor, enabled = enabled, icon = icon, text = text ) }

Slide 77

Slide 77 text

MatchPhotos 77

Slide 78

Slide 78 text

Private and con fi dential 78 @Composable fun MatchPhotos( leftPhoto: ImageBitmap, rightPhoto: ImageBitmap, badge: ImageVector, modifier: Modifier = Modifier )

Slide 79

Slide 79 text

Private and con fi dential 79 MatchPhotos Colum n Ro w Phot o Phot o Badg e

Slide 80

Slide 80 text

Private and con fi dential 80 MatchPhotos Colum n Ro w Phot o - vertical linear layout Phot o Badg e

Slide 81

Slide 81 text

Private and con fi dential 81 MatchPhotos Colum n Ro w Phot o - horizontal linear layout Phot o Badg e

Slide 82

Slide 82 text

Private and con fi dential 82 MatchPhotos Colum n Ro w Phot o Phot o Badg e - content

Slide 83

Slide 83 text

Private and con fi dential 83 Image( asset = rightPhoto, modifier = Modifier .graphicsLayer( rotationZ = rotationDegrees, translationX = rightTranslateX, translationY = rightTranslateY ) .zIndex(rightZIndex) .size(photoSize) .clip(photoShape) .border( width = borderWidth, Colors.matchPhotosBorder, photoShape ) )

Slide 84

Slide 84 text

Private and con fi dential 84 Image( asset = rightPhoto, modifier = Modifier .graphicsLayer( rotationZ = rotationDegrees, translationX = rightTranslateX, translationY = rightTranslateY ) .zIndex(rightZIndex) .size(photoSize) .clip(photoShape) .border( width = borderWidth, Colors.matchPhotosBorder, photoShape ) )

Slide 85

Slide 85 text

Private and con fi dential 85 Image( asset = rightPhoto, modifier = Modifier .graphicsLayer( rotationZ = rotationDegrees, translationX = rightTranslateX, translationY = rightTranslateY ) .zIndex(rightZIndex) .size(photoSize) .clip(photoShape) .border( width = borderWidth, Colors.matchPhotosBorder, photoShape ) )

Slide 86

Slide 86 text

Private and con fi dential 86 Image( asset = rightPhoto, modifier = Modifier .graphicsLayer( rotationZ = rotationDegrees, translationX = rightTranslateX, translationY = rightTranslateY ) .zIndex(rightZIndex) .size(photoSize) .clip(photoShape) .border( width = borderWidth, Colors.matchPhotosBorder, photoShape ) )

Slide 87

Slide 87 text

Private and con fi dential 87 Image( asset = rightPhoto, modifier = Modifier .graphicsLayer( rotationZ = rotationDegrees, translationX = rightTranslateX, translationY = rightTranslateY ) .zIndex(rightZIndex) .size(photoSize) .clip(photoShape) .border( width = borderWidth, Colors.matchPhotosBorder, photoShape ) )

Slide 88

Slide 88 text

Private and con fi dential 88 Row { Image( asset = leftPhoto, ... ) Image( asset = rightPhoto, ... ) }

Slide 89

Slide 89 text

Private and con fi dential 89 Column{ Row { Image() Image() } Image( imageVector = badge, modifier = Modifier .size(iconSize) .align(Alignment.CenterHorizontally) .shadow( elevation = badgeElevation, shape = CircleShape ) ) }

Slide 90

Slide 90 text

Private and con fi dential 90 CtaBox

Slide 91

Slide 91 text

Private and con fi dential 91 @Composable fun CtaBox( media: @Composable () - > Unit, header: @Composable () -> Unit, content: @Composable () - > Unit, buttons: @Composable() -> Unit, modifier: Modifier = Modifier )

Slide 92

Slide 92 text

Private and con fi dential 92 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)) header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 93

Slide 93 text

Private and con fi dential 93 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)) header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 94

Slide 94 text

Private and con fi dential 94 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)); header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 95

Slide 95 text

Private and con fi dential 95 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)) ;; header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 96

Slide 96 text

Private and con fi dential 96 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)); header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 97

Slide 97 text

Private and con fi dential 97 Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(Spacing.xlg) ) { media() Spacer(modifier = Modifier.size(Spacing.xlg)) header() Spacer(modifier = Modifier.size(Spacing.md)) content() Spacer(modifier = Modifier.size(Spacing.xlg)) buttons() }

Slide 98

Slide 98 text

Private and con fi dential 98 @Composable fun MatchScreen(config: MatchConfig) { CtaBox( media = { MatchPhotos( .. ) }, header = { Text(text="",style=TextStyles.H1) }, content = { Text(text="",style=TextStyles.P1) }, buttons = { Button( text = " ... ", color = Color.White, contentColor = Color.Black ) } ) }

Slide 99

Slide 99 text

Private and con fi dential 99

Slide 100

Slide 100 text

Private and con fi dential 1. Design syste m • Token s • Component s • Pattern s 2. Android View intero p 3. Testin g 4. Integration 100

Slide 101

Slide 101 text

Android Views + Compose 101

Slide 102

Slide 102 text

Private and con fi dential 102 val count = remember { mutableStateOf(0) } Column { Text(text = "Compose Text Count = ${count.value}") AndroidView( viewBlock = { context -> TextView(context).apply { setOnClickListener { count.value = count.value + 1 } } }, update = { view -> view.text = "AndroidViews World! Count = ${count.value}" } ) }

Slide 103

Slide 103 text

Private and con fi dential 103 val count = remember { mutableStateOf(0) } Column { Text(text = "Compose Text Count = ${count.value}") AndroidView( viewBlock = { context -> TextView(context).apply { setOnClickListener { count.value = count.value + 1 } } }, update = { view -> view.text = "AndroidViews World! Count = ${count.value}" } ) }

Slide 104

Slide 104 text

Private and con fi dential 104 val count = remember { mutableStateOf(0) } Column { Text(text = "Compose Text Count = ${count.value}") AndroidView( viewBlock = { context -> TextView(context).apply { setOnClickListener { count.value = count.value + 1 } } }, update = { view -> view.text = "AndroidViews World! Count = ${count.value}" } ) }

Slide 105

Slide 105 text

Private and con fi dential 105 val count = remember { mutableStateOf(0) } Column { Text(text = “Compose Count = ${count.value}") AndroidView( viewBlock = { context -> TextView(context).apply { setOnClickListener { count.value = count.value + 1 } } }, update = { view -> view.text = " .. . Count = ${count.value}" } ) }

Slide 106

Slide 106 text

Compose + Android Views 106

Slide 107

Slide 107 text

Private and con fi dential 107

Slide 108

Slide 108 text

Private and con fi dential override fun onCreate() { val composeView = findViewById( .. ) composeView.setContent { Text(text = "ComposeText") } } 108

Slide 109

Slide 109 text

Private and con fi dential override fun onCreate() { val composeView = findViewById( .. ) composeView.setContent { Text(text = "ComposeText") } } 109

Slide 110

Slide 110 text

Private and con fi dential 1. Design syste m • Token s • Component s • Pattern s 2. Android View intero p 3. Testin g 4. Integration 110

Slide 111

Slide 111 text

Testing 111

Slide 112

Slide 112 text

Private and con fi dential 112 object TestTagMatchScreen { const val button = "MatchScreenButton" ... } Button( ... modifier = Modifier.testTag(TestTagMatchScreen.button) )

Slide 113

Slide 113 text

Private and con fi dential 113 @get:Rule val composeTestRule = createComposeRule() fun launchScreen() { composeTestRule.setContent { BadooTheme { MatchScreen() } } }

Slide 114

Slide 114 text

Private and con fi dential 114 @Test fun matchScreenTest() { launchScreen() composeTestRule .onNodeWithTag(TestTagMatchScreen.button) .assertIsDisplayed() .assertTextEquals("Send Message") .performClick() } }

Slide 115

Slide 115 text

Private and con fi dential 115 @Test fun interopTest() { launchScreen() onView(ViewMatchers.withId(R.dimen.counterAndroidView)) .perform(click()) .check(matches(withText("Hello From AndroidViews World! Count = 1"))) composeTestRule .onNodeWithText(“Compose Text Count = 1").assertExists() }

Slide 116

Slide 116 text

Private and con fi dential 116 @Test fun interopTest() { launchScreen() onView(ViewMatchers.withId(R.dimen.counterAndroidView)) .perform(click()) .check(matches(withText("Hello From AndroidViews World! Count = 1"))) composeTestRule .onNodeWithText(“Compose Text Count = 1").assertExists() }

Slide 117

Slide 117 text

Private and con fi dential 117 @Test fun interopTest() { launchScreen() onView(ViewMatchers.withId(R.id.counterAndroidView)) .perform(click()) .check(matches(withText("Hello From AndroidViews World! Count = 1"))) composeTestRule .onNodeWithText(“Compose Text Count = 1").assertExists() }

Slide 118

Slide 118 text

Private and con fi dential Screenshot tests Karumi — Shot 118

Slide 119

Slide 119 text

Private and con fi dential 1. Design syste m • Token s • Component s • Pattern s 2. Android View intero p 3. Testin g 4. Integration 119

Slide 120

Slide 120 text

Integration 120

Slide 121

Slide 121 text

Private and con fi dential Business logic integration — MVI 121 Feature Compose State UI events

Slide 122

Slide 122 text

Private and con fi dential Business logic integration — MVVM 122 ViewModel Compose LiveData Method calls

Slide 123

Slide 123 text

Private and con fi dential val value: String by observable.subscribeAsState("initial") val value: String by liveData.observeAsState(“initial") val value: String? by observable.subscribeAsState(null) val value: String? by liveData.observeAsState(null) Text("Value is $value") 123

Slide 124

Slide 124 text

Private and con fi dential Business logic integration — MVP 124 Method Calls ViewWrapper Compose State UI events Presenter

Slide 125

Slide 125 text

Private and con fi dential ✅ How to create a design syste m ✅ How to integrate with the current U I ✅ How to tes t ✅ How to integrate within the business logic Summary 125

Slide 126

Slide 126 text

Private and con fi dential • Set of main UI-component s • Experiment s • Non critical feature s • Wait for stable releas e • Ship to prod 🚀🚀🚀 Next steps 126

Slide 127

Slide 127 text

Private and con fi dential • Design system talk • Compose pathway • Сompose samples • Repo from the talk • Bumble Tech blog Resources 127

Slide 128

Slide 128 text

128 CHEERS ! antonshilov_