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

Jetpack ComposeのBottomSheetとの戦い / Fight with Bo...

Jetpack ComposeのBottomSheetとの戦い / Fight with BottomSheet of Jetpack Compose

Masatoshi Kubode

July 23, 2024
Tweet

More Decks by Masatoshi Kubode

Other Decks in Programming

Transcript

  1. © 2024 Wantedly, Inc. 標準のBottomSheet実装 https://m3.material.io/components/bottom-sheets/overview ① Standard ② Modal

    Material 2 BottomSheetScaffold ModalBottomSheetLayout Material 3 BottomSheetScaffold ModalBottomSheet
  2. © 2024 Wantedly, Inc. Modalの実装の違い @Composable private fun M3ModalPreview() {

    var showSheet by remember { mutableStateOf(false) } Button(onClick = { showSheet = true }) { Text("Show") } if (showSheet) { ModalBottomSheet( onDismissRequest = { showSheet = false } ) { Text("Hello, World!") } } } @Composable private fun M2ModalPreview() { val sheetState = rememberModalBottomSheetState( ModalBottomSheetValue.Hidden ) val coroutineScope = rememberCoroutineScope() ModalBottomSheetLayout( sheetContent = { Text("Hello, World!") }, sheetState = sheetState ) { Button( onClick = { coroutineScope.launch { sheetState.show() } } ) { Text("Show") } } }
  3. © 2024 Wantedly, Inc. 課題 1 BottomNavigationは Activityが所有する XMLのView Activity

    BottomNavigation FragmentContainer SearchFragment with Compose
  4. © 2024 Wantedly, Inc. 解決策 Modalを自作した • ウィンドウ操作が必要と判断 • ComposeのDialogのコードをもとに自作

    ◦ Material3のModalじゃない理由は後述しますが、Material3のModalとほぼ同じ実 装
  5. © 2024 Wantedly, Inc. @Composable fun Modal( onDismissRequest: () !>

    Unit, sheetContent: @Composable () !> Unit, overlayContent: @Composable (() !> Unit)? = null, sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), ) { !/ 色々省略あり val dialog = remember(view) { DialogWrapper(/*省略*/).apply { setContent(composition) { DialogLayout { Box(modifier = Modifier.fillMaxSize()) { ModalLayout( sheetState = sheetState, onDismissRequest = onDismissRequest, sheetContent = sheetContent, ) if (overlayContent != null) { OverlayContent( sheetState = sheetState, overlayContent = overlayContent, ) } } } } } }
  6. © 2024 Wantedly, Inc. @Composable fun Modal( onDismissRequest: () !>

    Unit, sheetContent: @Composable () !> Unit, overlayContent: @Composable (() !> Unit)? = null, sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), ) { !/ 色々省略あり val dialog = remember(view) { DialogWrapper(/*省略*/).apply { setContent(composition) { DialogLayout { Box(modifier = Modifier.fillMaxSize()) { ModalLayout( sheetState = sheetState, onDismissRequest = onDismissRequest, sheetContent = sheetContent, ) if (overlayContent != null) { OverlayContent( sheetState = sheetState, overlayContent = overlayContent, ) } } } } } } 透明なフルスクリーンウィンドウ
  7. © 2024 Wantedly, Inc. @Composable fun Modal( onDismissRequest: () !>

    Unit, sheetContent: @Composable () !> Unit, overlayContent: @Composable (() !> Unit)? = null, sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), ) { !/ 色々省略あり val dialog = remember(view) { DialogWrapper(/*省略*/).apply { setContent(composition) { DialogLayout { Box(modifier = Modifier.fillMaxSize()) { ModalLayout( sheetState = sheetState, onDismissRequest = onDismissRequest, sheetContent = sheetContent, ) if (overlayContent != null) { OverlayContent( sheetState = sheetState, overlayContent = overlayContent, ) } } } } } } ウィンドウの中にBottomSheet BottomSheetの制御系
  8. © 2024 Wantedly, Inc. @Composable fun Modal( onDismissRequest: () !>

    Unit, sheetContent: @Composable () !> Unit, overlayContent: @Composable (() !> Unit)? = null, sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), ) { !/ 色々省略あり val dialog = remember(view) { DialogWrapper(/*省略*/).apply { setContent(composition) { DialogLayout { Box(modifier = Modifier.fillMaxSize()) { ModalLayout( sheetState = sheetState, onDismissRequest = onDismissRequest, sheetContent = sheetContent, ) if (overlayContent != null) { OverlayContent( sheetState = sheetState, overlayContent = overlayContent, ) } } } } } } BottomSheetの前面にオーバーレイ BottomSheetのスクロールには連動 しない→Sticky
  9. © 2024 Wantedly, Inc. Usage @Composable private fun ModalPreview() {

    var showSheet by remember { mutableStateOf(false) } Button(onClick = { showSheet = true }) { Text("Show") } if (showSheet) { Modal( onDismissRequest = { showSheet = false }, sheetContent = { Text("Content") }, overlayContent = { Button(/*省略*/) }, ) } }
  10. © 2024 Wantedly, Inc. まとめ • ないものは作る • ソースを読んで理解する ◦

    ComposeのWindow関連APIは結構泥臭いことをやってて面白い • 下調べは重要 ◦ 実装時はMaterial3のModalを知らなかった ◦ 知っていれば、もう少し実装時間を減らせたはず