Flutter屏幕方向管理插件aspectd的使用
Flutter屏幕方向管理插件aspectd的使用
简介
AspectD 是一个基于 AOP(面向切面编程)的框架,专为 Dart 语言设计。它类似于其他传统的 AOP 框架,提供了 call
和 execute
语法。由于在 Flutter 中不能使用 dart:mirrors
,AspectD 还提供了一种名为 inject
的方式来增强 Dart 代码的操作。
此外,AspectD 提供了一个 dill 变换器容器,开发者可以在其基础上实现自己的变换器,例如钩子、JSON 处理、镜像等。
假设你有一个名为 example
的 Flutter 项目,位于 hf_dir
目录下。
安装与配置
1. 在 hf_dir/example
创建名为 aspectd_impl
的 Dart 包
flutter create --template=package aspectd_impl
2. 将 aspectd
和 example
添加为 aspectd_impl
的依赖项
编辑 aspectd_impl/pubspec.yaml
文件:
dependencies:
flutter:
sdk: flutter
aspectd:
git:
url: git@github.com:alibaba-flutter/aspectd.git
ref: master
example:
path: ../
然后获取依赖项:
flutter packages get
3. 修改 aspectd_impl
包
aspectd_impl.dart
(入口文件)
import 'package:example/main.dart' as app;
import 'aop_impl.dart';
void main() => app.main();
aop_impl.dart
(AOP 实现)
import 'package:aspectd/aspectd.dart';
@Aspect()
@pragma("vm:entry-point")
class ExecuteDemo {
@pragma("vm:entry-point")
ExecuteDemo();
@Execute("package:example/main.dart", "_MyHomePageState", "-_incrementCounter")
@pragma("vm:entry-point")
void _incrementCounter(PointCut pointcut) {
pointcut.proceed(); // 执行原始方法
print('KWLM called!'); // 自定义逻辑
}
}
4. 修改 flutter_tools
以应用 aspectd.dart.snapshot
cd path-for-flutter-git-repo
git apply --3way path-for-aspectd-package/0001-aspectd.patch
rm bin/cache/flutter_tools.stamp
如果 flutter_tools
不支持钩子,aspectd.patch
是必要的。随着 Flutter 的发展,这个补丁可能会失败,但解决冲突很简单,因为 AspectD 只会在构建 dill 文件时添加两个钩子。
如果你自定义了 aspectd_impl
包,请编辑 path-for-flutter-git-repo/flutter/packages/flutter_tools/lib/src/aspectd.dart
中的以下内容:
const String aspectdImplPackageRelPath = '.';
const String aspectdImplPackageName = 'aspectd_impl';
步骤 1~3 每次添加 aspectd_impl
到 Flutter/Dart 包时都需要运行。步骤 4 只有在 dart-sdk 更改时才需要重新运行,例如升级 Flutter 后需要检查是否需要重新运行。
如果你使用的是本地生成的 aspectd_impl
包,请记住在 aspectd_impl
包中运行 flutter packages get
来获取 aspectd
并检查步骤 4。
5. 如果需要,实现自己的变换器
如上所述,AspectD 不仅是一个 AOP 框架,还提供了一个 dill 变换器容器。你可以按照以下步骤实现自己的变换器(例如 pluginDemo
):
a. 在 config.yaml
的 plugins
部分声明 pluginDemo
b. 运行以下命令生成统一的文件夹结构:
dart bin/generate_plugins_entry.dart
c. 如果需要,编写导出的注解到 pluginDemo.dart
d. 编写变换器实现到 pluginDemo_transformer_wrapper.dart
中的 PluginDemoWrapperTransformer.transform
注意以下两点:
- 如果要实现自己的变换器,最好通过路径引用导入 AspectD,而不是通过 git 依赖。否则你的修改可能会被意外覆盖。
- 每次更改 dill 变换器实现后,应删除
snapshot/aspectd.dart.snapshot
以使更改生效。
教程
使用 call
每个特定函数的调用点都会被操作。
import 'package:aspectd/aspectd.dart';
@Aspect()
@pragma("vm:entry-point")
class CallDemo {
@Call("package:app/calculator.dart", "Calculator", "-getCurTime")
@pragma("vm:entry-point")
Future<String> getCurTime(PointCut pointcut) async {
print('Aspectd:KWLM02');
print('${pointcut.sourceInfos.toString()}');
Future<String> result = pointcut.proceed();
String test = await result;
print('Aspectd:KWLM03');
print('${test}');
return result;
}
@Call("package:app/calculator.dart", "Calculator", "+getCurTemporature")
@pragma("vm:entry-point")
String getCurTemporature(PointCut pointcut) {
print('Aspectd:KWLM04');
print('${pointcut.sourceInfos.toString()}');
try {
String res = pointcut.proceed();
} catch (error, trace) {
print('Aspectd:KWLM05');
}
return null;
}
}
使用 execute
每个特定函数的实现都会被操作。
import 'package:aspectd/aspectd.dart';
@Aspect()
@pragma("vm:entry-point")
class ExecuteDemo {
@Execute("package:app/calculator.dart", "Calculator", "-getCurTime")
@pragma("vm:entry-point")
Future<String> getCurTime(PointCut pointcut) async {
print('Aspectd:KWLM12');
print('${pointcut.sourceInfos.toString()}');
Future<String> result = pointcut.proceed();
String test = await result;
print('Aspectd:KWLM13');
print('${test}');
return result;
}
}
使用 inject
对于原始函数(如 GestureDetector
的 build
方法),可以进行注入。
import 'package:aspectd/aspectd.dart';
import 'package:flutter/services.dart';
@Aspect()
@pragma("vm:entry-point")
class InjectDemo {
@Inject("package:flutter/src/widgets/gesture_detector.dart", "GestureDetector", "-build", lineNum: 452)
@pragma("vm:entry-point")
static void onTapBuild() {
Object instance; // Aspectd 忽略
Object context; // Aspectd 忽略
print(instance);
print(context);
print('Aspectd:KWLM25');
}
}
使用说明
用于纯 Dart 包
虽然可以直接使用 AspectD,但我建议先运行 Flutter 示例包。原因包括:
- 生成正确的
aspectd.dart.snapshot
。 - 获取正确的
platform_strong.dill
。
然后可以按以下步骤使用 AspectD:
# 生成 aspectd_impl.dill
dart --snapshot=example/aspectd_impl.dill example/aspectd_impl/lib/aspectd_impl.dart
# 执行内核到内核的变换
dart snapshot/aspectd.dart.snapshot --input example/aspectd_impl.dill --mode dart --sdk-root flutter/bin/cache/artifacts/engine/common/flutter_patched_sdk/ --output example/app.dill.aspectd.dill
# 运行变换后的 dill 文件
dart example/app.dill.aspectd.dill
更多关于Flutter屏幕方向管理插件aspectd的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter屏幕方向管理插件aspectd的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
aspectd
是一个用于 Flutter 的 AOP(面向切面编程)插件,它可以帮助你在不修改原始代码的情况下,对 Flutter 应用程序进行横切关注点(如日志记录、性能监控、异常处理等)的管理。虽然 aspectd
本身并不直接用于屏幕方向管理,但你可以通过它来拦截和修改与屏幕方向相关的逻辑。
以下是如何使用 aspectd
来管理 Flutter 应用程序屏幕方向的基本步骤:
1. 添加 aspectd
依赖
首先,在你的 pubspec.yaml
文件中添加 aspectd
依赖:
dependencies:
flutter:
sdk: flutter
aspectd: ^0.1.0
然后运行 flutter pub get
来获取依赖。
2. 创建 Aspect 类
接下来,创建一个 Aspect 类来拦截与屏幕方向相关的方法。例如,你可以拦截 SystemChrome.setPreferredOrientations
方法,该方法用于设置应用程序的首选屏幕方向。
import 'package:aspectd/aspectd.dart';
import 'package:flutter/services.dart';
@Aspect()
class ScreenOrientationAspect {
[@Execute](/user/Execute)("package:flutter/src/services/system_chrome.dart", "SystemChrome", "-setPreferredOrientations")
Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async {
// 在这里你可以修改或记录屏幕方向设置
print('Preferred orientations set to: $orientations');
// 你可以在这里添加自定义逻辑,例如强制某个方向
// orientations = [DeviceOrientation.portraitUp];
// 调用原始方法
return await Aspectd.invokeRealMethod(
this,
'setPreferredOrientations',
[orientations],
);
}
}
3. 应用 Aspectd
在 main.dart
中应用 aspectd
:
import 'package:flutter/material.dart';
import 'package:aspectd/aspectd.dart';
import 'screen_orientation_aspect.dart'; // 导入你创建的 Aspect 类
void main() {
Aspectd().apply();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Text('Hello, world!'),
),
);
}
}
4. 运行应用程序
现在,当你运行应用程序并调用 SystemChrome.setPreferredOrientations
时,ScreenOrientationAspect
中的 setPreferredOrientations
方法将会被拦截,并且你可以在这里添加自定义逻辑。
5. 自定义屏幕方向逻辑
在 ScreenOrientationAspect
中,你可以根据需要修改 orientations
参数,例如强制应用程序始终以竖屏模式运行:
[@Execute](/user/Execute)("package:flutter/src/services/system_chrome.dart", "SystemChrome", "-setPreferredOrientations")
Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async {
print('Preferred orientations set to: $orientations');
// 强制竖屏模式
orientations = [DeviceOrientation.portraitUp];
// 调用原始方法
return await Aspectd.invokeRealMethod(
this,
'setPreferredOrientations',
[orientations],
);
}