Slide 1

Slide 1 text

@Preview(device = "spec:width=960dp,height=1080dp,dpi=480") @Composable private fun TitleSlidePreview() { PreviewBox { TitleSlide() } } @Composable private fun TitleSlide() { Column( modifier = Modifier .fillMaxSize() .paint( painter = painterResource( id = R.drawable.glenn_carstens_peters, ), contentScale = ContentScale.FillBounds, ) .padding(32.dp) ) { Title( modifier = Modifier .fillMaxWidth() .padding(top = 32.dp), fontSize = 70.sp, ) Column( horizontalAlignment = CenterHorizontally, modifier = Modifier .fillMaxWidth() ) { Name( modifier = Modifier .weight(1f) .padding(bottom = 66.dp), imageHeight = 66.dp, textSize = 70.sp, ) Contacts( modifier = Modifier .padding( bottom = 16.dp, end = 16.dp, ), fontSize = 50.sp, ) } } } Practical Tips and Tricks to Improve Your Compose Previews István Juhos @istvanjuhos.dev

Slide 2

Slide 2 text

Food for thought 🍲💭

Slide 3

Slide 3 text

#DevFestVenezia @istvanjuhos.dev XML preview

Slide 4

Slide 4 text

#DevFestVenezia @istvanjuhos.dev Compose preview

Slide 5

Slide 5 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 6

Slide 6 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 7

Slide 7 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } • Code Compose preview

Slide 8

Slide 8 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach production @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 9

Slide 9 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach productio n​ … 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 10

Slide 10 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach production… • others will read 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 11

Slide 11 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach production… • others will read • needs to be maintained 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 12

Slide 12 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach production… • others will read • needs to be maintained • might be duplicated 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 13

Slide 13 text

#DevFestVenezia @istvanjuhos.dev • Code, that • will never reach production… • others will read • needs to be maintained • might be duplicated • might be used for other purposes 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview

Slide 14

Slide 14 text

#DevFestVenezia @istvanjuhos.dev Agenda • Developer experience • Annotation limitations • Custom tooling • Interactive previews

Slide 15

Slide 15 text

#DevFestVenezia @istvanjuhos.dev Agenda • Developer experience • Annotation limitations • Custom tooling • Interactive previews

Slide 16

Slide 16 text

#DevFestVenezia @istvanjuhos.dev Agenda • Developer experience • Annotation limitations • Custom tooling • Interactive previews

Slide 17

Slide 17 text

#DevFestVenezia @istvanjuhos.dev Agenda • Developer experience • Annotation limitations • Custom tooling • Interactive previews

Slide 18

Slide 18 text

#DevFestVenezia @istvanjuhos.dev Agenda • Developer experience • Annotation limitations • Custom tooling • Interactive previews

Slide 19

Slide 19 text

Consider IDE themes 🎨

Slide 20

Slide 20 text

#DevFestVenezia @istvanjuhos.dev Consider IDE themes @Composable fun ListItem( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier .clickable(onClick = onClick) .padding(16.dp), ) { Text( text = text, style = PreviewTheme.typography.bodyMedium, color = PreviewTheme.colors.text, modifier = Modifier.weight(1f), ) Image( imageVector = KeyboardArrowRight, contentDescription = null, colorFilter = ColorFilter.tint(PreviewTheme.colors.text) ) } }

Slide 21

Slide 21 text

Consider IDE themes • Render Composable functions annotated with @Preview @Composable fun ListItem( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier .clickable(onClick = onClick) .padding(16.dp), ) { Text( text = text, style = PreviewTheme.typography.bodyMedium, color = PreviewTheme.colors.text, modifier = Modifier.weight(1f), ) Image( imageVector = KeyboardArrowRight, contentDescription = null, colorFilter = ColorFilter.tint(PreviewTheme.colors.text) ) } }

Slide 22

Slide 22 text

