Flutter利用注解生成可自定义的路由的实现
作者:microtears 发布时间:2022-08-28 15:12:16
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
来源:https://juejin.im/post/5d49b4556fb9a06b317b4ab1


猜你喜欢
- Android 自定义阴影效果详解及实例Android5.X中,Google为其增加了两个属性 android:elevation=” ”
- 前言不得不说SpringBoot的开发者是在为大众程序猿谋福利,把大家都惯成了懒汉,xml不配置了,连tomcat也懒的配置了,典型的一键启
- Feign多参数传递及注意的问题这边沿用前面的Eureka,Feign,Service在服务提供者cloud-shop-userservic
- 在Android中,使用摄像头拍照一般有两种方法, 一种是调用系统自带的Camera,另一种是自己写一个摄像的界面。
- 1、Android内存管理机制1.1 Java内存分配模型先上一张JVM将内存划分区域的图程序计数器:存储当前线程执行目标方法执行到第几行。
- 面向过程和面向对象的区别面向过程:当事件比较简单的时候,利用面向过程,注重的是事件的具体步骤和过程,注重的是过程中的具体行为,以函数为最小单
- Spring Boot文件上传与下载在实际的Web应用开发中,为了成功上传文件,必须将表单的method设置为post,并将enctype设
- HandlerThread 简介:我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了。
- Spring 配置文件报错:元素 "context:component-scan" 的前缀 "context&
- 1、使用场景 因为最近项目需要国际化,需要能够支持多种国际化语言,目前需要支持三种(法
- 1.user实体package com.demo.dto;public class User { private Integer
- 关于静态类型检查和动态类型检查的解释:静态类型检查:基于程序的源代码来验证类型安全的过程;动态类型检查:在程序运行期间验证类型安全的过程;J
- 本文实例为大家分享了DataGridView带图标的单元格实现具体代码,供大家参考,具体内容如下目的:扩展 C# WinForm 自带的表格
- 今天一上班 被github上的一篇名为《教你用python玩跳一跳》吸引,它的星也瞬间从3400涨到4400 原作者主要就是用py
- 很久没写文章了,一方面是最近几个月比较忙,没太多时间,另一方面是最近拖延症严重,写文章的想法总是一拖再拖。今天找一个小案例写一下,与懒惰对抗
- 背景知识同步、异步、阻塞、非阻塞首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下。同步:API调用返回时调用者就知道操作的结
- Mybatis映射文件mapper.xml的注释问题从昨天夜晚9点到今天中午,一直被项目bug所困惑,中间这段时间一直未解决这个问题,也咨询
- 如果你发现在一个接口使用有如下定义方法: public String[] getParameters();那么你应该认
- 配置详情pom.xmldependency> <groupId>com.baomidou<
- SpringBoot的持久化层可以是Spring内置的轻量级JdbcTemplate、也可以是Hibernate或Mybatis等等,只需要