Current workaround for Compose's ModalNavigationDrawer (M3) #430
malliaridis
started this conversation in
Show and tell
Replies: 2 comments 8 replies
-
I guess I found an even cleaner approach: /**
* @param drawerContent content inside this drawer
* @param modifier the [Modifier] to be applied to this drawer
* @param slot The child slot to use for the navigation drawer.
* @param onDrawerStateChange Optional callback invoked to confirm or veto a pending drawer state
* change.
* @param gesturesEnabled whether or not the drawer can be interacted by gestures
* @param scrimColor color of the scrim that obscures content when the drawer is open
* @param content content of the rest of the UI
*/
@Composable
@ExperimentalMaterial3Api
fun <C : Any, T : Any> ChildSlotModalNavigationDrawer(
drawerContent: @Composable () -> Unit,
modifier: Modifier = Modifier,
slot: Value<ChildSlot<C, T>>,
onDrawerStateChange: (Boolean) -> Unit,
gesturesEnabled: Boolean = true,
scrimColor: Color = DrawerDefaults.scrimColor,
content: @Composable () -> Unit
) {
val slotState by slot.subscribeAsState()
val drawerState = rememberDrawerState(
initialValue = if (slotState.child != null) DrawerValue.Open else DrawerValue.Closed,
confirmStateChange = {
onDrawerStateChange(it == DrawerValue.Open)
// Always return false, since the state is updated only by the child slot
false
},
)
val scope = rememberCoroutineScope()
scope.launch {
if (slotState.child?.instance != null) drawerState.open()
else drawerState.close()
}
ModalNavigationDrawer(
drawerState = drawerState,
modifier = modifier,
gesturesEnabled = gesturesEnabled,
drawerContent = drawerContent,
scrimColor = scrimColor,
content = content,
)
} That way The component implemention would look something like this: class DefaultRootComponent(
componentContext: ComponentContext,
) : RootComponent, ComponentContext by componentContext {
private val drawerNavigation = SlotNavigation<DrawerConfig>()
private val _drawer =
childSlot(
source = drawerNavigation,
handleBackButton = true,
) { config, componentContext ->
DefaultDrawerComponent(
componentContext = componentContext,
onDismissed = drawerNavigation::dismiss,
)
}
override val drawer: Value<ChildSlot<*, DrawerComponent>> = _drawer
override fun onDrawerStateChange(isOpen: Boolean) {
if (isOpen) drawerNavigation.activate(DrawerConfig.Default)
else drawerNavigation.dismiss()
}
sealed interface DrawerConfig : Parcelable {
@Parcelize
object Default : DrawerConfig
}
} This just wraps the workaround from before in a Composable function and looks much cleaner. The |
Beta Was this translation helpful? Give feedback.
8 replies
-
Linked this discussion here: #336 (comment). |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
I was recently implementing a navigation drawer in Compose with Material 3, which was previously part of the
Scaffold
but now there is a separate composable with a special state (DrawerState
).I tried to find a solution to combine Decompose's component state with the
ModalNavigationDrawer
, but I was only able to implement a workaround (see below). If you have any improvement suggestions I would appreciate if you can share them. :)I started to migrate the
Children
composable andstack
animations from Decompose's extensions library to work withChildSlot
and allow a "drawer-like" behavior, but I messed up the animations and I still have to integrate swipe gestures. It is a bit time-consuming, but if I manage to finish something before someone else does, I will share it (or even create a PR).Beta Was this translation helpful? Give feedback.
All reactions