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

Flutter 三方库 chrome_extension 鸿蒙跨核混血双向协议映射沙盒环境适配实录:强势拉起浏览器扩展内核生命周期管理并赋予终端对外部注入插件管线级调度安全权限

封面图

前言

在 OpenHarmony 平台上,大量的 B/S 架构应用需要向混合(Hybrid)模式迁移。尤其是在企业办公领域,许多原本运行在 Chrome 浏览器上的扩展程序(Extensions)承载了核心业务逻辑(如表单自动填报、安全证书调度等)。chrome_extension 库旨在为 Dart 环境提供对 Chrome 扩展特有 API 的类型安全封装。本文将实战介绍如何在鸿蒙端利用此库构建高性能的 Web 控制面板,并打通 Flutter 到鸿蒙 Web 组件的“次元壁”。

一、原理解析 / 概念介绍

1.1 基础原理/概念介绍

chrome_extension 库通过 JS 互操作(JS Interop)机制,将原本属于浏览器环境的 chrome.* 命名空间 API(如 chrome.runtime, chrome.tabs, chrome.storage)映射为 Dart 类。在鸿蒙端,这通常配合 Web 组件的 Javascript 执行机制来实现其跨平台逻辑调用。

调用指令

JS 代码串注入

访问浏览器 API

Callback 返回

鸿蒙 Flutter UI 线程

chrome_extension 封装库

鸿蒙 Web 控制桥 (WebMessagePort)

鸿蒙内置 Webview 核心

chrome.runtime / tabs

执行扩展逻辑 (如跨域请求)

结果返回 Flutter UI 渲染

1.2 为什么在鸿蒙上使用它?

  1. 资产复用:已有的 Chrome 插件核心 Dart 逻辑可以平滑迁移至鸿蒙应用,无需重写。
  2. 安全隔离:利用浏览器扩展的 API 权限模型,在鸿蒙端实现比普通 Webview 更细粒度的安全控制。
  3. 开发效率:提供强类型定义的 API,规避了大量手动编写字符串注入代码的行为。

二、鸿蒙基础指导

2.1 适配情况

  1. 是否原生支持?:由于浏览器 API 依赖环境,需在鸿蒙 Web 宿主环境下运行。
  2. 是否鸿蒙官方支持?:支持鸿蒙 Web 组件的 API 透传模式。
  3. 是否社区支持?:Dart 官方团队(Package authors: dart.dev)背书,属于高标准库。
  4. 是否需要安装额外的 package?:必须配合 js 库以及具备 Web 内容展示能力的插件。

2.2 适配代码

在鸿蒙项目的 pubspec.yaml 中配置:

dependencies:
  chrome_extension: ^2.0.0
  js: ^0.6.7

三、核心 API / 组件详解

3.1 基础配置(模拟存储扩展 API)

import 'package:chrome_extension/storage.dart';
import 'package:js/js_util.dart';
// 实现一个鸿蒙端兼容的扩展存储逻辑
void syncConfigWithChromeExtension() {
  // 获取 chrome.storage.local 的引用
  final storage = chrome.storage.local;
  // 真实业务:存储应用配置
  storage.set(jsify({
    'harmony_auth_token': 'ABC_123',
    'last_sync_time': DateTime.now().toIso8601String()
  }));
  // 获取数据回调
  storage.get(['harmony_auth_token']).then((result) {
    String? token = getProperty(result, 'harmony_auth_token');
    _processHarmonyAuth(token);
  });
}

示例图

3.2 高级定制(标签页生命周期监听)

import 'package:chrome_extension/tabs.dart';
// 针对鸿蒙多窗口 Web 应用的标签页监听器
void setupTabMonitor() {
  // 监听新标签页创建事件(在鸿蒙 Webview 触发新窗口时联动)
  chrome.tabs.onCreated.addListener(allowInterop((Tab tab) {
    // 真实业务:获取新窗口的 URL 并汇报给鸿蒙原生侧
    final String? newUrl = tab.url;
    if (newUrl != null) {
      _reportSecureUrlToHarmony(newUrl);
    }
  }));
}

四、典型应用场景

4.1 示例场景一:鸿蒙企业应用的自动化表单填充

在鸿蒙端政务系统中,利用已有的 Chrome 脚本逻辑自动填充复杂的 Web 表单。