Consider IDE themes • Render Composable functions annotated with @Preview @Composable fun ListItem( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier .clickable(onClick = onClick) .padding(16.dp), ) { Text( text = text, style = PreviewTheme.typography.bodyMedium, color = PreviewTheme.colors.text,

Slide 23

Slide 23 text

Consider IDE themes • Render Composable functions annotated with @Preview @Composable fun ListItem( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier .clickable(onClick = onClick) .padding(16.dp), ) { Text( text = text, style = PreviewTheme.typography.bodyMedium, color = PreviewTheme.colors.text,

Slide 24

Slide 24 text

#DevFestVenezia @istvanjuhos.dev Consider IDE themes @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 25

Slide 25 text

#DevFestVenezia @istvanjuhos.dev Consider IDE themes @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 26

Slide 26 text

#DevFestVenezia @istvanjuhos.dev Consider IDE themes @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 27

Slide 27 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes

Slide 28

Slide 28 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes

Slide 29

Slide 29 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes

Slide 30

Slide 30 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes

Slide 31

Slide 31 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes vs

Slide 32

Slide 32 text

Use annotation parameters ⚙

Slide 33

Slide 33 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters

Slide 34

Slide 34 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters

Slide 35

Slide 35 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, backgroundColor = 0xFF_E7ECF4, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters

Slide 36

Slide 36 text

#DevFestVenezia @istvanjuhos.dev @Preview( showBackground = true, backgroundColor = 0xFF_E7ECF4, // gray.w500 ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters

Slide 37

Slide 37 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } const val GRAY_W500 = 0xFF_E7ECF4 ...

Slide 38

Slide 38 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 39

Slide 39 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, uiMode = Configuration.UI_MODE_NIGHT_YES, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 40

Slide 40 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } }

Slide 41

Slide 41 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, )

Slide 42

Slide 42 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Preview( showBackground = true, backgroundColor = DARK_GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, )

Slide 43

