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
-
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