flutter登录aop_Dart Aop 使用 source_gen 做 flutter 页面跳转 page router

code_gen

a apt tools provider some annotation to generate dart code ,like router make page jump simple

example

flutter中无法使用反射做hook,通常使用Aop比较多,基于builder_runner(Dart代码生成文件库)的 source_gen 可实现注解生成代码 类似 java Aop AbstractProcessor

目标:自动生成路由配置,页面带参跳转,参数获取

使用方法:

# pubspec.yaml 引入

dependencies:

# flutter 项目下 dependencies 引入 code_gen (使用 source_gen 编写的 aop lib)

code_gen:

git:

url: git://github.com/iloveq/code_gen.git

ref: main

dev_dependencies:

# flutter 项目下 dev_dependencies 引入 builder_runner

build_runner: ^1.10.0

1:创建 app_router_table.dart 工厂模式引用生成的类(实现了模版方法configureRoutes),通过 build_runner 会生成文件 app_router_table.table.dart

import 'package:code_gen/router_gen.dart';

import 'package:flutter/cupertino.dart';

import 'app_router_table.table.dart'; // 生成的文件

@RouterTable()

abstract class AppRouterTable {

factory AppRouterTable() = $AppRouterTable; // 生成的类

Map configureRoutes();

}

2:MyApp 配置路由表

Widget _buildMaterialApp() {

return MaterialApp(

debugShowCheckedModeBanner: false,

theme: ThemeData(

platform: TargetPlatform.iOS,

primarySwatch: Colors.blue,

),

routes: AppRouterTable().configureRoutes(),

initialRoute: "/",

);

}

3:注解标记需要注册的Page和参数Arg

import '../config/app_router_table.table.dart'; // 引入生成文件

// 主页

@RouterPage(isIndex: true)

class HomePage extends StatefulWidget {

HomePage({Key key}) : super(key: key);

@override

_HomePageState createState() => _HomePageState();

}

class _HomePageState extends State {

@override

Widget build(BuildContext context) {

return Center(

child: GestureDetector(

child: Text("home page"),

// 跳转(调用生成方法)

onTap: ()=> context.navigator2TestPage(url: "from home page")

),

);

}

}

...

// testPage

import '../config/app_router_table.table.dart';

@RouterPage() // 标明页面 Page

class TestPage extends StatefulWidget{

@RouterArg(required: true) // 标明参数 Arg

final String url = "";

TestPage({Key key}) : super(key: key);

@override

_TestPageState createState() => _TestPageState();

}

class _TestPageState extends State{

@override

Widget build(BuildContext context) {

return Center(

child: GestureDetector(

child: Text(context.getTestPageArguments().url), // 获取参数

onTap: ()=> context.navigator2MinePage(num:2000),

),

);

}

}

...

4:使用 flutter packages pub run build_runner build 生成文件 app_router_table.table.dart:

export 'app_router_table.table.dart';

import 'app_router_table.dart';

import 'package:flutter/cupertino.dart';

import 'package:flutter_app/pages/test_page.dart';

import 'package:flutter_app/pages/home_page.dart';

import 'package:flutter_app/pages/mine_page.dart';

class $AppRouterTable implements AppRouterTable {

@override

Map configureRoutes() {

return {

'/': (context) => HomePage(),

'TestPage': (context) => TestPage(),

'Mine': (context) => MinePage(),

};

}

}

// **************************************************************************

// TestPage

class TestPageArguments {

final String url;

TestPageArguments({@required this.url});

}

extension TestPageContext on BuildContext {

void navigator2TestPage({@required String url}) {

Navigator.pushNamed(this, "TestPage",

arguments: TestPageArguments(url: url));

}

TestPageArguments getTestPageArguments() {

return ModalRoute.of(this).settings.arguments;

}

}

// **************************************************************************

// **************************************************************************

// MinePage

class MinePageArguments {

final int num;

MinePageArguments({this.num});

}