Slide 43 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Preview( showBackground = true, backgroundColor = DARK_GRAY_W500, uiMode = Configuration.UI_MODE_NIGHT_YES, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {},

Slide 44

Slide 44 text

#DevFestVenezia @istvanjuhos.dev Use annotation parameters @Preview( showBackground = true, backgroundColor = GRAY_W500, ) @Preview( showBackground = true, backgroundColor = DARK_GRAY_W500, uiMode = Configuration.UI_MODE_NIGHT_YES, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {},

Slide 45

Slide 45 text

#DevFestVenezia @istvanjuhos.dev Preview annotation parameters android x​ .compose.ui.tooling.preview @Preview( ... ) ✅

Slide 46

Slide 46 text

#DevFestVenezia @istvanjuhos.dev No Preview annotation parameters org.jetbrain s​ .compose.ui.tooling.preview @Preview (​ ) 😢 ❌

Slide 47

Slide 47 text

Group your previews 🏘

Slide 48

Slide 48 text

#DevFestVenezia @istvanjuhos.dev @Preview @Composable private fun ListItemPreview() { ... } Group your previews

Slide 49

Slide 49 text

#DevFestVenezia @istvanjuhos.dev @Preview( name = "Light - Primary", group = "Light/Dark - Primary", showBackground = true, backgroundColor = GRAY_W500, ) @Preview( name = "Dark - Primary", group = "Light/Dark - Primary”, showBackground = true, backgroundColor = DARK_GRAY_W500, uiMode = UI_MODE_NIGHT_YES, ) @Composable private fun ListItemPreview() { ... } Group your previews

Slide 50

Slide 50 text

#DevFestVenezia @istvanjuhos.dev @Preview( name = "Light - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = GRAY_W100, ) @Preview( name = "Dark - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = DARK_GRAY_W100, uiMode = UI_MODE_NIGHT_YES, ) @Composable private fun ListItemPreview() { ... } Group your previews

Slide 51

Slide 51 text

#DevFestVenezia @istvanjuhos.dev @Preview( ... ) @Preview( ... ) @Preview( ... ) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Group your previews

Slide 52

Slide 52 text

#DevFestVenezia @istvanjuhos.dev @Preview( ... ) @Preview( ... ) @Preview( ... ) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Group your previews

Slide 53

Slide 53 text

Create multipreviews 👥

Slide 54

Slide 54 text

#DevFestVenezia @istvanjuhos.dev @Preview( ... ) @Preview( ... ) @Preview( ... ) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Create multipreviews

Slide 55

Slide 55 text

@Preview( name = "Light - Primary", group = "Light/Dark - Primary", showBackground = true, backgroundColor = GRAY_W500, ) @Preview( name = "Dark - Primary", group = "Light/Dark - Primary", showBackground = true, backgroundColor = DARK_GRAY_W500, uiMode = UI_MODE_NIGHT_YES, ) annotation class PreviewPrimaryBackground Create multipreviews

Slide 56

Slide 56 text

@Preview( name = "Light - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = GRAY_W100, ) @Preview( name = "Dark - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = DARK_GRAY_W100, uiMode = UI_MODE_NIGHT_YES, ) annotation class PreviewSecondaryBackground Create multipreviews

Slide 57

Slide 57 text

@Preview( name = "Light - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = GRAY_W100, ) @Preview( name = "Dark - Secondary", group = "Light/Dark - Secondary", showBackground = true, backgroundColor = DARK_GRAY_W100, uiMode = UI_MODE_NIGHT_YES, ) annotation class PreviewSecondaryBackground Create multipreviews

Slide 58

Slide 58 text

@PreviewPrimaryBackground @PreviewSecondaryBackground @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Create multipreviews

Slide 59

Slide 59 text

#DevFestVenezia @istvanjuhos.dev @PreviewPrimaryBackground @PreviewSecondaryBackground @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Create multipreviews

Slide 60

Slide 60 text

#DevFestVenezia @istvanjuhos.dev @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Create multipreviews @PreviewPrimaryBackground @PreviewSecondaryBackground annotation class PreviewAllBackgrounds

Slide 61

Slide 61 text

#DevFestVenezia @istvanjuhos.dev @PreviewAllBackgrounds @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Create multipreviews @PreviewPrimaryBackground @PreviewSecondaryBackground annotation class PreviewAllBackgrounds

Slide 62

Slide 62 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes Multipreview templates

Slide 63

Slide 63 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 64

Slide 64 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 65

Slide 65 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 66

Slide 66 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 67

Slide 67 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 68

Slide 68 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes

Slide 69

Slide 69 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes @PreviewDynamicColors Multipreview templates

Slide 70

Slide 70 text

#DevFestVenezia @istvanjuhos.dev Multipreview templates androidx.compose.ui.tooling.preview @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes @PreviewDynamicColors ✅ developer.android.com/develop/ui/compose/tooling/previews#multipreview-templates

Slide 71

Slide 71 text

#DevFestVenezia @istvanjuhos.dev No multipreview support yet @Preview @Preview annotation class PreviewPrimaryBackground @PreviewLightDark @PreviewFontScale @Previe w​ ScreenSizes @PreviewDynamicColors 😢 ❌

Slide 72

Slide 72 text

Build custom tools 🪛

Slide 73

Slide 73 text

#DevFestVenezia @istvanjuhos.dev Build custom tools - a new example @Composable fun PrimaryButton( text: String, @DrawableRes leadingIcon: Int? = null, onClick: () - > Unit, modifier: Modifier = Modifier ) { Button( colors = ButtonDefaults.buttonColors().copy( containerColor = AppTheme.colors.background, ), elevation = ButtonDefaults.buttonElevation(4.dp), modifier = modifier, onClick = onClick, ) { Row(verticalAlignment = CenterVertically) { leadingIcon ?. let { Image( painter = painterResource(id = leadingIcon), contentDescription = null, colorFilter = ColorFilter.tint(AppTheme.colors.primaryButtonText), modifier = Modifier.size(24.dp) ) } Text( text = text, style = AppTheme.typography.labelMedium, color = AppTheme.colors.primaryButtonText, modifier = Modifier.padding(start = 8.dp) ) } } }

Slide 74

Slide 74 text

#DevFestVenezia @istvanjuhos.dev Let’s build our preview @PreviewLightDark @Composable private fun PrimaryButtonPreview() { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) }

Slide 75

Slide 75 text

#DevFestVenezia @istvanjuhos.dev Let’s build our preview @PreviewLightDark @Composable private fun PrimaryButtonPreview() { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) }

Slide 76

Slide 76 text

#DevFestVenezia @istvanjuhos.dev Something’s missing… @PreviewLightDark @Composable private fun PrimaryButtonPreview() { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) }

Slide 77

Slide 77 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } } Something’s missing… the theme 🤦

Slide 78

Slide 78 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } } Something’s missing… the theme 🤦

Slide 79

Slide 79 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } } … and flexibility 💧

Slide 80

Slide 80 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } } … and flexibility 💧 🫸

Slide 81

Slide 81 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } } … and flexibility

Slide 82

Slide 82 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { PreviewBox { PrimaryButton( ... ) } } Let’s build tools!

Slide 83

Slide 83 text

#DevFestVenezia @istvanjuhos.dev @PreviewLightDark @Composable private fun PrimaryButtonPreview() { PreviewBox { PrimaryButton( ... ) } } Let’s build tools!

Slide 84

Slide 84 text

