Saltar al contenido principal

La transición de página predeterminada en Android ahora es PredictiveBackPageTransitionsBuilder

La transición de página predeterminada de Android se ha actualizado para coincidir con la plataforma y para soportar el retroceso predictivo (predictive back).

Resumen

#

La transición de página predeterminada en Android se ha actualizado de ZoomPageTransitionsBuilder a PredictiveBackPageTransitionsBuilder. Cuando no se utiliza el retroceso predictivo, esta recurre a FadeForwardsPageTransitionsBuilder.

Contexto

#

Android ha estado implementando una función llamada retroceso predictivo (predictive back), en la cual realizar un gesto de retroceso permite al usuario echar un vistazo a la ruta o aplicación anterior y, posiblemente, cancelar la navegación. Flutter añadió soporte para esto con el widget PopScope seguido de PredictiveBackPageTransitionsBuilder.

Mientras tanto, Android también actualizó su transición de página predeterminada. Flutter añadió soporte para esto con FadeForwardsPageTransitionsBuilder.

Descripción del cambio

#

Con este cambio, PredictiveBackPageTransitionsBuilder ha reemplazado a ZoomPageTransitionsBuilder como la transición de página predeterminada en Android. Durante una transición de página normal sin un gesto de retroceso predictivo, los usuarios verán la nueva FadeForwardsPageTransitionsBuilder como la transición de página predeterminada. Al usar un gesto de retroceso predictivo, la página se anima junto con el gesto y permite cancelar o confirmar la navegación hacia atrás.

En este ejemplo, no se proporciona explícitamente ninguna transición de página, por lo que el default se establece en PredictiveBackPageTransitionsBuilder en el tema de Android.

dart
MaterialApp(
  theme: ThemeData(
    brightness: Brightness.light,
  ),
  home: const PageOne(),
);

Guía de migración

#

Si deseas mantener la transición de página de tu aplicación en la antigua ZoomPageTransitionsBuilder, simplemente puedes establecer tu transición de página explícitamente en el tema de tu aplicación. Ten en cuenta que no podrás soportar las transiciones de ruta de retroceso predictivo.

Código antes de la migración:

dart
return MaterialApp(
  theme: ThemeData(
    brightness: Brightness.light,
    // pageTransitionsTheme is the default.
  ),
  home: const MyFirstScreen(),
);

Código después de la migración:

dart
MaterialApp(
  theme: ThemeData(
    // pageTransitionsTheme is explicitly set to the old transition on Android.
    pageTransitionsTheme: const PageTransitionsTheme(
      builders: {
        TargetPlatform.android: ZoomPageTransitionsBuilder(),
      },
    ),
  ),
  home: const MyFirstScreen(),
);

Un efecto secundario de cambiar la transición predeterminada es que la duración que toma la transición entre páginas ha aumentado de 300ms a 450ms. Esto podría causar fallos en las pruebas que dependen de la duración de transición anterior. Afortunadamente, es posible usar TransitionDurationObserver para mantener las pruebas independientes de cualquier transición de página que se utilice.

Código antes de la migración:

dart
testWidgets('example', (WidgetTester tester) async {
  await tester.pumpWidget(
    MaterialApp(
      onGenerateRoute: (RouteSettings settings) { ... },
    ),
  );

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsNothing);

  // Pump through the whole transition, hardcoded to 300ms.
  await tester.tap(find.text('Next'));
  await tester.pump(const Duration(milliseconds: 300));

  expect(find.text('Page 1'), findsNothing);
  expect(find.text('Page 2'), findsOneWidget);
});

Código después de la migración:

dart
testWidgets('example', (WidgetTester tester) async {
  final TransitionDurationObserver observer = TransitionDurationObserver();

  await tester.pumpWidget(
    MaterialApp(
      navigatorObservers: <NavigatorObserver>[observer],
      onGenerateRoute: (RouteSettings settings) { ... },
    ),
  );

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsNothing);

  // Pump through the whole transition independent of the duration.
  await tester.tap(find.text('Next'));
  await observer.pumpPastTransition(tester);

  expect(find.text('Page 1'), findsNothing);
  expect(find.text('Page 2'), findsOneWidget);
});

Incluso es posible escribir pruebas que necesitan procesar parte del camino a través de una transición de página sin depender de la duración exacta.

Código antes de la migración:

dart
testWidgets('example', (WidgetTester tester) async {
  await tester.pumpWidget(
    MaterialApp(
      onGenerateRoute: (RouteSettings settings) { ... },
    ),
  );

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsNothing);

  // Pump through half of the transition with a hardcoded value.
  await tester.tap(find.text('Back'));
  await tester.pump(const Duration(milliseconds: 150));

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsOneWidget);
});

Código después de la migración:

dart
testWidgets('example', (WidgetTester tester) async {
  final TransitionDurationObserver observer = TransitionDurationObserver();

  await tester.pumpWidget(
    MaterialApp(
      navigatorObservers: <NavigatorObserver>[observer],
      onGenerateRoute: (RouteSettings settings) { ... },
    ),
  );

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsNothing);

  // Pump through half of the transition independent of the duration.
  await tester.tap(find.text('Back'));
  await tester.pump(observer.transitionDuration ~/ 2);

  expect(find.text('Page 1'), findsOneWidget);
  expect(find.text('Page 2'), findsOneWidget);
});

Timeline

#

Introducido en la versión: 3.37.0-0.0.pre
En la versión estable: 3.38.0

Referencias

#

Documentación de la API:

Issues relevantes:

PRs relevantes: