0

How to slide the contents of scaffold when drawers open. Similar to this question but in jetpack compose.

sunny52525
  • 143
  • 2
  • 6

1 Answers1

5

I found a solution...

// This is optional if you need to open/close the drawer programmatically
val coroutineScope = rememberCoroutineScope()

// We need the drawer state to check:
// 1. if it is opened or closed
// 2. request to open/close it
// 3. get the drawer's offset (and do the slide of the content)
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)

// Drawer's width. It will be updated later in LaunchedEffect
var drawerWidth by remember {
    mutableStateOf(drawerState.offset.value)
}
// As soon the user move the drawer, the content must move in sync.
// So here we're creating a derived state of the drawer state 
// to update the content position.
val contentOffset = remember {
    derivedStateOf {
        drawerState.offset.value
    }
}
// The scaffold state contains the drawer state.
val scaffoldState = rememberScaffoldState(
    drawerState = drawerState
)

Scaffold(
    scaffoldState = scaffoldState,
    drawerContent = {
        Box {
            // your drawer content here...
        }
    }
) {
    Box(Modifier.fillMaxSize()) {
        // Here's the content's offset calculation logic
        val xPos = (abs(drawerWidth) - abs(contentOffset.value))
        Column(
            // Set the content's offset
            Modifier.offset(
                x = with(LocalDensity.current) {
                    max(0.dp, xPos.toDp() - 56.dp)
                }
            )
        ) {
            // Optional: opening the drawer using a button
            Button(onClick = {    
                coroutineScope.launch {
                    drawerState.open()
                }
            }) {
                Text("Open Drawer")
            }
        }
    }
}
// Important! Initializing the drawerWidth
SideEffect {
    if (drawerWidth == 0f) {
        drawerWidth = drawerState.offset.value
    }
}

Here's the result:

enter image description here

Warning! This solution has a problem: as you can see, I'm using a hardcoded value 56.dp. This is because the Material Design library uses this value as end padding of the drawer. You can see this constant in the Drawer.kt file in Material Design library.

private val EndDrawerPadding = 56.dp
nglauber
  • 18,674
  • 6
  • 70
  • 75
  • 1
    All credit to you for the idea and for teaching me this cool kotlin trick with ```with``` blocks. But it seems to me like if the 56dp is already baked in, then drawerWidth is just a proxy for screen width, which requires very little maneuvering. So I opted to use ```.offset(x = max(0.dp,with(LocalDensity.current){(LocalConfiguration.current.screenWidthDp.dp + scaffoldState.drawerState.offset.value.toDp()-56.dp)}))``` – D. Kupra Jan 12 '22 at 06:42