Flutter 注解开发

1,478 阅读4分钟

Flutter 注解开发:轻松愉快地探索注解之旅

本文将带您轻松愉快地探索 Flutter 中注解的开发。我们将通过以下小节展开这次有趣的旅程。

[TOC]

一、 介绍背景:开启神奇的注解之旅

相信在日常开发过程中,我们经常会面临以下挑战:大量重复的模板代码、臃肿的代码降低可读性,以及JSON序列化和反序列化问题。为应对这些问题,各种编程语言都有自己的解决方案。在Dart语言中,采用了注解这一高效手段来解决这些问题。

二、什么是注解?

在Dart语言中,注解是一种特殊的语法,用于在编译时或运行时向代码添加额外的元数据。注解以@符号开头,后跟一个编译时常量表达式。这种元数据可以用于指导工具(如静态分析器、编译器和构建器)执行特定操作,例如代码生成、静态检查和优化。注解不会影响程序的执行过程,但可以在编译时或运行时被工具和库访问,以实现各种目的。

用人话来说,就好比购物网站给每个人打上标签,A是大款,B是美女,C是抠脚大汉,然后给你生成首页的时候,找出这些标签去显示不同的商品。

三、 为什么要使用注解开发

注解就像是一把神奇的钥匙,为我们解锁了许多高效开发的大门。以下是使用注解的一些原因:

  1. 自动生成代码:注解就像会魔法的小精灵,能够在编译时自动生成代码,减轻了我们手动编写重复代码的负担,让开发变得更加轻松。
  2. 简化代码:注解可以减少重复代码,提高代码质量。
  3. 提高可读性:注解可以将元数据与实际代码分离,使代码更易于理解。
  4. 扩展功能:通过注解和注解处理器,我们可以在编译时执行一些额外操作,例如代码检查、代码优化等

以下是一些常见的注解示例:

  1. @override:这个注解表示一个方法覆盖了父类的方法。它可以帮助我们检查是否正确地实现了方法覆盖,如果没有正确实现,编译器会给出警告。

    class Animal {
      String makeSound() {
        return "makeSound";
      }
    }
    
    class Dog extends Animal {
      @override
      void makeSound() {
        print('makeSound');
      }
    }
    
  2. @JsonSerializable():这个注解就是我们项目中使用到的json_annotation库的@JsonSerializable()注解。

    @JsonSerializable()
    class ConversationListEntity {
      String? nextSeq;
      late List<ConversationListResList> resList;
    
      ConversationListEntity();
    
      factory ConversationListEntity.fromJson(Map<String, dynamic> json) => $ConversationListEntityFromJson(json);
    
      Map<String, dynamic> toJson() => $ConversationListEntityToJson(this);
    
      @override
      String toString() {
        return jsonEncode(this);
      }
    }
    
  3. 我们项目中的@RootView(jumpId: "1005"):用来为订单详情页面生成路由代码。

    @RootView(jumpId: "1005")
    class OrderDetail extends BaseWidget {
      OrderDetail({Key? key}) : super();
    
      @override
      OrderDetailState createState() => OrderDetailState();
    }
    

四、如何进行注解开发

1、创建 package

想要创建初始的 Flutter package,请使用带有 --template=package 标志的 flutter create 命令:

flutter create --template=package my_router

2、配置依赖

dev_dependencies:
  build: ^2.0.0
  build_runner: ^2.0.0
  source_gen: ^0.9.1

3、开发

annotation_img_03.png

  1. 创建注解类

    class InjectMath{
      const InjectMath();
    }
    

    使用

    @InjectMath()
    abstract class MyMath{
      add(int a,int b){}
    }
    
    void main() {
      var sum = MyMathImp().add(1, 2);
      print("sum:${sum}");
    }
    
  2. 针对注解类创建Generator去解析

    MathMethodGenerator
    1. 收集每个页面的 注解(RootView),并对注解信息进行解析,存到静态变量rootViewInfo中,不要生成文件
    // 这个是比较关键的代码
    class MathMethodGenerator extends GeneratorForAnnotation<InjectMath> {
    
      late String className;
    
      @override
      generateForAnnotatedElement(Element element, ConstantReader annotation,
          BuildStep buildStep) {
        final emitter = DartEmitter();
        className = element.displayName;
        ClassBuilder classBuilder;
    
        var channelHelper = Class((builder) {
          classBuilder = builder;
          // 类名
          classBuilder.name = "${className}Imp";
          // 对某个类进行实现
          classBuilder.implements.add(refer(className));
          // 添加方法
          classBuilder.methods.add(Method((methodBuild) {
            methodBuild.name = "add";
            methodBuild.annotations.add(TypeReference((build) {
              // 给方法添加注解
              build.symbol = "override"; //注解类型是override
            }));
            methodBuild.returns = Reference("int");
            methodBuild.requiredParameters.add(Parameter(((parameterBuilder) {
              parameterBuilder.name = "a";
              parameterBuilder.type = Reference("int");
            })));
            methodBuild.requiredParameters.add(Parameter(((parameterBuilder) {
              parameterBuilder.name = "b";
              parameterBuilder.type = Reference("int");
            })));
            methodBuild.body = Code("return a + b;");
          }));
    
        });
    
        String channelHelperStr =
        DartFormatter().format('${channelHelper.accept(emitter)}');
    
        return """
            part of '${Path.basename(buildStep.inputId.path)}';
            $channelHelperStr
        """;
      }
    }
    
  3. 创建Builder去调用Generator

    Builder mathMethodBuilder(BuilderOptions options) =>
        LibraryBuilder(MathMethodGenerator(), generatedExtension: '.math.g.dart');
    
  4. 配置build.yaml

    build.yaml文件中,你可以定义生成器、构建器以及它们的相关设置。这些设置包括:

    • 生成器和构建器的输入和输出文件类型
    • 生成器和构建器的自动应用规则
    • 目标源文件的匹配模式
    • 构建选项,如是否启用缓存、输出目录等
    builders:
      math_builder:
        import: 'package:my_router/builder.dart'
        builder_factories: ['mathMethodBuilder']
        build_extensions: {'.dart': ['.math.g.dart']}
        auto_apply: root_package
        build_to: source
    
    

五、使用

通过运行一下命令生成代码

flutter pub run build_runner clean
flutter pub run build_runner build --delete-conflicting-outputs

注意点: build_runner会存在缓存,如果generateForAnnotatedElement返回null,那后面此注解就不会被build了,可以通过一下命令解决。

flutter pub run build_runner clean

六、debug

​ 在注解开发过程中,我们常常会遇到一些调试难题,例如:为什么代码没有生成;如何查看复杂对象的属性;如何进行流程分析等。如果不利用断点调试技术,解决这些问题将变得相当困难。接下来,我将分享在调试过程中积累的一些经验和心得,希望能对大家有所帮助。

  1. build/entrypoint/build.dart,复制到项目根目录中

annotation_img_01.jpg

  1. 点击Edit Configurations进行配置
    • 添加Dart Command Line App
    • 选择Dart file
    • program arguments中输入serve

annotation_img_02.jpg

六、总结

通过本次轻松愉快的注解之旅,相信您已经对 Flutter 中注解的开发有了更深入的了解。注解不仅为我们的代码增添了趣味,还让我们的开发变得更加高效。在实际开发过程中,不妨尝试使用注解,体验它们带来的神奇力量吧!