警告:此包仍被视为实验性。
Mint 提供了一个通过模板生成代码的框架。它开箱即用地支持 copyWith、copyJar、equality、toJson 和 fromJson。但是,它允许您生成任何您想要的东西,并以您希望的任何方式与生成的代码进行交互。所有生成的代码都来自您可以轻松修改以满足您需求的模板。这种能力延伸到与生成的第三方代码进行交互。这使得您的类不需要任何“钩子”来连接到生成的代码。Mint 的目标是干净、简单、直观的数据类。
用法
用法非常简单
将依赖项添加到您的 pubspec.yaml
dependencies:
au: ^0.2.1
json_annotation: ^4.7.0
dev_dependencies:
build_runner: ^2.0.0
json_serializable: ^6.3.1
mint: ^0.6.0
将其添加到模型
// Import the au annotation
import 'package:au/au.dart';
// Import whatever other libraries you may need
import 'package:json_annotation/json_annotation.dart';
// Include the generated code. Notice the extension is .au.dart and not .g.dart.
part 'person.au.dart';
// Annotate the class with Au
@Au()
// Add whatever other annotations you may need.
@JsonSerializable(explicitToJson: true)
// Include the _$Person mixin (Pattern is _$CLASS)
class Person with _$Person {
final String name;
final int age;
const Person(this.name, this.age);
// Copy this constructor from the generated code
// The positional arguments represent the field names in alphabetical order
const Person._fromAu(
this.age,
this.name,
);
}
更新您的 build.yaml
确保您的 build.yaml(在您项目的根目录下)具有以下配置。有关这些内容的更多详细信息,请参阅下面的配置部分。
targets:
$default:
builders:
mint:mint_builder:
enabled: True
options:
templates:
abstract: "package:mint/src/templates/abstract.mustache"
child: "package:mint/src/templates/child.mustache"
from_au_hint: "package:mint/src/templates/from_au_hint.mustache"
jar: "package:mint/src/templates/jar.mustache"
mixin: "package:mint/src/templates/mixin.mustache"
from_json: "package:mint/src/templates/from_json.mustache"
to_json: "package:mint/src/templates/to_json.mustache"
mixin_annotations:
- annotation: 'JsonSerializable'
template: 'to_json'
child_annotations:
- annotation: 'JsonSerializable'
template: 'from_json'
source_gen:combining_builder:
enabled: False
mint:mint_combining_builder:
enabled: True
options:
mint_rewire_parts:
- 'json_serializable.g.part'
运行构建运行器
运行 build_runner 以生成代码(或使用 watch 选项在保存时自动执行): dart run build_runner build --delete-conflicting-outputs 或: dart run build_runner watch --delete-conflicting-outputs 或对于 Flutter: flutter packages pub run build_runner build --delete-conflicting-outputs
复制 _fromAu 构造函数(如果您没有手动编写)
在生成的代码顶部,您应该会找到 au 构造函数代码
// Copy the _fromAu constructor into your base class.
// const Person._fromAu(this.age,this.name,);
一切就绪
final p1 = const Person('John', 35);
final p2 = p1.copyWith(
age: const AuValue<int>(25),
);
assert(p1 != p2);
// or just copy the jar
final p3 = p1.copyJar(const AuPersonJar(
age: 21,
name: 'Joe',
));
final p4 = AuPerson.fromJson(p3.toJson());
assert(p3 == p4);
final p5 = const AuPerson('John', 35);
assert(p5 == p1);
请注意,您不再需要为这些常见功能编写代码。您也不再需要添加 fromJson 工厂构造函数,您可以使用 AuPerson(AuCLASS)生成的子类的 fromJson 工厂。这是可行的,因为 Person 和 AuPerson 的实例是等效的。
重新生成
您唯一需要重新运行构建运行器的情况是,如果您修改了模型中的字段或构造函数定义。如前所述,这可以通过 watch 命令自动完成:dart run build_runner watch --delete-conflicting-outputs。
如果您想进一步操作,您可以自动化 VSCode 中该命令的执行,使其在您打开项目时运行。这可以通过 VSCode 任务配置来实现。要进行设置,只需在您的 .vscode 目录中添加此 tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "flutter",
"command": "flutter",
"args": [
"pub",
"run",
"build_runner",
"watch",
"lib/",
"--delete-conflicting-outputs",
],
"problemMatcher": [
"$dart-build_runner"
],
"options": {
"cwd": "example",
},
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": false,
"clear": false
},
"group": "build",
"label": "Flutter Build Runner",
"detail": "example"
}
]
}
配置
代码生成的配置通过 build.yaml 文件完成
targets:
$default:
builders:
mint:mint_builder:
enabled: True
options:
templates:
# Required templates
abstract: "package:mint/src/templates/abstract.mustache"
child: "package:mint/src/templates/child.mustache"
from_au_hint: "package:mint/src/templates/from_au_hint.mustache"
jar: "package:mint/src/templates/jar.mustache"
mixin: "package:mint/src/templates/mixin.mustache"
# Annotation templates
from_json: "package:mint/src/templates/from_json.mustache"
to_json: "package:mint/src/templates/to_json.mustache"
mixin_annotations:
- annotation: 'JsonSerializable'
template: 'to_json'
child_annotations:
- annotation: 'JsonSerializable'
template: 'from_json'
# Disable source gen combining_builder for improved performance
source_gen:combining_builder:
enabled: False
# Utilize the mint:combining_builder instead
mint:mint_combining_builder:
enabled: True
options:
# Which parts will have generated model references replaced with Au child references (Person > AuPerson). This is only needed when there are factories being created in child_annotations. It essentially rewires the part generated code to function as if it were generated for the child class instead of the model. This allows you to interact with it from other generated code without ever needing to add anything to the model.
mint_rewire_parts:
- 'json_serializable.g.part'
第一步是定义模板映射。必需的键是: abstract、child、from_au_hint、jar 和 mixin。键代表每个模板的标识符,值是 mustache 模板文件的 URI。如果您想使用自己的模板,只需将相应的 URI 替换为您项目中的一个。
还支持注解模板(这是 Mint 提供 JsonSerializable 支持的方式)。它也可以用来创建您自己的注解驱动生成。一旦定义了模板,我们只需为 mixin_annotations 和 child_annotations 提供一个映射列表,其中包含相应的模板标识符。
例如:如果类带有 JsonSerializable 注解,则将 to_json 模板生成到 mixin 中。如果我们想为子类添加一些功能(例如,用于工厂构造函数),则情况相同,只需使用 child_annotations 列表即可。
注意:您可能已经意识到,配置的注解是字符串而不是实际的 TypeInterfaces。如果您有多个同名的注解类,这可能会带来一些挑战。
注意:您可能不想在代码库中引用生成的 Au* 类。如果是这样,您可以选择仅使用 mixin,它仍然为您提供大部分功能:copyWith、copyJar、equality、toJson 等。当然,您需要为您的模型定义必要的工厂(fromJson)。唯一的其他更改是禁用 mint_combining_builder,并启用 source_gen。
模板参数
在创建自己的注解模板时,您需要访问一些模型元数据。这可以通过以下变量完成
[
'model_class_name',
'model_abstract_class_name',
'model_child_class_name',
'model_jar_class_name',
'fields': [
'field_name',
'field_name_capitalized',
'field_type',
'field_type_with_nullability',
'field_is_nullable',
'field_is_private',
'field_is_last',
]
]
这是 abstract.mustache 文件的示例,它创建了一个与模型具有相同字段的抽象类
abstract class {{model_abstract_class_name}} extends AuMinted {
{{#fields}}
{{field_type_with_nullability}} get {{field_name}};
{{/fields}}
}
自定义模板 targeting an annotation
在示例项目中,您可以看到如何设置它的示例。这非常简单。
创建您的注解
在 dart 中,任何类都可以是注解,它只需要有一个 const 构造函数。
class Foo {
const Foo();
}
创建您的自定义模板
// Custom generated function from Foo annotation
String foo() {
return 'foo on {{model_class_name}} - {{#fields}}{{field_name}}{{^field_is_last}},{{/field_is_last}}{{/fields}}';
}
更新 build.yaml
templates:
abstract: "package:mint/src/templates/abstract.mustache"
child: "package:mint/src/templates/child.mustache"
from_au_hint: "package:mint/src/templates/from_au_hint.mustache"
jar: "package:mint/src/templates/jar.mustache"
mixin: "package:mint/src/templates/mixin.mustache"
from_json: "package:mint/src/templates/from_json.mustache"
to_json: "package:mint/src/templates/to_json.mustache"
# Add your custom template
with_foo: "package:example/templates/with_foo.mustache"
mixin_annotations:
- annotation: 'JsonSerializable'
template: 'to_json'
# Add your custom annotation, and configure it to be added to the mixin_annotations
- annotation: 'Foo'
template: 'with_foo'
使用它
@Au()
@Foo()
@JsonSerializable(explicitToJson: true)
class WithFoo with _$WithFoo {
final String name;
final int age;
const WithFoo(this.name, this.age);
const WithFoo._fromAu(
this.age,
this.name,
);
}
由于模型带有 Foo 注解,并且我们配置了 Foo 来生成 with_foo 模板,因此系统将作为方法的一部分生成此附加函数
// Custom generated function from Foo annotation
String foo() {
return 'foo on WithFoo - age,name';
}
您现在可以像使用其他任何方法一样使用它
final w = WithFoo('foo', 99);
print(w.foo());
工厂构造函数
工厂构造函数无法作为 mixin 的一部分进行定义。因此,Mint 还生成了一个扩展原始模型的子类。这个子类被命名为 Au(CLASS)。这个子类负责与生成的代码进行交互。它允许我们不必定义 fromJson 或其他可能被第三方代码生成器要求的类似工厂构造函数。您可以使用子类,就像使用其父模型一样。
如果您希望代码是子类的一部分,您只需将 Foo 注解配置移动到 child_annotations 部分
child_annotations:
- annotation: 'JsonSerializable'
template: 'from_json'
# Add the foo annotation configuration
- annotation: 'Foo'
template: 'with_foo'
可选的重新连接
根据您尝试集成的第三方代码生成库,您可能还需要将第三方生成的代码“重新连接”到子类而不是最初生成的模型(Person > AuPerson)。要实现这一点,只需将 part 文件的扩展添加到 mint_combining_builder 配置中
mint:mint_combining_builder:
enabled: True
options:
mint_rewire_parts:
- 'json_serializable.g.part'
# Add parts to rewire
结束语
有趣的事实:此包最初名为 Augment,然后我发现了同名的 Dart 正在进行中的功能。因此,我将其重命名为:Au + Mint。希望未来增强功能能为我们提供更好的实现此类行为的选项,但在此之前……继续铸造黄金!