Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Compose Previews as a Power User
Search
Subhrajyoti Sen
October 06, 2024
Programming
230
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Compose Previews as a Power User
Subhrajyoti Sen
October 06, 2024
More Decks by Subhrajyoti Sen
See All by Subhrajyoti Sen
Understanding Incremental Processing in the JVM World
subhrajyotisen
0
44
Updated Lessons from a KMP Developer's Toolkit
subhrajyotisen
0
60
Building Mobile Apps and Scaling them
subhrajyotisen
0
71
Understanding WindowInsets
subhrajyotisen
0
260
Exploring a KMM Developer’s Toolkit
subhrajyotisen
1
270
Shipping Apps Confidently with Firebase
subhrajyotisen
0
140
Understanding WindowInsets - Android Worldwide
subhrajyotisen
0
390
Understanding WindowInsets
subhrajyotisen
1
240
Demystifying Styles and Themes
subhrajyotisen
0
280
Other Decks in Programming
See All in Programming
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.3k
A2UI という光を覗いてみる
satohjohn
1
140
OSもどきOS
arkw
0
570
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.7k
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
11
5.8k
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
120
Strategic Design in the Frontend: Moduliths & Micro Frontends @DDDEurope
manfredsteyer
PRO
0
110
RTSPクライアントを自作してみた話
simotin13
0
610
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
260
Creating Composable Callables in Contemporary C++
rollbear
0
150
The NotImplementedError Problem in Ruby
koic
1
840
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
160
Featured
See All Featured
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
320
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.5k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.5k
Ethics towards AI in product and experience design
skipperchong
2
310
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Designing Powerful Visuals for Engaging Learning
tmiket
1
420
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
310
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
44k
Transcript
Compose Previews as a Power User Chennai Subhrajyoti Sen Motive
Simple Previews
@Composable fun BorrowedItemRowPreview() { BorrowedItemRow( item = BorrowItem( id =
0, itemName = "XBox Series X", borrowerName = "Steve", borrowDate = "2-11-2023" ) ) }
@Preview @Composable fun BorrowedItemRowPreview() { BorrowedItemRow( item = BorrowItem( id
= 0, itemName = "XBox Series X", borrowerName = "Steve", borrowDate = "2-11-2023" ) ) }
@Preview @Composable fun BorrowedItemRowPreview() { BorrowedItemRow( item = BorrowItem( id
= 0, itemName = "XBoz Series X", borrowerName = "Steve", borrowDate = "2-11-2023" ) ) }
None
Available Arguments • name • group • uiMode • locale
• showSystemUi • showBackground • device • apiLevel
Dark Mode
@Preview(uiMode = UI_MODE_NIGHT_YES) @Composable fun BorrowedItemRowDevicePreview() { BorrowTheme { BorrowedItemRow(
item = //.. ) } }
None
Multi Preview Annotation
@Preview(uiMode = UI_MODE_NIGHT_NO) @Preview(uiMode = UI_MODE_NIGHT_YES) @Composable fun BorrowedItemRowDevicePreview() {
BorrowTheme { BorrowedItemRow( item = //.. ) } }
@Preview(uiMode = UI_MODE_NIGHT_NO) @Preview(uiMode = UI_MODE_NIGHT_YES) annotation class LightDarkThemePreview
@LightDarkThemePreview @Composable fun BorrowedItemRowDevicePreview() { BorrowTheme { BorrowedItemRow( item =
//.. ) } }
@LightDarkThemePreview @Composable fun BorrowedItemRowDevicePreview() { BorrowTheme { BorrowedItemRow( item =
//.. ) } }
None
@Preview( showSystemUi = true, device = Devices.NEXUS_5X ) @Preview( showSystemUi
= true, device = Devices.NEXUS_10 ) annotation class MultiDevicePreview
@Preview( showSystemUi = true, device = Devices.NEXUS_5X ) @Preview( showSystemUi
= true, device = Devices.NEXUS_10 ) annotation class MultiDevicePreview
@Preview(locale = "en") @Preview(locale = "fr-rCA") @Preview(locale = "es-rUS") annotation
class MultiLocalePreview Valid locale format: https://developer.android.com/guide/topics/resources/provid ing-resources#LocaleQualifier
@LightDarkThemePreview @MultiLocalePreview annotation class MultiLocaleLightDarkPreview
Dataset Driven Preview
None
@Preview @Composable fun BorrowedItemRowPreview() { BorrowedItemRow( item = BorrowItem( id
= 0, itemName = "XBox Series X", borrowerName = "Steve", borrowDate = "2-11-2023" ) ) } @Preview @Composable fun BorrowedItemRowPreview() { BorrowedItemRow( item = BorrowItem( id = 0, itemName = "PlayStation", borrowerName = "Steve", borrowDate = "2-11-2023" ) ) }
class BorrowItemsProvider : PreviewParameterProvider<BorrowItem> { override val values = sequenceOf(
BorrowItem( id = 0, itemName = "PlayStation", borrowerName = "Steve", borrowDate = "2-11-2022" ), BorrowItem( id = 0, itemName = "XBox", borrowerName = "Steve", borrowDate = "2-11-2022" ) ) }
class BorrowItemsProvider : PreviewParameterProvider<BorrowItem> { override val values = sequenceOf(
BorrowItem( id = 0, itemName = "PlayStation", borrowerName = "Steve", borrowDate = "2-11-2022" ), BorrowItem( id = 0, itemName = "XBox", borrowerName = "Steve", borrowDate = "2-11-2022" ) ) }
@Preview @Composable fun BorrowedItemRowPreview( @PreviewParameter(BorrowItemsProvider::class) item: BorrowItem ) { BorrowedItemRow(
item ) }
@Preview @Composable fun BorrowedItemRowPreview( @PreviewParameter(BorrowItemsProvider::class) item: BorrowItem ) { BorrowedItemRow(
item ) }
Declutter using Groups
@MultiLocaleLightDarkPreview @Composable fun BorrowedItemRowDevicePreview() { BorrowTheme { BorrowedItemRow( item =
//.. ) } }
None
@Preview(locale = "en", group = "English") @Preview(locale = "fr-rCA", group
= "Canadian French") @Preview(locale = "es-rUS", group = "US Spanish") annotation class MultiLocalePreview
None
Local Inspection
None
@Composable fun BorrowedItemRow(item: BorrowItem) { var expanded by remember {
mutableStateOf(false) } // this will only affect previews if (LocalInspectionMode.current) { expanded = true } //.. }
@Composable fun BorrowedItemRow(item: BorrowItem) { var expanded by remember {
mutableStateOf(false) } // this will only affect previews if (LocalInspectionMode.current) { expanded = true } //.. }
Showkase
None
@ShowkaseComposable(group = "Individual rows") @Preview @Composable fun BorrowedItemRowExpandedPreview() { BorrowTheme
{ BorrowedItemRow( //.. ) } }
@ShowkaseComposable(group = "Individual rows") @Preview @Composable fun BorrowedItemRowExpandedPreview() { BorrowTheme
{ BorrowedItemRow( //.. ) } }
@ShowkaseComposable(group = "Individual rows") @Composable @Preview fun BorrowedItemRowCollapsedPreview() { CompositionLocalProvider(LocalInspectionMode
provides false) { BorrowTheme { BorrowedItemRow( //.. ) } } }
None
None
None
Animation Previews
None
None
updateTransition Supported Animations AnimatedVisibility animate*AsState rememberInfiniteTransition CrossFade AnimatedContent
Screenshot Testing
Available Options
Paparazzi Available Options
Paparazzi Roborazzi Available Options
Paparazzi Roborazzi Compose Preview Available Options
Paparazzi
Works on JVM Paparazzi
Works on JVM Based on Layoutlib Paparazzi
Works on JVM Based on Layoutlib Needs JUnit tests Paparazzi
Works on JVM Based on Layoutlib Needs JUnit tests Paparazzi
Supports Views
Works on JVM Based on Layoutlib Needs JUnit tests Paparazzi
Supports Views Emulates Production
class PaparazziTests { @get:Rule val paparazzi = Paparazzi( deviceConfig =
PIXEL_5.copy(nightMode = NightMode.NIGHT), renderingMode = RenderingMode.SHRINK ) }
class PaparazziTests { @get:Rule val paparazzi = Paparazzi( deviceConfig =
PIXEL_5.copy(nightMode = NightMode.NIGHT), renderingMode = RenderingMode.SHRINK ) }
class PaparazziTests { //.. @Test fun launchComposable() { paparazzi.snapshot {
BorrowTheme { BorrowedItemRow( item = //.. ) } } } }
class PaparazziTests { //.. @Test fun launchComposable() { paparazzi.snapshot {
CompositionLocalProvider(LocalInspectionMode provides true) { BorrowTheme { BorrowedItemRow( item = //.. ) } } } } }
class PaparazziTests { //.. @Test fun launchComposable() { paparazzi.snapshot {
CompositionLocalProvider(LocalInspectionMode provides true) { BorrowTheme { BorrowedItemRow( item = //.. ) } } } } }
Baseline
After changes
Failure Comparison
Compose Preview Screenshot
Works on JVM Compose Preview Screenshot
Works on JVM Based on Layoutlib Compose Preview Screenshot
Works on JVM Based on Layoutlib Fully Automated Compose Preview
Screenshot
Works on JVM Based on Layoutlib Fully Automated Compose Preview
Screenshot Needs a special directory
Works on JVM Based on Layoutlib Fully Automated Compose Preview
Screenshot Needs a special directory Emulates Debug
Compose Preview Screenshot - Source
Compose Preview Screenshot - Source @LightDarkThemePreview @Composable fun BorrowedItemRowPreview( @PreviewParameter(BorrowItemsProvider::class)
item: BorrowItem ) { BorrowedItemRow( item ) }
Compose Preview Screenshot - Destination
Screen Comparison
Screen Comparison
Can’t decide? sergio-sastre/ComposablePreviewScanner
Further Exploration https://github.com/takahirom/roborazzi Composable Preview Driven Development: TDD-fying your UI
with ease!
Questions?
Thank You! Chennai