Flutter屏幕方向管理插件aspectd的使用

Flutter屏幕方向管理插件aspectd的使用

简介

AspectD 是一个基于 AOP(面向切面编程)的框架,专为 Dart 语言设计。它类似于其他传统的 AOP 框架,提供了 callexecute 语法。由于在 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. 将 aspectdexample 添加为 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.yamlplugins 部分声明 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

对于原始函数(如 GestureDetectorbuild 方法),可以进行注入。

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 示例包。原因包括:

  1. 生成正确的 aspectd.dart.snapshot
  2. 获取正确的 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

1 回复

更多关于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],
  );
}
回到顶部