Flutter代码生成插件source_gen的使用
Flutter代码生成插件source_gen的使用
Overview
source_gen
提供了用于Dart的自动源代码生成工具:
- 框架:用于编写消费和生产Dart代码的Builder。
- 约定:为人类和工具生成的Dart代码提供清晰分离,以及在同一项目中集成多个代码生成器。
其主要目的是在较低级别的包(如analyzer或build)之上暴露一个开发者友好的API。虽然不是必须使用source_gen
来生成源代码,但它提供了一套可能对你的生成器有用的库API。
Quick Start Guide for writing a Generator
添加依赖
在你的pubspec.yaml
文件中添加source_gen
的依赖项。如果你只在自己的项目中使用source_gen
生成代码,并且不会将生成器发布给其他人使用,则可以将其作为dev_dependency
添加:
dev_dependencies:
source_gen: ^1.1.0
编写生成器
通过继承Generator
或GeneratorForAnnotation
类并让source_gen
调用你的生成器来生成Dart库或库中带有你感兴趣的注解的每个元素。
示例:简单的注解生成器
下面是一个简单的示例,展示了如何创建一个基于注解的生成器。这个生成器会根据自定义注解@JsonSerializable
来自动生成JSON序列化代码。
首先,在lib/json_serializable.dart
中定义注解:
library json_serializable;
class JsonSerializable {
const JsonSerializable();
}
然后,在lib/src/json_serializable_generator.dart
中实现生成器:
import 'package:build/src/builder/build_step.dart';
import 'package:source_gen/source_gen.dart';
class JsonSerializableGenerator extends GeneratorForAnnotation<JsonSerializable> {
@override
generateForAnnotatedElement(element, constantReader, BuildStep buildStep) {
// 在这里实现你的生成逻辑
return '''
String toJson() => '{"name": "\$name"}';
factory ${element.name}.fromJson(String jsonString) => null;
''';
}
}
最后,在lib/builder.dart
中配置生成器:
import 'package:build_runner_core/build_runner_core.dart' as _i1;
import 'package:source_gen/source_gen.dart' as _i2;
import 'package:json_serializable/src/json_serializable_generator.dart' as _i3;
import 'dart:isolate' as _i4;
import 'package:build_runner/build_runner.dart' as _i5;
import 'dart:io' as _i6;
final List<_i1.BuilderApplication> builders = [
_i1.apply(
r'source_gen:combining_builder',
[_i2.SharedPartBuilder([_i3.JsonSerializableGenerator()], 'json_serializable')],
_i1.toPart,
hideOutput: true,
appliesBuilders: ['source_gen:combining_builder']
),
];
配置和运行生成器
source_gen
基于build包,并暴露选项以在Builder
中使用你的Generator
。选择一个Builder取决于你想让生成的代码放在哪里:
- 如果你想写入
.g.dart
文件,这些文件将作为原始源文件中的part
引用,使用SharedPartBuilder
。 - 如果你想写入
.some_name.dart
文件,这些文件将作为原始源文件中的part
引用,使用PartBuilder
。你应该选择一个对你包唯一的扩展名。 - 如果你想写入独立的Dart库文件,可以被
import
,使用LibraryBuilder
。只能使用单个Generator
作为LibraryBuilder
。
为了使Builder
与build_runner一起使用,它必须在build.yaml
文件中配置。参见build_config了解更多信息。每当你要发布包含build.yaml
文件的包时,应该在pubspec
中包含对build_config
的依赖。
当使用SharedPartBuilder
时,它应该始终配置为build_to: cache
(隐藏文件),并应用此包提供的combining_builder
。combining_builder
读取由不同共享部分构建器写入的所有片段,并将它们写入用户源目录中的最终.g.dart
输出。你不应该使用任何其他Builder使用.g.dart
扩展名。
build.yaml
配置示例
builders:
some_cool_builder:
import: "package:this_package/builder.dart"
builder_factories: ["someCoolBuilder"]
# The `partId` argument to `SharedPartBuilder` is "some_cool_builder"
build_extensions: {".dart": [".some_cool_builder.g.part"]}
auto_apply: dependents
build_to: cache
# To copy the `.g.part` content into `.g.dart` in the source tree
applies_builders: ["source_gen:combining_builder"]
Configuring combining_builder
ignore_for_file
有时生成的代码不支持目标包中指定的所有lints。当你使用基于package:source_gen
的Builder
应用combining_builder
时,设置ignore_for_file
选项为一个你希望在所有生成库中忽略的lint列表。
preamble
当你使用基于package:source_gen
的Builder
应用combining_builder
时,设置preamble
选项为你希望预先添加到所有生成库的字符串。
Generating files in different directories
你可以更改输入文件的输出位置:
- 使用
PartBuilder
或LibraryBuilder
时。 - 使用
SharedPartBuilder
并应用combining_builder
作为构建的一部分时。
默认情况下,.g.dart
或.some_name.dart
文件会生成在输入文件旁边。要更改这一点,可以在相应的构建器上设置build_extensions
选项。在选项中,build_extensions
是从String
到String
的地图,其中键匹配输入,值是单个构建输出。
例如,你可以使用这些选项在lib/generated
下生成文件,如下所示的构建配置:
targets:
$default:
builders:
# A SharedPartBuilder which uses the combining builder
source_gen:combining_builder:
options:
build_extensions:
'^lib/{{}}.dart': 'lib/generated/{{}}.g.dart'
# A PartBuilder or LibraryBuilder
some_cool_builder:
options:
build_extensions:
'^lib/models/{{}}.dart': 'lib/models/generated/{{}}.foo.dart'
记得更改输入中的part
语句以引用其他目录中的正确输出文件。
注意,构建器选项是source_gen
的公共API!当你在构建配置中使用它们时,始终添加对source_gen
的依赖。
FAQ
What is the difference between source_gen
and build
?
Build
是一个平台无关的Dart资产或代码生成框架,可插入构建系统(包括bazel),以及像build_runner这样的独立工具。你也可以构建自己的。
同时,source_gen
提供了易于使用的API和工具,用于简化基于build
的常见任务。例如,PartBuilder
类包装了一个或多个Generator
实例,以创建part of
文件,而LibraryBuilder
类包装了一个Generator
以创建Dart库文件。
更多关于Flutter代码生成插件source_gen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter代码生成插件source_gen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用source_gen
插件的代码示例。source_gen
是一个Dart包,用于在构建时生成源代码文件,通常用于生成与注解相关的代码。在Flutter项目中,你可以利用它来自动生成一些样板代码,如JSON序列化代码、数据模型代码等。
首先,你需要在你的pubspec.yaml
文件中添加source_gen
和相关的依赖,比如json_serializable
(用于生成JSON序列化和反序列化的代码)。
dependencies:
flutter:
sdk: flutter
json_annotation: ^4.0.1 # 这是必须的,因为json_serializable依赖于它
dev_dependencies:
build_runner: ^2.1.4 # Flutter构建工具,用于运行代码生成器
json_serializable: ^6.1.4 # 用于生成JSON序列化和反序列化的代码
source_gen: ^1.0.0 # 源代码生成插件
然后,你需要一个Dart文件,比如models/user.dart
,用于定义你的数据模型,并使用@JsonSerializable()
注解。
// models/user.dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // 这行告诉构建工具生成的代码应该放在哪个文件里
@JsonSerializable()
class User {
final String name;
final int age;
User({required this.name, required this.age});
// 生成的fromJson方法会放在user.g.dart文件中
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
// 生成的toJson方法会放在user.g.dart文件中
Map<String, dynamic> toJson() => _$UserToJson(this);
}
接下来,你需要在项目根目录下运行flutter pub get
来获取依赖项,然后运行flutter pub run build_runner build
来生成代码。这个命令会读取注解并生成相应的代码文件。
flutter pub get
flutter pub run build_runner build
运行上述命令后,你应该会在models/
目录下看到一个名为user.g.dart
的文件,这个文件包含了User
类的fromJson
和toJson
方法的实现。
// models/user.g.dart (自动生成)
part of 'user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
'name': instance.name,
'age': instance.age,
};
现在,你可以在项目的其他地方使用User
类,并轻松地进行JSON序列化和反序列化。
import 'package:flutter/material.dart';
import 'models/user.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 创建一个User对象
User user = User(name: 'John Doe', age: 30);
// 将User对象序列化为JSON
Map<String, dynamic> userJson = user.toJson();
print('User JSON: $userJson');
// 从JSON反序列化为User对象
User userFromJson = User.fromJson(userJson);
print('User from JSON: ${userFromJson.name}, Age: ${userFromJson.age}');
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Source Gen Example'),
),
body: Center(
child: Text('Check console for JSON serialization/deserialization output'),
),
),
);
}
}
这个示例展示了如何使用source_gen
插件和json_serializable
注解来生成JSON序列化和反序列化的代码。你可以根据需要扩展这个示例,以生成更多类型的代码。