Navigator 2.0 en Flutter

 

La navegación es parte esencial de cualquier software, tanto web como móvil, ya que este nos permite realizar el desplazamiento entre enlaces de una página a otra.

Para entender mejor la apuesta que Flutter está realizando al cambiar el concepto de su Navigator a Navigator 2.0, es necesario entrar en contexto sobre estas dos API’s:

Understanding Flutter Navigator 2.0 | Codemagic Blog

 

Navigator

Si estás usando Navigator significa que estás familiarizado con:

 

  • Navigator: un widget que administra una pila de objetos de rutas.
  • Route: un objeto administrado por un Navigator que representa una pantalla, normalmente implementado por clases como MaterialPageRoute.

 

Uno de los principales problemas con el Navigator actual es que su implementación se basa en una programación imperativa (vamos definiendo el stack de la navegación a medida que el desarrollo va avanzando). Lo anterior es completamente opuesto a la programación declarativa y reactiva con el cual está construido Flutter.

Otro de los grandes problemas que tiene la navegación actual son la cantidad de parámetros en los que podemos declarar nuestra navegación dentro del Widget MaterialApp, ya que en la actualidad Flutter nos permite implementar la navegación a través de los siguientes parámetros:

  • Routes
  • OnGenerateRoute
  • OnGenerateInitialRoutes
  • OnUnknownRoute

Lo anterior hace que la implementación de la navegación en un inicio sea un tanto confusa, sin contar que existe otra cantidad de formas de poder declarar nuestra navegación y aplicarla imperativamente.

Por ejemplo:

Opción 1

  • MaterialApp(
      debugShowCheckedModeBanner: false,
      title: ExampleFlutterLab,
      theme: ThemeData(
        primaryColor: Colors.white,
        scaffoldBackgroundColor: AppColors.backgroundGrey,
        textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme),
      ),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: L10n.all,
      navigatorKey: AppNavigator.navigatorKey,
      onGenerateRoute: Routes.generateRoute,
    ); 
  • class Routes {
      static Route<dynamic> generateRoute(RouteSettings settings) {

        switch (settings.name) {
          case ‘/’: return MaterialPageRoute(builder: (_) => const Login());
          case ‘/Home: return MaterialPageRoute(builder: (_) => const Home());
          default: return MaterialPageRoute(builder: (_) => const Login());
        }
      }
    }
  • Material(
        elevation: 4,
      borderRadius: BorderRadius.circular(3),
      child: InkWell(
        onTap: () => Navigator.of(context).pushNamed(«/home»),
        child: const Text(«let’s go home»),
      ),
    );

(Se agrego en el parámetro OnGenerateRoute una static Function que nos retorne Route<dynamic> y que esta nos agregara en la pila de navegación la nueva vista, en este caso Home())

Opción 2

  • Material(
      elevation: 4,
      borderRadius: BorderRadius.circular(3),
      child: InkWell(
        onTap: () => Navigator.of(context).push(
          new MaterialPageRoute(
            builder: (context) => home(
              user: User(),
            ),
          ),
        ),
        child: const Text(«let’s go home»),
      ),
    ),

(Imperativamente se declara la función que nos permitirá realizar la navegación a la siguiente vista en este caso Home() )

 

Navigator 2.0

Esta API nos permite un control más preciso sobre la pila de navegación, logrando analizar las rutas y eliminar páginas debajo de la actual.

Se agregan nuevas clases al marco, para hacer que las pantallas de la aplicación sean una función de estado (muy similar a lo que se implementa en URL web) entre las que encontramos las siguientes.

  • Page: objeto inmutable que nos permite configurar los Stack en la pila de navegación.
  • Router: es el encargado de abrir y cerrar páginas de una aplicación.

Este widget escucha la información de enrutamiento del sistema operativo, analiza los parámetros de la ruta y luego convierte esos datos en un widget que posteriormente se agrega a la pila de navegación.

  • RouteInformationParser: Cuando el enrutador obtiene una nueva información de ruta de RouteInformationProvider, el Router usa este delegado para analizar la información de ruta y producir una configuración que será utilizada por RouterDelegate que eventualmente procederá a construir el widget de Router.
  • RouterDelegate: Un delegado que utiliza el widget de Router para crear y configurar un widget de navegación.
  • BackButtonDispatcher: Informa en un Router cuando el usuario toca el botón atrás en plataformas que admiten botones atrás (como Android).

 

Por ejemplo:

main.dart

import ‘package:example_flutter_lab/page/home/home.dart’;
import ‘package:example_flutter_lab/page/login/login.dart’;
import ‘package:flutter/material.dart’;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static late String nameUser;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: ‘ExampleFlutterLab’,
      theme: ThemeData(
          primaryColor: Colors.white, scaffoldBackgroundColor: Colors.white),
      home: Navigator(
        pages: [
          MaterialPage(
            child: Login(
              handledOnTap: handledOnTap,
            ),
            key: const ValueKey(«login»),
          ),
          if (nameUser != null)
            const MaterialPage(
              child: Home(),
              key: ValueKey(«home»),
            ),
        ],
        onPopPage: (route, result) => route.didPop(result),
      ),
    );
  }

  handledOnTap(String nameUserParams) => setState(() {
        nameUser = nameUserParams;
      });
}

login.dart

import ‘package:flutter/material.dart’;

class Login extends StatefulWidget {
  final Function(String) handledOnTap;

  const Login({Key? key, required this.handledOnTap}) : super(key: key);

  @override
  _LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Material(
          elevation: 4,
          borderRadius: BorderRadius.circular(3),
          child: InkWell(
            onTap: () => widget.handledOnTap(«user»),
            child: const Text(«let’s go home»),
          ),
        )
      ],
    );
  }
}

¡Navigator 2.0, un mundo fascinante a la espera de la acción!

 

Dash, la mascota de Flutter y Dart

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaqueipsa quae abillo inventore veritatis quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem.

Seguir leyendo