跨端路由统一方案: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 同步方案》
一起为中国基础软件生态添砖加瓦!
参考资料
- OpenHarmony Router 文档:https://developer.harmonyos.com/cn/docs/documentation/doc-references-v5/router-0000001122556517
- Flutter Navigation 2.0:https://docs.flutter.dev/ui/navigation
- URI 协议设计指南:RFC 3986
- 示例项目 GitHub:https://github.com/l218/ohrouter
❤️ 欢迎交流
你在项目中是怎么管理多端跳转的?有没有踩过坑?
欢迎在评论区留言分享你的经验!
📌 关注我 @L、218,获取更多 Flutter × OpenHarmony 实战干货,助你成为下一代操作系统生态的先行者!
版权声明 :本文原创,转载请注明出处及作者。商业转载请联系授权。
作者主页 :https://blog.csdn.net/L218
✅ 点赞 + 收藏 + 转发,让更多人告别"跳转黑盒"的痛苦!
📌 标签:#Flutter #OpenHarmony #跨端路由 #页面跳转 #DeepLink #统一导航 #Scheme协议 #ArkTS #L218 #CSDN #2025工程实践