reading

Based on the Navigator1.0:

  • Annotations for Flutter routing
  • Annotated route 2.0 by Flutter method
  • Annotated route 4.0 by the Flutter method

preface

With the release of Flutter 1.22 comes Navigator2.0, which gives developers more options. You can flexibly manage the routing stack, handle typing in the browser, and nest multiple Navigators. There are still disadvantages to Flutter 1.22, but you can basically route yourself. Follow me into the world of Flutter routing. The source version of this article is Flutter Stable 1.22.6.

Routing based

Navigator

It is responsible for the entire routing Stack, which is actually an Overlay on the structure, which is kind of like a Stack, and people often use it to make toast, like an oktoast, where each page is actually an OverlayEntry.

RouteSettings

Saves the route name and parameters

Page

Navigator2.0, inherited from RouteSettings. I am responsible for creating the Route and carrying a key, which is the basis for the subsequent Page changes. Note that this key should normally be a unique key.

Route

Animating the jumps and saving the RouteSettings (route names and parameters). Also the link between OverlayEntry, Navigator, _RouteEntry.

  • inpushMethod,RoutePass it in, assign it to_RouteEntryIncreased to_history
  Future<T> push<T extends Object>(Route<T> route) {
    _history.add(_RouteEntry(route, initialState: _RouteLifecycle.push));
    _flushHistoryUpdates();
    _afterNavigation(route);
    return route.popped;
  }
Copy the code
  • NavigatorState.buildThe value of initialEntries for Overlay in _history is equal to the OverlayEntry of all _routeentry. route in _history
  可迭代<OverlayEntry> get _allRouteOverlayEntries sync* {
    for (final _RouteEntry entry in _history)
      yield* entry.route.overlayEntries;
  }
 
 Overlay(
   key: _overlayKey,
    initialEntries: overlay == null ?  _allRouteOverlayEntries.toList(growable: false) : const <OverlayEntry>[],
   ),
Copy the code

RouteTransitionRecord / _RouteEntry

The latter inherits the former and records the status of each route

RouteTransitionRecord has the following properties and methods, they are TransitionDelegate. Set in the resolve method

IsWaitingForEnteringDecision routing is waiting to enter the screen isWaitingForExitingDecision marking routing is waiting to leave the screen

methods In and out animation Returns the parameter
markForPush() Into the There are N/A
markForAdd() Into the There is no N/A
markForPop([dynamic result]) Out of the There are There are
markForComplete([dynamic result]) Out of the There is no There are
markForRemove() Out of the There is no There is no

TransitionDelegate / DefaultTransitionDelegate

Set in the RouteTransitionRecord in TransitionDelegate. Resolve.

1. When the Pages change triggered update, perform the NavigatorState. DidUpdateWidget 2. In this method, the old Pages and the new Pages are compared. 3. Resolve determines which Pages are added and which are removed.

DefaultTransitionDelegate about logic in the below

New Pages The old Pages state
A=>B A=>B=>C C markForPop
A=>C A=>B=>C B markForComplete
A=>B=>C=>D A=>B=>C D markForPush
A=>B=>D=>C A=>B=>C D markForAdd

NavigatorObserver

Monitor push, POP, replace, remove routes, and swipe left to exit on ios. This is usually where we can make page entry and exit burials, as well as resolve conflicts between the Flutter and native ios swipe left exit in mixed development.

class NavigatorObserver {
  NavigatorState get navigator => _navigator;
  NavigatorState _navigator;
  
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) { }

  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }

  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) { }

  void didReplace({ Route<dynamic> newRoute, Route<dynamic> oldRoute }) { }

  void didStartUserGesture(Route<dynamic> route, Route<dynamic> previousRoute) { }

  void didStopUserGesture() { }
}
Copy the code

The Navigator 1.0

It is recommended to use named routes and manage routes uniformly in the onGenerateRoute callback. The French routing annotations are also based on Navigator 1.0.

  • Annotations for Flutter routing
  • Annotated route 2.0 by Flutter method
  • Annotated route 4.0 by the Flutter method

The Navigator 2.0

