Create a bottom sheet composable
This commit is contained in:
@@ -31,9 +31,7 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import app.lawnchair.ui.preferences.about.aboutGraph
|
||||
import app.lawnchair.ui.preferences.components.PreferenceCategoryList
|
||||
import app.lawnchair.ui.preferences.components.SystemUi
|
||||
import app.lawnchair.ui.preferences.components.TopBar
|
||||
import app.lawnchair.ui.preferences.components.*
|
||||
import app.lawnchair.util.Meta
|
||||
import app.lawnchair.util.pageMeta
|
||||
import app.lawnchair.util.preferences.getMajorVersion
|
||||
@@ -121,24 +119,26 @@ fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel
|
||||
val navController = rememberNavController()
|
||||
|
||||
SystemUi()
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
CompositionLocalProvider(
|
||||
LocalNavController provides navController,
|
||||
LocalPreferenceInteractor provides interactor,
|
||||
) {
|
||||
NavHost(navController = navController, startDestination = "preferences") {
|
||||
composable(route = Routes.PREFERENCES) {
|
||||
pageMeta.provide(Meta(title = stringResource(id = R.string.settings)))
|
||||
PreferenceCategoryList(navController)
|
||||
ProvidePortalNode {
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
CompositionLocalProvider(
|
||||
LocalNavController provides navController,
|
||||
LocalPreferenceInteractor provides interactor,
|
||||
) {
|
||||
NavHost(navController = navController, startDestination = "preferences") {
|
||||
composable(route = Routes.PREFERENCES) {
|
||||
pageMeta.provide(Meta(title = stringResource(id = R.string.settings)))
|
||||
PreferenceCategoryList(navController)
|
||||
}
|
||||
generalGraph(route = Routes.GENERAL)
|
||||
homeScreenGraph(route = Routes.HOME_SCREEN)
|
||||
dockGraph(route = Routes.DOCK)
|
||||
appDrawerGraph(route = Routes.APP_DRAWER)
|
||||
folderGraph(route = Routes.FOLDERS)
|
||||
aboutGraph(route = Routes.ABOUT)
|
||||
}
|
||||
generalGraph(route = Routes.GENERAL)
|
||||
homeScreenGraph(route = Routes.HOME_SCREEN)
|
||||
dockGraph(route = Routes.DOCK)
|
||||
appDrawerGraph(route = Routes.APP_DRAWER)
|
||||
folderGraph(route = Routes.FOLDERS)
|
||||
aboutGraph(route = Routes.ABOUT)
|
||||
TopBar()
|
||||
}
|
||||
TopBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import app.lawnchair.util.backHandler
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun BottomSheet(
|
||||
sheetContent: @Composable ColumnScope.() -> Unit,
|
||||
content: @Composable (showSheet: suspend () -> Unit) -> Unit,
|
||||
) {
|
||||
val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||
|
||||
val currentSheetContent by rememberUpdatedState(sheetContent)
|
||||
val addedToHost = rememberSaveable { mutableStateOf(false) }
|
||||
val wasVisible = rememberSaveable { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
if (wasVisible.value && !state.isVisible) {
|
||||
addedToHost.value = false
|
||||
}
|
||||
wasVisible.value = state.isVisible
|
||||
|
||||
if (addedToHost.value) {
|
||||
Portal {
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = state,
|
||||
sheetContent = currentSheetContent
|
||||
) {
|
||||
backHandler {
|
||||
scope.launch { state.hide() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val showSheet = remember {
|
||||
suspend {
|
||||
addedToHost.value = true
|
||||
state.show()
|
||||
}
|
||||
}
|
||||
content(showSheet)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
||||
@Composable
|
||||
fun ProvidePortalNode(content: @Composable () -> Unit) {
|
||||
val views = remember { mutableStateListOf<View>() }
|
||||
Box {
|
||||
CompositionLocalProvider(
|
||||
LocalPortalNode provides remember { PortalNode(views) }
|
||||
) {
|
||||
content()
|
||||
}
|
||||
views.forEach { view ->
|
||||
AndroidView(factory = { view })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Portal(portalNode: PortalNode = LocalPortalNode.current, content: @Composable () -> Unit) {
|
||||
val currentContent by rememberUpdatedState(content)
|
||||
val compositionContext = rememberCompositionContext()
|
||||
val context = LocalContext.current
|
||||
|
||||
DisposableEffect("portal") {
|
||||
val view = ComposeView(context)
|
||||
view.setParentCompositionContext(compositionContext)
|
||||
view.setContent { currentContent() }
|
||||
portalNode.addView(view)
|
||||
onDispose { portalNode.removeView(view) }
|
||||
}
|
||||
}
|
||||
|
||||
class PortalNode(private val views: MutableList<View>) {
|
||||
fun addView(view: View) {
|
||||
views.add(view)
|
||||
}
|
||||
|
||||
fun removeView(view: View) {
|
||||
views.remove(view)
|
||||
}
|
||||
}
|
||||
|
||||
val LocalPortalNode = compositionLocalOf<PortalNode> {
|
||||
error("CompositionLocal LocalPortalNode not present")
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package app.lawnchair.util
|
||||
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
|
||||
@Composable
|
||||
fun backHandler(onBack: () -> Unit) {
|
||||
val onBackPressedDispatcherOwner = LocalOnBackPressedDispatcherOwner.current
|
||||
val onBackPressedDispatcher = onBackPressedDispatcherOwner?.onBackPressedDispatcher
|
||||
val currentOnBack = rememberUpdatedState(onBack)
|
||||
|
||||
DisposableEffect(onBackPressedDispatcher) {
|
||||
if (onBackPressedDispatcher == null) {
|
||||
return@DisposableEffect onDispose { }
|
||||
}
|
||||
val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
currentOnBack.value()
|
||||
}
|
||||
}
|
||||
onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
||||
onDispose { onBackPressedCallback.remove() }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user