Flutter利用注解生成可自定义的路由的实现

route_generator是什么

这是一个简单的 Flutter 路由生成库,只需要少量的代码,然后利用注解配合源代码生成,自动生成路由表,省去手工管理路由代码的烦恼。

特性

  • 自定义路由名称
  • 自定义路由动画
  • 自定义路由参数
  • 自定义路由逻辑

依赖

dependencies:
 # Your other regular dependencies here
 route_annotation: ^0.1.0

dev_dependencies:
 # Your other dev_dependencies here
 build_runner: ^1.5.0
 route_generator: ^0.1.2

生成代码

单次构建

在项目根目录中运行flutter pub run build_runner build,可以在需要时为项目生成路由代码。这会触发一次性构建,该构建遍历源文件,选择相关文件,并为它们生成必要的路由代码。虽然这很方便,但如果您不必每次在模型类中进行更改时都必须手动构建,那么你可以选择持续构建。

持续构建

在项目根目录中运行flutter pub run build_runner watch来启动watcher,它可以使我们的源代码生成过程更加方便。它会监视项目文件中的更改,并在需要时自动构建必要的文件。

route_annotation

annotation description
Router 此注解用来标志某个为 Flutter App 的类,并以此生成相应的路由代码
RoutePage 此注解用来注解一个路由页面
RouteParameter 一个用来标志页面参数的注解,只为可选参数设计。用于 RoutePage 。
RouteField 此注解用来标志一个完全自定义的路由,被注解的对象必须作为路由页面类静态字段
PageRouteBuilderFuntcion 这个注解用来标识一个路由页面的 RouteFactory 静态方法
RoutePageBuilderFunction 这个注解用来标识一个路由页面的 RoutePageBuilder静态方法
RouteTransitionBuilderFunction 这个注解用来标识一个路由页面的 TransitionBuilder 静态方法
RouteTransitionDurationField 这个注解用来标识一个自定义路由页面的过渡时长

代码示例

定义路由 App

@Router()
class DemoApp extends StatefulWidget {
 @override
 _DemoAppState createState() => _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
 @override
 Widget build(BuildContext context) {
  return MaterialApp(
   initialRoute: "/",
   onGenerateRoute: onGenerateRoute,
  );
 }
}

定义路由页面

// isInitialRoute为true表示它将作为initial page
@RoutePage(isInitialRoute: true)
class HomePage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  return Scaffold();
 }
}

定义路由页面参数

对于单个参数

@RoutePage(params: [RouteParameter("title")])
class OneArgumentPage extends StatelessWidget {
 final String title;

 const OneArgumentPage({Key key, this.title}) : super(key: key);

 @override
 Widget build(BuildContext context) {
  return Container();
 }
}

导航

Navigator.of(context).pushNamed(
 ROUTE_ONE_ARGUMENT_PAGE,
 arguments: "title is empty",
);

注意事项:

对于单个参数的路由,利用Navigator进行导航的时候arguments即为原始参数。

对于多个参数

@RoutePage(params: [RouteParameter("title"), RouteParameter("subTitle")])
class TwoArgumentPage extends StatelessWidget {
 final String title;
 final String subTitle;

 TwoArgumentPage({this.title, Key key, this.subTitle}) : super(key: key);

 @override
 Widget build(BuildContext context) {
  return Scaffold();
 }
}

导航

Navigator.of(context).pushNamed(
 ROUTE_TWO_ARGUMENT_PAGE,
 arguments: {
  "title": _titleController.text.isNotEmpty
    ? _titleController.text
    : "title is empty",
  "subTitle": _subTitleController.text.isNotEmpty
    ? _subTitleController.text
    : "sub title is empty",
 },
);

注意事项:

对于多个参数的路由,利用Navigator进行导航的时候arguments必须为Map<string,dynamic>。

如果你不需要自定义路由,以下部分,你可以什么都不用添加,就让route_generator为你自动生成相关代码吧!

自定义路由(优先级:3)

