Flutter开发:在 Dart 3.5 中创建自己的宏,而不是代码生成
Flutter 开发:在 Dart 3.5 中创建自己的宏,而不是代码生成
问题
Flutter开发:在 Dart 3.5 中创建自己的宏,而不是代码生成。
答案
Dart 3.5 引入了一个重大新特性:宏。宏可以看作是在编译时完全在内存中发生的代码生成,不需要临时文件。这一特性目前还处于测试阶段,因此不建议在生产环境中使用。不过,我们可以尝试创建和使用宏来熟悉这个特性。
以下是如何设置实验环境、编写并运行自己的宏的详细步骤。
设置实验环境 Dart 3.5
-
下载 Dart 3.5 测试版: 按照官方指示切换到 Dart 3.5 测试版:Dart 3.5 宏设置指南。
-
安装 Dart 插件: VSCode 需要安装最新的 Dart 插件来查看宏生成的代码。
创建 pubspec.yaml
要使用示例宏,必须至少使用 Dart 3.5.0-154。创建如下的 pubspec.yaml
文件:
name: macro_example
version: 1.0.0
dependencies:
# 可以在这里添加其他依赖
dev_dependencies:
build_runner: ^2.0.0 # 确保使用支持宏的版本
environment:
sdk: ">=3.5.0-154 <4.0.0"
创建 analysis_options.yaml
由于你正在编写代码时使用的是实验特性,分析器会给出警告,因此需要告诉它你正在实验这个特性。创建以下的 analysis_options.yaml
文件:
analyzer:
enable-experiment:
- macros
编写代码
使用 Dart 团队提供的示例代码。
main.dart
@JsonCodable
class User {
String name;
int age;
}
void main() {
var user = User.fromJson({'name': 'John', 'age': 30});
print(user.toJson());
}
注意:@JsonCodable
是一个示例宏,它将在未来稳定。
运行代码
通过终端运行代码,启用实验标志:
dart --enable-experiment=macros run main.dart
或者配置 VSCode 来进行实验。打开 settings.json
并进行如下修改:
{
"dart.enableExperiments": ["macros"]
}
现在它就能正常工作并打印以下内容:
{"name":"John","age":30}
注意,类 User
只有 6 行代码,而使用 json_serializable
的等效代码需要 16 行。
查看生成的代码
在 VSCode 中,你可以点击 “Go to Augmentation” 来查看生成的代码。与旧的代码生成方式不同,生成的代码并不是保存在真实的文件中,而是在内存中。你不能直接编辑这些代码,但是当你更改 main.dart
中的内容时,生成的代码会自动更新,所以你不需要单独运行生成器。
宏的工作原理:增强
增强是通过添加成员或替换类体外部的代码来修改类或函数。宏所做的实际操作是生成一个包含增强的文件。这个文件现在是内存中的,而不是一个 .g.dart
的物理文件。
创建自己的 hello-world 宏
创建 hello.dart
文件,写入以下代码来实现宏:
import 'dart:_internal' as internal;
import 'package:meta/meta.dart';
@macro
class HelloMacro implements ClassDeclarationsMacro {
const HelloMacro();
void buildDeclarationsForClass(
ClassBuilder builder,
ClassDeclaration classDecl
) {
var className = classDecl.name;
var fields = classDecl.fields;
var helloMethodCode = '''
void hello() {
print('Hello, I am $className!');
${fields.map((field) => "print('Field: ${field.name}');").join('\n')}
}
''';
var printIdentifier = builder.resolveIdentifier('print');
var helloMethod = builder.declareInType(
classDecl.type,
internal.MethodDeclaration(
name: builder.name('hello'),
returnType: builder.coreType('void'),
isStatic: false,
isAbstract: false,
body: internal.FunctionBody.block(
[
internal.Statement.expression(
internal.Expression.invocation(
receiver: printIdentifier,
arguments: [
internal.StringLiteral(
"'Hello, I am $className!'"
)
]
)
),
...fields.map((field) => internal.Statement.expression(
internal.Expression.invocation(
receiver: printIdentifier,
arguments: [
internal.StringLiteral(
"'Field: ${field.name}'"
)
]
)
)).toList()
]
)
)
);
builder.addMember(helloMethod);
}
}
使用这个新宏:
main.dart
import 'hello.dart';
@HelloMacro
class MyClass {
String name;
int age;
}
void main() {
var myClass = MyClass();
myClass.hello();
}
点击 “Go to Augmentation” 查看生成的代码。注意,print
函数前面加上了 prefix0
,这是引入 dart:core
时的标识符前缀。
运行代码:
dart --enable-experiment=macros run main.dart
你将看到它输出:
Hello, I am MyClass!
Field: name
Field: age
真实有用的宏
以下是两个可以深入学习的实际宏:
-
JsonCodable: 这是 Dart 团队发布的第一个宏,帮助我们了解这一特性。
-
Args: 如果你开发的是命令行应用,可以创建 Args 宏来从数据类生成解析器,并在编译时提供类型安全。
总结
虽然实现宏相对较为复杂,但它在编译器中的实现要难得多。增强(augmentation)是宏的一个关键特性,它允许我们在编译时修改类和函数,从而简化代码并提高安全性。
更多关于Flutter开发:在 Dart 3.5 中创建自己的宏,而不是代码生成的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter开发:在 Dart 3.5 中创建自己的宏,而不是代码生成的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在 Dart 3.5 中,虽然宏(Macros)的概念尚未正式引入 Dart 语言,但你可以通过一些现有的特性来模拟类似宏的行为,尤其是通过代码生成工具来实现。虽然这不是真正的宏系统,但可以在编译时或构建时动态生成代码,从而达到类似的效果。
在 Flutter 开发中,你可以使用 build_runner
和 build
包来创建自定义的构建步骤,这些步骤可以在构建过程中生成 Dart 代码。下面是一个简要的示例,展示如何使用这些工具来生成代码。
1. 添加依赖
首先,在你的 pubspec.yaml
文件中添加必要的依赖:
dependencies:
flutter:
sdk: flutter
dev_dependencies:
build_runner: ^2.1.7
build: ^2.1.1
build_config: ^1.0.0
2. 创建构建配置
在项目的根目录下创建一个 build.yaml
文件,用于配置构建步骤:
targets:
$default:
builders:
generate_code:
enabled: true
builders:
generate_code:
import: "package:your_package_name/builder.dart"
builder_factories: ["generateCode"]
build_extensions: { ".dart": [".g.dart"] }
auto_apply: root_package
build_to: source
注意:将 your_package_name
替换为你的实际包名。
3. 编写代码生成器
在 lib
目录下创建一个 builder.dart
文件,并编写代码生成逻辑:
import 'dart:async';
import 'package:build/build.dart';
Builder generateCode(BuilderOptions options) {
return new AsyncMultiBuilder([
new PartBuilder(
[new _CodeGenerator()],
'.g.dart'
),
]);
}
class _CodeGenerator implements Generator {
@override
FutureOr<String> generate(AssetId id, Context context) async {
if (id.extension.endsWith('.dart')) {
var content = await context.readAsString(id);
// 这里添加你的代码生成逻辑
var generatedContent = """
// This is generated code
part of '${id.pathSegments.lastWithoutExtension}';
void generatedFunction() {
print('Hello from generated code!');
}
""";
var outputId = id.changeExtension('.g.dart');
await context.writeAsString(outputId, generatedContent);
}
return null;
}
}
4. 使用生成的代码
在你的 Dart 文件中,你可以这样使用生成的代码:
// example.dart
part 'example.g.dart';
void main() {
generatedFunction(); // 调用生成的函数
}
5. 运行构建
最后,在项目根目录下运行以下命令来生成代码:
flutter pub run build_runner build
这将生成 .g.dart
文件,并在其中包含你定义的生成代码。
总结
虽然 Dart 3.5 尚未正式引入宏功能,但通过使用 build_runner
和 build
包,你可以在构建过程中动态生成代码,从而模拟类似宏的行为。这种方法虽然不如真正的宏系统灵活,但在许多情况下已经足够使用。