#DevFestVenezia @istvanjuhos.dev Building preview tools @Composable fun PreviewBox( modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { }

Slide 85

Slide 85 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { AppTheme { content() } } Building preview tools

Slide 86

Slide 86 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { AppTheme { Box { content() } } } Building preview tools

Slide 87

Slide 87 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box { content() } } } Building preview tools

Slide 88

Slide 88 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor), ) { content() } } } Building preview tools

Slide 89

Slide 89 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: @Composable () -> Color = { AppTheme.colors.backgroundSecondary }, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor()), ) { content() } } } Building preview tools

Slide 90

Slide 90 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: @Composable () -> Color = { AppTheme.colors.backgroundSecondary }, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor()), ) { content() } } } Building preview tools

Slide 91

Slide 91 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: @Composable () -> Color = { AppTheme.colors.backgroundSecondary }, content: @Composable () -> Unit, ) { AppTheme { Box( ... ) { content() } } } Building preview tools

Slide 92

Slide 92 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor: @Composable () -> Color = { AppTheme.colors.backgroundSecondary }, paddingValues: PaddingValues = PaddingValues(4.dp), content: @Composable () -> Unit, ) { AppTheme { Box( ... ) { content() } } } Building preview tools

Slide 93

Slide 93 text

#DevFestVenezia @istvanjuhos.dev @Composable fun Previe w​ Box( ... content: @Composable () -> Unit, ) { AppTheme { Box( ... ) { content() } } } Building preview tools

Slide 94

Slide 94 text

#DevFestVenezia @istvanjuhos.dev @Composable fun Previe w​ Column( ... content: @Composable () -> Unit, ) { AppTheme { Column( ... ) { content() } } } Building preview tools

Slide 95

Slide 95 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewDialog( modifier: Modifier = Modifier, factory: (context: Context) -> Dialog, ) { PreviewBox( contentPadding = PaddingValues(), ) { AndroidView( modifier = modifier, factory = { context -> val dialog = factory(context) dialog.create() val view = dialog.findViewById(android.R.id.content) (view.parent as ViewGroup).removeView(view) view }, ) } } Building preview tools

Slide 96

Slide 96 text

#DevFestVenezia @istvanjuhos.dev @Composable fun PreviewDialog( modifier: Modifier = Modifier, factory: (context: Context) -> Dialog, ) { PreviewBox( contentPadding = PaddingValues(), ) { AndroidView( modifier = modifier, factory = { context -> val dialog = factory(context) dialog.create() val view = dialog.findViewById(android.R.id.content) (view.parent as ViewGroup).removeView(view) view }, ) } } Building preview tools

Slide 97

Slide 97 text

Override CompositionLocals 🗺

Slide 98

Slide 98 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @Composable fun PreviewColumn( ... content: @Composable () -> Unit, ) { AppTheme { Column( ... ) { content() } } }

Slide 99

Slide 99 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @Composable fun PreviewColumn( ... layoutDirection: LayoutDirection = LayoutDirection.Ltr, content: @Composable () -> Unit, ) { AppTheme { Column( ... ) { content() } } }

Slide 100

Slide 100 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @Composable fun PreviewColumn( ... layoutDirection: LayoutDirection = LayoutDirection.Ltr, content: @Composable () -> Unit, ) { CompositionLocalProvider( LocalLayoutDirection provides layoutDirection, ) { AppTheme { Column( ... ) { content() } } } }

Slide 101

Slide 101 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() { PreviewColumn { PrimaryButton( ... ) ListItem( ... ) } }

Slide 102

Slide 102 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() { PreviewColumn( layoutDirection = LayoutDirection.Ltr, ) { PrimaryButton( ... ) ListItem( ... ) } }

Slide 103

Slide 103 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() { PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( ... ) ListItem( ... ) } }

Slide 104

Slide 104 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() { PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } }

Slide 105

Slide 105 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @Preview @Composable private fun PreviewColumnPreview() { PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } }

Slide 106

Slide 106 text

#DevFestVenezia @istvanjuhos.dev Override CompositionLocals @Preview @Composable private fun PreviewColumnPreview() { PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } }

Slide 107

Slide 107 text

Make previews interactive 🎮

Slide 108

Slide 108 text

Circuit Route Planner / Circuit for Teams

Slide 109

Slide 109 text

Make previews interactive 🎮

Slide 110

Slide 110 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { ... }

Slide 111

Slide 111 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ }

Slide 112