这种方法自定义路由的优先级最高,如果同时存在多种自定义路由选择,该种方案最先被选择。

@RoutePage()
class CustomRoutePage extends StatelessWidget {
 @RouteField()
 static Map<String, RouteFactory> route = <String, RouteFactory>{
  'custom_route': (RouteSettings settings) =>
    MaterialPageRoute(builder: (BuildContext context) => CustomRoutePage()),
  'alias_route': (RouteSettings settings) => PageRouteBuilder(
     pageBuilder: (BuildContext context, Animation animation,
         Animation secondaryAnimation) =>
       CustomRoutePage(),
    ),
 };

 ...

}

它会生成如下代码:

Map<String, RouteFactory> _customRoutePage = CustomRoutePage.route;

自定义路由(优先级:2)

这种方法自定义路由的优先级较低,如果同时存在多种自定义路由选择,则按优先级从大到小选择。

@RoutePage()
class CustomRoutePage extends StatelessWidget {
 @PageRouteBuilderFuntcion()
 static Route buildPageRoute(RouteSettings settings) => PageRouteBuilder(
    pageBuilder: (BuildContext context, Animation animation,
        Animation secondaryAnimation) =>
      CustomRoutePage(),
   );

 ...

}

它会生成如下代码:

Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
 'custom_route_page': CustomRoutePage.buildPageRoute,
};

自定义路由(优先级:1)

这种方法自定义路由的优先级最低,如果同时存在多种自定义路由选择,则按优先级从大到小选择。

@RoutePage()
class CustomRoutePage extends StatelessWidget {
 // RoutePageBuilderFunction注解表明这个方法用来定义如何返回RoutePage
 // 它是可选的
 @RoutePageBuilderFunction()
 static Widget buildPage(BuildContext context, Animation animation,
     Animation secondaryAnimation, RouteSettings settings) =>
   CustomRoutePage();

 // RouteTransitionBuilderFunction注解表明这个方法用来定义如何应用动画过渡
 // 它是可选的
 @RouteTransitionBuilderFunction()
 static Widget buildTransitions(
     BuildContext context,
     Animation<double> animation,
     Animation<double> secondaryAnimation,
     Widget child,
     RouteSettings settings) =>
   child;

 // RouteTransitionDurationField注解表明这个字段用来定义页面过渡时常长,默认值为300 milliseconds
 // 它是可选的
 @RouteTransitionDurationField()
 static Duration transitionDuration = Duration(milliseconds: 400);

 ...

}

它会生成如下代码:

Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
 'custom_route_page': (RouteSettings settings) => PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) =>
      CustomRoutePage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) =>
      CustomRoutePage.buildTransitions(
        context, animation, secondaryAnimation, child, settings),
    transitionDuration: CustomRoutePage.transitionDuration,
   ),
};

注意事项

  • 只允许有一个initalRoute
  • initalRoute会忽略自定义路由名,但会生成名为ROUTE_HOME的路由名称常量。
  • 所有自定义路由method或getter必须定义在路由所在类,且必须为static所修饰的和非私有的。

最终生成代码

最终生成的文件名为FILENAME.route.dart

其中FILENAME是被Router注解的App类所在的文件名。

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// RouteGenerator
// **************************************************************************

import 'package:flutter/material.dart';
import 'home_page.dart';
import 'custom_route_page.dart';
import 'custom_route_name_page.dart';
import 'second_page.dart';
import 'one_arguement_page.dart';
import 'two_arguement_page.dart';

const ROUTE_HOME = '/';
const ROUTE_CUSTOM_ROUTE_PAGE = 'custom_route_page';
const ROUTE_CUSTOM = 'custom';
const ROUTE_SECOND_PAGE = 'second_page';
const ROUTE_ONE_ARGUMENT_PAGE = 'one_argument_page';
const ROUTE_TWO_ARGUMENT_PAGE = 'two_argument_page';

