flutter second generation routing navigation system

 The most important classes of the second-generation routing are Page, RouterDelegate, and Router.

 

overview

The new version of Flutter contains two versions of the navigator, namely Navigator 1.0 and Navigator 2.0. Navigator 2.0 is a brand new navigator introduced since Flutter version 1.22. Compared with Flutter 1.0, it has more functions and is more flexible. Of course, in terms of ease of use, Navigator 2.0 is more difficult to use than Navigator 1.0.

QA: Can Navigator 1.0 continue to be used after Navigator 2.0 comes out?

I believe that many students have such questions. Navigator 2.0 is a supplement to Navigator 1.0. At present, Flutter has no official plan to remove Navigator 1.0 from Flutter, so you can choose to use these two navigators according to your needs. Of course, as a student engaged in Flutter development, if you don’t know anything about the new features of Flutter, such as Navigator 2.0, then it must be unreasonable. Then let’s focus on learning the features, principles and usage skills of Navigator 2.0.

QA: Navigator 2.0 or fluro?

Fluro is also a good Flutter navigation framework. It is not absolutely good or bad with Navigator. The main difference is that one is community-based and the other is official. The emergence of fluro is mainly to solve the weak function of Navigator 1.0, but this has been well alleviated in Navigator 2.0.

  • If your project was fluro before, you can continue to use it as needed;
  • If your project uses Navigator 1.0, you can choose to upgrade to Navigator 2.0;
  • If your project is a new project without historical burden, then you can get started directly with Navigator 2.0 in one step;

QA: Why is Navigator 2 so much more complicated than Navigator 1?

  • In order to provide support for Flutter Web, Navigator 2 introduces many new APIs and concepts, such as: Pag, Router, RouterDelegate, RouteInformationParser. In addition, it also needs to manage the routing stack by itself, which is more complex than Navigator 1. class;
  • But these APIs do not have to be practical. For the navigation framework of an APP, only Router, Page, and RouterDelegate are needed. The use of Router and Page is relatively simple, mainly because RouterDelegate is more complicated;
  • The core point of RouterDelegate is to realize the management of the routing stack in the build method, and the design in this reference course is fine;
  • The use and understanding of Navigator 2 is not only limited to watching videos, but also to refer to the explanations in the course, write it several times by hand, and follow the actual combat projects to learn step by step, and then go back and look at what you didn’t know before. The part of understanding will suddenly become clear;
  • Routing stack that supports custom pages;
  • Support opening or closing multiple pages at one time;
  • Support to delete pages under the current page;

These features can be a function that Navigator 1.0 does not have or is difficult to implement.

Opening and closing pages is the most commonly used function in development, so what is the most commonly used way to open and close pages in Navigator 1.0?

open a page

Navigator.push(context, MaterialPageRoute(builder: (context) => page));

close a page

Navigator.pop(context);

Walk into Navigator 2.0

Navigator 2.0 provides a series of new interfaces, which can make the routing state a part of the application state, and provide the function of parsing routes from the underlying platform such as: (Web URL). The new APIs are as follows:

  • Page: An immutable object used to represent each page in the Navigator routing stack;
    • Page is an abstract class and its derived classes are usually used: MaterialPage or CupertinoPage;
  • Router: Used to configure the list of pages to be displayed by the Navigator. Usually, the list of pages will change according to the status of the system or application;
    • In addition to using the Router itself directly, you can also use MaterialApp.router() to create a Router;
  • RouterDelegate: defines the routing behavior in the application, such as how the Router knows about changes in the application state and how to respond;
    • The main job is to monitor RouteInformationParser and application status, and use the current list to build Pages;
  • RouteInformationParser: It can be defaulted. It is mainly applied to the web. It holds the RouteInformation provided by RouteInformationProvider and can parse it into the data type we defined;
  • BackButtonDispatcher: Respond to the back button and notify Router

Tips: BackButtonDispatcher is rarely used in the above APIs. In addition, for mobile APP development, we only need to use the three APIs Page, Router, and RouterDelegate in Navigator 2.0. RouteInformationParser and RouteInformationProvider are mainly used in It is used to develop the routing of the web site;

The following figure shows the interaction of RouterDelegate with Router and RouteInformationParser and the state of the application:

Process analysis:

  1. When the system opens a new page (such as "/detail"), RouteInformationParser will convert it to a specific data type T in the application (such as: BiliRoutePath);
  2. This data type will be passed to the setNewRoutePath method of RouterDelegate, where we can update the routing status;
  3. notifyListeners will notify Router to rebuild RouterDelegate (via build() method);
  4. RouterDelegate.build() returns a new Navigator instance, and finally displays the page we want to open;

 A small chestnut is written below for reference only: first define the subclass of RouterDelegate: what needs to be noted here is that when rewriting the ChangeNotify method, there is no need to rewrite addListener and removeListener, otherwise the result may not be able to jump, I After testing, I stepped on a pit in this place, and I don't want people to roll over in the same position later.