extension MinePageContext on BuildContext {

void navigator2MinePage({int num}) {

Navigator.pushNamed(this, "Mine", arguments: MinePageArguments(num: num));

}

MinePageArguments getMinePageArguments() {

return ModalRoute.of(this).settings.arguments;

}

}

// **************************************************************************

那么如何创建一个Aop 功能的 lib ,以下是通过 source_gen 做的一个Aop 工具 :

我们先来看 page_router 的包结构:

├── README.md

├── build.yaml

├── lib

│ ├── builder.dart

│ ├── router_gen.dart

│ └── src

│ ├── annotation

│ │ ├── router_arg.dart

│ │ ├── router_page.dart

│ │ └── router_table.dart

│ ├── generator

│ │ ├── router_generator.dart

│ │ └── router_table_generator.dart

│ └── tools

│ └── router_collector.dart

├── pubspec.lock

└── pubspec.yaml

1: code_gen 文件夹下,创建 pubspec.yaml 文件 引入 builder_runner 和 source_gen 及 dart 配置,并运行 pub get

name: code_gen

description: auto generate router params

version: 0.0.1

author: haoran

homepage: 1549112908@qq.com

environment:

sdk: ">=2.1.0 <3.0.0"

dependencies:

analyzer: any

build: any

build_config: '>=0.3.0'

source_gen: ^0.9.7

dev_dependencies:

build_runner: ^1.10.0

2: 创建 build.yaml 配置注解生成器信息

targets:

$default:

builders:

# code_gen 工程下的 router_gen_builder(builder 名字随意,和下面对应就可以)

code_gen|router_gen_builder:

options: { 'write': true }

enabled: true

generate_for:

exclude: ['**.params.g.dart']

code_gen|router_table_gen_builder:

options: { 'write': true }

enabled: true

generate_for:

exclude: ['**.table.dart']

builders:

router_gen_builder:

import: "package:code_gen/builder.dart" # builder.dart 文件位置

builder_factories: ["generateRouterParams"] # 对应 build.dart 文件中的方法

build_extensions: {".dart": ['.params.g.dart']} # 生成文件后缀名

auto_apply: dependents

build_to: source

# runs_before 先于 router_table_gen_builder 执行

runs_before: ['code_gen|router_table_gen_builder']

router_table_gen_builder:

import: "package:code_gen/builder.dart"

builder_factories: ["generateRouterTable"]

build_extensions: {".dart": ['.table.dart']}

auto_apply: dependents

build_to: source

3:看到上面创建了 build.dart 文件,这个是类似于 java Aop 的 resource/META-INF.services 配置 Processor,相当于生成器的入口

// build.dart

import 'package:build/build.dart';

import 'package:source_gen/source_gen.dart';

import 'src/generator/router_generator.dart';

import 'src/generator/router_table_generator.dart';

Builder generateRouterParams(BuilderOptions options) =>

LibraryBuilder(RouterGenerator(), generatedExtension:'.params.g.dart');

Builder generateRouterTable(BuilderOptions options)=>

LibraryBuilder(RouterTableGenerator(), generatedExtension: '.table.dart');

4: 定义注解,创建注解生成器

// 定义注解:

class RouterTable{

const RouterTable();

}

class RouterPage {

final bool isIndex;

final String path;

const RouterPage({this.path = "",this.isIndex = false});

}

class RouterArg {

final bool required;

const RouterArg({this.required = false});

}

// 创建注解生成器:

// 1 RouterGenerator:

