Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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 androidx.annotation.MainThread
import androidx.navigationevent.NavigationEvent
import androidx.navigationevent.NavigationEventHandler
import androidx.navigationevent.NavigationEventInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The remember function is required to ensure that the NavigationEventHandler instance persists across recompositions. Please add the corresponding import.

Suggested change
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember

import androidx.navigationevent.NavigationEventDispatcher
import androidx.navigationevent.NavigationEventDispatcherOwner
import androidx.navigationevent.NavigationEventInput
import androidx.navigationevent.compose.NavigationEventState

@Composable
private fun HandlingBackEvents() {
// [START android_compose_predictiveback_navevent_handler]
val myHandler = object: NavigationEventHandler<NavigationEventInfo>(
initialInfo = NavigationEventInfo.None,
isBackEnabled = true
) {
override fun onBackStarted(event: NavigationEvent) {
// Prepare for the back event
}

override fun onBackProgressed(event: NavigationEvent) {
// Use event.progress for predictive animations
}

// This is the required method for final event handling
override fun onBackCompleted() {
// Complete the back event
}

override fun onBackCancelled() {
// Cancel the back event
}
}
Comment on lines +35 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

In Jetpack Compose, objects created directly within a @Composable function are recreated on every recomposition. Wrapping the NavigationEventHandler in remember ensures that the same instance is reused, which is critical for maintaining internal state and preventing redundant registration/unregistration cycles when passed to effects like DisposableEffect.

    val myHandler = remember {
        object : NavigationEventHandler<NavigationEventInfo>(
            initialInfo = NavigationEventInfo.None,
            isBackEnabled = true
        ) {
            override fun onBackStarted(event: NavigationEvent) {
                // Prepare for the back event
            }

            override fun onBackProgressed(event: NavigationEvent) {
                // Use event.progress for predictive animations
            }

            // This is the required method for final event handling
            override fun onBackCompleted() {
                // Complete the back event
            }

            override fun onBackCancelled() {
                // Cancel the back event
            }
        }
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

@praneethatchana since this portion of the code is multiplatform, I would ignore this suggestion and instead remove the "@ Composable" on line 32.

// [END android_compose_predictiveback_navevent_handler]
}

// [START android_compose_predictiveback_navevent_register_handler]
@Composable
fun RegisterHandler(
navigationEventDispatcher: NavigationEventDispatcher,
myHandler: NavigationEventHandler<*>
) {
DisposableEffect(navigationEventDispatcher, myHandler) {
navigationEventDispatcher.addHandler(myHandler)
onDispose {
myHandler.remove()
}
}
}
// [END android_compose_predictiveback_navevent_register_handler]



// [START android_compose_predictiveback_navevent_NavigationEventHandler]
@Composable
public fun NavigationBackHandler(
state: NavigationEventState<out NavigationEventInfo>,
isBackEnabled: Boolean = true,
onBackCancelled: () -> Unit = {},
onBackCompleted: () -> Unit,
){

}
// [END android_compose_predictiveback_navevent_NavigationEventHandler]

Copy link
Contributor

Choose a reason for hiding this comment

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

@praneethatchana add a new code sample here on how to handle back using NavigationEventTransitionState with NavigationBackHandler.


// [START android_compose_predictiveback_navevent_NavigationEvent_dispatcher_owner]
class MyComponent: NavigationEventDispatcherOwner {
override val navigationEventDispatcher: NavigationEventDispatcher =
NavigationEventDispatcher()
}
// [END android_compose_predictiveback_navevent_NavigationEvent_dispatcher_owner]


// [START android_compose_predictiveback_navevent_navigation_event_input]
public class MyInput : NavigationEventInput() {
@MainThread
public fun backStarted(event: NavigationEvent) {
dispatchOnBackStarted(event)
}

@MainThread
public fun backProgressed(event: NavigationEvent) {
dispatchOnBackProgressed(event)
}

public fun backCancelled() {
dispatchOnBackCancelled()
}
Comment on lines +109 to +111
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with the other event dispatch methods in this class (backStarted, backProgressed, and backCompleted), the backCancelled method should also be annotated with @MainThread.

    @MainThread
    public fun backCancelled() {
        dispatchOnBackCancelled()
    }


@MainThread
public fun backCompleted() {
dispatchOnBackCompleted()
}
}
// [END android_compose_predictiveback_navevent_navigation_event_input]

// TODO: We then need to provide that input to our dispatcher:
Copy link
Contributor

Choose a reason for hiding this comment

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

@praneethatchana these are so short what do you think about removing them entirely and just mentioning them in-line in the documentation? I'm also ok if you would rather keep them, but perhaps first remove the TODO.

// [START android_compose_predictiveback_navevent_register_input]
fun setupDispatcher() {
val myComponent = MyComponent()
val myInput = MyInput()

// Register the custom input with the dispatcher
myComponent.navigationEventDispatcher.addInput(myInput)
}
// [END android_compose_predictiveback_navevent_register_input]

// [START android_compose_predictiveback_navevent_dispose]
fun cleanupDispatcher(myComponent: MyComponent) {
// Explicitly remove the dispatcher from the hierarchy when the component is destroyed
myComponent.navigationEventDispatcher.dispose()
}
// [END android_compose_predictiveback_navevent_dispose]


Loading