One: Custom RouterDelegate contains page stack information

import 'package:bilibili_demo/nv2/detail.dart';
import 'package:bilibili_demo/nv2/navigator2.dart';
import 'package:flutter/material.dart';

MaterialPage wrapPage(
    {required Widget child, Object? args, required String name}) {
  return MaterialPage(
      child: child, name: name, arguments: args, key: ValueKey(child.hashCode));
}

class MyRouterDelegate extends RouterDelegate<String>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<String> {
  bool isDetail = false;

  List<MaterialPage> pages = [];
  /// 管理的页面堆栈
  final _stack = <String>["/"];

  List<String> get stack => List.unmodifiable(_stack);

  static MyRouterDelegate of(BuildContext context) {
    final delegate = Router.of(context).routerDelegate;
    assert(delegate is MyRouterDelegate, "delegate mush match");
    return delegate as MyRouterDelegate;
  }

  void push(String newRouter, {Object? args}) {
    if (_stack.contains(newRouter)) return;

    _stack.add(newRouter);
    addPageMap(newRouter, args: args);
    notifyListeners();
  }
  
  /// 向pageMap中加入路由信息
  void addPageMap(String router, {Object? args}) {
    MaterialPage page = wrapPage(child: Container(), name: router);
    switch (router) {
      case "/detail":
        page = wrapPage(child: DetailDemo(), name: router, args: args);
        break;
      case "/":
        page = wrapPage(child: Navigator2Home(), name: router, args: args);
        break;
    }
    _pageMap.addAll({router: page});
  }

  void removePageMap(String router) {
    _pageMap.remove(router);
  }

  final Map<String, Page> _pageMap = <String, Page>{
    "/": wrapPage(child: Navigator2Home(), name: "/"),
  };

  void remove(String routername) {
    if (!_stack.contains(routername)) return;
    if (routername == "/") return;

    _stack.remove(routername);
    removePageMap(routername);

    notifyListeners();
  }

  // final RouteFactory onGenerateRouter;

  @override
  // TODO: implement currentConfiguration
  String? get currentConfiguration {
    if (_stack.isNotEmpty) return _stack.last;
    return null;
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Navigator(
      key: navigatorKey,
      pages: [for (String name in _stack) _pageMap[name]!],
      onPopPage: (router, value) {
        if (router.didPop(value)) {
          print("value is $value router is ${router.settings.name}");
          remove(router.settings.name!);
          return true;
        }
        print("false");
        return false;
      },
    );
  }

  @override
  Future<bool> popRoute() async {
    // TODO: implement popRoute
    print("popRouter");
    // return true;
    return super.popRoute();
  }

  @override
  Future<void> setInitialRoutePath(String configuration) {
    print("init config is $configuration");
    // TODO: implement setInitialRoutePath
    return super.setInitialRoutePath(configuration);
  }

  @override
  Future<void> setNewRoutePath(configuration) async {
    // TODO: implement setNewRoutePath
    print("new router is $configuration");
  }

  @override
  // TODO: implement navigatorKey
  GlobalKey<NavigatorState>? get navigatorKey => GlobalKey<NavigatorState>();
}

class MyRouterInformationParser extends RouteInformationParser<String> {
  @override
  Future<String> parseRouteInformation(
      RouteInformation routeInformation) async {
    // TODO: implement parseRouteInformation
    print("information is $routeInformation");
    return routeInformation.location!;
  }

  @override
  RouteInformation? restoreRouteInformation(configuration) {
    print("config is $configuration");
    // TODO: implement restoreRouteInformation
    RouteInformation? info = super.restoreRouteInformation(configuration);
    print("info is $info");
    return info;
  }
}

 Two: Use RouterDelegate in main.dart:

void main() async {
  try {
    await DbManager.init();
    print("db init over");
  } catch (e) {
    printLog("==> e is $e");
  }
  runApp(const MyApp());
}

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

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

class _MyAppState extends State<MyApp> {
  final RouterDelegate<String> _delegate = MyRouterDelegate();
  /// 想使用parse的可以自行添加
  final RouteInformationParser<String> _parser = MyRouterInformationParser();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "flutter navigator2",
      home: Router(routerDelegate: _delegate),
    );
  }
}

Define two pages, one is homePage, the other is detail,

 Three: Routing usage 

MyRouterDelegate.of(context).push("/detail");
import 'package:bilibili_demo/nv2/my_router_delegate.dart';
import 'package:flutter/material.dart';

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

  @override
  State<Navigator2Home> createState() => _Navigator2HomeState();
}

class _Navigator2HomeState extends State<Navigator2Home> {
  void goDetail() {
    MyRouterDelegate.of(context).push("/detail");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(onPressed: goDetail, child: Text("go detail"))
          ],
        ),
      ),
    );
  }
}

The logic of the detail page is relatively simple, so I won’t post it. So far, the content of navigator2 is complete, isn’t it very simple? 

Guess you like

Origin blog.csdn.net/lck8989/article/details/126806470