Material3 Adaptive - Minimal Example
With the release of 1.3.0-rc01 for Material3 Adaptive, I felt it was a good time to try out a minimal example. In the same spirit, this will be a minimal blog that should help you get an example up and running ASAP.
This follows on from my Navigation 3 series which covers adaptive using Navigation3 so definitely check that out if you want to see a more detailed example (or if you just like cool cars).
Dependencies
dependencies {
implementation "androidx.compose.material3.adaptive:adaptive:1.3.0-rc01"
implementation "androidx.compose.material3.adaptive:adaptive-layout:1.3.0-rc01"
implementation "androidx.compose.material3.adaptive:adaptive-navigation:1.3.0-rc01"
implementation "androidx.compose.material3.adaptive:adaptive-navigation3:1.3.0-rc01"
}
The Code
We'll start with the NavigableListDetailPaneScaffold. I have split everything into smaller sections to hopefully make things easier to understand and also remind you about scoping functions.
As you can tell from the code below, we'll be building a simple list and detail layout. This will respond to the amount of space you have available on screen.
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun AdaptiveDemo() {
val navigator =
rememberListDetailPaneScaffoldNavigator<Int>()
NavigableListDetailPaneScaffold(
navigator = navigator,
listPane = { ListPane(navigator) },
detailPane = { DetailPane(navigator) }
)
}
Next we have the ListPane. Notice we have scoped this to ThreePaneScaffoldPaneScope so AnimatedPane understands what to do. We need to pass in the navigator so it responds correctly.
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun ThreePaneScaffoldPaneScope.ListPane(
navigator: ThreePaneScaffoldNavigator<Int>
) {
val scope =
rememberCoroutineScope()
AnimatedPane {
ItemList(
onItemClick = { id ->
scope.launch {
navigator.navigateTo(
ListDetailPaneScaffoldRole
.Detail,
id
)
}
}
)
}
}
Next we have our DetailPane. Again, this requires ThreePaneScaffoldPaneScope so AnimatedPane knows how to function.
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
private fun ThreePaneScaffoldPaneScope.DetailPane(
navigator: ThreePaneScaffoldNavigator<Int>
) {
AnimatedPane {
val id = navigator.currentDestination?.contentKey
DetailContent(id)
}
}
Scopes
As a quick intermission, ListPane and DetailPane are extension functions for ThreePaneScaffoldPaneScope. AnimatedPane will only work if it knows it is inside ThreePaneScaffoldPaneScope. An easy way to test this is to just remove the scope. You will get an error message when it tries to access AnimatedPane:
"Unresolved reference. None of the following candidates is applicable because of a receiver type mismatch:..."
An example you may be more familiar with is weight not working in a box:
@Composable
private fun ScopeTest() {
Row {
Text("A", modifier = Modifier.weight(1f)) // fine, RowScope provides weight()
}
Box {
Text("A", modifier = Modifier.weight(1f)) // error - BoxScope has no weight()
}
}
Back to the Code
And finally, we have the actual content. There shouldn't be anything surprising here:
@Composable
private fun ItemList(onItemClick: (Int) -> Unit) {
LazyColumn {
items(20) { index ->
ListItem(
headlineContent = { Text("Item $index") },
modifier = Modifier.clickable {
onItemClick(index)
}
)
}
}
}
@Composable
private fun DetailContent(id: Int?) {
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
if (id != null) "Detail for item $id"
else "Select an item"
)
}
}
Screenshots
Done
And that is everything. A simple code listing with some basic screenshots of everything in action.

Comments
Post a Comment