跨端路由统一方案:Flutter 与 OpenHarmony ArkTS 页面跳转的无缝集成

跨端路由统一方案:Flutter 与 OpenHarmony ArkTS 页面跳转的无缝集成

作者 :L、218
发布平台 :CSDN
发布时间 :2025年12月9日
关键词:Flutter、OpenHarmony、跨端路由、页面跳转、统一导航、DeepLink、Scheme 协议、Navigation 2.0、ArkTS Router


引言

在混合架构应用中,我们常遇到这样的场景:

❓ 用户在 OpenHarmony 原生首页点击"商品推荐",如何跳转到 Flutter 实现的商品详情页?

❓ 反之,Flutter 页面中的"我的订单"按钮,又该如何打开 ArkTS 编写的个人中心?

如果处理不当,就会出现:

  • ❌ 白屏或崩溃
  • ❌ 栈混乱(无法返回)
  • ❌ 数据传递失败

本文将由 L、218 带你构建一套 Flutter + OpenHarmony 统一路由系统,实现:

✅ 任意页面间自由跳转

✅ 支持参数传递与结果回调

✅ 兼容 DeepLink 和通知栏唤醒

✅ 统一协议格式 ohrouter://page/detail?id=123

这不是简单的 Navigator.push,而是一套真正可落地的跨框架导航解决方案


一、为什么需要统一路由?

场景 问题
多团队协作 Flutter 团队用 /product/123,ArkTS 团队用 Pages/ProductPage?pid=123
分布式流转 手机上点击链接,在智慧屏上打开对应页面
推送唤醒 点击通知,跳转至指定 Flutter 页面

若无统一标准,最终只能靠硬编码对接,维护成本极高。

我们的目标是:

🔗 写一次跳转逻辑,两端都能识别


二、整体架构设计

复制代码
+----------------------------+
|        DeepLink / Notification      |
|         ohrouter://...     |
+--------------↑-------------+
               |
       +--------↓--------+
       |   Router Center   | ← 统一入口
       | (C++/NDK Layer)   |
       +--------↑--------+
                |
     +----------+-----------+------------------+
     |                      |                  |
+----↓----+          +------↓------+    +------↓------+
| Flutter |          | OpenHarmony  |    | Other Device|
| Navigator|<--------| Router API  |    | (Distributed)|
+---------+  Bridge  +-------------+    +-------------+

所有跳转请求都通过中央路由表解析并分发。


三、定义统一协议:ohrouter://

我们自定义一种 Scheme 协议:

复制代码
ohrouter://{page_name}?{key=value}&{key=value}

示例

目标页面 跳转 URL
商品详情 ohrouter://product/detail?id=789&from=recommend
订单列表 ohrouter://order/list?type=paid
设置页 ohrouter://settings/theme?dark=true

💡 可结合 Figma 或语义化命名规范统一管理 page_name


四、实战步骤:从零搭建跨端路由系统

🧩 功能目标

  • 在 ArkTS 页面点击按钮,跳转到 Flutter 商品页
  • 支持传参 id=123
  • 返回时栈正确

步骤 1:创建中央路由注册表(C++ 层)

router_registry.h
cpp 复制代码
#ifndef ROUTER_REGISTRY_H
#define ROUTER_REGISTRY_H

#include <string>
#include <functional>
#include <map>

// 定义跳转处理器
typedef std::function<bool(const std::map<std::string, std::string>&)> RouteHandler;

class RouterRegistry {
public:
    static RouterRegistry& GetInstance();

    // 注册页面
    void RegisterRoute(const std::string& path, RouteHandler handler);

    // 解析并跳转
    bool Navigate(const std::string& url);

    // 辅助:解析 query 参数
    static std::map<std::string, std::string> ParseQuery(const std::string& query);

private:
    std::map<std::string, RouteHandler> routes_;
};

// C 导出函数
extern "C" {
    void register_route(const char* path, void* handler_fn);
    bool navigate_to(const char* url);
}