class RouterGenerator extends GeneratorForAnnotation {

static RouterCollector collector = RouterCollector();

@override

generateForAnnotatedElement(

Element element, ConstantReader annotation, BuildStep buildStep) {

print(element);

if (element.kind == ElementKind.CLASS) {

var importStr = "";

if (buildStep.inputId.path.contains('lib/')) {

importStr =

"package:${buildStep.inputId.package}/${buildStep.inputId.path.replaceFirst('lib/', '')}";

} else {

importStr = "${buildStep.inputId.path}";

}

collector.importList.add(importStr);

String className = element.name;

String aptRouterPath = annotation.read("path").stringValue;

String routerName = aptRouterPath != null && aptRouterPath.isNotEmpty

? aptRouterPath

: className;

var page = Page();

page.arguments = [];

for (FieldElement e in ((element as ClassElement).fields)) {

List fieldAnnotationList = e.metadata;

fieldAnnotationList.forEach((element) {

if (element.toString().startsWith("@RouterArg")) {

Argument argument = Argument();

argument.isRequired = element

.computeConstantValue()

.getField("required")

.toBoolValue();

argument.name = e.name;

print("arguments-field: ${e.toString()}");

String type_ = e.toString().split(" ")[0];

argument.type = type_.replaceAll("*", "");

page.arguments.add(argument);

print("arguments: ${argument.toString()}");

}

});

}

if (aptRouterPath == "/" || annotation.read("isIndex").boolValue) {

page.routerPath = "/";

page.name = className;

collector.indexRouter["/"] = page;

} else {

page.name = className;

page.routerPath = routerName;

collector.routerMap[routerName] = page;

}

}

return null;

}

}

// 2 RouterTableGenerator:

class RouterTableGenerator extends GeneratorForAnnotation {

@override

generateForAnnotatedElement(

Element element, ConstantReader annotation, BuildStep buildStep) {

if (element.kind == ElementKind.CLASS) {

String path = buildStep.inputId.path; // lib/xxx.dart

String relatedFileName = Path.basename(path); // xxx.dart

String relatedClassName = element.name;

return generateRouterTable(relatedFileName, relatedClassName);

}

return "class TestTable{}";

}

String generateRouterTable(String relatedFileName, String relatedClassName) {

String export = relatedFileName.split(".")[0] +

".table." +

relatedClassName.split(".")[1];

String imports = "";

String routerMap = "";

var ArgsAndNavigatorExtension = "";

for (String import in RouterGenerator.collector.importList) {

imports = imports + "import '" + import + "';\n";

}

RouterGenerator.collector.routerMap.forEach((key, value) {

routerMap = routerMap + "'${key}':( context ) => ${value.name}(),";

ArgsAndNavigatorExtension =

ArgsAndNavigatorExtension + _genArgsAndNavigatorExtension(value);

});

return """

export '${export}';

import '${relatedFileName}';

import 'package:flutter/cupertino.dart';

${imports}

class \$${relatedClassName} implements ${relatedClassName}{

@override

Map configureRoutes() {

return {

'/': ( context) => ${RouterGenerator.collector.indexRouter['/'].name}(),

${routerMap}

};

}

}

${ArgsAndNavigatorExtension}

""";

}

}

String _genArgsAndNavigatorExtension(Page page) {

var fields = "";

var argument = "";

var extension = "";

var constructorParams = ""; // this.a,this.b

var functionParams = ""; // String a,String b

var selectedConstructorParams = ""; // a:a, b:b

if (page.arguments != null && page.arguments.isNotEmpty) {

var size = page.arguments.length;

for (int i = 0; i <= size - 1; i++) {

fields = fields +

"final " +

page.arguments[i].type +

" " +

page.arguments[i].name +

";\n";

constructorParams = constructorParams +

(page.arguments[i].isRequired ? "@required " : "") +

"this." +

page.arguments[i].name +

(size == 1 || i == size - 1 ? "" : ",");

functionParams = functionParams +

(page.arguments[i].isRequired ? "@required " : "") +

page.arguments[i].type +

" " +

page.arguments[i].name +

(size == 1 || i == size - 1 ? "" : ",");

selectedConstructorParams = selectedConstructorParams +

page.arguments[i].name +

" : " +

page.arguments[i].name +

(size == 1 || i == size - 1 ? "" : ",");

}

}

var explainName = "${page.name}";

argument = _genArgument(page, fields, constructorParams);

extension =

_genNavigatorExtension(page, functionParams, selectedConstructorParams);

return """

// **************************************************************************

// ${explainName}

$argument

$extension

// **************************************************************************

""";

}