Let’s take a look at some of the changes in the Navigator’s latest build.

  const Navigator({
    Key key,
    // New in Navigator 2.0, which indirectly exposes the routing stack to users
    this.pages = const <Page<dynamic> > [].// A callback when using code pop or pressing the browser back button, at which point the user can handle the logic themselves
    this.onPopPage,
    this.initialRoute,
    this.onGenerateInitialRoutes = Navigator.defaultGenerateInitialRoutes,
    this.onGenerateRoute,
    this.onUnknownRoute,
    Since Pages is exposed to the user, you can set the page's TransitionDelegate state yourself, as shown in the [TransitionDelegate] section above. In general, just use the default
    this.transitionDelegate = const DefaultTransitionDelegate<dynamic> (),// Whether to notify the engine, mainly on the Web, of URL changes to the browser, synchronization address
    this.reportsRouteUpdateToEngine = false.this.observers = const <NavigatorObserver>[],
  }) 
Copy the code

The Navigator chestnuts

  1. Let’s get readyPage, simple implementationcreateRoutemethods
class MyPage extends Page<void> {
  const MyPage({
    @required LocalKey key,
    @required String name,
    @required this.widget,
    Object arguments,
  }) : super(
          key: key,
          name: name,
          arguments: arguments,
        );

  final Widget widget;
  @override
  Route<void> createRoute(BuildContext context) {
    return MaterialPageRoute<void>(
      settings: this, builder: (BuildContext context) => widget, ); }}Copy the code
  1. To preparepagesThis is our routing stack, initializing a MainPage. Note that the key must be a unique key.
  final List<MyPage> _pages = <MyPage>[
    MyPage(
        name: 'MainPage', widget: const TestPage('MainPage'), key: UniqueKey()),
  ];
Copy the code
  1. With Navigator, it’s worth noting
  • pagesIt should be a new set, like thisNavigatorState.didUpdateWidgetWill judge the difference, and update
  • onPopPageCallbacks can operate on their own, and then finallysetStateTo informpagesChange. By calling thenavigatorKey.currentState.pop()Either clicking the Appbar back button triggers the callback
    Navigator(
      reportsRouteUpdateToEngine: true,
      key: navigatorKey,
      pages: _pages.toList(),
      onPopPage: (Route<dynamic> route, dynamic result) {
        if (_pages.length > 1) {
          _pages.removeLast();
          setState(() {});
          return route.didPop(result);
        }
        return false; },),Copy the code
  1. Now you can manipulate the routing stack in any way you want. Here are a few examples.

Add a new page, equivalent to a push

    _pages.add(
      MyPage(
          name: 'MainPageA',
          widget: const TestPage('MainPageA'),
          key: UniqueKey()),
    );
    setState(() {});
Copy the code

Remove the last one, equivalent to pop

    if (_pages.length > 1) {
      _pages.removeLast();
      setState(() {});
    }
Copy the code

Trigger the onPopPage callback directly using the Navigatorstate.pop () method

    navigatorKey.currentState.pop();    
Copy the code
  • Full Demo address

Now that it looks like we can control the entire routing stack perfectly, isn’t that enough? The answer is definitely not enough, and we haven’t dealt with browser input to modify urls, browser back keys, Android physical back keys, and Navigator nesting.

Router

What’s new in Navigator 2.0 is, at first glance, all new.

  const Router({
    Key key,
    this.routeInformationProvider,
    this.routeInformationParser,
    @required this.routerDelegate,
    this.backButtonDispatcher,
  })
Copy the code

RouteInformation

There are two scenarios:

  1. RouteInformationProvider => Router, which occurs when a new route is available, such as entering a new URL in the browser or initializing the route in code Settings.
  2. The Router = > RouteInformationProvider, that only happens in the notification engine change the browser URL.
class RouteInformation {

  const RouteInformation({this.location, this.state});
  /// Such as:` / `.`/path`.`/path/to/the/app`.
  final String location;

  /// The current page state, such as scroll position, must be serializable.
  final Object state;
}
Copy the code

RouteInformationParser

I was responsible for parsing RouteInformation, where T was usually String or RouteSettings, which was convenient for us to resolve.

abstract class RouteInformationParser<T> {

  const RouteInformationParser();
  /// Enter a new URL in the browser or initialize the route in the code Settings
  Future<T> parseRouteInformation(RouteInformation routeInformation);
  /// Note if reportsRouteUpdateToEngine set to true, this must be implemented, can not return null.
  /// T from the incoming RouterDelegate currentConfiguration gain
  RouteInformation restoreRouteInformation(T configuration) => null;
}
Copy the code

