Flutter — go_router


Cómo ya vimos antes Flutter tiene un sistema de navegación entre páginas propio, pero también hay paquetes muy completos que nos pueden ayudar con este manejo de navegación y rutas como lo es go_router.


go_router

Es un paquete de navegación y enrutamiento para Flutter que utiliza la API Router para navegar entre diferentes pantallas. Se puede definir patrones de URL, navegar con URLs, con deep links, entre otras características.

Características

  • Manejo de parámetros por URL(PathParams) o consulta (QueryParams) por ejemplo “car/:id”.
  • Se pueden configurar rutas hijas.
  • Redirección de URLs.
  • Soporte para navegadores.
  • Compatibilidad con Material y Cupertino.
  • Retrocompatibilidad con Navigator API.

Instalación

Se puede instalar de varias formas un paquete en Flutter.

  • Ejecutando el comando.
flutter pub add go_router
  • Agregando directamente el paquete en el archivo pubspec.yaml y luego ejecutando el comando flutter pub get .
dependencies:
  go_router: ^13.1.0

Para usarlo basta con realizar el import.

import 'package:go_router/go_router.dart';

Uso básico 

Un ejemplo básico agregando la configuración inicial del GoRouter.

import 'package:go_router/go_router.dart';

final _router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
    ),
  ],
);

para poder utilizar GoRouter en una aplicación de Flutter se debe realizar una configuración en el Material o Cupertino, el paquete proporciona un par de constructores para reemplazar MaterialApp por MaterialApp.router o CupertinoApp por CupertinoApp.router y en el parámetro routerConfig se debe colocar el objeto de configuración GoRouter.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

Configuración

Para crear una configuración de GoRouter se realiza utilizando el constructor de GotRouter y dentro del parámetro routes una lista de objetos GoRoute.

GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/detail',
      builder: (context, state) => const DetailScreen(),
    ),
  ],
);

GoRoute

Para configurar un GoRoute, se debe colocar como mínimo los parámetros ruta path y un constructor builder o pageBuilder .

// para ir a este Route utiliza el método go()
GoRoute(
  path: '/categories:categoryId',
  // context es BuildContext
  // state es GoRouterState
  builder: (context, state) => const CategoryScreen(),
),

Parámetros

Para agregar un parámetro en el path se debe hacer con :categoryId . Se puede acceder al parámetro a través de GoRouterState, que viene siendo el tipo de dato del segundo parámetro que se envía en el builder .

GoRoute(
  path: '/categories/:categoryId',
  builder: (context, state) => const CategoryScreen(id: state.pathParameters['categoryId']),
),

Algo similar se hace para los QueryParams, cuando termina el Path se agrega /categories?filter=gold . Se acceder de la siguiente manera:

GoRoute(
  path: '/categories',
  builder: (context, state) => const UsersScreen(filter: state.uri.queryParameters['filter']),
),

Rutas hijas

Una ruta que coincide con otra hace que se muestren varias pantallas en un Navigator. Es lo mismo que llamar a un push() una pantalla reemplaza la anterior y un botón de volver atrás. Para mostrar una pantalla encima de otra se puede añadir una ruta hija a la lista de routes de una ruta padre.

GoRoute(
  path: '/',
  builder: (context, state) {
    return HomeScreen();
  },
  routes: [
    GoRoute(
      path: 'categories',
      builder: (context, state) {
        return CategoriesScreen();
      },
    ),
  ],
)

Navegación anidada

En algunas aplicaciones se utilizan ciertos widgets que permanecen en la misma parte para diferentes pantallas como los BottomNavigationBar.

Para estos casos se puede usar un ShellRoute el cual tiene como parámetro un builder que devuelve un widget y un routes con la lista de rutas.

ShellRoute(
  builder:
      (BuildContext context, GoRouterState state, Widget child) {
    return Scaffold(
      body: child,
      /* ... */
      bottomNavigationBar: BottomNavigationBar(
      /* ... */
      ),
    );
  },
  routes: <RouteBase>[
    GoRoute(
      path: 'categories',
      builder: (BuildContext context, GoRouterState state) {
        return const CategoriesScreen();
      },
    ),
  ],
),

El widget child es un Navigator configurado para mostrar las subrutas coincidentes.

Ruta inicial

Se puede realizar la configuración de la ruta inicial donde abrirá la aplicación. Basta con agregar el parámetro initialLocation al constructor de GoRouter.

GoRouter(
  initialLocation: '/home',
  /* ... */
);

Logging

Si se necesita tener registro de logs, se puede realizar activando la propiedad debugLogDiagnostics .

final _router = GoRouter(
  routes: [/* ... */],
  debugLogDiagnostics: true,
);

Navegación 

Hay diferentes formas de navegar dentro de una aplicación.

Ir directo a un destino

Lo que hace es reemplazar el historial actual de pantallas del GoRouter por las pantallas configuradas para mostrarse en la ruta de destino. Para ir a una nueva pantalla utilizar context.go() enviando la ruta dentro de un String.

build(BuildContext context) {
  return TextButton(
    onPressed: () => context.go('/categories/123'),
  );
}

Esto es una forma abreviada de llamar a GoRouter.of(context).go('/categories/123).

También se puede armar una URI con ayuda de la clase Uri de la librería de Dart.

context.go(Uri(path: '/categories/123', queryParameters: {'filter': 'abc'}).toString());

Utilizando nombre de rutas

Se puede usar los nombres únicos en los GoRoute, basta simplemente con agregar el parámetro name .

GoRoute(
   name: 'user',
   path: 'users/:userId',
   builder: /* ... */,
 ),

Para navegar entre rutas utilizando un nombre único, se debe realizar llamando el método goNamed .

TextButton(
  onPressed: () {
    context.goNamed('user', pathParameters: {'userId': 123});
  },
  child: const Text('Go to user'),
),

También es posible utilizar el método go() siempre y cuando se busque primero el nombre de la localización de la ruta, utilizando namedLocation .

TextButton(
  onPressed: () {
    final String location = context.namedLocation('user', pathParameters: {'userId': 123});
    context.go(location);
  },
  child: const Text('Go to user'),
),

evitar usar

  • La navegación imperativa causa problemas con el historial de Navigator
  • GoRouter y otras APIs basadas en Routers no son compatibles con el widget WillPopScope.

Demostración

Conclusión

En conclusión, go_router es un paquete bastante robusto y bien estructurado lo cual nos ayuda a tener mejor orden en nuestra navegación de pantallas. Una excelente alternativa a Navigator.

Siguiente artículo

Vamos a revisar los widgets y su funcionamiento básico.

¡¡¡Gracias por leer este artículo!!!

Referencias

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *