前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

大多数 Flutter 插件的架构很直白:Dart 层调 MethodChannel.invokeMethod,原生端处理完返回结果,完事。但 secure_application 走了一条完全不同的路——它的 Dart 层不是薄薄的一层封装,而是一个完整的状态管理框架,包含 Widget、Controller、Provider、事件流。

理解这个架构设计,是理解整个适配工作的前提。今天我们来拆解它的设计哲学。请添加图片描述

一、与传统 Flutter Plugin 的架构对比

1.1 传统 Plugin 架构

以 flutter_speech 为例,典型的 Flutter Plugin 架构是这样的:

┌─────────────────────────┐
│     Dart 层(薄封装)      │
│  SpeechRecognition       │
│    - activate()          │
│    - listen()            │
│    - cancel()            │
│    - stop()              │
└──────────┬──────────────┘
           │ MethodChannel
┌──────────▼──────────────┐
│     Native 层(重逻辑)    │
│  FlutterSpeechPlugin     │
│    - 引擎创建/销毁        │
│    - 权限申请             │
│    - 监听器管理           │
│    - 回调转发             │
└─────────────────────────┘

特点:Dart 层轻,Native 层重。核心逻辑在原生端。

1.2 secure_application 的架构

┌──────────────────────────────────────┐
│          Dart 层(重逻辑)              │
│  SecureApplication (Widget)           │
│    ├── SecureApplicationController    │
│    │     ├── State 管理               │
│    │     ├── BehaviorSubject 事件流    │
│    │     └── secure/lock/unlock 方法  │
│    ├── SecureApplicationProvider      │
│    ├── SecureGate (Widget)            │
│    │     └── BackdropFilter 模糊遮罩  │
│    └── WidgetsBindingObserver 生命周期 │
└──────────────┬───────────────────────┘
               │ MethodChannel(指令下发 + 事件上报)
┌──────────────▼───────────────────────┐
│          Native 层(轻逻辑)            │
│  SecureApplicationPlugin              │
│    ├── setWindowPrivacyMode           │
│    ├── 窗口事件监听                    │
│    └── 生命周期回调                    │
└──────────────────────────────────────┘

特点:Dart 层重,Native 层轻。核心逻辑在 Dart 端。

1.3 为什么这样设计

考量 传统 Plugin secure_application
核心能力在哪 平台原生 API Flutter Widget 层
跨平台一致性 各平台行为可能不同 Dart 层保证一致
UI 相关吗 不相关 强相关(模糊遮罩)
状态复杂度 简单 复杂(四状态机)
适配工作量 原生端为主 原生端较少

💡 核心洞察:secure_application 的模糊遮罩是用 Flutter 的 BackdropFilter 实现的,不是原生 UI。原生端只负责"告诉 Flutter 该锁了"和"设置截屏防护"。这意味着适配 OpenHarmony 时,Dart 层代码完全不用改

二、Widget 驱动 vs 纯方法调用

2.1 纯方法调用模式

// flutter_speech 的使用方式
final speech = SpeechRecognition();
speech.activate('zh_CN');
speech.listen();
speech.setRecognitionResultHandler((text) => print(text));

开发者直接调用方法,手动管理回调。

2.2 Widget 驱动模式

// secure_application 的使用方式
SecureApplication(
  onNeedUnlock: (controller) async {
    // 认证逻辑
    controller?.authSuccess(unlock: true);
    return null;
  },
  child: SecureGate(
    blurr: 20,
    opacity: 0.6,
    lockedBuilder: (context, controller) => UnlockScreen(),
    child: MyProtectedContent(),
  ),
)

开发者通过声明式 Widget 来使用插件,状态管理由框架自动处理。

2.3 两种模式的优劣

维度 纯方法调用 Widget 驱动
学习成本
使用灵活性 中(受 Widget 树约束)
状态管理 开发者自己管 框架自动管
UI 集成 需要手动 setState 自动响应状态变化
适合场景 功能型插件 UI 型插件

2.4 对适配的影响

