Skip to content
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the naming of this file since there's nothing in here that's actually using Navigation 3. Would it make sense to move these snippets into NavEventSnippets.kt as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I have moved these Snippets into NavEventSnippets.kt

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you delete this file as well? It looks like the build is failing due to the duplication between the two files https://github.com/android/snippets/actions/runs/23192468917/job/67391917984?pr=800

Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@file:Suppress("unused", "UNUSED_VARIABLE")

package com.example.compose.snippets.predictiveback

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent.EDGE_LEFT
import android.view.MotionEvent.EDGE_RIGHT
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import androidx.navigationevent.NavigationEventInfo
import androidx.navigationevent.NavigationEventTransitionState
import androidx.navigationevent.compose.NavigationEventState
import androidx.navigationevent.compose.rememberNavigationEventState


// [START android_compose_predictiveback_navevent_animation]
object Routes {
const val SCREEN_A = "Screen A"
const val SCREEN_B = "Screen B"
}

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
var state by remember { mutableStateOf(Routes.SCREEN_A) }
val backEventState = rememberNavigationEventState<NavigationEventInfo>(currentInfo = NavigationEventInfo.None)

when (state) {
Routes.SCREEN_A -> {
ScreenA { state = Routes.SCREEN_B }
}
else -> {
if (backEventState.transitionState is NavigationEventTransitionState.InProgress) {
ScreenA { }
}
ScreenB(
backEventState = backEventState,
onBackCompleted = { state = Routes.SCREEN_A }
)
}
}
}
}
}

@SuppressLint("ConfigurationScreenWidthHeight")
@Composable
fun ScreenB(
backEventState: NavigationEventState<NavigationEventInfo>,
onBackCompleted: () -> Unit = {},
) {
val transitionState = backEventState.transitionState
val latestEvent =
(transitionState as? NavigationEventTransitionState.InProgress)
?.latestEvent
val backProgress = latestEvent?.progress ?: 0f
val swipeEdge = latestEvent?.swipeEdge ?: 0

if (transitionState is NavigationEventTransitionState.InProgress) {
Log.d("BackGesture", "Progress: ${transitionState.latestEvent.progress}")
} else if (transitionState is NavigationEventTransitionState.Idle) {
Log.d("BackGesture", "Idle")
}

val animatedScale by animateFloatAsState(
targetValue = 1f - (backProgress * 0.1f),
label = "ScaleAnimation"
)

val configuration = LocalConfiguration.current
val maxShift = remember(configuration) {
(configuration.screenWidthDp / 20f) - 8
}

val offsetX = when (swipeEdge) {
EDGE_LEFT -> (backProgress * maxShift).dp
EDGE_RIGHT -> (-backProgress * maxShift).dp
else -> 0.dp
}

NavigationBackHandler(
state = backEventState,
onBackCompleted = onBackCompleted,
isBackEnabled = true
)
Box(
modifier = Modifier
.offset(x = offsetX)
.scale(animatedScale)
){
// Rest of UI
}
}
// [END android_compose_predictiveback_navevent_animation]

@Composable
fun ScreenA(onNavigate: () -> Unit) {
// Basic ScreenA implementation for snippet
}

Loading
Loading