Share
Practical Tips and Tricks to Improve Your Compose Previews (KKON 2024)
Jetpack Compose Preview is a powerful tool that provides real-time feedback on how our composables are rendered and even on how they behave and animate in certain conditions. While writing useful previews seems like a straightforward task, there are some things that we need to keep in mind. Preview code won't reach users of your app (hopefully!); however, it's more likely that the author of the preview and future peers as well might have to work with them eventually.
This talk will touch on the following topics:
- Recap on the main features and limitations of Compose previews
- How we can overcome the limitations of @Preview being an annotation
- How we can make our previews developer-friendly
- What we can use our previews for besides checking how our composables are rendered and how they behave
István Juhos
Transcript
129 slides
-
@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 @stewemetal istvanjuhos.dev -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Compose preview -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Compose preview -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } • Code Compose preview -
#KKON @stewemetal • Code, that • will never reach production
@Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview -
#KKON @stewemetal • Code, that • will never reach productio
n … 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview -
#KKON @stewemetal • Code, that • will never reach production…
• others will read 🤞 @Preview @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Compose preview -
#KKON @stewemetal • 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 -
#KKON @stewemetal • 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 -
#KKON @stewemetal • 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 -
#KKON @stewemetal 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) ) } } -
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) ) } } -
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, -
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, -
#KKON @stewemetal Consider IDE themes @Preview @Composable private fun ListItemPreview()
{ AppTheme { ListItem( text = "List item", onClick = {}, ) } } -
#KKON @stewemetal Consider IDE themes @Preview @Composable private fun ListItemPreview()
{ AppTheme { ListItem( text = "List item", onClick = {}, ) } } -
#KKON @stewemetal Consider IDE themes @Preview @Composable private fun ListItemPreview()
{ AppTheme { ListItem( text = "List item", onClick = {}, ) } } -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes -
#KKON @stewemetal @Preview( showBackground = true, ) @Composable private fun
ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes -
#KKON @stewemetal @Preview( showBackground = true, ) @Composable private fun
ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Consider IDE themes vs -
#KKON @stewemetal @Preview( showBackground = true, ) @Composable private fun
ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters -
#KKON @stewemetal @Preview( showBackground = true, ) @Composable private fun
ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters -
#KKON @stewemetal @Preview( showBackground = true, backgroundColor = 0xFF_E7ECF4, )
@Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters -
#KKON @stewemetal @Preview( showBackground = true, backgroundColor = 0xFF_E7ECF4, //
gray.w500 ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } Use annotation parameters -
#KKON @stewemetal 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 ... -
#KKON @stewemetal Use annotation parameters @Preview( showBackground = true, backgroundColor
= GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } -
#KKON @stewemetal 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 = {}, ) } } -
#KKON @stewemetal Use annotation parameters @Preview( showBackground = true, backgroundColor
= GRAY_W500, ) @Composable private fun ListItemPreview() { AppTheme { ListItem( text = "List item", onClick = {}, ) } } -
#KKON @stewemetal 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 = {}, ) -
#KKON @stewemetal 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 = {}, ) -
#KKON @stewemetal 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 = {}, -
#KKON @stewemetal 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 = {}, -
#KKON @stewemetal @Preview @Composable private fun ListItemPreview() { ... }
Group your previews -
#KKON @stewemetal @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 -
#KKON @stewemetal @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 -
#KKON @stewemetal @Preview( ... ) @Preview( ... ) @Preview( ...
) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Group your previews -
#KKON @stewemetal @Preview( ... ) @Preview( ... ) @Preview( ...
) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Group your previews -
#KKON @stewemetal @Preview( ... ) @Preview( ... ) @Preview( ...
) @Preview( ... ) @Composable private fun ListItemPreview() { ... } Create multipreviews -
@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 -
@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 -
@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 -
@PreviewPrimaryBackground @PreviewSecondaryBackground @Composable private fun ListItemPreview() { AppTheme { ListItem(
text = "List item", onClick = {}, ) } } Create multipreviews -
#KKON @stewemetal @PreviewPrimaryBackground @PreviewSecondaryBackground @Composable private fun ListItemPreview() { AppTheme
{ ListItem( text = "List item", onClick = {}, ) } } Create multipreviews -
#KKON @stewemetal @Composable private fun ListItemPreview() { AppTheme { ListItem(
text = "List item", onClick = {}, ) } } Create multipreviews @PreviewPrimaryBackground @PreviewSecondaryBackground annotation class PreviewAllBackgrounds -
#KKON @stewemetal @PreviewAllBackgrounds @Composable private fun ListItemPreview() { AppTheme {
ListItem( text = "List item", onClick = {}, ) } } Create multipreviews @PreviewPrimaryBackground @PreviewSecondaryBackground annotation class PreviewAllBackgrounds -
#KKON @stewemetal Multipreview templates androidx.compose.ui.tooling.preview @PreviewLightDark @PreviewFontScale @Previe w ScreenSizes
@PreviewDynamicColors ✅ developer.android.com/develop/ui/compose/tooling/previews#multipreview-templates -
#KKON @stewemetal No multipreview support yet @Preview @Preview annotation class
PreviewPrimaryBackground @PreviewLightDark @PreviewFontScale @Previe w ScreenSizes @PreviewDynamicColors 😢 ❌ -
#KKON @stewemetal 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) ) } } } -
#KKON @stewemetal Let’s build our preview @PreviewLightDark @Composable private fun
PrimaryButtonPreview() { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } -
#KKON @stewemetal Let’s build our preview @PreviewLightDark @Composable private fun
PrimaryButtonPreview() { PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } -
#KKON @stewemetal Something’s missing… @PreviewLightDark @Composable private fun PrimaryButtonPreview() {
PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {}, ) } -
#KKON @stewemetal @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme {
PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {} ) } } Something’s missing… the theme 🤦 -
#KKON @stewemetal @PreviewLightDark @Composable private fun PrimaryButtonPreview() { AppTheme {
PrimaryButton( text = "Primary Button", leadingIcon = R.drawable.ic_add, onClick = {} ) } } … and flexibility 💧 -
#KKON @stewemetal @Preview @Composable private fun PrimaryButtonPreview() { PreviewBox {
PrimaryButton( ... ) } } Let’s build tools! -
#KKON @stewemetal @Preview @Composable private fun PrimaryButtonPreview() { PreviewBox {
PrimaryButton( ... ) } } Let’s build tools! -
#KKON @stewemetal Building preview tools @Composable fun PreviewBox( modifier: Modifier
= Modifier, content: @Composable () -> Unit, ) { } -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, content:
@Composable () -> Unit, ) { AppTheme { content() } } Building preview tools -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, content:
@Composable () -> Unit, ) { AppTheme { Box { content() } } } Building preview tools -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box { content() } } } Building preview tools -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor), ) { content() } } } Building preview tools -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor), ) { content() } } } Building preview tools -
#KKON @stewemetal Building preview tools - theme insights AppTheme.colors.backgroundSecondary object
AppTheme { val colors: AppColors @Composable @ReadOnlyComposable get() = LocalAppColors.current //... } -
#KKON @stewemetal Building preview tools - theme insights AppTheme.colors.backgroundSecondary object
AppTheme { val colors: AppColors @Composable @ReadOnlyComposable get() = LocalAppColors.current //... } val LocalAppColors = staticCompositionLocalOf<AppColors> { error("Make sure to use this value in an AppTheme.") } -
#KKON @stewemetal Building preview tools - theme insights AppTheme.colors.backgroundSecondary object
AppTheme { val colors: AppColors @Composable @ReadOnlyComposable get() = LocalAppColors.current //... } val LocalAppColors = staticCompositionLocalOf<AppColors> { lightColors() } -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor), ) { content() } } } Building preview tools -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
Color = AppTheme.colors.backgroundSecondary, content: @Composable () -> Unit, ) { AppTheme { Box( modifier = modifier .background(backgroundColor), ) { content() } } } Building preview tools -
#KKON @stewemetal @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 -
#KKON @stewemetal @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 -
#KKON @stewemetal @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 -
#KKON @stewemetal @Composable fun PreviewBox( modifier: Modifier = Modifier, backgroundColor:
@Composable () -> Color = { AppTheme.colors.backgroundSecondary }, content: @Composable () -> Unit, ) { AppTheme { Box( ... ) { content() } } } Building preview tools -
#KKON @stewemetal @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 -
#KKON @stewemetal @Composable fun Previe w Box( ... content: @Composable
() -> Unit, ) { AppTheme { Box( ... ) { content() } } } Building preview tools -
#KKON @stewemetal @Composable fun Previe w Column( ... content: @Composable
() -> Unit, ) { AppTheme { Column( ... ) { content() } } } Building preview tools -
#KKON @stewemetal Override CompositionLocals @Composable fun PreviewColumn( ... content: @Composable
() -> Unit, ) { AppTheme { Column( ... ) { content() } } } -
#KKON @stewemetal Override CompositionLocals @Composable fun PreviewColumn( ... layoutDirection: LayoutDirection
= LayoutDirection.Ltr, content: @Composable () -> Unit, ) { AppTheme { Column( ... ) { content() } } } -
#KKON @stewemetal Override CompositionLocals @Composable fun PreviewColumn( ... layoutDirection: LayoutDirection
= LayoutDirection.Ltr, content: @Composable () -> Unit, ) { CompositionLocalProvider( LocalLayoutDirection provides layoutDirection, ) { AppTheme { Column( ... ) { content() } } } } -
#KKON @stewemetal Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() {
PreviewColumn { PrimaryButton( ... ) ListItem( ... ) } } -
#KKON @stewemetal Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() {
PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( ... ) ListItem( ... ) } } -
#KKON @stewemetal Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() {
PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( ... ) ListItem( ... ) } } -
#KKON @stewemetal Override CompositionLocals @PreviewLightDark @Composable private fun PreviewColumnPreview() {
PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } } -
#KKON @stewemetal Override CompositionLocals @Preview @Composable private fun PreviewColumnPreview() {
PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } } -
#KKON @stewemetal Override CompositionLocals @Preview @Composable private fun PreviewColumnPreview() {
PreviewColumn( layoutDirection = LayoutDirection.Rtl, ) { PrimaryButton( .. . ) ListItem( .. . ) } } -
#KKON @stewemetal Some more ideas • Check the behavior of
components in interactive mode -
#KKON @stewemetal Some more ideas • Check the behavior of
components in interactive mode -
#KKON @stewemetal Some more ideas • Use previews for preliminary
accessibility checks -
#KKON @stewemetal Some more ideas • Use previews for preliminary
accessibility checks -
#KKON @stewemetal Some more ideas • Generate previews with PreviewParemeterProviders
• Providing Composables as preview parameters 🤯 -
#KKON @stewemetal Some more ideas • Generate previews with PreviewParemeterProviders
• Providing Composables as preview parameters 🤯 proandroiddev.com/using-previewparameters-and-providing-composables-to-jetpack-compose-previews-5b1f5a8fe192 -
#KKON @stewemetal Some more ideas • Use previews to build
a component showcase -
#KKON @stewemetal Some more ideas • Use previews to build
a component showcase github.com/airbnb/Showkase -
#KKON @stewemetal Some more ideas • Use previews for screenshot
testing developer.android.com/studio/preview/compose-screenshot-testing -
#KKON @stewemetal Some more ideas • … or just make
the whole app runnable in Preview 🤷 -
#KKON @stewemetal Some more ideas • … or just make
the whole app runnable in Preview 🤷 medium.com/whatnot-engineering/preview-driven-development-with-compose-f7a5beee95aa -
#KKON @stewemetal 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 😅 -
Practical Tips and Tricks to Improve Your Compose Previews •
Compose previews are code • Simplify previews as much as possible (but go wild if it’s worth it!) • Compose custom reusable tools istvanjuhos.dev @stewemetal István Juhos Photo by Glenn Carstens-Peters