Widget 驱动模式对 OpenHarmony 适配来说是个好消息:

  1. Dart 层零改动:所有 Widget、Controller、Provider 代码跨平台通用
  2. 原生端职责明确:只需要实现 MethodChannel 的几个方法
  3. 测试更容易:可以在任意平台上测试 Dart 层逻辑

三、ValueNotifier + InheritedWidget 状态管理

3.1 为什么不用 Provider 或 Bloc

secure_application 没有引入 Provider、Bloc、Riverpod 等状态管理库,而是用了 Flutter 自带的 ValueNotifier + InheritedWidget

原因很简单:作为一个基础设施级别的插件,它不应该强制用户依赖某个状态管理方案。

// SecureApplicationController 继承自 ValueNotifier
class SecureApplicationController extends ValueNotifier<SecureApplicationState> {
  SecureApplicationController(SecureApplicationState value) : super(value);
  // ...
}

3.2 ValueNotifier 的工作方式

// 状态变更时通知监听者
void lock() {
  SecureApplicationNative.lock();
  if (!value.locked) {
    value = value.copyWith(locked: true);
    notifyListeners();  // 触发所有监听者重建
  }
}

notifyListeners() 会通知所有通过 addListener 注册的回调,包括 SecureGate 的 _sercureNotified 方法。

3.3 InheritedWidget 的上下文传递

// SecureApplicationProvider 是一个 InheritedWidget
class SecureApplicationProvider extends InheritedWidget {
  final SecureApplicationController secureData;

  static SecureApplicationController? of(BuildContext context, {bool listen = true}) {
    // 从 Widget 树中查找最近的 Provider
    if (listen) {
      return context.dependOnInheritedWidgetOfExactType<SecureApplicationProvider>()?.secureData;
    }
    return context.findAncestorWidgetOfExactType<SecureApplicationProvider>()?.secureData;
  }
}

这样任何子 Widget 都可以通过 SecureApplicationProvider.of(context) 获取 Controller。

3.4 状态流转的完整链路

用户切后台
    │
    ▼
WidgetsBindingObserver.didChangeAppLifecycleState(inactive)
    │
    ▼
SecureApplicationController.lock()
    │
    ├── SecureApplicationNative.lock()  →  MethodChannel → Native
    ├── value = value.copyWith(locked: true)
    └── notifyListeners()
            │
            ▼
        SecureGate._sercureNotified()
            │
            ▼
        _gateVisibility.value = 1  →  模糊遮罩显示

四、BehaviorSubject 响应式事件流

4.1 为什么用 rxdart

secure_application 引入了 rxdart 依赖,使用了其中的 BehaviorSubject

dependencies:
  rxdart: ^0.28.0
final BehaviorSubject<SecureApplicationAuthenticationStatus>
    _authenticationEventsController =
    BehaviorSubject<SecureApplicationAuthenticationStatus>.seeded(
        SecureApplicationAuthenticationStatus.NONE);

4.2 BehaviorSubject vs StreamController

特性 StreamController BehaviorSubject
新订阅者收到最新值
默认值 ✅(seeded)
多次订阅 broadcast 才行 默认支持
获取当前值 不支持 .value 属性

BehaviorSubject 的关键特性是新订阅者立即收到最新值。这对 secure_application 很重要——当一个新页面订阅认证事件时,它需要立即知道当前的认证状态,而不是等到下次状态变化。

4.3 两个事件流

// 认证事件流
Stream<SecureApplicationAuthenticationStatus> get authenticationEvents =>
    _authenticationEventsController.stream;

// 锁定事件流
Stream<bool> get lockEvents => _lockEventsController.stream;
事件流 触发时机 用途
authenticationEvents authSuccess/authFailed/authLogout 清除敏感数据、更新 UI
lockEvents lock/unlock 暂停媒体播放、保存草稿

4.4 使用示例

