美文网首页
法法路由 4.0

法法路由 4.0

作者: 法的空间 | 来源:发表于2020-09-15 18:01 被阅读0次

前言

法法路由发布1年多了,总感觉有一些弊端,或者说用起来不舒服的地方。

  • 之前是利用命名参数,通过路由的 arguments (Map<String, dynamic>),利用 key 设置对应的命名参数。需要去注解参数的名字,如下图。
import 'package:ff_annotation_route/ff_annotation_route.dart';

@FFRoute(
  name: "fluttercandies://picswiper",
  routeName: "PicSwiper",
  argumentNames: ["index", "pics"],
  showStatusBar: false,
  pageRouteType: PageRouteType.transparent,
)
class PicSwiper extends StatefulWidget {
  final int index;
  final List<PicSwiperItem> pics;
  PicSwiper({this.index, this.pics});
  // ...
}

  • 因为是命名参数,参数也可能是可选非必填。 但是生成的代码中,我不知道你有没有传递这个参数。但是根据注解生成的代码依然会从 arguments 中去试图拿到参数对应的值,这就会造成这个参数被设置成 null。所以在平时使用的时候,就必须在 pushName 的时候将可选参数也一起传递才行。

  • 如果一个页面有多个构造,那怎么办呢?是否可以选择使用哪个构造呢?之前是不支持的。

  • 构造如果是 const 的,之前是没法在生成的代码中也创建 const 的构造。

4.0

4.0 版本中,我解决了上面4个问题。

  • 通过对 analyzer, ast 更多的了解,通过 ast 我们可以直接拿到 ConstructorDeclaration ,它是对构造的定义. 代码地址 analyzer/lib/dart/ast/ast.dart , 下面是主要用的一些参数。
abstract class ConstructorDeclaration implements ClassMember {

  /// Return the token for the 'const' keyword, or `null` if the constructor is
  /// not a const constructor.
  /// 构造是否是 const
  Token get constKeyword;

  /// Set the token for the 'const' keyword to the given [token].
  set constKeyword(Token token);

  /// Return the name of the constructor, or `null` if the constructor being
  /// declared is unnamed.
  /// 构造的名字,默认构造这个值为null
  /// factory TestPageE.deafult(), 这种的话,返回 deafult
  SimpleIdentifier get name;

  /// Set the name of the constructor to the given [identifier].
  set name(SimpleIdentifier identifier);

  /// Return the parameters associated with the constructor.
  /// 返回参数的集合
  FormalParameterList get parameters;

  /// Set the parameters associated with the constructor to the given list of
  /// [parameters].
  set parameters(FormalParameterList parameters);
}
abstract class FormalParameter implements AstNode {
  /// The 'covariant' keyword, or `null` if the keyword was not used.
  Token get covariantKeyword;

  /// Return the element representing this parameter, or `null` if this
  /// parameter has not been resolved.
  ParameterElement get declaredElement;

  /// Return the name of the parameter being declared.
  SimpleIdentifier get identifier;

  /// Return `true` if this parameter was declared with the 'const' modifier.
  bool get isConst;

  /// Return `true` if this parameter was declared with the 'final' modifier.
  ///
  /// Parameters that are declared with the 'const' modifier will return
  /// `false` even though they are implicitly final.
  bool get isFinal;

  /// Return `true` if this parameter is a named parameter.
  ///
  /// Named parameters can either be required or optional.
  bool get isNamed;

  /// Return `true` if this parameter is an optional parameter.
  ///
  /// Optional parameters can either be positional or named.
  bool get isOptional;

  /// Return `true` if this parameter is both an optional and named parameter.
  bool get isOptionalNamed;

  /// Return `true` if this parameter is both an optional and positional
  /// parameter.
  bool get isOptionalPositional;

  /// Return `true` if this parameter is a positional parameter.
  ///
  /// Positional parameters can either be required or optional.
  bool get isPositional;

  /// Return `true` if this parameter is a required parameter.
  ///
  /// Required parameters can either be positional or named.
  ///
  /// Note: this will return `false` for a named parameter that is annotated
  /// with the `@required` annotation.
  bool get isRequired;

  /// Return `true` if this parameter is both a required and named parameter.
  ///
  /// Note: this will return `false` for a named parameter that is annotated
  /// with the `@required` annotation.
  bool get isRequiredNamed;