import 'package:chrome_extension/runtime.dart';
// 向鸿蒙 Web 页面发送自动填单指令
void triggerAutoFill() {
  final message = jsify({
    'action': 'FILL_FORM',
    'data': {'id_number': '123456...', 'name': '张鸿蒙'}
  });
  // 真实调用 runtime.sendMessage 打通逻辑链路
  chrome.runtime.sendMessage(null, message, null, allowInterop((response) {
    // 监听填单结果回执
    _handleFormResponse(response);
  }));
}

示例图

4.2 示例场景二:鸿蒙单机侧的浏览器插件化消息中转

利用 runtime.onMessage 在 Flutter 与 嵌入式 Web 资源间构建高安全性的双向总线。

import 'package:chrome_extension/runtime.dart';
// 鸿蒙原生侧接收来自 Web 文档的消息
void listenFromWebContext() {
  chrome.runtime.onMessage.addListener(allowInterop((message, sender, sendResponse) {
    // 根据消息类型分发到鸿蒙系统原生对话框
    if (getProperty(message, 'type') == 'ALERT') {
      _showHarmonyDialog(getProperty(message, 'content'));
      // 回复成功信号
      sendResponse(jsify({'status': 'SUCCESS'}));
    }
    return true; // 保持通道异步开启
  }));
}

五、OpenHarmony 平台适配挑战

5.1 Platform Channel 与原生桥接 - JS 环境注入 (6.2)

chrome_extension 能够在 Flutter 中编译成功,但在鸿蒙端运行时,最核心的挑战在于 chrome 全局变量的注入。鸿蒙原生的 Webview 默认不包含该对象。开发者必须在鸿蒙 ArkTS 侧,通过 WebviewController.runJavaScript 在页面加载前动态注入一个符合 Chrome Extension 规范的 Mock 对象层,这样 Flutter 端的库调用才能找到真实的物理映射,从而避免 undefined 导致的运行时异常。

5.2 平台差异化处理 - 权限与生命周期 (6.6)

鸿蒙系统的 UIAbility 对于 Web 容器的生命周期管理与标准浏览器桌面端有巨大差异。例如,扩展中的 background_scripts 在浏览器是常驻的,但在鸿蒙应用挂起时,对应的 Web 环境可能会被冻结。建议结合鸿蒙的 backgroundTaskManager,确保在执行核心扩展逻辑(如离线同步)时,通过 NAPI 向系统申请短时保活,以防插件任务中途夭折。

六、综合实战演示

下面是一个用于鸿蒙应用的高性能综合实战展示页面 HomePage.dart。为了符合真实工程标准,我们假定已经在 main.dart 中建立好了全局鸿蒙根节点初始化,并将应用首页指向该层进行渲染展现。你只需关注本页面内部的复杂交互处理状态机转移逻辑:

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
// ignore: unused_import
import 'package:chrome_extension/chrome_extension.dart';
// ignore: unused_import
import 'package:js/js.dart';

/// 鸿蒙跨核混血双向协议映射沙盒环境适配展示
/// 核心功能驱动:强势拉起浏览器扩展内核生命周期管理并赋予终端对外部注入插件管线级调度安全权限
class ChromeExtension6Page extends StatefulWidget {
  const ChromeExtension6Page({super.key});

  
  State<ChromeExtension6Page> createState() => _ChromeExtension6PageState();
}

class _ChromeExtension6PageState extends State<ChromeExtension6Page> {
  final List<String> _consoleLogs = [
    ">> Initializing Harmony-Bridge Plugin Core...",
    ">> Detecting Chrome Runtime Environment...",
  ];

  bool _isKernelActive = false;
  double _loadProgress = 0.0;

  void _addLog(String log) {
    setState(() {
      _consoleLogs.add(">> $log");
    });
  }

  /// 模拟 Chrome 扩展逻辑调用(实际应在 Web 环境下触发)
  /// 此处展示如何编写使用 chrome_extension API 的代码
  void _simulateExtensionCall() {
    if (!kIsWeb) {
      _addLog("PLATFORM_MISMATCH: chrome.* API 仅在 Web 环境下激活。");
      _addLog("SIMULATING: 正在通过鸿蒙原生桥接注入 Mock 控制层...");
    }

    _addLog("EXEC: chrome.runtime.getManifest()");
    _addLog("RESULT: { 'name': 'Harmony_Helper', 'version': '6.0.2' }");

    _addLog("EXEC: chrome.storage.local.set({ 'sync': true })");
    _addLog("EVENT: chrome.tabs.onUpdated.addListener(...)");
  }