RouteInformationProvider

Mainly responsible for notifying RouteInformation changes

abstract class RouteInformationProvider extends ValueListenable<RouteInformation> {
  void routerReportsNewRouteInformation(RouteInformation routeInformation) {}
}
Copy the code

It is often used to initialize routes

routeInformationProvider: PlatformRouteInformationProvider(
  initialRouteInformation: const RouteInformation(
    location: '/mainpage',),),Copy the code

RouterDelegate

Create and configure the proxy for the Navigator, much like the Navigator demo you wrote earlier. The difference is added to the browser input to modify the URL, browser back key, Android physical back key processing.

abstract class RouterDelegate<T> extends Listenable {
  
  /// Initializing the route calls this method
  Future<void> setInitialRoutePath(T configuration) {

​    return setNewRoutePath(configuration);

  }
  
  /// Add routes such as entering a new URL in the browser or initializing routes in code Settings
  Future<void> setNewRoutePath(T configuration);


  /// 'Browser back key'.'Android physics back key'The method is called
  Future<bool> popRoute();
  
  /// RouteInformationParser restoreRouteInformation will
  /// Get this value for reporting to the engine, especially in Web applications
  T get currentConfiguration => null;
  
  /// Returns the Navigator
  Widget build(BuildContext context);

}

Copy the code

PopNavigatorRouterDelegateMixin

Implements popRoute methods in RouterDelegate, not required.

mixin PopNavigatorRouterDelegateMixin<T> on RouterDelegate<T> {
  /// The key used for retrieving the current navigator.
  ///
  /// When using this mixin, be sure to use this key to create the navigator.
  GlobalKey<NavigatorState> get navigatorKey;

  @override
  Future<bool> popRoute() {
    finalNavigatorState navigator = navigatorKey? .currentState;if (navigator == null)
      return SynchronousFuture<bool> (false);
    returnnavigator.maybePop(); }}Copy the code

Source code analysis

I’ve just said which APIS solve which problems, but let’s track how the official implementation works. I’ll type in a new URL in the browser.

  • From the last step on the diagram, it is clear that this is a native approach from the engine

  • By _handleNavigationInvocation method, pop and push here, here is the receiving to notification of the engine including browser input, the browser and the android back button click physics.

  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
        return handlePushRoute(methodCall.arguments as String);
      case 'pushRouteInformation':
        return _handlePushRouteInformation(methodCall.arguments as Map<dynamic.dynamic>);
    }
    return Future<dynamic>.value();
  }
  
Copy the code
  • In the method, give the registered onesWidgetsBindingObserverDistribute the event, and return when it is found and processed. (Embedded nesting hereNavigatorThe pit of)
  Future<void> handlePushRoute(String route) async {
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
      if (await observer.didPushRoute(route))
        return;
    }
  }
  
 Future<void> handlePopRoute() async {
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
      if (await observer.didPopRoute())
        return;
    }
    SystemNavigator.pop();
  }
  
  Future<void> _handlePushRouteInformation(Map<dynamic.dynamic> routeArguments) async {
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
      if (
        await observer.didPushRouteInformation(
          RouteInformation(
            location: routeArguments['location'] as String,
            state: routeArguments['state'] as Object))),return; }}Copy the code
  • WidgetsApp inherits WidgetsBindingObserver inWidgetsBinding.instance.addObserver(this)Add yourself to the list_observersinside
class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
  }
  
    @override
  Future<bool> didPushRoute(String route) async{}Copy the code
  • whilePlatformRouteInformationProviderAdd itself to addListener during Router initialization_observersIn the.