  /// Return `true` if this parameter is both a required and positional
  /// parameter.
  bool get isRequiredPositional;

  /// Return the kind of this parameter.
  @deprecated
  ParameterKind get kind;

  /// Return the annotations associated with this parameter.
  NodeList<Annotation> get metadata;

  /// The 'required' keyword, or `null` if the keyword was not used.
  Token get requiredKeyword;
}
  • 上面4个问题白嫖官方,自己再把逻辑搞清楚就行了,ast 很强力。但是也发现一个问题,想判断下这个参数是否是 dart core 库的,因为如果是类或者枚举,我希望能有个提示,让用户设置 import 地址。

虽然可以扫描全部的 dart 文件,但是考虑到还有三方引用,所以没这样做。找到 DartType 参数,但是这个值一直为 null,最后用 dart:mirrors 反射做了,但是发现最后因为 Flutter 要引入法法路由,会报错,就放弃了。

话说,法法路由只是 dev_dependencies 中引用,为啥会影响 Flutter 的运行时呢?ff_annotation_route 中暴露了一些类给 Flutter 使用,所以说运行时 ff_annotation_route 也是参加编译了? 有大佬知道吗?

dev_dependencies:
  ff_annotation_route:
    path: ../

使用

增加引用

添加引用到dev_dependencies,及你需要注解的 project/packages 到pubspec.yaml

dev_dependencies:
  ff_annotation_route: latest-version

执行 flutter packages get 下载

添加注解

空构造

import 'package:ff_annotation_route/ff_annotation_route.dart';

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

带参数构造

工具会自动处理带参数的构造,不需要做特殊处理。唯一需要注意的是,你需要使用 argumentImports 为class/enum的参数提供 import 地址。

import 'package:ff_annotation_route/ff_annotation_route.dart';

@FFRoute(
  name: 'flutterCandies://testPageE',
  routeName: 'testPageE',
  description: 'This is test page E.',
  argumentImports: <String>[
    'import \'package:example/src/model/test_model.dart\';',
    'import \'package:example/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;
}

FFRoute

Parameter Description Default
name 路由的名字 (e.g., "/settings") required
showStatusBar 是否显示状态栏 true
routeName 用于埋点收集数据的页面名字 ''
pageRouteType 路由的类型 (material, cupertino, transparent) -
description 路由的描述 ''
exts 其他扩展参数. -
argumentImports 某些参数的导入.有一些参数是类或者枚举,需要指定它们的导入地址 -

生成文件

环境

添加 dart 的 bin 的路径到你的系统 $PATH.

cache\dart-sdk\bin

更多信息

不清楚的可以看掘金

激活

pub global activate ff_annotation_route

执行命令

到你的项目根目录下面执行.

ff_route <command> [arguments]

命令参数

可用的命令:

command name description
-h, --help 打印帮助信息.
-p, --path [arguments] 执行命令的目录,没有就是当前目录.
-rc, --route-constants 是否在根项目中的 xxx_route.dart 生成全部路由的静态常量
-rh, --route-helper 生成 xxx_route_helper.dart 来帮助你处理路由
-rn, --route-names 是否在根项目中的 xxx_route.dart 生成全部路由的名字
-s, --save 是否保存命令到本地,如果保存了,下一次就只需要执行ff_route就可以了
-na, --no-arguments FFRouteSettings 将没有 arguments 这个参数,这个是主要是为了适配 Flutter 低版本
-g, --git package1,package2 是否扫描 git 引用的 package,你需要指定 package 的名字
--package 这个是否是一个 package
--no-is-initial-route FFRouteSettings 将没有 isInitialRoute 这个参数,这个是主要是为了适配 Flutter 高版本
-o --output route和helper文件的输出目录路径,路径相对于主项目的lib文件夹
-rfo --routes-file-output routes 文件的输出目录路径,路径相对于主项目的lib文件夹

Main.dart

  • 如果运行的命令带有参数 --route-helper , FFNavigatorObserver/FFRouteSettings
    将会生成在 xxx_route_helper.dart 中,用于协助追踪页面和设置状态栏。

  • 如果运行的命令带有参数 --route-helperFFTransparentPageRoute 将会生成在
    xxx_route_helper.dart 中,可以使用它来 push 一个透明的 PageRoute

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) {
        //when refresh web, route will as following
        //   /
        //   /fluttercandies:
        //   /fluttercandies:/
        //   /fluttercandies://mainpage
        if (kIsWeb && settings.name.startsWith('/')) {
          return onGenerateRouteHelper(
            settings.copyWith(name: settings.name.replaceFirst('/', '')),
            notFoundFallback:
                getRouteResult(name: Routes.fluttercandiesMainpage).widget,
          );
        }
        return onGenerateRouteHelper(settings,
            builder: (Widget child, RouteResult result) {
          if (settings.name == Routes.fluttercandiesMainpage ||
              settings.name == Routes.fluttercandiesDemogrouppage) {
            return child;
          }
          return CommonWidget(
            child: child,
            result: result,
          );
        });
      },
    );
  }
}

