Flutter 与鸿蒙原生交互传参教程
本教程将手把手教你实现 Flutter 向鸿蒙原生传递参数,并接收返回结果。
效果图

最终效果
- Flutter 页面有两个输入框,输入两个数字
- 点击按钮调用鸿蒙原生方法
- 鸿蒙原生计算两数之和
- 将结果返回给 Flutter 页面显示
项目结构
项目根目录/
├── lib/
│ └── pages/
│ └── Demo/
│ └── index.dart ← Flutter 页面(步骤1)
└── ohos/
└── entry/
└── src/
└── main/
└── ets/
├── entryability/
│ └── EntryAbility.ets ← 注册插件(步骤3)
└── plugins/
└── NativePlugin.ets ← 鸿蒙原生插件(步骤2)
步骤 1:创建 Flutter 页面
1.1 新建文件夹
在 lib/pages/ 目录下新建 Demo 文件夹。
1.2 创建 index.dart
在 Demo 文件夹中新建 index.dart 文件,写入以下代码:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// Demo 页面 - Flutter 与鸿蒙原生交互:传参计算
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
// MethodChannel 名称需要与鸿蒙端一致
static const platform = MethodChannel('com.example.native/channel');
final TextEditingController _num1Controller = TextEditingController();
final TextEditingController _num2Controller = TextEditingController();
String _result = '';
bool _isLoading = false;
// 调用鸿蒙原生方法进行计算
Future<void> _calculate() async {
final num1 = int.tryParse(_num1Controller.text) ?? 0;
final num2 = int.tryParse(_num2Controller.text) ?? 0;
setState(() {
_isLoading = true;
_result = '';
});
try {
// 调用鸿蒙原生的 calculate 方法,传入两个数字
final result = await platform.invokeMethod('calculate', {
'num1': num1,
'num2': num2,
});
setState(() => _result = '计算结果: $result');
} on PlatformException catch (e) {
setState(() => _result = '调用失败: ${e.message}');
} on MissingPluginException {
setState(() => _result = '插件未注册,请检查鸿蒙端代码');
} finally {
setState(() => _isLoading = false);
}
}
@override
void dispose() {
_num1Controller.dispose();
_num2Controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter ↔ 鸿蒙 计算器'),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 说明卡片
_buildInfoCard(),
const SizedBox(height: 20),
// 输入区域
_buildInputSection(),
const SizedBox(height: 20),
// 计算按钮
ElevatedButton(
onPressed: _isLoading ? null : _calculate,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'调用鸿蒙原生计算',
style: TextStyle(fontSize: 16, color: Colors.white),
),
),
const SizedBox(height: 20),
// 结果显示
_buildResultCard(),
],
),
),
);
}
Widget _buildInfoCard() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.blue[200]!),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('📱 Flutter 与鸿蒙原生交互示例',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
SizedBox(height: 8),
Text('1. 在下方输入两个数字'),
Text('2. 点击按钮调用鸿蒙原生方法'),
Text('3. 鸿蒙端计算后返回结果'),
],
),
);
}
Widget _buildInputSection() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('输入两个数字',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
controller: _num1Controller,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: '数字 1',
border: OutlineInputBorder(),
hintText: '请输入第一个数字',
),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text('+', style: TextStyle(fontSize: 24)),
),
Expanded(
child: TextField(
controller: _num2Controller,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: '数字 2',
border: OutlineInputBorder(),
hintText: '请输入第二个数字',
),
),
),
],
),
],
),
),
);
}
Widget _buildResultCard() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: _result.contains('失败') ? Colors.red[50] : Colors.green[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: _result.contains('失败') ? Colors.red[200]! : Colors.green[200]!,
),
),
child: Column(
children: [
const Text('鸿蒙原生返回结果',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.grey)),
const SizedBox(height: 8),
Text(
_result.isEmpty ? '等待计算...' : _result,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: _result.contains('失败') ? Colors.red : Colors.green[700],
),
),
],
),
);
}
}
代码说明
| 代码 | 作用 |
|---|---|
MethodChannel('com.example.native/channel') |
创建通信通道,名称需与鸿蒙端一致 |
platform.invokeMethod('calculate', {...}) |
调用鸿蒙原生方法,传递参数 |
{'num1': num1, 'num2': num2} |
传递给鸿蒙端的参数(Map 格式) |
步骤 2:创建鸿蒙原生插件
2.1 新建文件夹
在 ohos/entry/src/main/ets/ 目录下新建 plugins 文件夹(如果不存在)。
2.2 创建 NativePlugin.ets
在 plugins 文件夹中新建 NativePlugin.ets 文件,写入以下代码:
typescript
import { FlutterEngine, MethodChannel, MethodCall, MethodResult, MethodCallHandler } from '@ohos/flutter_ohos';
/**
* 原生交互插件
*/
export class NativePlugin implements MethodCallHandler {
private channel: MethodChannel | null = null;
constructor() {}
// 注册到 FlutterEngine
onAttachedToEngine(flutterEngine: FlutterEngine): void {
// 创建 MethodChannel,名称需要与 Flutter 端一致
this.channel = new MethodChannel(
flutterEngine.dartExecutor.getBinaryMessenger(),
'com.example.native/channel'
);
// 设置方法调用处理器
this.channel.setMethodCallHandler(this);
}
// 处理 Flutter 端的方法调用
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case 'calculate':
// 从 Map 中获取参数(重要:Flutter 传递的 Map 需要用这种方式获取)
const argsMap = call.args as Map<string, Object>;
const n1 = argsMap.get('num1') as number || 0;
const n2 = argsMap.get('num2') as number || 0;
// 计算两数之和
const sum = n1 + n2;
// 打印日志
console.info('[NativePlugin] Calculate: ' + n1 + ' + ' + n2 + ' = ' + sum);
// 返回结果给 Flutter
result.success(sum);
break;
default:
result.notImplemented();
break;
}
}
// 从 FlutterEngine 解绑
onDetachedFromEngine(): void {
if (this.channel) {
this.channel.setMethodCallHandler(null);
this.channel = null;
}
}
}
代码说明
| 代码 | 作用 |
|---|---|
call.args as Map<string, Object> |
Flutter 传递的 Map 参数需要转换为 Map 类型 |
argsMap.get('num1') |
通过 key 获取 Map 中的值 |
MethodChannel(..., 'com.example.native/channel') |
创建通道,名称必须与 Flutter 端一致 |
call.method |
获取 Flutter 调用的方法名 |
result.success(sum) |
返回结果给 Flutter |
重要提示 :Flutter 传递的 Map 参数在鸿蒙端不能直接用 interface 接收,必须用
Map<string, Object>类型,然后通过.get('key')获取值。
步骤 3:注册插件
3.1 修改 EntryAbility.ets
打开 ohos/entry/src/main/ets/entryability/EntryAbility.ets,修改为:
typescript
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { NativePlugin } from '../plugins/NativePlugin';
export default class EntryAbility extends FlutterAbility {
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine);
GeneratedPluginRegistrant.registerWith(flutterEngine);
// 注册自定义原生插件
const nativePlugin = new NativePlugin();
nativePlugin.onAttachedToEngine(flutterEngine);
}
}
代码说明
import { NativePlugin }- 导入我们创建的插件nativePlugin.onAttachedToEngine(flutterEngine)- 将插件注册到 FlutterEngine
步骤 4:在 main.dart 中添加页面
4.1 导入页面
在 lib/main.dart 顶部添加导入:
dart
import 'package:lesson77/pages/Demo/index.dart';
4.2 添加到页面列表
在 _page 列表中添加:
dart
final List<Widget> _page = const[
// ... 其他页面
DemoPage(),
];
4.3 添加底部导航项
在 _navBarItems 列表中添加:
dart
const BottomNavigationBarItem(
icon: Icon(Icons.code_outlined),
activeIcon: Icon(Icons.code, color: Colors.blue),
label: 'demo',
),
4.4 更新标题
在 _getAppBarTitle() 方法中添加:
dart
case 5: return 'Demo'; // 根据实际索引调整
步骤 5:构建运行
5.1 清理项目
bash
flutter clean
5.2 运行项目
bash
flutter run
5.3 测试
- 切换到 Demo 页面
- 在两个输入框中输入数字(如 10 和 20)
- 点击"调用鸿蒙原生计算"按钮
- 查看返回结果(应显示 30)
交互流程图
┌─────────────────────────────────────────────────────────────┐
│ Flutter 端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 输入: num1 = 10, num2 = 20 │ │
│ │ 调用: platform.invokeMethod('calculate', {...}) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│
▼ MethodChannel
┌─────────────────────────────────────────────────────────────┐
│ 鸿蒙端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 接收: call.method = 'calculate' │ │
│ │ 参数: call.args = {num1: 10, num2: 20} │ │
│ │ 计算: sum = 10 + 20 = 30 │ │
│ │ 返回: result.success(30) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│
▼ 返回结果
┌─────────────────────────────────────────────────────────────┐
│ Flutter 端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 接收: result = 30 │ │
│ │ 显示: "计算结果: 30" │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
常见问题
Q1: 显示"插件未注册"
原因:MethodChannel 名称不匹配或插件未注册。
解决:
- 检查 Flutter 端和鸿蒙端的 channel 名称是否一致
- 确保在
EntryAbility.ets中注册了插件
Q2: 计算结果为 0
原因:参数获取方式错误。
解决 :Flutter 传递的 Map 参数在鸿蒙端必须用 Map<string, Object> 类型接收,然后通过 .get('key') 获取值:
typescript
const argsMap = call.args as Map<string, Object>;
const n1 = argsMap.get('num1') as number || 0;
const n2 = argsMap.get('num2') as number || 0;
注意 :不能直接用 interface 接收,如
call.args as CalcArgs会导致参数为 undefined。
Q3: 编译报错 "Object literal must correspond to some explicitly declared class or interface"
原因:ArkTS 要求对象字面量必须有对应的 interface。
解决 :这个错误通常出现在创建对象时,需要定义 interface。但对于 Flutter 传递的参数,使用 Map<string, Object> 即可。
Q4: 热重载不生效
原因:修改了鸿蒙端代码。
解决 :执行 flutter clean 后重新 flutter run。
扩展练习
练习 1:实现减法
在鸿蒙端添加 subtract 方法:
typescript
case 'subtract':
const subArgs = call.args as CalcArgs;
const diff = (subArgs.num1 || 0) - (subArgs.num2 || 0);
result.success(diff);
break;
练习 2:实现乘法和除法
自行实现 multiply 和 divide 方法。
练习 3:返回复杂对象
鸿蒙端返回 JSON 字符串,Flutter 端解析:
typescript
// 鸿蒙端
result.success(JSON.stringify({sum: 30, product: 200}));
// Flutter 端
final data = json.decode(result);
总结
| 步骤 | 文件 | 操作 |
|---|---|---|
| 1 | lib/pages/Demo/index.dart |
新建 Flutter 页面,使用 MethodChannel 调用原生 |
| 2 | ohos/.../plugins/NativePlugin.ets |
新建鸿蒙插件,处理方法调用 |
| 3 | ohos/.../entryability/EntryAbility.ets |
修改,注册插件 |
| 4 | lib/main.dart |
修改,添加页面和导航 |
| 5 | 终端 | 执行 flutter clean 和 flutter run |
通过 MethodChannel,你可以实现 Flutter 与鸿蒙原生的任意数据交互!