Cómo agregar compatibilidad para el gesto atrás predictivo

Figura 1: Simulación del aspecto y la experiencia del gesto atrás predictivo en un teléfono

El gesto atrás predictivo, una función de navegación por gestos, permite a los usuarios obtener una vista previa del lugar al que los lleva el gesto de deslizar hacia atrás.

Por ejemplo, con un gesto atrás, se puede mostrar una vista previa animada de la pantalla principal detrás de la app, como se muestra en la simulación de la Figura 1.

A partir de Android 15, la opción para desarrolladores de animaciones de atrás predictivo ya no está disponible. Las animaciones del sistema, como volver a la pantalla principal, cambiar de tarea y cambiar de actividad, ahora aparecen para las apps que habilitaron el gesto atrás predictivo por completo o a nivel de la actividad.

Puedes probar esta animación de regreso a la pantalla principal (como se describe en una sección posterior de esta página).

Para admitir el gesto atrás predictivo, se requiere actualizar la app, con OnBackPressedCallback AppCompat 1.6.0-alpha05 (AndroidX) retrocompatible o una API posterior, o bien con la nueva API de plataforma OnBackInvokedCallback. La mayoría de las apps usan la API de AndroidX retrocompatible.

Esta actualización proporciona una ruta de migración para interceptar correctamente la navegación hacia atrás, lo que implica reemplazar las interceptaciones de retroceso de KeyEvent.KEYCODE_BACK y cualquier clase con métodos onBackPressed, como Activity y Dialog con las nuevas APIs de retroceso del sistema.

Codelab y video de Google I/O

Además de usar la documentación en esta página, prueba nuestro codelab. Este proporciona una implementación de casos de uso común de un WebView que controla el gesto atrás predictivo mediante las APIs de actividad de AndroidX.

También puedes ver nuestro video de Google I/O, que abarca ejemplos adicionales sobre la implementación de AndroidX y las APIs de la plataforma.

Actualiza una app que usa la navegación hacia atrás predeterminada

Actualizar tu app para que sea compatible con esta función es sencillo si esta no implementa ningún comportamiento de navegación hacia atrás personalizada (es decir, permite que el sistema continúe controlando este tipo de navegación). Habilita esta función como se describe en esta guía.

Si tu app usa Fragments o el componente Navigation, también actualiza a AndroidX Activity 1.6.0-alpha05 o versiones posteriores.

Actualiza una app que usa una navegación hacia atrás personalizada

Si tu app implementa un comportamiento de navegación hacia atrás personalizada, existen diferentes rutas de migración en función de si usa AndroidX y cómo controla este tipo de navegación.

Tu app usa AndroidX Cómo tu app controla la navegación hacia atrás Ruta de migración recomendada (vínculo en esta página)
APIs de AndroidX Migra una implementación actual de AndroidX
APIs de plataforma no compatibles Migra una app de AndroidX que contiene APIs de navegación hacia atrás no compatibles a las APIs de AndroidX
No APIs de plataforma no compatibles, que se pueden migrar Migra una app que usa APIs de navegación hacia atrás no compatibles a las APIs de la plataforma
APIs de plataforma no compatibles, pero que no se pueden migrar No habilites la función hasta que esta se convierta en obligatoria

Migra una implementación de navegación hacia atrás de AndroidX

Este caso de uso es el más común (y el más recomendado). Se aplica a apps nuevas o existentes que implementan el manejo de navegación por gestos personalizada con OnBackPressedDispatcher, como se describe en Cómo brindar navegación personalizada hacia atrás.

