Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply paneExpansionDragHandle in ListDetailPaneScaffold?

It's unclear how the paneExpansionDragHandle can be applied.

The code documentation says only this:

paneExpansionDragHandle - provide a custom pane expansion drag handle to allow users to resize panes and change the pane expansion state by dragging. This is null by default, which renders no drag handle. Even there's no drag handle, you can still change pane size directly via modifying paneExpansionState.

The web documentation mentions nothing about that at all.

I tried to use of paneExpansionDragHandle and paneExpansionState to make a custom drag handle, but it was inconvenient. How can I achieve this?

like image 364
alamoudi Avatar asked Oct 20 '25 23:10

alamoudi


1 Answers

The ListDetailPaneScaffold Composable is part of the material3/adaptive library. You can find useful documentation and samples there.

You must use the latest material3 dependency to your build.gradle file to get the predefined VerticalDragHandle Composable:

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
//...
implementation("androidx.compose.material3:material3:1.4.0-alpha04")  // VerticalDragHandle

You then can use the paneExpansionDragHandle like this:

paneExpansionDragHandle = { state ->
    val interactionSource = remember { MutableInteractionSource() }
    VerticalDragHandle(
        modifier = Modifier.paneExpansionDraggable(
            state,
            LocalMinimumInteractiveComponentSize.current,
            interactionSource
        ),
        interactionSource = interactionSource
    )
}

Output:

Screen Recording no snapping

If you want the drag position to snap to predefined anchors, you can also pass anchors in the paneExpansionState:

paneExpansionState =
    rememberPaneExpansionState(
        keyProvider = scaffoldNavigator.scaffoldValue,
        anchors = listOf(
            PaneExpansionAnchor.Proportion(0.25f),
            PaneExpansionAnchor.Proportion(0.5f),
            PaneExpansionAnchor.Proportion(0.75f),
        )
    ),

Output:

Screen Recording with snapping


Code Sample

@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3Api::class)
@Composable
fun SampleListDetailPaneScaffoldFull() {
    val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator()
    val coroutineScope = rememberCoroutineScope()
    ListDetailPaneScaffold(
        directive = scaffoldNavigator.scaffoldDirective,
        value = scaffoldNavigator.scaffoldValue,
        listPane = {
            AnimatedPane(
                modifier = Modifier.preferredWidth(200.dp),
            ) {
                Surface(
                    color = MaterialTheme.colorScheme.secondary,
                    onClick = {
                        coroutineScope.launch {
                            scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                        }
                    }
                ) {
                    Text("List")
                }
            }
        },
        detailPane = {
            AnimatedPane(modifier = Modifier) {
                Surface(
                    color = MaterialTheme.colorScheme.primary,
                ) {
                    Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
                        Text("Detail")
                        Row(
                            modifier = Modifier.fillMaxWidth().padding(horizontal = 4.dp),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            Surface(
                                onClick = {
                                    coroutineScope.launch { scaffoldNavigator.navigateBack() }
                                },
                                modifier = Modifier.weight(0.5f).fillMaxHeight(),
                                color = MaterialTheme.colorScheme.primary.copy(alpha = 0.8f)
                            ) {
                                Box(
                                    modifier = Modifier.fillMaxSize(),
                                    contentAlignment = Alignment.Center
                                ) {
                                    Text("Previous")
                                }
                            }
                            VerticalDivider()
                            Surface(
                                onClick = {
                                    coroutineScope.launch {
                                        scaffoldNavigator.navigateTo(
                                            ListDetailPaneScaffoldRole.Extra
                                        )
                                    }
                                },
                                modifier = Modifier.weight(0.5f).fillMaxHeight(),
                                color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)
                            ) {
                                Box(
                                    modifier = Modifier.fillMaxSize(),
                                    contentAlignment = Alignment.Center
                                ) {
                                    Text("Next")
                                }
                            }
                        }
                    }
                }
            }
        },
        extraPane = {
            AnimatedPane(modifier = Modifier.fillMaxSize()) {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.tertiary,
                    onClick = { coroutineScope.launch { scaffoldNavigator.navigateBack() } }
                ) {
                    Text("Extra")
                }
            }
        },
        paneExpansionState =
        rememberPaneExpansionState(
            keyProvider = scaffoldNavigator.scaffoldValue,
            anchors = listOf(
                PaneExpansionAnchor.Proportion(0.25f),
                PaneExpansionAnchor.Proportion(0.5f),
                PaneExpansionAnchor.Proportion(0.75f),
            )
        ),
        paneExpansionDragHandle = { state ->
            val interactionSource = remember { MutableInteractionSource() }
            VerticalDragHandle(
                modifier =
                Modifier.paneExpansionDraggable(
                    state,
                    LocalMinimumInteractiveComponentSize.current,
                    interactionSource
                ),
                interactionSource = interactionSource
            )
        }
    )
}
like image 104
BenjyTec Avatar answered Oct 23 '25 13:10

BenjyTec



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!