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
Jetpack ComposeのBottomSheetとの戦い / Fight with Bo...
Search
Masatoshi Kubode
July 23, 2024
Programming
1.1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack ComposeのBottomSheetとの戦い / Fight with BottomSheet of Jetpack Compose
https://sansan.connpass.com/event/321922/
Masatoshi Kubode
July 23, 2024
More Decks by Masatoshi Kubode
See All by Masatoshi Kubode
AIチャットの改善から見えた、良いAI体験とは / What Constitutes a Good AI Experience: Insights from Improving AI Chat
kubode
0
120
3リポジトリーを2ヶ月でモノレポ化した話 / How I turned 3 repositories into a monorepo in 2 months
kubode
0
230
ウォンテッドリーの「ココロオドル」モバイル開発 / Wantedly's "kokoro odoru" mobile development
kubode
2
1.3k
Firebase Dynamic Linksの代替手段を自作する / Create your own Firebase Dynamic Links alternative
kubode
0
720
技術を根付かせる / How to make technology take root
kubode
1
500
WantedlyでのKotlin Multiplatformの導入と課題 / Kotlin Multiplatform Implementation and Challenges at Wantedly
kubode
0
640
Google Play Consoleデベロッパー アカウントの確認 / Verifying your Play Console developer account
kubode
1
1.4k
Make your Android app into Multiplatform app
kubode
0
240
ウォンテッドリーにおけるモバイルアプリ開発 / iOSDC Japan 2024 Sponsor Session
kubode
1
1.5k
Other Decks in Programming
See All in Programming
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
140
Vite+ Unified Toolchain for the Web
naokihaba
0
330
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
350k
OSもどきOS
arkw
0
580
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
14
5.8k
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
13k
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
3
1.3k
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
140
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
210
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
560
技術記事、 専門家としてのプログラマ、 言語化
mizchi
13
6.4k
Featured
See All Featured
AI: The stuff that nobody shows you
jnunemaker
PRO
8
730
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
540
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
290
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
54k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
560
Designing for Timeless Needs
cassininazir
1
260
A better future with KSS
kneath
240
18k
HDC tutorial
michielstock
2
720
KATA
mclloyd
PRO
35
15k
sira's awesome portfolio website redesign presentation
elsirapls
0
280
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
30 Presentation Tips
portentint
PRO
1
330
Transcript
© 2024 Wantedly, Inc. Jetpack Composeの BottomSheetとの戦い Mobile勉強会#14 2024/07/23 久保出雅俊
© 2024 Wantedly, Inc. wantedly.com/id/kubode @swiz_ard @kubode
© 2024 Wantedly, Inc. Agenda • BottomSheetの苦労話 • 実装については少し難しめの話
© 2024 Wantedly, Inc. Jetpack Composeの標準のBottomSheet実装
© 2024 Wantedly, Inc. 標準のBottomSheet実装 https://m3.material.io/components/bottom-sheets/overview ① Standard ② Modal
Material 2 BottomSheetScaffold ModalBottomSheetLayout Material 3 BottomSheetScaffold ModalBottomSheet
© 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") } } }
© 2024 Wantedly, Inc. 課題
© 2024 Wantedly, Inc. やりたいこと • 検索画面のフィルターをModalで表示 したい • 複数選択のフィルター項目の場合、
OKボタンを画面の下にStickyで出す
© 2024 Wantedly, Inc. 課題 1 BottomNavigationは Activityが所有する XMLのView Activity
BottomNavigation FragmentContainer SearchFragment with Compose
© 2024 Wantedly, Inc. 課題 1 StandardやMaterial2のModalは BottomNavigationが前面に出る (Material3のModalだと問題ないが、現 状はMaterial2を使用
© 2024 Wantedly, Inc. 課題 2 標準のModalの上には、コンテンツを 配置できない
© 2024 Wantedly, Inc. 解決策
© 2024 Wantedly, Inc. 解決策 Modalを自作した • ウィンドウ操作が必要と判断 • ComposeのDialogのコードをもとに自作
◦ Material3のModalじゃない理由は後述しますが、Material3のModalとほぼ同じ実 装
© 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, ) } } } } } }
© 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, ) } } } } } } 透明なフルスクリーンウィンドウ
© 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の制御系
© 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
© 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(/*省略*/) }, ) } }
© 2024 Wantedly, Inc. まとめ • ないものは作る • ソースを読んで理解する ◦
ComposeのWindow関連APIは結構泥臭いことをやってて面白い • 下調べは重要 ◦ 実装時はMaterial3のModalを知らなかった ◦ 知っていれば、もう少し実装時間を減らせたはず
© 2024 Wantedly, Inc.