String _genNavigatorExtension(

Page page, String functionParams, String selectedConstructorParams) {

if (page.arguments == null || page.arguments.isEmpty) {

return """

extension ${page.name}Context on BuildContext{

void navigator2${page.name}(){

Navigator.pushNamed(this, "${page.routerPath}");

}

}

""";

} else {

return """

extension ${page.name}Context on BuildContext{

void navigator2${page.name}(

{${functionParams}}){

Navigator.pushNamed(this, "${page.routerPath}",

arguments:${page.name}Arguments(${selectedConstructorParams})

);

}

${page.name}Arguments get${page.name}Arguments(){

return ModalRoute.of(this).settings.arguments;

}

}

""";

}

}

String _genArgument(Page page, String fields, String constructorParams) {

if (page.arguments == null || page.arguments.isEmpty) {

return "";

}

return """

class ${page.name}Arguments{

${fields}

${page.name}Arguments({${constructorParams}});

}

""";

}

tools : 定义一些数据结构 Page/Arg ,存储 router_gen_builder 解析带 @RouterPage 标记带类信息的结果,用于之后执行 router_table_gen_builder 解析 @RouterTable 生成 .table.dart 类

class RouterCollector {

List importList = [];

Map routerMap = {};

Map indexRouter = {};

}

class Page {

String routerPath;

String name;

List arguments;

@override

String toString() {

return "{ routerPath:${this.routerPath},name:${this.name},arguments:${this.arguments.toString()}}";

}

}

class Argument {

String name;

String type;

bool isRequired;

@override

String toString() {

return "{ name:${this.name},type:${this.type},isRequired:${this.isRequired}}";

}

}

感谢:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Flutter 的 go_router 进行进阶使用时,您可以探索以下功能和技巧: 1. 命名路由:除了使用路径来导航页面,go_router 还支持命名路由,通过给每个路由规则指定一个唯一的名称,可以更方便地进行页面跳转。例如: ```dart final routes = [ GoRoute( path: '/', pageBuilder: (context, state) => HomePage(), name: 'home', ), GoRoute( path: '/details/:id', pageBuilder: (context, state) => DetailsPage(id: state.params['id']), name: 'details', ), ]; ``` 然后,您可以通过名称进行页面跳转: ```dart GoRouter.of(context).goNamed('details', params: {'id': '123'}); ``` 2. 参数传递:go_router 允许您在页面之间传递参数。在路由规则中,可以定义参数占位符,然后在页面构建器中获取这些参数并使用它们。例如: ```dart final routes = [ GoRoute( path: '/details/:id', pageBuilder: (context, state) => DetailsPage(id: state.params['id']), ), ]; ``` 在 DetailsPage 中可以通过 `widget.id` 访问传递的参数。 3. 路由拦截和重定向:go_router 允许您在路由跳转之前进行拦截和处理。您可以使用 `beforeEnter` 方法来拦截特定的路由,并根据需要执行操作,例如权限验证、参数校验等。还可以使用 `redirectTo` 方法来重定向到其他路由。例如: ```dart final routes = [ GoRoute( path: '/details/:id', pageBuilder: (context, state) => DetailsPage(id: state.params['id']), beforeEnter: (context, state) { // 进行权限验证或其他操作 if (!isLoggedIn) { return redirectTo('/login'); } return null; }, ), ]; ``` 4. 页面切换动画:go_router 支持自定义页面切换动画,您可以为每个路由规则定义不同的动画效果。使用 `transitionDuration` 和 `transitionBuilder` 属性来自定义页面切换动画。例如: ```dart final routes = [ GoRoute( path: '/', pageBuilder: (context, state) => HomePage(), transitionDuration: Duration(milliseconds: 500), transitionBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition(opacity: animation, child: child); }, ), ]; ``` 在上述示例中,我们使用了一个渐变的动画效果。 这些是 go_router 的一些进阶使用方法,您可以根据您的实际需求来灵活使用它们。请参考 go_router 的官方文档以获取更多详细信息和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值