RouteFactory onGenerateRoute = (settings) => Map.fromEntries([
   ..._home.entries,
   ..._customRoutePage.entries,
   ..._custom.entries,
   ..._secondPage.entries,
   ..._oneArgumentPage.entries,
   ..._twoArgumentPage.entries,
  ])[settings.name](settings);

Map<String, RouteFactory> _home = <String, RouteFactory>{
 '/': (RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => HomePage(),
   ),
};
Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
 'custom_route_page': (RouteSettings settings) => PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) =>
      CustomRoutePage.buildPage(
        context, animation, secondaryAnimation, settings),
    transitionsBuilder: (context, animation, secondaryAnimation, child) =>
      CustomRoutePage.buildTransitions(
        context, animation, secondaryAnimation, child, settings),
    transitionDuration: CustomRoutePage.transitionDuration,
   ),
};
Map<String, RouteFactory> _custom = <String, RouteFactory>{
 'custom': (RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => CustomRoutePageName(),
   ),
};
Map<String, RouteFactory> _secondPage = <String, RouteFactory>{
 'second_page': (RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => SecondPage(),
   ),
};
Map<String, RouteFactory> _oneArgumentPage = <String, RouteFactory>{
 'one_argument_page': (RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) =>
      OneArgumentPage(title: settings.arguments),
   ),
};
Map<String, RouteFactory> _twoArgumentPage = <String, RouteFactory>{
 'two_argument_page': (RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => TwoArgumentPage(
       title: (settings.arguments as Map<String, dynamic>)['title'],
       subTitle:
         (settings.arguments as Map<String, dynamic>)['subTitle'],
      ),
   ),
};

常见问题

没有生成路由文件

请检查是否添加了Router注解

Example