class _RouterState<T> extends State<Router<T>> {
  @override
  void initState() {
    super.initState(); widget.routeInformationProvider? .addListener(_handleRouteInformationProviderNotification);Copy the code
class PlatformRouteInformationProvider extends RouteInformationProvider with WidgetsBindingObserver.ChangeNotifier {
  @override
  void addListener(VoidCallback listener) {
    if(! hasListeners) WidgetsBinding.instance.addObserver(this);
    super.addListener(listener);
  }
Copy the code

Finally, we can get the route changes that the engine tells us!

BackButtonDispatcher

includingRootBackButtonDispatcherChildBackButtonDispatcher. Mainly for the purpose of solvingThe Navigator nestedTo return to the button priority question.

Here’s an example:

1.MyApp is a Navigator. The initial page is NestedMainPage. Now the first two pages in the Navigator (NestedMainPage ChildRouterPage)

2.ChildRouterPage is also a Navigator. The initial page is NestedTestPage. There are now two pages in the first Navigator (NestedTestPage,TestPageA)

3. Now we can see TestPageA, so what happens when you press the Android physical back button or the Browser Back button?

4. The page returns to the NestedMainPage, which must not be the desired result?

So how do we solve this problem? We know that we have a way to monitor in front Android physical return key Browser or the return key, namely _handleNavigationInvocation popRoute callback in the method, But the first popable Navigator (remember the pit when it was distributed) was processed first. We can actually decide for ourselves which Navigator the popRoute callback applies to. We just need to do the following on the second Router.

Get the backButtonDispatcher from the previous Router and get the priority.

 Widget build(BuildContext context) {
    final ChildBackButtonDispatcher childBackButtonDispatcher =
        Router.of(context)
            .backButtonDispatcher
            .createChildBackButtonDispatcher();
    childBackButtonDispatcher.takePriority();
    return Router<RouteSettings>(
      backButtonDispatcher: childBackButtonDispatcher,
    );
  }
Copy the code

Source code analysis:

  • Something very familiarWidgetsBindingObserver.addCallbackAdd yourself to the monitor_observers, wait for the engine to deliver the event,WidgetsBindingIn the distribution.
class RootBackButtonDispatcher extends BackButtonDispatcher with WidgetsBindingObserver {
  RootBackButtonDispatcher();

  @override
  void addCallback(ValueGetter<Future<bool>> callback) {
    if(! hasCallbacks) WidgetsBinding.instance.addObserver(this);
    super.addCallback(callback);
  }

  @override
  void removeCallback(ValueGetter<Future<bool>> callback) {
    super.removeCallback(callback);
    if(! hasCallbacks) WidgetsBinding.instance.removeObserver(this);
  }

  @override
  Future<bool> didPopRoute() => invokeCallback(Future<bool>.value(false));
}
Copy the code
  • In the calltakePriorityMethod when talking about yourself add toparentchildrenMake sure you’re the last one and have emptied yourschildren.
class ChildBackButtonDispatcher extends BackButtonDispatcher {
  ChildBackButtonDispatcher(this.parent) : assert(parent ! =null);
  final BackButtonDispatcher parent;
  @protected
  Future<bool> notifiedByParent(Future<bool> defaultValue) {
    return invokeCallback(defaultValue);
  }

  @override
  void takePriority() {
    parent.deferTo(this);
    super.takePriority();
  }
  
  /// BackButtonDispatcher implementation, easy to explain
  void deferTo(ChildBackButtonDispatcher child) {
    assert(hasCallbacks); _children ?? = <ChildBackButtonDispatcher>{}as LinkedHashSet<ChildBackButtonDispatcher>;
    _children.remove(child); // child may or may not be in the set already
    _children.add(child);
  }
  
  /// BackButtonDispatcher implementation, easy to explain
  void takePriority() {
    if(_children ! =null) _children.clear(); }}Copy the code
  • ininvokeCallbackMethods,childrenThe last one starts traversal (that’s why indeferToMethods first,removeAfter,add), depending on who handles itdidPopRouteEvent, stop if handled.
  Future<bool> invokeCallback(Future<bool> defaultValue) {
    if(_children ! =null && _children.isNotEmpty) {
      final List<ChildBackButtonDispatcher> children = _children.toList();
      int childIndex = children.length - 1;

      Future<bool> notifyNextChild(bool result) {
        // If the previous child handles the callback, we returns the result.
        if (result)
          return SynchronousFuture<bool>(result);
        // If the previous child did not handle the callback, we ask the next
        // child to handle the it.
        if (childIndex > 0) {
          childIndex -= 1;
          return children[childIndex]
            .notifiedByParent(defaultValue)
            .then<bool>(notifyNextChild);
        }
        // If none of the child handles the callback, the parent will then handle it.
        return super.invokeCallback(defaultValue);
      }

      return children[childIndex]
        .notifiedByParent(defaultValue)
        .then<bool>(notifyNextChild);
    }
    return super.invokeCallback(defaultValue);
  }
Copy the code
  • Because there is a listener for BackButtonDispatcher added to the Router (location in the source code), routerDelegate.popRoute will eventually be notified.

  • Full Demo address

The Navigator 2.0 summarizes

  • Based on theNavigator.pagesTo achieve complete control of the routing stack.
  • throughRouterAnd relatedApiTo solveBrowser input modifies the URL.Browser back key.Android Physics back keyIssues with native interaction, and rightNavigatorProxy and configuration.
  • throughBackButtonDispatcherDeal with theThe Navigator nestedThe problem.

Navigator 2.0 looks perfect, but there are some downsides.

  • It’s a bit much to achieve, isn’t itduangCan it be used immediately?
  • Manual input parameter parsing problem in Web browser
  • Due to theRoutePagecreateRouteMethod, which we cannot access directlyRoute. If we were to write something like thispushWhat about methods with callback arguments?
  Future<T> push<T extends Object>(Route<T> route) {
    _history.add(_RouteEntry(route, initialState: _RouteLifecycle.push));
    _flushHistoryUpdates();
    _afterNavigation(route);
    return route.popped;
  }
Copy the code

Well, yes, French routing Annotation 5.0 already supports Navigator 1.0 and 2.0 perfectly, scatters!

French routing note 5.0

Increase the reference

Add the reference to Dependencies and the project/packages you need to annotate to pubspec.yaml

dev_dependencies:
  ff_annotation_route_core: any
  ff_annotation_route_library: any
Copy the code

Execute the flutter Packages get download

Add annotations

Void structure

import 'package:ff_annotation_route/ff_annotation_route.dart';

@FFRoute(
  name: "fluttercandies://mainpage",
  routeName: "MainPage".)class MainPage extends StatelessWidget
{
  // ...
}

Copy the code

Parametric structure

The tool handles constructs with parameters automatically, no special processing is required. The only thing to note is that you need to use argumentImports to provide import addresses for class/enum arguments. Now you can use @ffArgumentimPort () instead

@FFArgumentImport('hide TestMode2')
import 'package:example1/src/model/test_model.dart';
@FFArgumentImport(a)import 'package:example1/src/model/test_model1.dart' hide TestMode3;
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';

@FFRoute(
  name: 'flutterCandies://testPageE',
  routeName: 'testPageE',
  description: 'Show how to push new page with arguments(class)'.// argumentImports are left in case @ffArgumentimPort () cannot be fully expressed.
  // argumentImports: <String>[
  // 'import \'package:example1/src/model/test_model.dart\'; ',
  // 'import \'package:example1/src/model/test_model1.dart\'; ',
  / /,
  exts: <String.dynamic> {'group': 'Complex'.'order': 1,},)class TestPageE extends StatelessWidget {
  const TestPageE({
    this.testMode = const TestMode(
      id: 2,
      isTest: false,),this.testMode1,
  });
  factory TestPageE.deafult() => TestPageE(
        testMode: TestMode.deafult(),
      );

  factory TestPageE.required({@required TestMode testMode}) => TestPageE(
        testMode: testMode,
      );

  final TestMode testMode;
  final TestMode1 testMode1;
}
Copy the code

FFRoute

Parameter Description Default
name Route name (e.g., “/ Settings “) required
showStatusBar Whether to display the status bar true
routeName The name of the page used for burying data ‘ ‘
pageRouteType Material, Cupertino, transparent route Type
description Description of routes ‘ ‘
exts Other extended parameters.
argumentImports Import of certain parameters. Some arguments are classes or enumerations, and you need to specify their import address. You can now use @ffArgumentimPort () instead

Generate the file

The environment

Add dart’s bin PATH to your system $PATH.

cache\dart-sdk\bin

For more information

If not, look at the Nuggets

The activation

pub global activate ff_annotation_route

Execute the command

Go to your project root directory and execute it.

ff_route <command> [arguments]

The command parameter

Available commands:

-h, --[no-]help Indicates the help information. -p, --path Command directory. The default directory is the current one. -o, --output route Output path of the helper files relative to the lib folder of the main project. -n, --name Specifies the name of the routing constant class. The default value is`Routes`. -g, --git scans git references for packages. You need to specify the name of the package`, `separate--routes-file-output routes Specifies the path to the output directory of the file. Path relative to main project lib folder --const-ignore ignores some const(not all const are expected to be generated) with regular expressions --[no-] Route-constants Whether 'xxx_route.dart' is in the root project Generate static constants for all routes --[no-]package Whether this is a package --[no-] clades-arguments Whether to generate route parameter help classes-s, --[no-]save Specifies whether to save the command locally. If you save it, you just need to execute it next time`ff_route`That's it.Copy the code

Note the Navigator 1.0

The complete code is in Example

Main.dart

import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/material.dart';
import 'example_route.dart';
import 'example_routes.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ff_annotation_route demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: Routes.fluttercandiesMainpage,
      onGenerateRoute: (RouteSettings settings) {
        return onGenerateRoute(
          settings: settings,
          getRouteSettings: getRouteSettings,
          routeSettingsWrapper: (FFRouteSettings ffRouteSettings) {
            if (ffRouteSettings.name == Routes.fluttercandiesMainpage ||
                ffRouteSettings.name ==
                    Routes.fluttercandiesDemogrouppage.name) {
              return ffRouteSettings;
            }
            returnffRouteSettings.copyWith( widget: CommonWidget( child: ffRouteSettings.widget, title: ffRouteSettings.routeName, )); }); }); }}Copy the code

