$30 off During Our Annual Pro Sale. View Details »

Jetpack Compose: 効果的なComposable関数のAPI設計

haru067
June 21, 2024

Jetpack Compose: 効果的なComposable関数のAPI設計

haru067

June 21, 2024
Tweet

More Decks by haru067

Other Decks in Technology

Transcript

  1. ࠓճ࿩͢͜ͱ Ҿ਺ΛͲͷΑ͏ʹఆٛ͢΂͖͔ @Composable fun Button( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit, ) { / / ... }
  2. ঢ়ଶΛҾ਺ͱͯ͠౉͢͜ͱͰɺίϯϙʔωϯτ͕εςʔτϨεʹͳΓɺίϯϙʔωϯτͷ֎ଆͰঢ়ଶΛ੍ޚɾڞ༗͢ Δ͜ͱ͕ग़དྷΔ TUBUFIPJTUJOH @Composable fun ParentComponent() { Accordion() } @Composable

    fun Accordion() { var expanded by remember { mutableStateOf(false) } Column { Text( text = "Click to expand", modifier = Modifier.clickable { expanded = !expanded }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  3. ঢ়ଶΛҾ਺ͱͯ͠౉͢͜ͱͰɺίϯϙʔωϯτ͕εςʔτϨεʹͳΓɺίϯϙʔωϯτͷ֎ଆͰঢ়ଶΛ੍ޚɾڞ༗͢ Δ͜ͱ͕ग़དྷΔ TUBUFIPJTUJOH @Composable fun ParentComponent() { Accordion() } @Composable

    fun Accordion() { var expanded by remember { mutableStateOf(false) } Column { Text( text = "Click to expand", modifier = Modifier.clickable { expanded = !expanded }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  4. ঢ়ଶΛҾ਺ͱͯ͠౉͢͜ͱͰɺίϯϙʔωϯτ͕εςʔτϨεʹͳΓɺίϯϙʔωϯτͷ֎ଆͰঢ়ଶΛ੍ޚɾڞ༗͢ Δ͜ͱ͕ग़དྷΔ TUBUFIPJTUJOH @Composable fun ParentComponent() { var expanded by

    remember { mutableStateOf(false) } Accordion( expanded = expanded, onExpandedChange = { expanded = !expanded } ) } @Composable fun Accordion( expanded: Boolean, onExpandedChange: () - > Unit, ) { Column { Text( text = "Click to expand", modifier = Modifier.clickable { onExpandedChange() }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  5. εςʔτϑϧͳ"1*Λެ։͠ɺεςʔτϨεͳ"1*ΛQSJWBUFʹ͢Ε͹ɺϓϨϏϡʔͭͭ͠ɺ"1*Λγϯϓϧʹอͭ͜ͱ ͕Ͱ͖Δ ΦʔόʔϩʔυʹΑΔTUBUFIPJTUJOHͷӅṭ @Composable fun Accordion() { var expanded by

    remember { mutableStateOf(false) } Accordion(expanded = expanded, onExpandedChange = { expanded = !expanded }) } @Composable private fun Accordion( expanded: Boolean, onExpandedChange: () - > Unit, ) { Column { Text( text = "Click to expand", modifier = Modifier.clickable { onExpandedChange() }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  6. εςʔτϑϧͳ"1*Λެ։͠ɺεςʔτϨεͳ"1*ΛQSJWBUFʹ͢Ε͹ɺϓϨϏϡʔͭͭ͠ɺ"1*Λγϯϓϧʹอͭ͜ͱ ͕Ͱ͖Δ ΦʔόʔϩʔυʹΑΔTUBUFIPJTUJOHͷӅṭ @Composable fun Accordion() { var expanded by

    remember { mutableStateOf(false) } Accordion(expanded = expanded, onExpandedChange = { expanded = !expanded }) } @Composable private fun Accordion( expanded: Boolean, onExpandedChange: () - > Unit, ) { Column { Text( text = "Click to expand", modifier = Modifier.clickable { onExpandedChange() }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  7. εςʔτϑϧͳ"1*Λެ։͠ɺεςʔτϨεͳ"1*ΛQSJWBUFʹ͢Ε͹ɺϓϨϏϡʔͭͭ͠ɺ"1*Λγϯϓϧʹอͭ͜ͱ ͕Ͱ͖Δ ΦʔόʔϩʔυʹΑΔTUBUFIPJTUJOHͷӅṭ @Composable fun Accordion() { var expanded by

    remember { mutableStateOf(false) } Accordion(expanded = expanded, onExpandedChange = { expanded = !expanded }) } @Composable private fun Accordion( expanded: Boolean, onExpandedChange: () - > Unit, ) { Column { Text( text = "Click to expand", modifier = Modifier.clickable { onExpandedChange() }, ) AnimatedVisibility(visible = expanded) { Text("Expanded content") } } }
  8. DPNQPTBCMFͳϥϜμΛҾ਺ʹ͢Δ͜ͱͰɺࢠཁૉΛॊೈʹઃఆ͢Δ͜ͱ͕Ͱ͖Δ @Composable fun AppBar( title: @Composable () -> Unit, navigationIcon:

    @Composable () - > Unit, ) TMPU"1* navigation ΞΠίϯ΍ςΩετͳͲɺத਎Λࣗ༝ʹઃఆͰ͖Δ ྫɿ"QQ#BS title AppBar( title = { Text( . .. ) }, navigationIcon = { IconButton( .. . ) }, )
  9. ॊೈʹͳΔҰํɺهड़͸গ͠৑௕ʹͳΔ Button( content = { Text("Save") }, onClick = {

    /* . .. */ }), ) TMPU"1*Λ࢖Θͳ͍ɺͱ͍͏બ୒ࢶ Button( content = "Save", onClick = { /* . .. */ }), ) هड़ͷ؆ܿ͞Λ༏ઌ͢Δબ୒ࢶ΋͋Δ SaveProfileButton( content = { Text("Save") }, onClick = { /* . .. */ }), ) SaveProfileButton( content = "Save", onClick = { /* . .. */ }), ) ༻్͕ݶఆతͳίϯϙʔωϯτʹ͸༗ޮ ඞཁʹͳ͔ͬͯΒTMPU"1*Խͯ͠΋ྑ͍
  10. TMPU"1*ʹϨγʔόʔΛࢦఆ͢Δ͜ͱͰίϯϙʔωϯτͷϓϦηοτΛ༻ҙ͢Δ͜ͱ͕Ͱ͖Δ %4-ͰͷTMPU"1* @Composable fun AppBar( title: @Composable () -> Unit,

    navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } ྫɿ"QQ#BS
  11. TMPU"1*ʹϨγʔόʔΛࢦఆ͢Δ͜ͱͰίϯϙʔωϯτͷϓϦηοτΛ༻ҙ͢Δ͜ͱ͕Ͱ͖Δ %4-ͰͷTMPU"1* @Composable fun AppBar( title: @Composable () -> Unit,

    navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } ྫɿ"QQ#BS AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, ) navigationIcon = {...} ͷதͰ͚ͩ BackButton͕࢖͑Δ
  12. ˔࢖͍ํΛ֮͑Δඞཁ͕͋Δ %4-ͰͷTMPU"1*ͷ஫ҙ఺ @Composable fun AppBar( title: @Composable () -> Unit,

    navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, )
  13. ˔࢖͍ํΛ֮͑Δඞཁ͕͋Δ %4-ͰͷTMPU"1*ͷ஫ҙ఺ @Composable fun AppBar( title: @Composable () -> Unit,

    navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, )
  14. ˔࢖͍ํΛ֮͑Δඞཁ͕͋Δ ˔࢖͍ํΛڧ੍͢Δ͜ͱ͸Ͱ͖ͳ͍ %4-ͰͷTMPU"1*ͷ஫ҙ఺ @Composable fun AppBar( title: @Composable () ->

    Unit, navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, )
  15. ˔࢖͍ํΛ֮͑Δඞཁ͕͋Δ ˔࢖͍ํΛڧ੍͢Δ͜ͱ͸Ͱ͖ͳ͍ %4-ͰͷTMPU"1*ͷ஫ҙ఺ @Composable fun AppBar( title: @Composable () ->

    Unit, navigationIcon: @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, ) AppBar( title = { Text("Title") }, navigationIcon = { IconButton( /* . .. */ ) { Icon( /* ... * / ) } }, )
  16. %4-ͰͷTMPU"1*ͷ஫ҙ఺ @Composable fun AppBar( title: @Composable () -> Unit, navigationIcon:

    @Composable AppBarIconScope.() -> Unit, ) @Stable class AppBarIconScope { @Composable fun BackButton(back: () - > Unit, modifier: Modifier = Modifier) { IconButton(onClick = back, modifier = modifier) { Icon( painter = painterResource(id = R.drawable.ic_back), contentDescription = null, ) } } } AppBar( title = { Text("Title") }, navigationIcon = { BackButton(back = { /* .. . * / }) }, ) AppBar( title = { Text("Title") }, navigationIcon = { IconButton( /* . .. */ ) { Icon( /* ... * / ) } }, ) ˔࢖͍ํΛ֮͑Δඞཁ͕͋Δ ˔࢖͍ํΛڧ੍͢Δ͜ͱ͸Ͱ͖ͳ͍ "QQ#BSͷྫͷΑ͏ʹɺ࢖༻ස౓͕ߴ͘ɺ༻్ʹҰఆͷ܏޲͕͋ΔʢΠϨΪϡϥʔ΋ͨ·ʹ͋Δʣ৔߹ʹ͸༗ޮ
  17. Ҿ਺ʹσϑΥϧτ஋Λઃఆ͢Δ͜ͱͰɺίϯϙʔωϯτͷॊೈੑΛอͪͭͭɺ؆ܿʹهड़͢Δ͜ͱ͕Ͱ͖Δ σϑΥϧτҾ਺ @Composable fun Button( onClick: () -> Unit, modifier:

    Modifier = Modifier, enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), ) @Composable fun SomeComponent() { Button(onClick = {}) { .. . } }
  18. Ҿ਺ʹσϑΥϧτ஋Λઃఆ͢Δ͜ͱͰɺίϯϙʔωϯτͷॊೈੑΛอͪͭͭɺ؆ܿʹهड़͢Δ͜ͱ͕Ͱ͖Δ σϑΥϧτҾ਺ σϑΥϧτ஋͕େྔʹ͋Δ৔߹͸ɺ ͦΕΒΛ·ͱΊͨΦϒδΣΫτΛ࡞Δ͜ͱ΋༗ޮ $PNQPOFOU%FGBVMUT @Composable fun Button( onClick: ()

    -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), ) @Composable fun SomeComponent() { Button(onClick = {}) { .. . } } object ButtonDefaults { val shape: Shape @Composable get() = ... @Composable fun buttonColors( ... ): ButtonColors = ... @Composable fun buttonElevation( .. . ): ButtonElevation = ... }
  19. Ұݟศརʹݟ͑ΔσϑΥϧτҾ਺͕ͩɺ҉໧తʹ஋Λઃఆ͢Δ͜ͱʹͳΔ ͜ͷ҉໧తͳ஋ͷଘࡏʹؾ͔ͮͳ͍͜ͱͰ໰୊ʹͳΔ৔߹͕͋Δ ҆қʹσϑΥϧτҾ਺Λઃఆ͠ͳ͍ @Composable fun CenteredText() { Column { Text(

    text = "தԝἧ͍͑ͨ͠จࣈྻ", textAlign = TextAlign.Center, modifier = Modifier .align(Alignment.CenterHorizontally) .padding(32.dp) ) } } ྫɿ5FYUͷதԝἧ͑ UFYU"MJHOͷࢦఆ͕ඞཁͩͬͨ
  20. Ұݟศརʹݟ͑ΔσϑΥϧτҾ਺͕ͩɺ҉໧తʹ஋Λઃఆ͢Δ͜ͱʹͳΔ ͜ͷ҉໧తͳ஋ͷଘࡏʹؾ͔ͮͳ͍͜ͱͰ໰୊ʹͳΔ৔߹͕͋Δ ҆қʹσϑΥϧτҾ਺Λઃఆ͠ͳ͍ @Composable fun CenteredText() { Column { Text(

    text = "தԝἧ͍͑ͨ͠จࣈྻ", textAlign = TextAlign.Center, modifier = Modifier .align(Alignment.CenterHorizontally) .padding(32.dp) ) } } ྫɿ5FYUͷதԝἧ͑ ʮσϑΥϧτҾ਺ʹ͠ͳ͚Ε͹ؾ෇͍ͨͷʹɾɾɾɾʯ
  21. Ұݟศརʹݟ͑ΔσϑΥϧτҾ਺͕ͩɺ҉໧తʹ஋Λઃఆ͢Δ͜ͱʹͳΔ ͜ͷ҉໧తͳ஋ͷଘࡏʹؾ͔ͮͳ͍͜ͱͰ໰୊ʹͳΔ৔߹͕͋Δ ҆қʹσϑΥϧτҾ਺Λઃఆ͠ͳ͍ @Composable fun CenteredText() { Column { Text(

    text = "தԝἧ͍͑ͨ͠จࣈྻ", textAlign = TextAlign.Center, modifier = Modifier .align(Alignment.CenterHorizontally) .padding(32.dp) ) } } ྫɿ5FYUͷதԝἧ͑ ஋Λ౎౓ߟ͑Δ͜ͱ͕༗ӹͳ৔߹ɺσϑΥϧτҾ਺Λઃఆ͠ͳ͍ ʮσϑΥϧτҾ਺ʹ͠ͳ͚Ε͹ؾ෇͍ͨͷʹɾɾɾɾʯ
  22. ݩʑίϯϙʔωϯτʹؔ࿈ͨ͠Ϋϥε͕ଘࡏ͍ͯͨ͠ΓɺҾ਺͕૿͑ͯ͘ΔͱҰͭͷΫϥεͰҾ਺Λ·ͱΊͨ͘ͳΔ σʔλΫϥεͰҾ਺Λ·ͱΊΔ΂͖͔ @Composable fun HouseHeader( name: String, address: String, )

    ϑϥοτʹॻ͘ @Composable fun HouseHeader( house: House, ) ΫϥεͰ·ͱΊΔ data class House( val name: String, val address: String, )
  23. Ϋϥεʹ·ͱΊͨ৔߹ɺఆٛΛݟͳ͍ͱίϯϙʔωϯτʹඞཁͳ஋͕Θ͔ΒͣɺϫϯΫογϣϯڬΉ͜ͱʹͳΔ data class House( val name: String, val address: String,

    ) σʔλΫϥεͰҾ਺Λ·ͱΊΔ΂͖͔ɿՄಡੑ )PVTFͷఆٛΛݟΔ·ͰΘ͔Βͳ͍ Ұ໨ͰԿ͕ඞཁ͔Θ͔Δ @Composable fun HouseHeader( name: String, address: String, ) @Composable fun HouseHeader( house: House, ) 👀
  24. ίϯϙʔωϯτ಺Ͱࢀর͠ͳ͍ϑΟʔϧυ͕Ϋϥεʹؚ·ΕΔ৔߹ɺແҙຯʹ࠶ίϯϙʔζ͕૸ͬͯ͠·͏ σʔλΫϥεͰҾ਺Λ·ͱΊΔ΂͖͔ɿύϑΥʔϚϯε name,address͕มΘͬͨ ͱ͖͚ͩ࠶ίϯϙʔζ data class House( val name: String,

    val address: String, val imageUrl: String, ) @Composable fun HouseHeader( house: House, ) @Composable fun HouseHeader( name: String, address: String, ) imageUrl͕มΘͬͨͱ͖ɺ ແҙຯʹ࠶ίϯϙʔζ
  25. ΫϥεͷϓϩύςΟ͕૿͑ͨࡍɺͦͷϓϩύςΟͱ͸ແؔ܎ͳίϯϙʔωϯτͷϓϨϏϡʔ͕ΤϥʔʹͳΔ ϓϨϏϡʔ͕ո͍͠ઃܭΛᖰΓग़͢ @Preview @Composable fun PreviewHouseHeader() { HouseHeader( House( name

    = "House", address = "Address", ) ) } @Composable fun HouseHeader( house: House, ) data class House( val name: String, val address: String, val imageUrl: String, )
  26. @Preview @Composable fun PreviewHouseHeader() { HouseHeader( House( name = "House",

    address = "Address", ) ) } ΫϥεͷϓϩύςΟ͕૿͑ͨࡍɺͦͷϓϩύςΟͱ͸ແؔ܎ͳίϯϙʔωϯτͷϓϨϏϡʔ͕ΤϥʔʹͳΔ ϓϨϏϡʔ͕ո͍͠ઃܭΛᖰΓग़͢ ͜͜ʹimageUrl͕ͳ͍ͷͰϏϧυΤϥʔ @Composable fun HouseHeader( house: House, ) data class House( val name: String, val address: String, val imageUrl: String, )
  27. લड़ͷ௨Γɺجຊతʹ͸ඞཁͳϓϩύςΟΛϑϥοτʹྻڍͨ͠΄͏͕͍͍ ʢ1SPQFSUZESJMMJOHͰߏΘͳ͍ʣ ΫϥεΛ࡞Δ͜ͱͰҙຯͷ͋ΔάϧʔϓԽ͕Ͱ͖ΔͷͰ͋Ε͹ɺΫϥεʹू໿͢Δͷ΋͋Γ 1SPQFSUZESJMMJOH͢΂͖͔ @Composable fun BookingOverview( mealSectionTitle: String, mealSectionSubtitle:

    String, scheduleSectionTitle: String, scheduleSectionSubtitle: String, meals: List<Meal>, schedule: Schedule, ) @Composable fun BookingOverview( mealSectionUiState: MealSectionUiState, scheduleSectionUiState: ScheduleSectionUiState, ) Ұ໨ͰΘ͔Δ͜ͱͰͷՄಡੑɹʻɹҙຯతʹ·ͱ·͍ͬͯΔ͜ͱͰͷՄಡੑ