Push

Push name

  Navigator.pushNamed(context, Routes.fluttercandiesMainpage /* fluttercandies://mainpage */);

Push name with arguments

参数必须是一个 Map<String, dynamic>

  Navigator.pushNamed(
    context,
    Routes.flutterCandiesTestPageE,
    arguments: <String, dynamic>{
      constructorName: 'required',
      'testMode': const TestMode(
        id: 100,
        isTest: true,
      ),
    },
  );

Code Hints

你能这样使用路由 'Routes.flutterCandiesTestPageE', 并且在编辑器中看到代码提示。
包括页面描述,构造,参数类型,参数名字,参数是否必填。

  /// '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';

结语

命名路由的另外一个问题就是参数不是强类型输入,对于使用者可能没有直接 new 构造器来的清晰舒服。但是我还是觉得不要再引入额外的参数类耦合代码,毕竟鱼和熊掌不可兼得。命名路由+注解能够有效的统一处理路由,还可以增加一些额外的辅助功能,还是挺香的。

欢迎加入Flutter Candies,一起生产可爱的Flutter小糖果( <a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5bcc0gy"><img border="0" src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20fb581e8fe5453892c6e9517c92e5ec~tplv-k3u1fbpfcp-zoom-1.image" alt="flutter-candies" title="flutter-candies"></a>QQ群:181398081)

最最后放上Flutter Candies全家桶,真香。

[图片上传失败...(image-76d78f-1600164063164)]

相关文章

  • 法法路由 4.0

    前言 法法路由发布1年多了,总感觉有一些弊端,或者说用起来不舒服的地方。 之前是利用命名参数,通过路由的 argu...

  • 2018-04-08

    路由算法 1.非自适应路由选择 固定路由 洪泛法等。 2.自适应路由选择 烫熟土豆法、分布式路由等

  • c#6.0nameof和4.0自定义方法使用

    6.0 4.0方法一: 方法二:

  • Flutter 法法路由注解

    最近改造项目,感觉Flutter路由这块需要打磨打磨,想起来之前好像听说过通过注解来生成路由映射的工具裤,嗯,很快...

  • Flutter 法法路由 10.0

    前言 我相信大家一定遇到过这种情况,创建新组件继承另一个组件,光写构造参数就要写半天,不相信的话,你去继承个 Te...

  • Flutter 路由我定

    相关阅读 基于 Navigator1.0 : Flutter 法法路由注解[https://juejin.cn/p...

  • Flutter 法法注解路由 2.0

    2.0的诞生 去年推出法法注解路由之后经(pian)过(lai)几个工具人维护,版本已经来到2.0,功能比较齐全稳...

  • 17、Vue 配置动态路由

    方式一(动态路由法): 步骤:1、配置动态路由(main.js)const routes=[{path:'/hom...

  • 作曲法(4.0-4.1)

    更多优秀的国外作曲编曲教程请戳这里哦:作曲编曲相关讲座资料翻译整理 原文地址:作曲法 第四章 4.0 小调的和弦与...

  • router4 ts动态添加路由

    今天研究route4.0 在vue3里面,使用ts的方式定义路由并且进行动态添加。route4.0在添加路由的方式...

网友评论

      本文标题:法法路由 4.0

      本文链接:https://www.haomeiwen.com/subject/fdwfyktx.html