Si tu app se ajusta a esta categoría, sigue los pasos que se mencionan a continuación para agregar compatibilidad con el gesto atrás predictivo:

  1. Para asegurarte de que las APIs que ya usan las APIs de OnBackPressedDispatcher (como Fragments y el componente Navigation) funcionen sin problemas con el gesto atrás predictivo, actualiza a AndroidX Activity 1.6.0-alpha05.

    // In your build.gradle file:
    dependencies {
    
    // Add this in addition to your other dependencies
    implementation "androidx.activity:activity:1.6.0-alpha05"
    
  2. Habilita el gesto atrás predictivo, como se describe en esta página.

Migra una app de AndroidX que contiene APIs de navegación hacia atrás no compatibles a las APIs de AndroidX

Si tu app usa bibliotecas de AndroidX, pero implementa APIs de navegación hacia atrás no compatibles o hace referencia a estas, deberás migrar al uso de las APIs de AndroidX para admitir el nuevo comportamiento.

Para migrar APIs no compatibles a las APIs de AndroidX, haz lo siguiente:

  1. Migra la lógica del control de navegación hacia atrás del sistema a OnBackPressedDispatcher de AndroidX con una implementación de OnBackPressedCallback. Para obtener ayuda detallada, consulta Cómo brindar navegación hacia atrás personalizada.

  2. Inhabilita OnBackPressedCallback cuando todo esté listo para dejar de interceptar el gesto atrás.

  3. Deja de interceptar eventos de retroceso mediante OnBackPressed o KeyEvent.KEYCODE_BACK.

  4. Asegúrate de actualizar a AndroidX Activity 1.6.0-alpha05.

    // In your build.gradle file:
    dependencies {
    
    // Add this in addition to your other dependencies
    implementation "androidx.activity:activity:1.6.0-alpha05"
    
  5. Una vez que migres la app de forma correcta, habilita el gesto atrás predictivo (como se describe en esta página) para ver la animación del sistema para volver a la pantalla principal.

Migra una app que usa APIs de navegación hacia atrás no compatibles a las APIs de la plataforma

Si tu app no puede usar bibliotecas de AndroidX y, en su lugar, implementa la navegación hacia atrás personalizada o hace referencia a esta mediante las APIs no compatibles, debes migrar a la API de la plataforma OnBackInvokedCallback.

Sigue estos pasos para migrar APIs no compatibles a la API de la plataforma:

  1. Usa la nueva API de OnBackInvokedCallback en dispositivos que ejecutan Android 13 o versiones posteriores, y usa las APIs no compatibles en dispositivos que ejecutan Android 12 o versiones anteriores.

  2. Registra la lógica de navegación hacia atrás personalizada en OnBackInvokedCallback con onBackInvokedDispatcher. De esta manera, se evita que se complete la actividad actual, y la devolución de llamada tendrá la oportunidad de reaccionar a la acción de atrás una vez que el usuario complete la navegación hacia atrás del sistema.

  3. Cancela el registro de OnBackInvokedCallback cuando todo esté listo para dejar de interceptar el gesto atrás. De lo contrario, los usuarios podrían ver un comportamiento no deseado cuando usen la navegación hacia atrás del sistema. Por ejemplo, podrían "atascarse" entre vistas y verse obligados a forzar el cierre de tu app.

    Este es un ejemplo de cómo migrar la lógica fuera de onBackPressed:

    Kotlin

    @Override
    fun onCreate() {
        if (BuildCompat.isAtLeastT()) {
            onBackInvokedDispatcher.registerOnBackInvokedCallback(
                OnBackInvokedDispatcher.PRIORITY_DEFAULT
            ) {
                /**
                 * onBackPressed logic goes here. For instance:
                 * Prevents closing the app to go home screen when in the
                 * middle of entering data to a form
                 * or from accidentally leaving a fragment with a WebView in it
                 *
                 * Unregistering the callback to stop intercepting the back gesture:
                 * When the user transitions to the topmost screen (activity, fragment)
                 * in the BackStack, unregister the callback by using
                 * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
                 * (https://developer.android.com/reference/kotlin/android/window/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
                 */
            }
        }
    }

    Java

    @Override
    void onCreate() {
      if (BuildCompat.isAtLeastT()) {
        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT,
            () -> {
              /**
               * onBackPressed logic goes here - For instance:
               * Prevents closing the app to go home screen when in the
               * middle of entering data to a form
               * or from accidentally leaving a fragment with a WebView in it
               *
               * Unregistering the callback to stop intercepting the back gesture:
               * When the user transitions to the topmost screen (activity, fragment)
               * in the BackStack, unregister the callback by using
               * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
               * (https://developer.android.com/reference/kotlin/android/view/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
               */
            }
        );
      }
    }
  4. Deja de interceptar eventos de retroceso a través de OnBackPressed o KeyEvent.KEYCODE_BACK para Android 13 y versiones posteriores.

  5. Una vez que se migró la app de forma correcta, habilita el gesto atrás predictivo (como se describe en esta página) para que OnBackInvokedCallback entre en vigencia.

Puedes registrar un objeto OnBackInvokedCallback con PRIORITY_DEFAULT o PRIORITY_OVERLAY, que no están disponibles en OnBackPressedCallback de AndroidX similar. El registro de una devolución de llamada con PRIORITY_OVERLAY es útil en algunos casos.

Esto se aplica cuando migras desde onKeyPreIme() y tu devolución de llamada necesita recibir el gesto de retroceso en lugar de un IME abierto. Los IMEs registran devoluciones de llamada con PRIORITY_DEFAULT cuando se abren. Registra la devolución de llamada con PRIORITY_OVERLAY para asegurarte de que OnBackInvokedDispatcher envíe el gesto atrás a la devolución de llamada en lugar del IME abierto.

Acepta el gesto atrás predictivo

Una vez que hayas determinado cómo actualizar tu app según tu caso, habilita el gesto atrás predictivo.

Para habilitarlo, en AndroidManifest.xml, en la etiqueta <application>, establece la marca android:enableOnBackInvokedCallback en true.

<application
    ...
    android:enableOnBackInvokedCallback="true"
    ... >
...
</application>

Si no proporcionas un valor, el valor predeterminado es false y hace lo siguiente:

  • Inhabilita la animación de gesto atrás predictivo del sistema.
  • Ignora OnBackInvokedCallback, pero las llamadas OnBackPressedCallback siguen funcionando.

Acepta el gesto a nivel de la actividad

A partir de Android 14, la marca android:enableOnBackInvokedCallback te permite habilitar animaciones predictivas del sistema a nivel de la actividad. Este comportamiento facilita la migración de apps grandes de varias actividades a gestos atrás predictivos. Con Android 15, el gesto atrás predictivo ya no está detrás de la opción para desarrolladores. Las apps pueden habilitar el gesto atrás predictivo por completo o a nivel de la actividad.

En el siguiente código, se muestra un ejemplo del uso de enableOnBackInvokedCallback para habilitar la animación del sistema para volver a la pantalla principal desde MainActivity:

<manifest ...>
    <application . . .

        android:enableOnBackInvokedCallback="false">

        <activity
            android:name=".MainActivity"
            android:enableOnBackInvokedCallback="true"
            ...
        </activity>
        <activity
            android:name=".SecondActivity"
            android:enableOnBackInvokedCallback="false"
            ...
        </activity>
    </application>
</manifest>

En el ejemplo anterior, configurar android:enableOnBackInvokedCallback=true para ".SecondActivity" habilita la animación del sistema de actividad cruzada.

Ten en cuenta las siguientes consideraciones cuando uses la marca android:enableOnBackInvokedCallback:

  • Cuando se configuraandroid:enableOnBackInvokedCallback=false, se desactivan las animaciones del gesto atrás predictivo a nivel de la actividad o de la app, según el lugar en el que establezcas la etiqueta, y le indica al sistema que ignore las llamadas al OnBackInvokedCallback de la API de la plataforma. Sin embargo, las llamadas a OnBackPressedCallback se siguen ejecutando porque OnBackPressedCallback es retrocompatible y llama a la API de onBackPressed, que no era compatible con versiones anteriores a Android 13.
  • Cuando se configura la marca enableOnBackInvokedCallback a nivel de la app, se establece el valor predeterminado para todas las actividades de la app. Puedes anular el valor predeterminado por actividad si configuras la marca a nivel de la actividad, como se muestra en el ejemplo de código anterior.

Prácticas recomendadas para devolución de llamadas

Estas son las prácticas recomendadas para usar las devoluciones de llamadas del sistema compatibles: BackHandler (para Compose), OnBackPressedCallback o OnBackInvokedCallback.

Determina el estado de la IU que habilita o inhabilita cada devolución de llamada

El estado de la IU es la propiedad que describe la IU. Te recomendamos que sigas estos pasos de alto nivel.

  1. Determina el estado de la IU que habilita o inhabilita cada devolución de llamada.

  2. Define ese estado con un tipo de contenedor de datos observables, como StateFlow o Compose State, y habilita o inhabilita la devolución de llamada a medida que cambia el estado.

Si tu app antes asociaba la lógica de atrás con sentencias condicionales, esto podría significar que reaccionas al evento de retroceso después de que ya ocurrió. Evita este patrón con devoluciones de llamada más recientes. Si es posible, mueve la devolución de llamada fuera de la sentencia condicional y, en su lugar, asóciala a un tipo de contenedor de datos observables.

Usa devoluciones de llamada hacia atrás del sistema para la lógica de la IU

La lógica de la IU determina cómo mostrar la IU. Usa devoluciones de llamada hacia atrás del sistema para ejecutar la lógica de la IU, como mostrar una ventana emergente o ejecutar una animación.

Si tu app habilita una devolución de llamada hacia atrás del sistema, las animaciones predictivas no se ejecutarán y deberás controlar el evento de retroceso. No crees devoluciones de llamadas solo para ejecutar una lógica que no sea de IU.

Por ejemplo, si interceptas eventos de retroceso solo para el registro, haz lo mismo en el ciclo de vida de la actividad o del fragmento.

  • Para casos de actividad a actividad o de fragmento a actividad, registra si isFinishing dentro de onDestroy es true en el ciclo de vida de la actividad.
  • Para casos de fragmento a fragmento, registra si isRemoving dentro de onDestroy es verdadero dentro del ciclo de vida de la vista del fragmento. O bien, haz el registro con los métodos onBackStackChangeStarted o onBackStackChangeCommitted dentro de FragmentManager.OnBackStackChangedListener.

En el caso de Compose, registra en la devolución de llamada onCleared() de un ViewModel asociado con el destino de Compose. Este es el mejor indicador para saber cuándo un destino de composición se quita de la pila de actividades y se destruye.

Crea devoluciones de llamadas de responsabilidad única

Puedes agregar varias devoluciones de llamadas al despachador. Las devoluciones de llamada se agregan a una pila en la que la devolución de llamada habilitada que se agregó recientemente controla el siguiente gesto atrás con una devolución de llamada por gesto atrás.

Es más fácil administrar el estado habilitado de una devolución de llamada si esta tiene una sola responsabilidad. Por ejemplo:

Orden de devoluciones de llamada en una pila
Figura 2: Diagrama de la pila de devolución de llamada.

En la figura 2, se muestra cómo puedes tener varias devoluciones de llamada en la pila, cada una responsable de una acción. Una devolución de llamada solo se ejecuta si las devoluciones de llamada que están por encima de ella en la pila están inhabilitadas. En este ejemplo, la devolución de llamada "¿Estás seguro…?" se habilita cuando el usuario ingresa datos en un formulario y se inhabilita de lo contrario. La devolución de llamada abre un diálogo de confirmación cuando el usuario desliza el dedo hacia atrás para salir del formulario.

La otra devolución de llamada puede incluir un componente de material que admita el gesto atrás predictivo, una transición de AndroidX con las APIs de Progress o cualquier otra devolución de llamada personalizada.

La devolución de llamada de una childFragmentManager se ejecuta si las devoluciones de llamada anteriores están inhabilitadas y la pila de actividades para este FragmentManager no está vacía, en la que childFragmentManager se adjunta dentro de un fragmento. En este ejemplo, esta devolución de llamada interna está inhabilitada.

Del mismo modo, la devolución de llamada interna de supportFragmentManager se ejecuta si las devoluciones de llamada anteriores están inhabilitadas y su pila no está vacía. Este comportamiento es coherente cuando se usa FragmentManager o NavigationComponent para la navegación, ya que NavigationComponent depende de FragmentManager. En este ejemplo, esta devolución de llamada se ejecuta si el usuario no ingresó texto en el formulario, lo que hace que se inhabilite la devolución de llamada "¿Estás seguro…?".

Por último, super.onBackPressed() es la devolución de llamada a nivel del sistema, que se vuelve a ejecutar si se inhabilitan las devoluciones de llamada anteriores. Para activar animaciones del sistema, como volver a la página principal, cambiar de actividad y cambiar de tarea, la pila de actividades de supportFragmentManager debe estar vacía para que se inhabilite su devolución de llamada interna.

Prueba la animación del gesto de retroceso predictivo

Si aún usas Android 13 o Android 14, puedes probar la animación para volver a la pantalla principal que se muestra en la Figura 1.

Para probar esta animación, sigue estos pasos:

  1. En tu dispositivo, ve a Configuración > Sistema > Opciones para desarrolladores.

  2. Selecciona Animaciones del gesto atrás predictivo.

  3. Inicia la app actualizada y usa el gesto de retroceso para verlo en acción.