Slide 112 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ }

Slide 113

Slide 113 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ }

Slide 114

Slide 114 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ } @Preview @Composable private fun PreviewFloatingPinMapOverlay() { var isLifted by remember { mutableStateOf(false) } PreviewBox( . .. modifier = Modifier.clickable { isLifted = !isLifted }, ) { FloatingPinMapOverlay( isLifted = isLifted, ) } }

Slide 115

Slide 115 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ } @Preview @Composable private fun PreviewFloatingPinMapOverlay() { var isLifted by remember { mutableStateOf(false) } PreviewBox( . .. modifier = Modifier.clickable { isLifted = !isLifted }, ) { FloatingPinMapOverlay( isLifted = isLifted, ) } }

Slide 116

Slide 116 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ } @Preview @Composable private fun PreviewFloatingPinMapOverlay() { var isLifted by remember { mutableStateOf(false) } PreviewBox( . .. modifier = Modifier.clickable { isLifted = !isLifted }, ) { FloatingPinMapOverlay( isLifted = isLifted, ) } }

Slide 117

Slide 117 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ } @Preview @Composable private fun PreviewFloatingPinMapOverlay() { var isLifted by remember { mutableStateOf(false) } PreviewBox( . .. modifier = Modifier.clickable { isLifted = !isLifted }, ) { FloatingPinMapOverlay( isLifted = isLifted, ) } }

Slide 118

Slide 118 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive @Composable fun FloatingPinMapOverlay( isLifted: Boolean, modifier: Modifier = Modifier, ) { /* Some beautifully animated content */ } @Preview @Composable private fun PreviewFloatingPinMapOverlay() { var isLifted by remember { mutableStateOf(false) } PreviewBox( . .. modifier = Modifier.clickable { isLifted = !isLifted }, ) { FloatingPinMapOverlay( isLifted = isLifted, ) } }

Slide 119

Slide 119 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive ... . ..

Slide 120

Slide 120 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive ... . .. ❌ ✅

Slide 121

Slide 121 text

#DevFestVenezia @istvanjuhos.dev Make previews interactive ❌ ✅

Slide 122

Slide 122 text

Some more ideas 💡

Slide 123

Slide 123 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews for preliminary accessibility checks

Slide 124

Slide 124 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews for preliminary accessibility checks

Slide 125

Slide 125 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Generate previews with PreviewParemeterProviders • Providing Composables as preview parameters

Slide 126

Slide 126 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Generate previews with PreviewParemeterProviders • Providing Composables as preview parameters proandroiddev.com/using-previewparameters-and-providing-composables-to-jetpack-compose-previews-5b1f5a8fe192

Slide 127

Slide 127 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews to build a component showcase

Slide 128

Slide 128 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews to build a component showcase github.com/airbnb/Showkase

Slide 129

Slide 129 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews for screenshot testing

Slide 130

Slide 130 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews for screenshot testing developer.android.com/studio/preview/compose-screenshot-testing

Slide 131

Slide 131 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • Use previews for screenshot testing cashapp.github.io/paparazzi

Slide 132

Slide 132 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • … or just make the whole app runnable in Preview 🤷

Slide 133

Slide 133 text

#DevFestVenezia @istvanjuhos.dev Some more ideas • … or just make the whole app runnable in Preview 🤷 medium.com/whatnot-engineering/preview-driven-development-with-compose-f7a5beee95aa

Slide 134

Slide 134 text

Wrapping up 📦

Slide 135

Slide 135 text

Worth the effort? 🤔

Slide 136

Slide 136 text

It depends 🤷

Slide 137

Slide 137 text

#DevFestVenezia @istvanjuhos.dev Resources • developer.android.com/develop/ui/compose/tooling/previews • developer.android.com/topic/performance/rendering/overdraw • proandroiddev.com/using-previewparameters-and-providing-composables- to-jetpack-compose-previews-5b1f5a8fe192 • github.com/airbnb/Showkase • developer.android.com/studio/preview/compose-screenshot-testing • medium.com/whatnot-engineering/preview-driven-development-with- compose-f7a5beee95aa • Good old fi eld experience 😅

Slide 138

Slide 138 text

Practical Tips and Tricks to Improve Your Compose Previews • Compose previews are code, treat them with care • Compose custom reusable tools • Simplify previews as much as possible • but go wild if it’s worth it! 😎 istvanjuhos.dev István Juhos Photo by Glenn Carstens-Peters