From 905047c30d251cae1cdbddf5debdd32f6bd270e3 Mon Sep 17 00:00:00 2001 From: "oleksandr.karpovich" Date: Wed, 1 Nov 2023 13:40:21 +0100 Subject: [PATCH 1/2] Add BottomSheet demo --- .../com/github/terrakok/ComponentScreen.kt | 51 ++++--- .../kotlin/com/github/terrakok/Containment.kt | 128 ++++++++++++++++++ 2 files changed, 162 insertions(+), 17 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt diff --git a/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt b/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt index f47f213..5d5c85d 100644 --- a/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt @@ -1,29 +1,46 @@ package com.github.terrakok -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ComponentScreen() { - Column( - modifier = Modifier - .verticalScroll(rememberScrollState()) - .fillMaxWidth(), - ) { - Actions() - Spacer(Modifier.size(16.dp)) - TextInputs() - Spacer(Modifier.size(16.dp)) - Communication() - Spacer(Modifier.size(16.dp)) - Navigation() + val scaffoldState = rememberBottomSheetScaffoldState() + val scope = rememberCoroutineScope() + + BottomSheetScaffold( + scaffoldState = scaffoldState, + sheetPeekHeight = 0.dp, + sheetContent = { + BottomSheetContent() + } + ) { _ -> + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .fillMaxWidth(), + ) { + Actions() + Spacer(Modifier.size(16.dp)) + TextInputs() + Spacer(Modifier.size(16.dp)) + Communication() + Spacer(Modifier.size(16.dp)) + Navigation() + Spacer(Modifier.size(16.dp)) + Containment( + toggleBottomSheet = { + scope.launch { scaffoldState.bottomSheetState.expand() } + } + ) + } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt b/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt new file mode 100644 index 0000000..b7f2210 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt @@ -0,0 +1,128 @@ +package com.github.terrakok + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp + +@Composable +fun Containment(toggleBottomSheet: (Boolean) -> Unit) { + val modalBottomSheetInfoUrl = + "https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary?hl=en#ModalBottomSheet(kotlin.Function0,androidx.compose.ui.Modifier,androidx.compose.material3.SheetState,androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Color,kotlin.Function0,androidx.compose.foundation.layout.WindowInsets,androidx.compose.ui.window.SecureFlagPolicy,kotlin.Function1)" + + ParentSection("Containment") { + ChildSection( + title = "Bottom sheet", + infoUrl = modalBottomSheetInfoUrl + ) { + BottomSheetDemo(toggleBottomSheet) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun BottomSheetDemo(toggleBottomSheet: (Boolean) -> Unit) { + var isModalSheetOpen by rememberSaveable { mutableStateOf(false) } + + OutlinedCard { + Row( + modifier = Modifier + .requiredWidthIn(400.dp) + .width(600.dp) + .padding(16.dp) + ) { + TextButton( + modifier = Modifier + .padding(horizontal = 8.dp, vertical = 4.dp) + .weight(1f), + enabled = true, + onClick = { + isModalSheetOpen = true + }, + content = { Text("Show modal bottom sheet") } + ) + + var bottomSheetShown by remember { mutableStateOf(false) } + + TextButton( + modifier = Modifier + .padding(horizontal = 8.dp, vertical = 4.dp) + .weight(1f), + enabled = true, + onClick = { + bottomSheetShown = !bottomSheetShown + toggleBottomSheet(bottomSheetShown) + }, + content = { + Text("${if(bottomSheetShown) "Hide" else "Show"} bottom sheet") + } + ) + } + } + + val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + if (isModalSheetOpen) { + ModalBottomSheet( + onDismissRequest = { isModalSheetOpen = false }, + sheetState = bottomSheetState, + ) { + BottomSheetContent() + } + } +} + +@Composable +internal fun ColumnScope.BottomSheetContent() { + Row(modifier = Modifier.padding(vertical = 20.dp).align(Alignment.CenterHorizontally)) { + BottomSheetButton( + title = "Share", + icon = Icons.Outlined.Share + ) + BottomSheetButton( + title = "Add to", + icon = Icons.Outlined.Add + ) + BottomSheetButton( + title = "Trash", + icon = Icons.Outlined.Delete + ) + BottomSheetButton( + title = "Archive", + icon = Icons.Outlined.Archive + ) + BottomSheetButton( + title = "Settings", + icon = Icons.Outlined.Settings + ) + BottomSheetButton( + title = "Favorite", + icon = Icons.Outlined.Favorite + ) + } +} + +@Composable +private fun BottomSheetButton( + icon: ImageVector, + title: String +) { + NavigationRailItem( + selected = false, + onClick = { }, + icon = { + Icon( + imageVector = icon, + contentDescription = null + ) + }, + label = { Text(title) } + ) +} \ No newline at end of file From 30b4bb4718e6be7c4e3b9dd33f22a9ee5223a420 Mon Sep 17 00:00:00 2001 From: "oleksandr.karpovich" Date: Wed, 1 Nov 2023 14:35:38 +0100 Subject: [PATCH 2/2] Fix BottomSheetDemo --- .../kotlin/com/github/terrakok/App.kt | 23 ++++++++-- .../com/github/terrakok/ComponentScreen.kt | 44 ++++++------------- .../kotlin/com/github/terrakok/Containment.kt | 18 +++++--- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/github/terrakok/App.kt b/composeApp/src/commonMain/kotlin/com/github/terrakok/App.kt index a31a3e2..99e7602 100644 --- a/composeApp/src/commonMain/kotlin/com/github/terrakok/App.kt +++ b/composeApp/src/commonMain/kotlin/com/github/terrakok/App.kt @@ -26,6 +26,9 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberBottomSheetScaffoldState +import androidx.compose.material3.BottomSheetScaffold +import androidx.compose.material3.BottomSheetScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf @@ -46,6 +49,10 @@ const val narrowScreenWidthThreshold = 1300 val LocalSnackbarHostState = compositionLocalOf { error("SnackbarHostState is not found") } +@OptIn(ExperimentalMaterial3Api::class) +val LocalBottomSheetScaffoldState = + compositionLocalOf { error("BottomSheetScaffoldState is not found") } + data class Screen( val title: String, val icon: ImageVector, @@ -134,10 +141,20 @@ internal fun App() = AppTheme { } } } - CompositionLocalProvider( - LocalSnackbarHostState provides snackbarHostState + val scaffoldState = rememberBottomSheetScaffoldState() + BottomSheetScaffold( + scaffoldState = scaffoldState, + sheetPeekHeight = 0.dp, + sheetContent = { + BottomSheetContent() + } ) { - selectedScreen.content() + CompositionLocalProvider( + LocalSnackbarHostState provides snackbarHostState, + LocalBottomSheetScaffoldState provides scaffoldState + ) { + selectedScreen.content() + } } } }, diff --git a/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt b/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt index 5d5c85d..09f3307 100644 --- a/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/github/terrakok/ComponentScreen.kt @@ -10,37 +10,21 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ComponentScreen() { - val scaffoldState = rememberBottomSheetScaffoldState() - val scope = rememberCoroutineScope() - - BottomSheetScaffold( - scaffoldState = scaffoldState, - sheetPeekHeight = 0.dp, - sheetContent = { - BottomSheetContent() - } - ) { _ -> - Column( - modifier = Modifier - .verticalScroll(rememberScrollState()) - .fillMaxWidth(), - ) { - Actions() - Spacer(Modifier.size(16.dp)) - TextInputs() - Spacer(Modifier.size(16.dp)) - Communication() - Spacer(Modifier.size(16.dp)) - Navigation() - Spacer(Modifier.size(16.dp)) - Containment( - toggleBottomSheet = { - scope.launch { scaffoldState.bottomSheetState.expand() } - } - ) - } + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .fillMaxWidth(), + ) { + Actions() + Spacer(Modifier.size(16.dp)) + TextInputs() + Spacer(Modifier.size(16.dp)) + Communication() + Spacer(Modifier.size(16.dp)) + Navigation() + Spacer(Modifier.size(16.dp)) + Containment() } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt b/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt index b7f2210..1f28903 100644 --- a/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt +++ b/composeApp/src/commonMain/kotlin/com/github/terrakok/Containment.kt @@ -10,9 +10,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch @Composable -fun Containment(toggleBottomSheet: (Boolean) -> Unit) { +fun Containment() { val modalBottomSheetInfoUrl = "https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary?hl=en#ModalBottomSheet(kotlin.Function0,androidx.compose.ui.Modifier,androidx.compose.material3.SheetState,androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Color,kotlin.Function0,androidx.compose.foundation.layout.WindowInsets,androidx.compose.ui.window.SecureFlagPolicy,kotlin.Function1)" @@ -21,14 +22,14 @@ fun Containment(toggleBottomSheet: (Boolean) -> Unit) { title = "Bottom sheet", infoUrl = modalBottomSheetInfoUrl ) { - BottomSheetDemo(toggleBottomSheet) + BottomSheetDemo() } } } @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun BottomSheetDemo(toggleBottomSheet: (Boolean) -> Unit) { +private fun BottomSheetDemo() { var isModalSheetOpen by rememberSaveable { mutableStateOf(false) } OutlinedCard { @@ -50,7 +51,8 @@ private fun BottomSheetDemo(toggleBottomSheet: (Boolean) -> Unit) { ) var bottomSheetShown by remember { mutableStateOf(false) } - + val scope = rememberCoroutineScope() + val bottomSheetScaffoldState = LocalBottomSheetScaffoldState.current TextButton( modifier = Modifier .padding(horizontal = 8.dp, vertical = 4.dp) @@ -58,7 +60,13 @@ private fun BottomSheetDemo(toggleBottomSheet: (Boolean) -> Unit) { enabled = true, onClick = { bottomSheetShown = !bottomSheetShown - toggleBottomSheet(bottomSheetShown) + scope.launch { + if (bottomSheetShown) { + bottomSheetScaffoldState.bottomSheetState.expand() + } else { + bottomSheetScaffoldState.bottomSheetState.hide() + } + } }, content = { Text("${if(bottomSheetShown) "Hide" else "Show"} bottom sheet")