Add predictive back and pane expansion back handling

Change-Id: I8e881bb2cf549303be8612732bfb7df7555096aa
pull/1837/head
Alex Vanyo 6 months ago
parent ff78308055
commit 05e4e4de31

@ -17,7 +17,6 @@
package com.google.samples.apps.nowinandroid.ui.interests2pane package com.google.samples.apps.nowinandroid.ui.interests2pane
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.annotation.Keep
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -27,23 +26,23 @@ import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.layout.AnimatedPane import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
import androidx.compose.material3.adaptive.layout.defaultDragHandleSemantics
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.material3.adaptive.navigation.BackNavigationBehavior
import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneScaffold
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldPredictiveBackHandler
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
@ -51,31 +50,20 @@ import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen
import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel
import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel_Factory
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID
import kotlin.math.max import kotlin.math.max
@Serializable internal object TopicPlaceholderRoute @Serializable internal object TopicPlaceholderRoute
// TODO: Remove @Keep when https://issuetracker.google.com/353898971 is fixed
@Keep
@Serializable internal object DetailPaneNavHostRoute
fun NavGraphBuilder.interestsListDetailScreen() { fun NavGraphBuilder.interestsListDetailScreen() {
composable<InterestsRoute> { composable<InterestsRoute> {
InterestsListDetailScreen() InterestsListDetailScreen()
@ -121,9 +109,17 @@ internal fun InterestsListDetailScreen(
), ),
) )
BackHandler(listDetailNavigator.canNavigateBack()) { ThreePaneScaffoldPredictiveBackHandler(
listDetailNavigator,
BackNavigationBehavior.PopUntilScaffoldValueChange,
)
BackHandler(
paneExpansionState.currentAnchor == PaneExpansionAnchor.Proportion(0f) &&
listDetailNavigator.isListPaneVisible() &&
listDetailNavigator.isDetailPaneVisible(),
) {
coroutineScope.launch { coroutineScope.launch {
listDetailNavigator.navigateBack() paneExpansionState.animateTo(PaneExpansionAnchor.Proportion(1f))
} }
} }
@ -138,14 +134,18 @@ internal fun InterestsListDetailScreen(
coroutineScope.launch { coroutineScope.launch {
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail) listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
} }
if (paneExpansionState.currentAnchor == PaneExpansionAnchor.Proportion(1f)) {
coroutineScope.launch {
paneExpansionState.animateTo(PaneExpansionAnchor.Proportion(0f))
}
}
} }
val mutableInteractionSource = remember { MutableInteractionSource() } val mutableInteractionSource = remember { MutableInteractionSource() }
val minPaneWidth = 300.dp val minPaneWidth = 300.dp
ListDetailPaneScaffold( NavigableListDetailPaneScaffold(
value = listDetailNavigator.scaffoldValue, navigator = listDetailNavigator,
directive = listDetailNavigator.scaffoldDirective,
listPane = { listPane = {
AnimatedPane { AnimatedPane {
Box( Box(
@ -209,7 +209,7 @@ internal fun InterestsListDetailScreen(
key = route.id, key = route.id,
) { factory -> ) { factory ->
factory.create(route.id) factory.create(route.id)
} },
) )
} }
is TopicPlaceholderRoute -> { is TopicPlaceholderRoute -> {
@ -227,6 +227,7 @@ internal fun InterestsListDetailScreen(
state = paneExpansionState, state = paneExpansionState,
minTouchTargetSize = LocalMinimumInteractiveComponentSize.current, minTouchTargetSize = LocalMinimumInteractiveComponentSize.current,
interactionSource = mutableInteractionSource, interactionSource = mutableInteractionSource,
semanticsProperties = paneExpansionState.defaultDragHandleSemantics(),
), ),
interactionSource = mutableInteractionSource, interactionSource = mutableInteractionSource,
) )

@ -16,11 +16,14 @@
package com.google.samples.apps.nowinandroid.feature.topic.navigation package com.google.samples.apps.nowinandroid.feature.topic.navigation
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen
import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable data class TopicRoute(val id: String) @Serializable data class TopicRoute(val id: String)
@ -36,11 +39,17 @@ fun NavGraphBuilder.topicScreen(
onBackClick: () -> Unit, onBackClick: () -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
) { ) {
composable<TopicRoute> { composable<TopicRoute> { entry ->
val id = entry.toRoute<TopicRoute>().id
TopicScreen( TopicScreen(
showBackButton = showBackButton, showBackButton = showBackButton,
onBackClick = onBackClick, onBackClick = onBackClick,
onTopicClick = onTopicClick, onTopicClick = onTopicClick,
viewModel = hiltViewModel<TopicViewModel, TopicViewModel.Factory>(
key = id,
) { factory ->
factory.create(id)
},
) )
} }
} }

@ -7,9 +7,9 @@ androidTools = "31.7.3"
androidxActivity = "1.9.3" androidxActivity = "1.9.3"
androidxAppCompat = "1.7.0" androidxAppCompat = "1.7.0"
androidxBrowser = "1.8.0" androidxBrowser = "1.8.0"
androidxComposeBom = "2024.12.01" androidxComposeBom = "2025.02.00"
androidxComposeFoundation = "1.8.0-alpha07" androidxComposeFoundation = "1.8.0-alpha07"
androidxComposeMaterial3Adaptive = "1.1.0-alpha08" androidxComposeMaterial3Adaptive = "1.1.0-rc01"
androidxComposeRuntimeTracing = "1.7.6" androidxComposeRuntimeTracing = "1.7.6"
androidxCore = "1.15.0" androidxCore = "1.15.0"
androidxCoreSplashscreen = "1.0.1" androidxCoreSplashscreen = "1.0.1"

Loading…
Cancel
Save