Push

Push name
  Navigator.pushNamed(context, Routes.fluttercandiesMainpage /* fluttercandies://mainpage */);
Copy the code
Push name with arguments
  • The parameter must be oneMap<String, dynamic>
  Navigator.pushNamed(
    context,
    Routes.flutterCandiesTestPageE,
    arguments: <String.dynamic>{
      constructorName: 'required'.'testMode': const TestMode(
        id: 100,
        isTest: true,)});Copy the code
  • Open – supper – the arguments
  Navigator.pushNamed(
    context,
    Routes.flutterCandiesTestPageE.name,
    arguments: Routes.flutterCandiesTestPageE.requiredC(
      testMode: const TestMode(
        id: 100,
        isTest: true,),),);Copy the code

Note the Navigator 2.0

The complete code is in example1

Main.dart

import 'dart:convert';
import 'package:example1/src/model/test_model.dart';
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'example1_route.dart';
import 'example1_routes.dart';

void main() {
  // The tool will handle simple types, but not all
  // For example, enter the following address in a browser
  // http://localhost:64916/#flutterCandies://testPageF? List = (4 and 6) & map = {" DDD ": 123} & testMode = {" id" : 2, "isTest" : true}
  // queryParameters will convert to your own type based on your situation
  FFConvert.convert = <T>(dynamic value) {
    if (value == null) {
      return null;
    }
    print(T);
    final dynamic output = json.decode(value.toString());
    if (<int> []is T && output is List<dynamic>) {
      return output.map<int> ((dynamic e) => asT<int>(e)).toList() as T;
    } else if (<String.String> {}is T && output is Map<dynamic.dynamic>) {
      return output.map<String.String> ((dynamic key, dynamic value) =>
          MapEntry<String.String>(key.toString(), value.toString())) as T;
    } else if (const TestMode() is T && output is Map<dynamic.dynamic>) {
      return TestMode.fromJson(output) as T;
    }

    return json.decode(value.toString()) as T;
  };
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final FFRouteInformationParser _ffRouteInformationParser =
      FFRouteInformationParser();

  final FFRouterDelegate _ffRouterDelegate = FFRouterDelegate(
    getRouteSettings: getRouteSettings,
    pageWrapper: <T>(FFPage<T> ffPage) {
      returnffPage.copyWith( widget: ffPage.name == Routes.fluttercandiesMainpage || ffPage.name == Routes.fluttercandiesDemogrouppage.name ? ffPage.widget :  CommonWidget( child: ffPage.widget, routeName: ffPage.routeName, ), ); });// This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'ff_annotation_route demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // Initialize the first page
      routeInformationProvider: PlatformRouteInformationProvider(
        initialRouteInformation: constRouteInformation( location: Routes.fluttercandiesMainpage, ), ), routeInformationParser: _ffRouteInformationParser, routerDelegate: _ffRouterDelegate, ); }}Copy the code

