Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Compose 新機能総まとめ / What's New in Jetpack Compose

Compose 新機能総まとめ / What's New in Jetpack Compose

Google I/O Extended Tokyo 2026
https://gdg-tokyo.connpass.com/event/394136/

Avatar for Yuki Anzai

Yuki Anzai

July 11, 2026

More Decks by Yuki Anzai

Other Decks in Technology

Transcript

  1. ͋Μ͍͟Ώ͖ w "OESPJE&OHJOFFS w (PPHMF%FWFMPQFS&YQFSUGPS"OESPJE w 9 UXJUUFS !ZBO[Nʢ΍Μ͟Ήʣ w

    CMPHZBO[NCMPHTQPUDPN w גࣜձࣾ΢ϑΟΧ୅දऔక໾ w ࡳຈɾؔ౦ڌ఺ੜ׆
  2. "OESPJE6*͸ $PNQPTF'JSTU ΁ w ैདྷͷ7JFXπʔϧΩοτ͸ ϝϯςφϯεϞʔυ w ॏେόάमਖ਼ͷΈɺػೳ௥Ճͳ͠ w ഇࢭͰ͸ͳ͍

    w 'SBHNFOU 7JFX1BHFS 3FDZDMFS7JFX "OESPJE4UVEJPͷ-BZPVU /BWJHBUJPO&EJUPSͳͲ΋ಉ͘͡ϝϯςφϯεϞʔυ 4
  3. $PNQPTFUFTUJOHW"1*T w DSFBUF$PNQPTF3VMF SVO$PNQPTF6J5FTUͳͲͷW൛ w %FQSFDBUFE W androidx.compose.ui.test.junit4.createComposeRule w ˠWandroidx.compose.ui.test.junit4.v2.createComposeRule

    w $PNQPTFςετͷԾ૝ΫϩοΫʹ$PSPVUJOFT΋৐ΔΑ͏ʹͳͬͨ w W6ODPO fi OFE5FTU%JTQBUDIFSˠW4UBOEBSE5FTU%JTQBUDIFS 7 https://developer.android.com/develop/ui/compose/testing/migrate-v2 Stable
  4. $PNQPTFUFTUJOHW"1*T w DPNQPTF5FTU3VMF SVO5FTUˠSVO$PNQPTF6J5FTUΛ࢖͏ 8 Stable @get:Rule val composeTestRule =

    createComposeRule() @Test fun testWithCoroutines() { composeTestRule.setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000.milliseconds) status = "Done!" } Text(text = status) } runTest { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() advanceTimeBy(1000) composeTestRule.onNodeWithText("Done!").assertIsDisplayed() } }
  5. $PNQPTFUFTUJOHW"1*T w DPNQPTF5FTU3VMF SVO5FTUˠSVO$PNQPTF6J5FTUΛ࢖͏ 9 Stable @get:Rule val composeTestRule =

    createComposeRule() @Test fun testWithCoroutines() = runComposeUiTest { composeTestRule.setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000.milliseconds) status = "Done!" } Text(text = status) } runTest { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() mainClock.advanceTimeBy(1000 + 16 /* Frame buffer */) composeTestRule.onNodeWithText("Done!").assertIsDisplayed() } }
  6. 5SBDLQBEσεΫτοϓೖྗ w τϥοΫύουͷجຊΠϕϯτ͕ PointerType.Mouse ѻ͍ʹ w ຊࢦεϫΠϓɾϐϯνΛ scrollable / transformable

    ͕ࣗಈೝࣝ w ςΩετͷμϒϧτϦϓϧΫϦοΫબ୒ɺσεΫτοϓ෩ϝχϡʔ w ςετ༻ performTrackpadInput "1*௥Ճ w DMJDLESBH͸ʮબ୒ʯͱͯ͠ಈ࡞ 10 Stable
  7. 1SFWJFX8SBQQFS w ϓϨϏϡʔʹςʔϚద༻ͳͲͷఆܕॲཧΛ஫ೖ 11 Stable class ThemeWrapper : PreviewWrapperProvider {

    @Composable override fun Wrap(content: @Composable () -> Unit) { MyTheme { content() } } } @PreviewWrapper(ThemeWrapper::class) @Preview(showBackground = true) @Composable private fun ButtonPreview() { Button(onClick = {}) { Text("Demo") } } 5IFNF8SBQQFSʹΑͬͯ.Z5IFNF͕ద༻͞ΕΔ
  8. ࡉ͔͍มߋͭ w $PNQPTJUJPOIPTUEFGBVMUTʢDPNQPTFSVOUJNFʣ w -PDBM$POUFYU ͷΑ͏ͳϗετ؀ڥ༝དྷͷ஋ͷ஫ೖޱΛɺDPNQPTFVJൈ͖ DPNQPTF SVOUJNF୯ମ Ͱ࡞ΕΔΑ͏ʹͨ͠࢓૊Έ w

    ϥΠϒϥϦͷґଘ͕ܰ͘ͳΓ,.1͕ਐΉ w Modifier.onFirstVisible() ͕EFQSFDBUFEʹ w ޙܧ͸ Modifier.onVisibilityChanged() w ΑΓࡉ͔͍ՄࢹੑτϥοΩϯά͕Մೳʹ w 5FYU'JFMEͷ%QBEφϏ͕ৗ࣌༗ޮʹ w ComposeFoundationFlags.isTextFieldDpadNavigationEnabled ϑϥά͸࡟আ 12 Stable
  9. 4UZMFT"1* w IUUQTEFWFMPQFSBOESPJEDPNEFWFMPQVJDPNQPTFTUZMFT w ݟͨ໨͸ 4UZMFʹɺৼΔ෣͍͸.PEJ fi FSʹ໾ׂΛ෼཭ w ͳʹ͕͏Ε͍͠ʁ

    w ίϯϙʔωϯτͷػೳར༻ͱݟͨ໨ͷΧεϚΠζͷཱ྆ w ύϑΥʔϚϯεվળ w QSFTT࣌ͳͲͷΞχϝʔγϣϯ͕ॻ͖΍͍͢ 14 Experimental BOM 2026.04.01 ~
  10. 4UZMFT"1* ίϯϙʔωϯτͷػೳར༻ͱݟͨ໨ͷΧεϚΠζͷཱ྆ 15 Experimental @Composable fun MyButton( text: String, onClick:

    () -> Unit, modifier: Modifier = Modifier, containerColor: Color = …, contentColor: Color = …, ) { … } @Composable fun MyScreen() { MyButton( text = "Button", containerColor = Color.DarkGray, contentColor = Color.White, onClick = {}, ) } BOM 2026.04.01 ~
  11. 4UZMFT"1* ίϯϙʔωϯτͷػೳར༻ͱݟͨ໨ͷΧεϚΠζͷཱ྆ 16 Experimental @Composable fun MyButton( text: String, onClick:

    () -> Unit, modifier: Modifier = Modifier, containerColor: Color = …, contentColor: Color = …, ) { … } @Composable fun MyScreen() { MyButton( text = "Button", containerColor = Color.DarkGray, contentColor = Color.White, onClick = {}, ) } BOM 2026.04.01 ~ എܠΛάϥσʔγϣϯʹ ͍ͨ͠ͱ͖͸Ͳ͏͢Δʁ
  12. 4UZMFT"1* 17 Experimental @Composable fun MyButton( text: String, onClick: ()

    -> Unit, modifier: Modifier = Modifier, style: Style = Style, ) { … val buttonDefaultStyle = Style { background(containerColor) shape(shape) clip(true) } Box( modifier = modifier … .styleable(styleState, buttonDefaultStyle, style), ) … } σϑΥϧτͷελΠϧ ΧελϚΠζ༻ͷελΠϧ ޙউͪͰద༻ BOM 2026.04.01 ~
  13. 4UZMFT"1* 18 Experimental @Composable fun MyScreen() { MyButton( text =

    "Button", onClick = {}, style = Style { // background(Color.DarkGray) background( Brush.verticalGradient( listOf( Color.LightGray, Color.DarkGray ) ) ) } ) } άϥσʔγϣϯ΋ࢦఆͰ͖Δ ৭ࢦఆͰഎܠΛม͑Δ৔߹ BOM 2026.04.01 ~
  14. .FEJB2VFSZ"1* ΢Οϯυ΢෯ɾંΓͨͨΈ࢟੎ɾϙΠϯλਫ਼౓ͳͲͷ؀ڥΛએݴతʹऔಘ 20 Experimental val scope = LocalUiMediaScope.current SignalRow("pointerPrecision", "${scope.pointerPrecision}")

    SignalRow("keyboardKind", "${scope.keyboardKind}") SignalRow("viewingDistance", "${scope.viewingDistance}") SignalRow("hasMicrophone", "${scope.hasMicrophone}") SignalRow("hasCamera", "${scope.hasCamera}") val isWide by derivedMediaQuery { windowWidth >= 600.dp } val isTabletop = mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } class App : Application() { override fun onCreate() { ComposeUiFlags.isMediaQueryIntegrationEnabled = true super.onCreate() } } "QQMJDBUJPO ΫϥεͰ༗ޮԽ සൟʹมΘΔ஋͸ EFSJWFE.FEJB2VFSZ Ͱऔಘ BOM 2026.04.01 ~
  15. (SJEϨΠΞ΢τ DPMVNO SPXʢྻߦʣɾHBQɾTQBOͰߏ଄Λఆٛ 21 Experimental Grid( config = { repeat(4)

    { column(0.25f) } repeat(2) { row(80.dp) } gap(8.dp) }, ) { Cell("rowSpan=2", Color(0xFFE57373), Modifier.gridItem(rowSpan = 2)) Cell("colSpan=3", Color(0xFF81C784), Modifier.gridItem(columnSpan = 3)) Cell("A", Color(0xFF64B5F6)) Cell("B", Color(0xFFBA68C8)) Cell("C", Color(0xFFFFB74D)) } BOM 2026.04.01 ~
  16. 'MFY#PYϨΠΞ΢τ $44'MFYCPYͱ΄΅ಉ͡ͰɺંΓฦ͠ɾ੔ྻɾ৳ॖ͕Մೳ 22 Experimental BOM 2026.04.01 ~ FlexBox( config =

    { wrap(FlexWrap.Wrap) gap(8.dp) }, ) { labels.forEachIndexed { i, label -> Chip(label, colors[i % colors.size]) } }
  17. 'MFY#PYϨΠΞ΢τ 'MPX3PXͱͲ͏ҧ͏ʁ 23 Experimental BOM 2026.04.01 ~ FlexBox( config =

    { wrap(FlexWrap.Wrap) gap(8.dp) }, modifier = Modifier.fillMaxWidth(), ) { labels… } FlowRow( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth(), ) { labels… }
  18. 'MFY#PYϨΠΞ΢τ 'MPX3PXͱͲ͏ҧ͏ʁ 24 Experimental BOM 2026.04.01 ~ FlexBox( config =

    { wrap(FlexWrap.Wrap) gap(8.dp) }, modifier = Modifier.fillMaxWidth(), ) { labels.forEachIndexed { i, label -> Chip(…, Modifier.flex { grow(1f) }) } } FlowRow( horizontalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth(), ) { labels… }
  19. ৽͍͠4MPU5BCMF w ৽͍͠MJOLMJTUCBTFETMPUUBCMF࣮૷ʢैདྷ͸HBQCV ff FSCBTFEʣ w DPNQPTBCMFDPOUFOUͷ࡟আɺҠಈɺ͓Αͼฒ΂ସ͑࣌ʹɺ಺෦഑ྻίϐʔ ૢ࡞ͷྔΛݮΒ͢͜ͱͰύϑΥʔϚϯεΛ޲্ͤ͞ΔΑ͏ʹઃܭ͞Ε͍ͯΔ w TFU$POUFOUΛॳΊͯݺͼग़͢લʹઃఆ͠ɺͦͷޙมߋෆՄ

    25 Experimental class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @OptIn(ExperimentalComposeApi::class) ComposeRuntimeFlags.isLinkBufferComposerEnabled = true setContent { … } } }
  20. 3FNPUF$PNQPTFͱ͸ʁ w ͜Ε·ͰϗʔϜͷ8JEHFU͸ 3FNPUF7JFXT ܦ༝ʢ(MBODFͰॻ͍ͯ΋ཪ͸ 3FNPUF7JFXTʣ w ແཧͩͬͨදݱ͕Մೳʹˠ৽ίϯϙʔωϯτ TOBQTDSPMMFYQSFTTJWFCVUUPOT QBSUJDMFF

    ff FDUT w ඳը໋ྩྻΛඳ͘ʹ͸ 04ͷରԠ͕ඞཁˠ"OESPJE ͕ରԠʢ ͳΒ 3FNPUF$PNQPTFํࣜͰඳըʣ 27 ૹΔ΋ͷ ϗʔϜଆ RemoteViews ϨΠΞ΢τ XML ͷϦιʔε ID
 + setter ݺͼग़͠ͷه࿥ View Λ૊ΈཱͯΔ
 → ࡞ΕΔϨΠΞ΢τʹ੍ݶ RemoteCompose όΠφϦඳը໋ྩྻ ͦΕΛ௚઀ඳը
  21. 8FBSͱ"VUP΋(MBODF΁ w ಉ͡Α͏ͳ8JEHFUΛϗʔϜɾ8FBSɾ"VUPͰผʑʹ࣮૷͍ͯͨ͠ w 8FBS͸8JEHFUͰ͸ͳ͘ 5JMFT Š 1SPUP-BZPVU ͱ͍͏ಠࣗ"1* w

    "VUPʹ΋ಠࣗͷ࢓૊Έ w "OESPJEੈ୅͔ΒҠߦ։࢝ w 5JMFTˠʮ8FBS8JEHFUTʯʹվ໊ɺ1SPUP-BZPVUޙܧ ͷ BOESPJEYHMBODFXFBS Ͱॻ͘ w "VUP΋(MBODF΁ w ˠ ϗʔϜɾ8FBSɾ"VUPΛผʑʹ࣮૷͠ͳͯ͘Α͘ͳΔʢίϯϙʔωϯτڞ༗ʣ 29