获取更详细信息,请参阅example

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Flutter路由的跳转、动画和传参详解(最简单)

    路由 做Android/iOS原生开发的时候,要打开一个新的页面,你得知道你的目标页面对象,然后初始化一个Intent或者ViewController,再通过startActivity或者pushViewController来推出一个新的页面,不能跟web一样,直接丢一个链接地址就跳转到新的页面.当然,可以自己去加一个中间层来实现这些功能. Flutter里面是原生支持路由的.Flutter的framework提供了路由跳转的实现.我们可以直接使用这些功能. Flutter路由介绍 Flutte

  • Flutter 局部路由实现详解

    Flutter是借鉴React的开发思想实现的,在子组件的插槽上,React有this.props.children,Vue有<slot></slot>. 当然Flutter也有类似的Widget,那就是Navigator,不过是以router的形式实现(像<router-view></router-view>???). Navigator的使用无非3个属性 initialRoute: 初始路由 onGenerateRoute: 匹配路由 onUnknown

  • Flutter利用注解生成可自定义的路由的实现

    route_generator是什么 这是一个简单的 Flutter 路由生成库,只需要少量的代码,然后利用注解配合源代码生成,自动生成路由表,省去手工管理路由代码的烦恼. 特性 自定义路由名称 自定义路由动画 自定义路由参数 自定义路由逻辑 依赖 dependencies: # Your other regular dependencies here route_annotation: ^0.1.0 dev_dependencies: # Your other dev_dependencies

  • Flutter利用Hero组件实现自定义路径效果的动画

    目录 前言 Hero 的定义 RectTween 自定义RectTween 运行效果 总结 前言 我们在 页面切换转场动画,英雄救场更有趣!介绍了 Hero 动画效果,使用 Hero 用于转场能够提供非常不错的体验.既然称之为英雄,肯定还有其他技能,本篇我们就来探索一下 Hero 动画的返回效果. Hero 的定义 Hero 组件是一个 StatefulWidget,构造方法如下: const Hero({   Key? key,   required this.tag,   this.crea

  • SpringBoot利用注解来实现Redis分布式锁

    目录 一.业务背景 二.分析流程 加锁 超时问题 解决方案:增加一个「续时」 三.设计方案 四.实操 相关属性类配置 核心切面拦截的操作 五.开始测试 六.总结 一.业务背景 有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数据库的数据进行操作,需要避免对之前的业务造成影响. 二.分析流程 使用 Redis 作为分布式锁,将锁的状态放到 Redis 统一维护,解决集群中单机 JVM 信息不互通的问题,规定操作顺序,保护用户的数据正确. 梳理设计流程 新建注解 @interface

  • 基于Java注解(Annotation)的自定义注解入门介绍

    要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. -------------------------------------------------------------------------------- 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.Java5.0定义的元注解:

  • 利用PHP生成静态html页面的原理

    前言 如果每次用户点击动态链接的时候都会对服务器发送数据查询的要求,对于一个访问量可能达百万千万级别的网站来说 这无疑是服务器一个大大的负担,所以把动态数据转换成静态html页面就成了节省人力物力的首选因为此前没有相应的经验,所以这篇文章和大家分享利用PHP生成静态html页面的原理和方法,有需要的可以一起来看看. 一.思路分析 其实,实现静态页面生成功能的原理很简单,主要利用几个常见的PHP文件操作函数对文件进行操作,其思路流程如下: 生成静态页面流程图 二.功能实现 设置example.ht

  • Java注解Annotation与自定义注解详解

    一:Java注解简介 开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解. 下面列举开发中常见的注解 @Override:用于标识该方法继承自超类, 当父类的方法被删除或修改了,编译器会提示错误信息(我们最经常看到的toString()方法上总能看到这货) @Deprecated:表示该类或者该方法已经不推荐使用,已经过期了,如果用户还是要使用,会生成编译的警告 @SuppressWarnings:用于忽略的编译器警告信息

  • python2利用wxpython生成投影界面工具的图文详解

    本投影界面工具的功能: 准备好.prj投影文件,将输入文件夹内的WGS84经纬度坐标shp文件,投影为平面文件,成果自动命名为prj_***并新建在输入文件夹同一路径下. 下一步目标: 利用pyinstaller或其他打包库生成exe文件,目前停滞在python2语法.arcpy打包出错相关问题上. 参考文献: <Using Py2exe with Arcpy- It can be done easily!> <如何使用py2exe打包arcpy脚本?> GUI界面示意图 投影文件

  • Android利用zxing生成二维码的过程记录

    目录 二维码生成原理(即工作原理) 效果图如下: 前提: 源码如下: 总结 二维码生成原理(即工作原理) 二维码官方叫版本Version.Version 1是21 x 21的矩阵,Version 2是 25 x 25的矩阵,Version 3是29的尺寸,每增加一个version,就会增加4的尺寸,公式是:(V-1)*4 + 21(V是版本号) 最高Version 40,(40-1)*4+21 = 177,所以最高是177 x 177 的正方形. 下面是一个二维码的样例: 效果图如下: 前提:

  • Django利用LogEntry生成历史操作实战记录

    在开发测试平台的时候,虽然对某些关键功能做了权限设置,但毕竟是公司多人使用,有些数据的配置可能不小心被他人修改但未告知其他使用者,造成了诸多不便.所以决定开发一个操作历史表,可以方便查看数据地改动. LogEntry是在后台开发中经常用到的模块,它在admin是默认开启的. 可以使用LogEntry模块记录所有用户的操作记录.一方面可以用来监督,另一方面可以用来做回滚. 使用LogEntry ModelAdmin本身就有日志记录功能.当新建一个实体(Post.Category.Tag)时,Mod

  • 利用Python生成Excel炫酷图表

    目录 前言 1.xlswriter库 2.生成折线图 3.生成柱状图 4.生成饼图 5.实战拓展 前言 本文对Python 自动化操作Excel并生成图表,做了详细的分析和说明 我们先来看一下python中能操作Excel的常用库对比 1.xlswriter库 XlsxWriter是一个用来写Excel2007和xlsx文件格式的python模块.它可以用来写文本.数字.公式并支持单元格格式化.图片.图表.文档配置.自动过滤等特性 优点:功能更多.文档高保真.扩展格式类型.更快并可配置 缺点:不

随机推荐