nav: swap the full slide-back for the predictive-back peek

Back was a full-width horizontal slide-off — too heavy. Replace it with
floret-kit's Modifier.predictiveBack on each pop-able destination (task list,
detail, edit) so the leaving screen scales/rounds with the gesture and the held
screen is revealed unmoved; NavHost now does no pop animation. Settings applies
it at the hub only (enabled = section == null) so its inner section back still
returns to the hub. Forward stays a calm fade-in.

Bumps the floret-kit submodule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 14:31:20 +02:00
parent 63c6191676
commit 37662e83bb
3 changed files with 14 additions and 8 deletions

View File

@@ -1,11 +1,9 @@
package de.jeanlucmakiola.agendula.ui.navigation
import de.jeanlucmakiola.floret.identity.rememberNavSlideSpec
import de.jeanlucmakiola.floret.identity.predictiveBack
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
@@ -35,7 +33,6 @@ import de.jeanlucmakiola.agendula.ui.tasklist.TaskListViewModel
@Composable
fun AgendulaNavHost(modifier: Modifier = Modifier) {
val nav = rememberNavController()
val slide = rememberNavSlideSpec()
NavHost(
navController = nav,
@@ -43,12 +40,14 @@ fun AgendulaNavHost(modifier: Modifier = Modifier) {
modifier = modifier,
// Only the screen on top moves; the background is always held static.
// Forward is a plain fade-in over the held screen — calm, not a slide.
// Back (and the predictive-back drag, which drives popExit) is the snappy
// slide-out to the right, revealing the held screen unmoved.
// Back is the system predictive-back peek: each destination applies
// Modifier.predictiveBack, so the leaving screen scales/rounds with the
// gesture and the held screen is revealed unmoved. NavHost itself does no
// pop animation (the screen owns the motion).
enterTransition = { fadeIn() },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { slideOutHorizontally(slide) { it } + fadeOut() },
popExitTransition = { ExitTransition.None },
) {
composable(Dest.LISTS) {
ListsScreen(
@@ -72,6 +71,7 @@ fun AgendulaNavHost(modifier: Modifier = Modifier) {
LaunchedEffect(filter) { vm.bind(filter) }
TaskListScreen(
modifier = Modifier.predictiveBack(onBack = { nav.popBackStack() }),
filter = filter,
viewModel = vm,
onOpenTask = { taskId -> nav.navigate(Dest.TaskDetail.build(taskId)) },
@@ -89,6 +89,7 @@ fun AgendulaNavHost(modifier: Modifier = Modifier) {
LaunchedEffect(taskId) { vm.bind(taskId) }
TaskDetailScreen(
modifier = Modifier.predictiveBack(onBack = { nav.popBackStack() }),
viewModel = vm,
onEdit = { nav.navigate(Dest.TaskEdit.buildEdit(taskId)) },
onDeleted = { nav.popBackStack() },
@@ -117,6 +118,7 @@ fun AgendulaNavHost(modifier: Modifier = Modifier) {
}
TaskEditScreen(
modifier = Modifier.predictiveBack(onBack = { nav.popBackStack() }),
viewModel = vm,
onSaved = { nav.popBackStack() },
onBack = { nav.popBackStack() },

View File

@@ -93,6 +93,7 @@ import de.jeanlucmakiola.floret.components.Position
import de.jeanlucmakiola.agendula.ui.common.ReminderLeadPicker
import de.jeanlucmakiola.floret.components.pastelize
import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.identity.predictiveBack
import de.jeanlucmakiola.floret.locale.AppLanguage
import de.jeanlucmakiola.floret.reminders.ReminderOverride
import de.jeanlucmakiola.agendula.ui.common.reminderLeadTimeLabel
@@ -128,6 +129,9 @@ fun SettingsScreen(
Box(
modifier = modifier
// At the hub, the back gesture previews the pop to the lists overview;
// in a section the BackHandler above takes it (section -> hub) instead.
.predictiveBack(onBack = onBack, enabled = section == null)
.fillMaxSize()
.background(MaterialTheme.colorScheme.surface),
) {