FFRouteInformationParser

Primarily used on the Web, routing tings are translated into RouteSettings when you type them into the browser, or RouteSettings when fed back to the browser

Here’s an example:

xxx? a=1&b=2 <=> RouteSettings(name:’xxx’,arguments:

{‘a’:’1′,’b’:’2′})
,>

FFRouterDelegate

Delegate for creating and configuring navigation, which provides similar methods in Navigator.

  FFRouterDelegate.of(context).pushNamed<void>(
    Routes.flutterCandiesTestPageF.name,
    arguments: Routes.flutterCandiesTestPageF.d(
      <int> [1.2.3],
      map: <String.String> {'ddd': 'dddd'},
      testMode: const TestMode(id: 1, isTest: true),),);Copy the code

You can find more examples on the test_page_c.dart page

Push

Push name
  FFRouterDelegate.of(context).pushNamed<void>(
    Routes.flutterCandiesTestPageA,
  );
Copy the code
Push name with arguments
  • The parameter must be oneMap<String, dynamic>
  FFRouterDelegate.of(context).pushNamed<void>(
    Routes.flutterCandiesTestPageF.name,
    arguments: Routes.flutterCandiesTestPageF.d(
      <int> [1.2.3],
      map: <String.String> {'ddd': 'dddd'},
      testMode: const TestMode(id: 1, isTest: true),),);Copy the code
  • Open – supper – the arguments
  FFRouterDelegate.of(context).pushNamed<void>(
    Routes.flutterCandiesTestPageF.name,
    arguments: <String.dynamic> {'list': <int> [1.2.3].'map': <String.String> {'ddd': 'dddd'},
        'testMode': const TestMode(id: 1, isTest: true})),Copy the code

