# RNFlutterH5
**Repository Path**: scenario-samples/rnflutter-h5
## Basic Information
- **Project Name**: RNFlutterH5
- **Description**: 【鸿蒙 Harmony Next 示例 代码】本示例主要演示如何实现RN、Flutter、H5三个框架的模块页面相互跳转的功能。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-11-26
- **Last Updated**: 2025-11-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 实现在RN、Flutter、H5页面之间互相跳转的Demo
## 场景介绍
应用在开发过程中可能涉及多个模块分别使用不同的框架开发的module,例如部分module涉及RN、Flutter、H5,本示例主要演示如何实现三个框架的模块页面相互跳转的功能。
## 效果图预览
## 实现思路
- RN实现:RN侧通过TurboModule实现RN与ArkTS通信,进行页面路由跳转。
- RN页面示例代码:
```tsx
{
SampleTurboModule.pushStringToHarmony('pages/FlutterPages', 1);
}}
/>
{
SampleTurboModule.pushStringToHarmony('pages/RNPage', 1);
}}
/>
{
SampleTurboModule.pushStringToHarmony('pages/H5Page', 1);
}}
/>
```
- RN侧TurboModule示例代码:
```tsx
// 使用自定义的TurboModules
export interface SpecSampleTurboModule extends TurboModule {
pushStringToHarmony(arg: string, id?: number): string;
}
export default TurboModuleRegistry.getEnforcing('SampleTurboModule');
```
- ArkTS侧TurboModule示例代码:
```TS
export class SampleTurboModule extends TurboModule {
public static readonly NAME = 'SampleTurboModule';
pushStringToHarmony(arg: string, id?: number): string {
if (id) {
let data: TurboModuleEventData = { param: arg }
emitter.emit({ eventId: id }, { data: data })
}
return arg;
}
}
```
- Flutter实现:Flutter使用MethodChannel实现与ArkTS通信,通过emitter发送通知到主页面,实现路由跳转到RN和H5页面。
- Flutter页面需要声明MethodChannel对象,按钮点击触发Flutter调用ArkTS方法,示例代码如下:
```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '居中按钮示例',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CenterButtonPage(),
);
}
}
class CenterButtonPage extends StatelessWidget {
final _platform = const MethodChannel('samples.flutter.dev/routerRN');
const CenterButtonPage({super.key});
void _handleRNButtonClick() async {
await _platform.invokeMethod('routerRN');
}
void _handleH5ButtonClick() async {
await _platform.invokeMethod('routerH5');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter页面'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _handleRNButtonClick,
child: Text('点击我跳转RN页面'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
textStyle: TextStyle(fontSize: 20),
),
),
SizedBox(height: 20), // 添加一些间距
ElevatedButton(
onPressed: _handleH5ButtonClick,
child: Text('点击我跳转到H5'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
textStyle: TextStyle(fontSize: 20),
),
),
],
),
),
);
}
}
```
- MethodChannel代码示例:
```TypeScript
export default class RouterPlugin implements FlutterPlugin, AbilityAware {
private channel?: MethodChannel;
private api = new RouterApi();
onAttachedToAbility(binding: AbilityPluginBinding): void {
Log.i(TAG, "onAttachedToAbility")
}
onDetachedFromAbility(): void {
Log.i(TAG, "onDetachedFromAbility")
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "onAttachedToEngine");
this.channel = new MethodChannel(binding.getBinaryMessenger(), "samples.flutter.dev/routerRN");
let that = this;
this.channel.setMethodCallHandler({
onMethodCall(call: MethodCall, result: MethodResult) {
switch (call.method) {
case "routerRN":
that.api.routerRN(result);
break;
case "routerH5":
that.api.routerH5(result);
break;
}
}
})
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "onDetachedFromEngine");
this.channel?.setMethodCallHandler(null);
}
getUniqueClassName(): string {
return "RouterPlugin";
}
}
class RouterApi {
routerH5(result: MethodResult) {
// 发送消息跳转页面
emitter.emit({ eventId: 1 }, { data: {param:"pages/H5Page"} })
result.success(null);
}
routerRN(result: MethodResult) {
// 发送消息跳转页面
emitter.emit({ eventId: 1 }, { data: {param:"pages/RNPage"} })
result.success(null);
}
}
```
- FlutterEntry示例代码:
```TypeScript
export class NavFlutterEntry extends FlutterEntry {
pathStack?: NavPathStack;
constructor(context: Context, params: Record = {}, pathStack?: NavPathStack) {
super(context, params)
this.pathStack = pathStack;
}
popSystemNavigator(): boolean {
if (this.pathStack) {
this.pathStack.pop();
return true;
}
return false;
}
configureFlutterEngine(flutterEngine: FlutterEngine): void {
super.configureFlutterEngine(flutterEngine);
GeneratedPluginRegistrant.registerWith(flutterEngine);
this.delegate?.addPlugin(new RouterPlugin());
}
}
```
- EntryAbility中需要在对应生命周期内调用FlutterManager的pushUIAbility和pushWindowStage方法,示例代码如下:
```TypeScript
export default class EntryAbility extends RNAbility implements ExclusiveAppComponent{
detachFromFlutterEngine(): void {
}
getAppComponent(): UIAbility {
return this;
}
getPagePath() {
return 'pages/Index';
}
onCreate(want: Want): void {
FlutterManager.getInstance().pushUIAbility(this);
super.onCreate(want)
}
onDestroy(): void | Promise {
FlutterManager.getInstance().popUIAbility(this);
}
onWindowStageCreate(windowStage: window.WindowStage) {
FlutterManager.getInstance().pushWindowStage(this, windowStage);
super.onWindowStageCreate(windowStage)
try {
let options: dataPreferences.Options = {
name: 'test'
};
preferences = dataPreferences.getPreferencesSync(this.context, options)
} catch (err) {
console.error('Failed to get preferences')
}
AppStorage.setOrCreate('RNAbility', this);
}
onWindowStageDestroy() {
FlutterManager.getInstance().popWindowStage(this);
}
}
```
- H5实现:H5新增button,跳转RN和Flutter页面,H5使用JsBridge调用ArkTS方法。
- H5示例代码:
```Html
Input Example
```
- JsBridge使用emitter发送消息,参数分别为FlutterPages、RNPage,由主页面接收后实现路由跳转,示例代码如下:
```TypeScript
class JsBridge {
private uiContext: UIContext
constructor(uiContext: UIContext) {
this.uiContext = uiContext
}
routeFlutter() {
emitter.emit({ eventId: 1 }, { data: {param:"pages/FlutterPages"} })
}
routeRN() {
emitter.emit({ eventId: 1 }, { data: {param:"pages/RNPage"} })
}
}
@Component
export struct H5Page {
uiContext?: UIContext
webController: webview.WebviewController = new webview.WebviewController();
@State js_bridge: JsBridge | null = null;
@Consume('pagePathsGoods') pagePathsGoods: NavPathStack;
aboutToAppear() {
this.uiContext = this.getUIContext();
this.js_bridge = new JsBridge(this.uiContext)
}
aboutToDisappear(): void {
this.webController.deleteJavaScriptRegister("JsBridge");
}
build() {
NavDestination() {
Column() {
Web({
src: $rawfile('test.html'),
controller: this.webController
})
// 将对象注入到web端
.javaScriptProxy({
object: this.js_bridge,
name: "JsBridge",
methodList: ["routeFlutter","routeRN"],
controller: this.webController
})
}
.margin({top:50})
.width('100%')
.height('100%')
}
.title("H5页面")
.hideTitleBar(true)
}
}
```
- ArkUI实现:ArkTS中使用emitter接收从Flutter、RN、H5中跳转页面的通知,通过Navigation进行页面路由跳转,默认展示RN页面,示例代码:
```TypeScript
@Builder
PageMap(name: string, param: string) {
if (name === 'FlutterPages') {
FlutterPages()
} else if (name === 'RNPage') {
RNPage()
} else if (name === 'H5Page') {
H5Page()
}
}
aboutToAppear() {
emitter.on({ eventId: 1 }, (eventData) => {
if (eventData.data) {
console.log('[TurboModule] param = ' + eventData.data['param']);
const path: string = eventData.data['param']
if (path === "pages/FlutterPages") {
this.pagePathsGoods.pushPath({ name: "FlutterPages" })
} else if (path === "pages/RNPage") {
this.pagePathsGoods.pushPath({ name: "RNPage" })
} else if (path === "pages/H5Page") {
this.pagePathsGoods.pushPath({ name: "H5Page" })
}
}
});
}
build() {
Navigation(this.pagePathsGoods) {
if (this.isMetroAvailable) {
MetroBaseRN({
moduleName: this.moduleName,
}).align(Alignment.Top).margin({ top: 0 })
} else if (this.instance) {
BaseRN({
rnInstance: this.instance,
moduleName: this.moduleName,
bundlePath: this.bundlePath,
}).align(Alignment.Top).margin({ top: 0 })
}
}
.navDestination(this.PageMap)
.hideTitleBar(true)
.hideToolBar(true)
}
```
## 使用说明
- 前提条件:本地完成Flutter环境搭建、RN环境搭建,本地已创建RN工程和Flutter module。
- 本示例以RN工程为主工程,Flutter_module以har包形式引入,H5在harmony工程内。
- 运行demo流程:
1. 在fluttermodule中执行flutter build har ,将.ohos中的har包复制到harmony/har目录下。
2. 在RNProject工程中,先执行npm install,再执行npm run codegen,再执行npm run dev:all。
3. 使用Deveco Studio打开harmony工程,签名运行到真机即可。
## 约束与限制
- 本示例支持API Version 17 Release及以上版本。
- 本示例支持HarmonyOS 5.0.5 Release SDK及以上版本。
- 本示例需要使用DevEco Studio 5.0.5 Release及以上版本进行编译运行。
## 工程结构&模块类型
```
|────flutter_module //flutter module
| |──lib
| | |──main.dart // flutter页面
| |──pubspec.yam //flutter依赖配置
|
|────RNProject // RN主工程
| |──harmony // harmonyOS主工程
| |────entry // ets代码
| |────src
| |──────bundles //RN 模块代码
| |────────Goods //RN页面
```
## 参考文档
[RN开发文档](https://gitcode.com/openharmony-sig/ohos_react_native/tree/0.72.5-ohos-5.0-release/docs/zh-cn)
[RN TurboModule](https://gitcode.com/openharmony-sig/ohos_react_native/blob/0.72.5-ohos-5.0-release/docs/zh-cn/TurboModule.md)
[Flutter开发文档](https://gitcode.com/openharmony-sig/flutter_samples/tree/master/ohos/docs)
[Flutter MethodChannel](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8Flutter%E4%B8%8EOpenHarmony%E9%80%9A%E4%BF%A1%20FlutterChannel.md)
[H5页面调用应用侧函数](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/web-in-page-app-function-invoking)
## ChangeLog:修改日志
| 修改内容 | 时间 |
|-------|---------|
| 第一次提交 | 2025.09 |