#endif
router_registry.cpp
cpp 复制代码
#include "router_registry.h"
#include <regex>

RouterRegistry& RouterRegistry::GetInstance() {
    static RouterRegistry instance;
    return instance;
}

void RouterRegistry::RegisterRoute(const std::string& path, RouteHandler handler) {
    routes_[path] = handler;
}

bool RouterRegistry::Navigate(const std::string& url) {
    // 解析 URL: ohrouter://product/detail?id=123
    std::regex pattern(R"(ohrouter://([^?]+)(?:\?(.*))?)");
    std::smatch match;
    if (!std::regex_match(url, match, pattern)) {
        return false;
    }

    std::string page = match[1].str();
    std::string query = match.size() > 2 ? match[2].str() : "";

    auto params = ParseQuery(query);

    auto it = routes_.find(page);
    if (it != routes_.end()) {
        return it->second(params);
    }

    return false;
}

std::map<std::string, std::string> RouterRegistry::ParseQuery(const std::string& query) {
    std::map<std::string, std::string> result;
    std::regex pair_regex("([^&=]+)=([^&]*)");
    auto begin = std::sregex_iterator(query.begin(), query.end(), pair_regex);
    auto end = std::sregex_iterator();

    for (auto i = begin; i != end; ++i) {
        std::string key = i->str(1);
        std::string value = i->str(2);
        result[key] = value;
    }
    return result;
}

// C 接口实现
void register_route(const char* path, void* handler_fn) {
    auto handler = reinterpret_cast<RouteHandler::result_type (*)(const std::map<std::string, std::string>&)>(handler_fn);
    RouterRegistry::GetInstance().RegisterRoute(std::string(path), [handler](const std::map<std::string, std::string>& p) {
        return handler(p);
    });
}

bool navigate_to(const char* url) {
    return RouterRegistry::GetInstance().Navigate(std::string(url));
}

步骤 2:Flutter 端注册页面

创建 lib/router/flutter_router.dart
dart 复制代码
import 'dart:ffi';
import 'package:ffi/ffi.dart';

class FlutterRouter {
  final DynamicLibrary _dylib;
  final int Function(Pointer<Utf8>) _registerRoute;
  final bool Function(Pointer<Utf8>) _navigateTo;

  FlutterRouter(String soPath)
      : _dylib = DynamicLibrary.open(soPath),
        _registerRoute = _dylib
            .lookup<NativeFunction<Int32 Function(Pointer<Utf8>)>>('register_route')
            .asFunction(),
        _navigateTo = _dylib
            .lookup<NativeFunction<Bool Function(Pointer<Utf8>)>>('navigate_to')
            .asFunction();

  void registerAllRoutes() {
    _registerRoute(_toUtf8('product/detail'), _onProductDetail);
  }

  Pointer<Utf8> _toUtf8(String str) {
    return Utf8.toUtf8(str);
  }

  int _onProductDetail(Map<String, String> params) {
    final id = params['id'];
    print('【Flutter Router】打开商品详情,ID=$id');

    // 使用 Navigation 2.0 或普通 Navigator
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // 假设你有全局 navigatorKey
      navigatorKey.currentState?.push(
        MaterialPageRoute(
          builder: (_) => ProductDetailPage(productId: id!),
        ),
      );
    });

    return 1; // 成功
  }

  bool open(String url) {
    final cStr = _toUtf8(url);
    final result = _navigateTo(cStr);
    malloc.free(cStr);
    return result;
  }
}

⚠️ 注意:实际需使用回调函数指针注册,此处简化为伪代码演示


步骤 3:OpenHarmony 端接收跳转

在 ArkTS 中调用原生路由
ets 复制代码
@Entry
@Component
struct HomePage {
  build() {
    Column({ space: 20 }) {
      Text('欢迎来到首页')
        .fontSize(20).fontWeight(FontWeight.Bold)

      Button('跳转到 Flutter 商品页')
        .onClick(() => {
          this.navigateToFlutter()
        })
    }
    .padding(30)
  }