// 监听认证失败,清除敏感数据
controller.authenticationEvents
    .where((s) => s == SecureApplicationAuthenticationStatus.FAILED)
    .listen((_) {
      // 清除缓存的用户数据
      userDataCache.clear();
      // 导航到登录页
      Navigator.of(context).pushReplacementNamed('/login');
    });

五、SecureApplicationState 不可变状态设计

5.1 不可变状态类


class SecureApplicationState {
  final bool locked;
  final bool secured;
  final bool paused;
  final bool authenticated;

  SecureApplicationState({
    this.locked = false,
    this.secured = false,
    this.paused = false,
    this.authenticated = false,
  });

  SecureApplicationState copyWith({
    bool? locked,
    bool? secured,
    bool? paused,
    bool? authenticated,
  }) {
    return SecureApplicationState(
      locked: locked ?? this.locked,
      secured: secured ?? this.secured,
      paused: paused ?? this.paused,
      authenticated: authenticated ?? this.authenticated,
    );
  }
}

5.2 为什么用不可变状态

  1. 线程安全:不可变对象天然线程安全
  2. 变更追踪:每次状态变更都产生新对象,方便调试
  3. 防止意外修改:不能直接修改字段,必须通过 copyWith

5.3 copyWith 模式

// 只修改 locked,其他保持不变
value = value.copyWith(locked: true);

// 同时修改多个字段
value = value.copyWith(locked: false, authenticated: true);

📌 设计启示:这种不可变状态 + copyWith 的模式在 Flutter 中非常常见(如 ThemeData、TextStyle),值得在自己的项目中借鉴。

六、架构设计对 OpenHarmony 适配的影响

6.1 适配工作量评估

层级 需要修改 工作量 原因
SecureApplication Widget 0 纯 Dart,跨平台通用
SecureApplicationController 0 纯 Dart,跨平台通用
SecureApplicationState 0 纯 Dart,跨平台通用
SecureApplicationProvider 0 纯 Dart,跨平台通用
SecureGate 0 纯 Flutter Widget
SecureApplicationNative 0 MethodChannel 调用不变
SecureApplicationPlugin.ets ✅ 新建 OpenHarmony 原生实现
pubspec.yaml ✅ 修改 添加 ohos 平台声明
ohos/ 目录 ✅ 新建 工程配置文件

6.2 原生端需要实现的方法

// OpenHarmony 原生端需要响应的 MethodChannel 方法
onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case "secure":   // 开启保护 → setWindowPrivacyMode(true)
    case "open":     // 关闭保护 → setWindowPrivacyMode(false)
    case "lock":     // 锁定(原生端可以空实现)
    case "unlock":   // 解锁(原生端可以空实现)
    case "opacity":  // 设置透明度(原生端可以空实现)
  }
}

6.3 原生端需要主动通知的事件

// 需要主动通知 Dart 层的事件
this.channel.invokeMethod("lock", null);  // 窗口失焦或进入后台时

这就是全部了。相比 flutter_speech 需要处理引擎创建、监听器回调、音频参数等复杂逻辑,secure_application 的原生端实现简洁得多。

6.4 架构优势总结

  • Dart 层零改动:所有业务逻辑在 Dart 层,跨平台通用
  • 原生端职责单一:只做窗口控制和事件监听
  • 测试友好:Dart 层可以用 Mock 测试,不依赖真机
  • 维护成本低:原生端代码量少,后续维护简单

总结

本文深入分析了 secure_application 的架构设计哲学:

  1. Widget 驱动模式:声明式 API,状态自动管理
  2. Dart 层重、Native 层轻:核心逻辑在 Dart 端,原生端只做平台对接
  3. ValueNotifier + InheritedWidget:轻量级状态管理,不引入额外依赖
  4. BehaviorSubject 事件流:响应式事件分发,新订阅者立即收到最新值
  5. 不可变状态:copyWith 模式保证状态变更的安全性

下一篇我们逐行解析 Dart 层的核心源码——从 SecureApplication Widget 到 SecureApplicationNative。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

请添加图片描述

secure_application 未锁定状态下的正常内容显示

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