Code Hints

You can do this using routing ‘Routes. FlutterCandiesTestPageE’, and see the code in the editor. Includes page description, structure, parameter type, parameter name, parameter mandatory.

  • The default
  /// 'This is test page E.'
  ///
  /// [name] : 'flutterCandies://testPageE'
  ///
  /// [routeName] : 'testPageE'
  ///
  /// [description] : 'This is test page E.'
  ///
  /// [constructors] :
  ///
  /// TestPageE : [TestMode testMode, TestMode1 testMode1]
  ///
  /// TestPageE.deafult : []
  ///
  /// TestPageE.required : [TestMode(required) testMode]
  ///
  /// [exts] : {group: Complex, order: 1}
  static const String flutterCandiesTestPageE = 'flutterCandies://testPageE';
Copy the code
  • Open – supper – the arguments
  /// 'This is test page E.'
  ///
  /// [name] : 'flutterCandies://testPageE'
  ///
  /// [routeName] : 'testPageE'
  ///
  /// [description] : 'This is test page E.'
  ///
  /// [constructors] :
  ///
  /// TestPageE : [TestMode testMode, TestMode1 testMode1]
  ///
  /// TestPageE.test : []
  ///
  /// TestPageE.requiredC : [TestMode(required) testMode]
  ///
  /// [exts] : {group: Complex, order: 1}
  static const _FlutterCandiesTestPageE flutterCandiesTestPageE =
      _FlutterCandiesTestPageE();

  class _FlutterCandiesTestPageE {
    const _FlutterCandiesTestPageE();

    String get name => 'flutterCandies://testPageE';

    Map<String.dynamic> d(
            {TestMode testMode = const TestMode(id: 2, isTest: false),
            TestMode1 testMode1}) =>
        <String.dynamic> {'testMode': testMode,
          'testMode1': testMode1,
        };

    Map<String.dynamic> test() => const <String.dynamic> {'constructorName': 'test'};Map<String.dynamic> requiredC({@required TestMode testMode}) =>
        <String.dynamic> {'testMode': testMode,
          'constructorName': 'requiredC'};@override
    String toString() => name;
  }

Copy the code

conclusion

  • Routing IIn life, there are many crossroads and choices for us to make. Choice is greater than effort, sometimes think there is only one way, in fact, there can be another choice. Sometimes when you think there’s another option, there’s only one. Standing at the crossroads of life, I hope we will no longer hesitate, whether I am now, or you in the future, do not want to know after the wake.
  • doFlutterIt’s been two years since I started my study alone and now I have a lot of friends to talk about,FlutterLet me learn a lot of things, but also meet more people. I wonder how long programmers can writecodeBut from chongqing light rail to do communication projects to switch to writing code, is really because of love. I think a person can do what he likes, should also be a kind of happiness.
  • Thank you for reading this and I hope this article has been helpful. Welcome to Join Flutter Candies, make lovely Flutter Candies. And finally, put Flutter Candies on it. It smells sweet.