  navigateToFlutter(): void {
    const url = 'ohrouter://product/detail?id=789&from=home'
    nativeModule.navigate(url, (err, result) => {
      if (err) {
        console.error('跳转失败:', err)
      } else {
        console.info('跳转成功:', result)
      }
    })
  }
}
NDK 桥接层 native_router.cpp
cpp 复制代码
#include <napi/native_api.h>
#include "router_registry.h"

static napi_value Navigate(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char url[512];
    size_t len;
    napi_get_value_string_utf8(env, args[0], url, sizeof(url), &len);

    bool success = navigate_to(url);

    napi_value result;
    napi_get_boolean(env, success, &result);
    return result;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        { "navigate", nullptr, Navigate, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, 1, desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "router",
    .nm_priv = nullptr,
    .reserved = { 0 }
};

extern "C" __attribute__((constructor)) void RegisterModule() {
    napi_register_module(&demoModule);
}

步骤 4:配置 module.json5 并打包

确保 .so 文件和模块注册正确。


控制台日志
复制代码
INFO: 【Flutter Router】打开商品详情,ID=789
DEBUG: Push to ProductDetailPage

五、进阶功能建议

功能 实现方式
返回结果回调 类似 startActivityForResult,支持带回数据
拦截器机制 登录校验、埋点统计
分布式跳转 结合 OH 分布式能力,跳转到其他设备
动态注册 插件化页面热加载

七、结语:路由即契约

在一个复杂的混合应用中,路由就是组件之间的通信契约

当我们用统一协议替代硬编码跳转时,我们就实现了:

✅ 解耦

✅ 可维护

✅ 易扩展

这正是现代移动架构的核心理念。

💬 如果你也正在做类似的事情,欢迎关注我 @L、218,后续将推出:

  • 《跨端状态共享 SDK 设计》
  • 《Flutter on OpenHarmony 性能优化白皮书》
  • 《基于 Figma 的自动化 UI 同步方案》

一起为中国基础软件生态添砖加瓦!


参考资料


❤️ 欢迎交流

你在项目中是怎么管理多端跳转的?有没有踩过坑?

欢迎在评论区留言分享你的经验!

📌 关注我 @L、218,获取更多 Flutter × OpenHarmony 实战干货,助你成为下一代操作系统生态的先行者!


版权声明 :本文原创,转载请注明出处及作者。商业转载请联系授权。
作者主页https://blog.csdn.net/L218

点赞 + 收藏 + 转发,让更多人告别"跳转黑盒"的痛苦!


📌 标签:#Flutter #OpenHarmony #跨端路由 #页面跳转 #DeepLink #统一导航 #Scheme协议 #ArkTS #L218 #CSDN #2025工程实践

https://openharmonycrossplatform.csdn.net/content

相关推荐
zlpzlpzyd8 小时前
vue.js 3中全局组件和局部组件的区别
前端·javascript·vue.js
浩星8 小时前
css实现类似element官网的磨砂屏幕效果
前端·javascript·css
一只小风华~8 小时前
Vue.js 核心知识点全面解析
前端·javascript·vue.js
2022.11.7始学前端8 小时前
n8n第七节 只提醒重要的待办
前端·javascript·ui·n8n
徐小夕8 小时前
知识库创业复盘:从闭源到开源,这3个教训价值百万
前端·javascript·github
xhxxx8 小时前
函数执行完就销毁?那闭包里的变量凭什么活下来!—— 深入 JS 内存模型
前端·javascript·ecmascript 6
L、2189 小时前
统一日志与埋点系统:在 Flutter + OpenHarmony 混合架构中实现全链路可观测性
javascript·华为·智能手机·electron·harmonyos
十一.3669 小时前
103-105 添加删除记录
前端·javascript·html
陳陈陳9 小时前
闭包、栈堆与类型之谜:JS 内存机制全解密,面试官都惊了!
前端·javascript
Tzarevich9 小时前
从栈与堆到闭包:深入 JavaScript 内存机制
javascript·面试