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
240
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
58
Updated Lessons from a KMP Developer's Toolkit
subhrajyotisen
0
74
Building Mobile Apps and Scaling them
subhrajyotisen
0
75
Understanding WindowInsets
subhrajyotisen
0
270
Exploring a KMM Developer’s Toolkit
subhrajyotisen
1
280
Shipping Apps Confidently with Firebase
subhrajyotisen
0
150
Understanding WindowInsets - Android Worldwide
subhrajyotisen
0
410
Understanding WindowInsets
subhrajyotisen
1
250
Demystifying Styles and Themes
subhrajyotisen
0
300
Other Decks in Programming
See All in Programming
FDEとは、何者なのか?
masapyon1212
0
140
私のClaude Code活用法 (個人開発編) - PHPerKaigi mini #4(2026/08/24)
panda_program
1
210
typoなんかねぇよ
raspython3
0
660
Kiroで創り、AgentCoreで繋ぐ!AWSで実践する「AI-DLC」から「AIエージェント統合」までの最新地図
licux
2
300
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
260
ソフトウェアラスタライザ
fadis
1
780
{ Android | Kotlin } Gradle Plugin in 2026
ryunen344
1
280
信頼性の目標を誰も求めてない
shubox
0
490
思考垂れ流し開発 ~音声入力 × AIエージェント × 開発ハーネスによる試行錯誤~
npostring
0
810
リアルな遅延を測る仕様
kota_yata
1
140
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
0
410
Can LLMs Replicate 4 Years of Compose Migration? Exploring the boundaries of automation with 279 XML files from a real product
makun
0
130
Featured
See All Featured
Marketing to machines
jonoalderson
1
5.7k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
530
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Exploring anti-patterns in Rails
aemeredith
3
490
Scaling GitHub
holman
464
140k
Test your architecture with Archunit
thirion
2
2.4k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
320
Optimizing for Happiness
mojombo
378
71k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.3k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
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