  Future<void> _bootKernel() async {
    if (_isKernelActive) return;

    setState(() => _loadProgress = 0.1);
    await Future.delayed(const Duration(milliseconds: 500));
    _addLog("Loading Manifest.json...");

    setState(() => _loadProgress = 0.4);
    await Future.delayed(const Duration(seconds: 1));
    _addLog("Injecting Content Scripts into Harmony-Webview...");

    setState(() => _loadProgress = 0.8);
    await Future.delayed(const Duration(milliseconds: 800));
    _addLog("Permissions Granted: [tabs, storage, runtime]");

    setState(() {
      _loadProgress = 1.0;
      _isKernelActive = true;
    });

    _simulateExtensionCall();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0F172A),
      appBar: AppBar(
        title: const Text('扩展内核调度舱',
            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
        backgroundColor: Colors.transparent,
        elevation: 0,
      ),
      body: Padding(
        padding: const EdgeInsets.all(24.0),
        child: Column(
          children: [
            _buildKernelMonitor(),
            const SizedBox(height: 24),
            _buildConsole(),
            const SizedBox(height: 24),
            _buildActionSlot(),
          ],
        ),
      ),
    );
  }

  Widget _buildKernelMonitor() {
    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.indigo.shade900, Colors.blue.shade800],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
              color: Colors.blue.withOpacity(0.2),
              blurRadius: 20,
              offset: const Offset(0, 10))
        ],
      ),
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Text("KERNEL STATUS",
                  style: TextStyle(
                      color: Colors.white70, fontSize: 12, letterSpacing: 2)),
              _isKernelActive
                  ? const Icon(Icons.check_circle,
                      color: Colors.greenAccent, size: 20)
                  : const Icon(Icons.pending,
                      color: Colors.amberAccent, size: 20),
            ],
          ),
          const SizedBox(height: 20),
          ClipRRect(
            borderRadius: BorderRadius.circular(10),
            child: LinearProgressIndicator(
              value: _loadProgress,
              backgroundColor: Colors.white10,
              color: Colors.cyanAccent,
              minHeight: 8,
            ),
          ),
          const SizedBox(height: 12),
          Text(
            _isKernelActive ? "EXTENSION CORE ACTIVE" : "AWAITING INJECTION...",
            style: const TextStyle(
                color: Colors.white,
                fontSize: 16,
                fontWeight: FontWeight.w900,
                fontFamily: 'monospace'),
          )
        ],
      ),
    );
  }

  Widget _buildConsole() {
    return Expanded(
      child: Container(
        width: double.infinity,
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: Colors.black,
          borderRadius: BorderRadius.circular(16),
          border: Border.all(color: Colors.white10),
        ),
        child: ListView.builder(
          reverse: true,
          itemCount: _consoleLogs.length,
          itemBuilder: (context, index) {
            final log = _consoleLogs[_consoleLogs.length - 1 - index];
            return Padding(
              padding: const EdgeInsets.symmetric(vertical: 4),
              child: Text(
                log,
                style: const TextStyle(
                    color: Colors.greenAccent,
                    fontSize: 12,
                    fontFamily: 'Courier'),
              ),
            );
          },
        ),
      ),
    );
  }

  Widget _buildActionSlot() {
    return SizedBox(
      width: double.infinity,
      height: 60,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          backgroundColor: _isKernelActive
              ? Colors.green.withOpacity(0.2)
              : Colors.blueAccent,
          foregroundColor: Colors.white,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        ),
        onPressed: _bootKernel,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(_isKernelActive ? Icons.auto_mode : Icons.electrical_services),
            const SizedBox(width: 16),
            Text(
              _isKernelActive ? "插件管线运行中..." : "激活浏览器扩展宿主",
              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
          ],
        ),
      ),
    );
  }
}

示例图

七、总结

本文全方位探讨了 chrome_extension 库在 OpenHarmony 环境下的混合开发实战,重点阐述了如何通过 JS 互操作机制将遗留的插件资产迁移至鸿蒙应用。通过对 runtimetabsstorage 的类型安全调用,开发者能显著提升 Web 内容在鸿蒙端的控制力。后续进阶可以探讨如何结合鸿蒙底层的 NAPI 编写高性能的消息转发代理,彻底打通 Flutter 端与鸿蒙 Web 容器底层的二进制通信。

Logo

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

更多推荐