鸿蒙 Flutter 开发:从方舟卡片集成到跨设备交互实战
鸿蒙 Flutter 开发:从方舟卡片集成到跨设备交互实战
鸿蒙系统的方舟卡片(服务卡片) 是其核心特色之一,能够让应用功能以轻量化的形式展现在手机桌面,提升用户触达效率;而跨设备交互则是鸿蒙分布式能力的核心体现。此前我们探讨了鸿蒙 Flutter 的基础开发、分布式能力集成和混合开发,本文将聚焦鸿蒙方舟卡片与 Flutter 的集成以及Flutter 应用的跨设备交互,通过完整代码案例,带你实现鸿蒙 Flutter 应用的轻量化和分布式体验升级。
一、鸿蒙方舟卡片与 Flutter 集成的核心逻辑
方舟卡片是鸿蒙系统的原生轻量化组件,运行在桌面端,而 Flutter 应用是独立的跨端应用。两者的集成主要通过以下两种方式实现:
- 数据联动:Flutter 应用更新数据后,通过鸿蒙的分布式数据管理或事件通知同步到方舟卡片。
- 交互跳转:点击方舟卡片,跳转到 Flutter 应用的对应页面,实现轻量化展示与完整功能的联动。
- Flutter 驱动卡片:通过鸿蒙原生代码封装 Flutter 逻辑,间接驱动方舟卡片的内容更新(本文重点采用此方案)。
需要注意的是,方舟卡片目前暂不支持直接运行 Flutter 代码,因此需通过Flutter 与鸿蒙原生的通信桥梁,实现 Flutter 应用与方舟卡片的双向联动。
二、案例 1:Flutter 应用与鸿蒙方舟卡片的数据联动
本案例将实现一个天气信息展示的场景:Flutter 应用获取并更新天气数据,鸿蒙方舟卡片实时展示该数据;点击方舟卡片,跳转到 Flutter 应用的天气详情页面。
前置条件
- 已配置鸿蒙 DevEco Studio 和 Flutter 环境,支持鸿蒙 SDK 7.0 及以上版本。
- 已创建鸿蒙 Flutter 项目,并开启分布式数据管理能力(参考前文分布式数据管理配置)。
- 已掌握鸿蒙方舟卡片的基础开发流程(创建卡片模板、配置卡片信息)。
步骤 1:配置鸿蒙方舟卡片基础结构
首先在鸿蒙原生项目中创建方舟卡片,这里以静态卡片 + 动态数据更新为例。
1. 配置卡片信息(config.json)
在entry > src > main > config.json中添加方舟卡片的配置:
{
"module": {
"abilities": [
{
"name": ".MainAbility",
"type": "page",
"visible": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
},
{
"name": ".WeatherCardAbility",
"type": "service",
"visible": true,
"metadata": [
{
"name": "ohos.extra.param.app.type",
"value": "service"
}
],
"skills": [
{
"entities": [
"entity.system.servicecard"
],
"actions": [
"action.system.servicecard"
]
}
]
}
],
"serviceCards": [
{
"name": "weather_card",
"description": "天气信息卡片",
"icon": "$media:icon",
"preview": "$media:preview",
"styles": [
{
"name": "style_1",
"dimensions": "2*2",
"description": "2x2尺寸的天气卡片"
}
],
"abilities": [
{
"name": ".WeatherCardAbility"
}
]
}
]
}
}
2. 创建方舟卡片布局(weather_card.xml)
在entry > src > main > resources > base > layout中创建卡片布局文件:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"
ohos:padding="10vp">
<Text
ohos:id="$+id:city_name"
ohos:width="match_parent"
ohos:height="40vp"
ohos:text="北京"
ohos:text_size="18fp"
ohos:text_alignment="center"/>
<Text
ohos:id="$+id:weather_info"
ohos:width="match_parent"
ohos:height="40vp"
ohos:text="晴 25℃"
ohos:text_size="16fp"
ohos:text_alignment="center"/>
<Text
ohos:id="$+id:update_time"
ohos:width="match_parent"
ohos:height="30vp"
ohos:text="更新时间:2025-06-01 10:00"
ohos:text_size="12fp"
ohos:text_alignment="center"
ohos:text_color="#999999"/>
</DirectionalLayout>
步骤 2:鸿蒙原生端实现卡片数据管理与跳转逻辑
创建WeatherCardAbility.java,负责方舟卡片的数据源管理,并实现点击卡片跳转到 Flutter 应用的逻辑:
package com.example.harmonyflutter;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.ServiceCardAbility;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.data.distributed.common.KvStoreException;
import ohos.data.distributed.user.SingleKvStore;
import ohos.data.distributed.user.KvStoreManager;
import ohos.data.distributed.user.KvStoreManagerFactory;
import ohos.data.distributed.user.Options;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import java.util.Optional;
public class WeatherCardAbility extends ServiceCardAbility {
private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "WeatherCardAbility");
private static final String KV_STORE_NAME = "weather_card_store";
private static final String WEATHER_KEY = "weather_data";
private SingleKvStore kvStore;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// 初始化分布式KvStore,用于接收Flutter传递的天气数据
initKvStore();
}
@Override
protected Component onRender(Intent intent) {
// 加载卡片布局
Component cardLayout = super.loadLayout(intent, "$layout:weather_card");
if (cardLayout == null) {
return null;
}
// 从KvStore获取天气数据并更新卡片
updateWeatherCard(cardLayout);
// 设置卡片点击事件:跳转到Flutter应用
cardLayout.setClickedListener(component -> {
// 跳转到Flutter应用的MainAbility
Intent flutterIntent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withBundleName("com.example.harmonyflutter")
.withAbilityName("com.example.harmonyflutter.MainAbility")
.build();
flutterIntent.setOperation(operation);
startAbility(flutterIntent);
});
return cardLayout;
}
/**
* 初始化分布式KvStore
*/
private void initKvStore() {
try {
KvStoreManager manager = KvStoreManagerFactory.getInstance().createKvStoreManager(getContext());
Options options = new Options();
options.setCreateIfMissing(true);
options.setKvStoreType(Options.KvStoreType.SINGLE_VERSION);
Optional<SingleKvStore> optional = manager.getKvStore(options, KV_STORE_NAME);
if (optional.isPresent()) {
kvStore = optional.get();
// 监听KvStore数据变化,实时更新卡片
kvStore.registerChangeListener((key, value) -> {
if (key.equals(WEATHER_KEY)) {
// 重新渲染卡片
refreshCard();
}
});
HiLog.info(LABEL, "KvStore初始化成功");
}
} catch (KvStoreException e) {
HiLog.error(LABEL, "KvStore初始化失败:%{public}s", e.getMessage());
}
}
/**
* 更新卡片天气数据
*/
private void updateWeatherCard(Component cardLayout) {
if (kvStore == null || !kvStore.containsKey(WEATHER_KEY)) {
return;
}
try {
// 从KvStore获取天气数据(格式:城市|天气|温度|更新时间)
String weatherData = kvStore.getString(WEATHER_KEY);
String[] dataArray = weatherData.split("\\|");
if (dataArray.length < 4) {
return;
}
// 更新卡片文本
Text cityName = (Text) cardLayout.findComponentById(ResourceTable.Id_city_name);
Text weatherInfo = (Text) cardLayout.findComponentById(ResourceTable.Id_weather_info);
Text updateTime = (Text) cardLayout.findComponentById(ResourceTable.Id_update_time);
if (cityName != null) {
cityName.setText(dataArray[0]);
}
if (weatherInfo != null) {
weatherInfo.setText(dataArray[1] + " " + dataArray[2]);
}
if (updateTime != null) {
updateTime.setText("更新时间:" + dataArray[3]);
}
} catch (Exception e) {
HiLog.error(LABEL, "更新卡片失败:%{public}s", e.getMessage());
}
}
}
步骤 3:Flutter 端实现天气数据更新与同步
在 Flutter 项目中,创建天气数据页面,通过MethodChannel将天气数据同步到鸿蒙原生的分布式 KvStore,进而更新方舟卡片。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '鸿蒙Flutter天气应用',
theme: ThemeData(primarySwatch: Colors.blue),
home: const WeatherPage(),
);
}
}
class WeatherPage extends StatefulWidget {
const WeatherPage({super.key});
@override
State<WeatherPage> createState() => _WeatherPageState();
}
class _WeatherPageState extends State<WeatherPage> {
// 天气数据
String _city = "北京";
String _weather = "晴";
String _temperature = "25℃";
// 与原生通信的MethodChannel
static const MethodChannel _channel = MethodChannel('com.example.weather_card');
// 同步天气数据到鸿蒙KvStore,更新方舟卡片
Future<void> _syncWeatherToCard() async {
try {
// 获取当前时间
String updateTime = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
// 拼接数据:城市|天气|温度|更新时间
String weatherData = "$_city|$_weather|$_temperature|$updateTime";
// 调用原生方法同步数据
await _channel.invokeMethod('syncWeatherData', {'weatherData': weatherData});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('天气数据已同步到方舟卡片!')),
);
} on PlatformException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('同步失败:${e.message}')),
);
}
}
// 模拟切换城市和天气
void _changeWeather() {
setState(() {
if (_city == "北京") {
_city = "上海";
_weather = "多云";
_temperature = "28℃";
} else if (_city == "上海") {
_city = "广州";
_weather = "雷阵雨";
_temperature = "30℃";
} else {
_city = "北京";
_weather = "晴";
_temperature = "25℃";
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('鸿蒙Flutter天气')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_city,
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Text(
_weather,
style: const TextStyle(fontSize: 24, color: Colors.grey),
),
const SizedBox(height: 10),
Text(
_temperature,
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 40),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _changeWeather,
child: const Text('切换天气'),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: _syncWeatherToCard,
child: const Text('同步到方舟卡片'),
),
],
),
],
),
),
);
}
}
步骤 4:鸿蒙原生端实现数据接收方法
在MainAbilitySlice.java中添加接收 Flutter 数据并存储到 KvStore 的方法:
// 新增MethodChannel处理天气数据同步
private static final String WEATHER_CHANNEL = "com.example.weather_card";
private SingleKvStore weatherKvStore;
// 在onStart方法中初始化
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// 初始化Flutter引擎
FlutterEngine flutterEngine = new FlutterEngine(getContext());
// 注册天气数据同步的MethodChannel
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), WEATHER_CHANNEL)
.setMethodCallHandler((call, result) -> {
if (call.method.equals("syncWeatherData")) {
String weatherData = call.argument("weatherData");
syncWeatherDataToKvStore(weatherData, result);
} else {
result.notImplemented();
}
});
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
// 初始化天气KvStore
initWeatherKvStore();
}
/**
* 初始化天气KvStore
*/
private void initWeatherKvStore() {
try {
KvStoreManager manager = KvStoreManagerFactory.getInstance().createKvStoreManager(getContext());
Options options = new Options();
options.setCreateIfMissing(true);
options.setKvStoreType(Options.KvStoreType.SINGLE_VERSION);
Optional<SingleKvStore> optional = manager.getKvStore(options, "weather_card_store");
if (optional.isPresent()) {
weatherKvStore = optional.get();
}
} catch (KvStoreException e) {
HiLog.error(LABEL, "天气KvStore初始化失败:%{public}s", e.getMessage());
}
}
/**
* 将Flutter传递的天气数据存储到KvStore
*/
private void syncWeatherDataToKvStore(String weatherData, MethodChannel.Result result) {
if (weatherKvStore == null) {
result.error("KV_STORE_NULL", "天气存储初始化失败", null);
return;
}
try {
weatherKvStore.putString("weather_data", weatherData);
result.success(null);
} catch (Exception e) {
result.error("SYNC_FAILED", e.getMessage(), null);
}
}
案例说明
- 方舟卡片端:通过分布式 KvStore 监听数据变化,实时更新卡片上的天气信息;点击卡片时,跳转到 Flutter 应用的天气页面。
- Flutter 端:提供天气数据的切换和同步功能,通过
MethodChannel将数据传递到鸿蒙原生端,存储到 KvStore 后触发卡片更新。 - 核心联动:实现了 Flutter 应用与鸿蒙方舟卡片的数据双向同步和交互跳转,充分利用了鸿蒙的分布式能力和 Flutter 的跨端优势。
三、案例 2:Flutter 应用的鸿蒙跨设备交互
鸿蒙的分布式能力允许应用在多个设备(如手机、平板、智慧屏)之间实现数据共享和操作同步。本案例将实现一个跨设备的图片传输功能:在手机端的 Flutter 应用中选择图片,点击发送后,平板端的 Flutter 应用实时接收并展示该图片。
核心技术:鸿蒙分布式文件服务 + Flutter 跨设备通信
- 鸿蒙分布式文件服务:用于在设备之间传输图片文件。
- MethodChannel + 鸿蒙分布式事件:用于触发跨设备的图片接收事件。
步骤 1:配置鸿蒙分布式文件服务权限
在config.json中添加分布式文件服务和网络权限:
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATA_SYNC",
"reason": "需要分布式数据同步权限",
"usedScene": {
"abilities": [".MainAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.INTERNET",
"reason": "需要网络权限",
"usedScene": {
"abilities": [".MainAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.READ_USER_STORAGE",
"reason": "需要读取存储权限",
"usedScene": {
"abilities": [".MainAbility"],
"when": "always"
}
}
]
}
}
步骤 2:鸿蒙原生端实现分布式文件传输
创建DistributedFileManager.java,封装鸿蒙分布式文件服务的文件传输逻辑:
package com.example.harmonyflutter;
import ohos.distributedschedule.interwork.DeviceInfo;
import ohos.distributedschedule.interwork.DeviceManager;
import ohos.file.fs.File;
import ohos.file.fs.FileSystem;
import ohos.file.fs.Stream;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.io.ByteArrayOutputStream;
import java.util.List;
public class DistributedFileManager {
private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "DistributedFileManager");
private static final String DISTRIBUTED_FILE_PATH = "/sdcard/DistributedFiles/";
/**
* 获取已配对的设备列表
*/
public static List<DeviceInfo> getPairedDevices() {
return DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ALL_DEVICE);
}
/**
* 发送文件到指定设备
*/
public static boolean sendFileToDevice(String deviceId, String localFilePath) {
try {
// 打开本地文件
File localFile = FileSystem.openFile(localFilePath);
if (!localFile.exists()) {
HiLog.error(LABEL, "本地文件不存在:%{public}s", localFilePath);
return false;
}
// 读取文件字节
Stream inputStream = localFile.openStream(File.OpenMode.READ_ONLY);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
byte[] fileBytes = outputStream.toByteArray();
inputStream.close();
outputStream.close();
// 分布式文件服务:将文件写入目标设备
String remoteFilePath = DISTRIBUTED_FILE_PATH + localFile.getName();
File remoteFile = FileSystem.createDistributedFile(deviceId, remoteFilePath);
Stream outputStreamRemote = remoteFile.openStream(File.OpenMode.WRITE_ONLY);
outputStreamRemote.write(fileBytes);
outputStreamRemote.close();
HiLog.info(LABEL, "文件发送成功:%{public}s", remoteFilePath);
return true;
} catch (Exception e) {
HiLog.error(LABEL, "文件发送失败:%{public}s", e.getMessage());
return false;
}
}
/**
* 读取分布式文件
*/
public static byte[] readDistributedFile(String deviceId, String remoteFilePath) {
try {
File remoteFile = FileSystem.openDistributedFile(deviceId, remoteFilePath);
if (!remoteFile.exists()) {
return null;
}
Stream inputStream = remoteFile.openStream(File.OpenMode.READ_ONLY);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
byte[] fileBytes = outputStream.toByteArray();
inputStream.close();
outputStream.close();
return fileBytes;
} catch (Exception e) {
HiLog.error(LABEL, "读取分布式文件失败:%{public}s", e.getMessage());
return null;
}
}
}
步骤 3:Flutter 端实现跨设备图片传输与接收
在 Flutter 项目中,使用image_picker插件选择图片,通过MethodChannel调用原生的分布式文件传输方法,同时监听跨设备图片接收事件。
1. 添加依赖(pubspec.yaml)
dependencies:
flutter:
sdk: flutter
image_picker: ^1.0.4 # 图片选择插件
flutter_image_compress: ^1.1.0 # 图片压缩(可选)
2. Flutter 端核心代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:typed_data';
import 'dart:io';
void main() {
runApp(const MyDistributedApp());
}
class MyDistributedApp extends StatelessWidget {
const MyDistributedApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '鸿蒙Flutter跨设备交互',
theme: ThemeData(primarySwatch: Colors.blue),
home: const DistributedImagePage(),
);
}
}
class DistributedImagePage extends StatefulWidget {
const DistributedImagePage({super.key});
@override
State<DistributedImagePage> createState() => _DistributedImagePageState();
}
class _DistributedImagePageState extends State<DistributedImagePage> {
static const MethodChannel _channel = MethodChannel('com.example.distributed_file');
// 已配对设备列表
List<String> _deviceList = [];
String? _selectedDevice;
// 选择的图片路径
XFile? _selectedImage;
// 接收的图片字节数据
Uint8List? _receivedImageBytes;
@override
void initState() {
super.initState();
// 获取已配对设备列表
_getPairedDevices();
// 监听跨设备图片接收事件
_channel.setMethodCallHandler((call) async {
if (call.method == "onImageReceived") {
String deviceId = call.argument("deviceId");
String filePath = call.argument("filePath");
// 读取图片字节数据
Uint8List? bytes = await _channel.invokeMethod('readDistributedFile', {
'deviceId': deviceId,
'filePath': filePath,
});
setState(() {
_receivedImageBytes = bytes;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('收到来自设备$deviceId的图片!')),
);
}
return null;
});
}
// 获取已配对的鸿蒙设备列表
Future<void> _getPairedDevices() async {
try {
List<dynamic> devices = await _channel.invokeMethod('getPairedDevices');
setState(() {
_deviceList = devices.map((e) => e.toString()).toList();
if (_deviceList.isNotEmpty) {
_selectedDevice = _deviceList.first;
}
});
} on PlatformException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('获取设备列表失败:${e.message}')),
);
}
}
// 选择图片
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
setState(() {
_selectedImage = image;
_receivedImageBytes = null; // 清空接收的图片
});
}
}
// 发送图片到选中的设备
Future<void> _sendImage() async {
if (_selectedImage == null || _selectedDevice == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请选择图片和设备!')),
);
return;
}
try {
bool success = await _channel.invokeMethod('sendFileToDevice', {
'deviceId': _selectedDevice,
'filePath': _selectedImage!.path,
});
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('图片发送成功!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('图片发送失败!')),
);
}
} on PlatformException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('发送失败:${e.message}')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('跨设备图片传输')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// 设备选择下拉框
DropdownButton<String>(
value: _selectedDevice,
hint: const Text('选择目标设备'),
items: _deviceList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
_selectedDevice = newValue;
});
},
),
const SizedBox(height: 20),
// 图片选择和发送按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _pickImage,
child: const Text('选择图片'),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: _sendImage,
child: const Text('发送图片'),
),
],
),
const SizedBox(height: 30),
// 展示选择的图片或接收的图片
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
if (_selectedImage != null)
Column(
children: [
const Text('已选择的图片:'),
const SizedBox(height: 10),
Image.file(
File(_selectedImage!.path),
width: 300,
height: 300,
fit: BoxFit.cover,
),
],
),
if (_receivedImageBytes != null)
Column(
children: [
const Text('接收的图片:'),
const SizedBox(height: 10),
Image.memory(
_receivedImageBytes!,
width: 300,
height: 300,
fit: BoxFit.cover,
),
],
),
],
),
),
),
],
),
),
);
}
}
步骤 4:鸿蒙原生端实现设备列表获取与事件通知
在MainAbilitySlice.java中添加设备列表获取和跨设备事件通知的逻辑:
// 新增分布式文件的MethodChannel
private static final String DISTRIBUTED_CHANNEL = "com.example.distributed_file";
// 在onStart方法中注册
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), DISTRIBUTED_CHANNEL)
.setMethodCallHandler((call, result) -> {
switch (call.method) {
case "getPairedDevices":
// 获取已配对设备ID列表
List<DeviceInfo> devices = DistributedFileManager.getPairedDevices();
List<String> deviceIds = new ArrayList<>();
for (DeviceInfo device : devices) {
deviceIds.add(device.getDeviceId());
}
result.success(deviceIds);
break;
case "sendFileToDevice":
// 发送文件到指定设备
String deviceId = call.argument("deviceId");
String filePath = call.argument("filePath");
boolean success = DistributedFileManager.sendFileToDevice(deviceId, filePath);
// 发送事件通知目标设备接收图片
sendImageReceivedEvent(deviceId, filePath);
result.success(success);
break;
case "readDistributedFile":
// 读取分布式文件
String dId = call.argument("deviceId");
String fPath = call.argument("filePath");
byte[] bytes = DistributedFileManager.readDistributedFile(dId, fPath);
result.success(bytes);
break;
default:
result.notImplemented();
break;
}
});
/**
* 发送图片接收事件到目标设备
*/
private void sendImageReceivedEvent(String deviceId, String filePath) {
// 此处简化处理,实际项目中可使用鸿蒙分布式事件服务实现跨设备事件通知
// 这里直接在本地触发事件(跨设备需结合鸿蒙分布式事件服务)
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), DISTRIBUTED_CHANNEL)
.invokeMethod("onImageReceived", Map.of("deviceId", deviceId, "filePath", filePath));
}
案例说明
- 设备发现:Flutter 端通过调用原生方法获取鸿蒙已配对的设备列表,供用户选择目标设备。
- 图片传输:选择图片后,通过鸿蒙分布式文件服务将图片发送到目标设备,并触发事件通知。
- 图片接收:目标设备的 Flutter 应用监听事件,读取分布式文件并展示图片,实现跨设备的图片交互。
- 扩展场景:此逻辑可扩展到跨设备的视频、文档传输,甚至跨设备的操作同步(如手机端点击按钮,平板端触发对应动作)。
四、鸿蒙 Flutter 开发的进阶建议
- 方舟卡片优化:针对不同尺寸的方舟卡片(1x1、2x2、4x4)设计适配布局,结合鸿蒙的动态卡片能力实现更丰富的交互。
- 跨设备权限管理:在跨设备交互时,需处理设备之间的权限验证,确保数据传输的安全性。
- 性能优化:跨设备传输大文件时,采用分片传输和压缩技术,减少网络开销;Flutter 端使用
CachedNetworkImage缓存接收的图片,提升渲染性能。 - 鸿蒙生态适配:结合鸿蒙的原子化服务能力,将 Flutter 应用封装为原子化服务,实现免安装运行,提升用户体验。
五、总结
本文通过方舟卡片集成和跨设备图片传输两个案例,展示了鸿蒙 Flutter 开发的进阶场景。方舟卡片的集成实现了应用轻量化展示,跨设备交互则充分发挥了鸿蒙的分布式能力,两者结合能让 Flutter 应用在鸿蒙生态中具备更强的竞争力。
随着鸿蒙系统的持续升级和 Flutter 对鸿蒙支持的深化,未来还将有更多的原生能力(如车载交互、智能穿戴适配)与 Flutter 融合,开发者可基于本文的思路,探索更多鸿蒙 Flutter 的创新应用场景
欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。
更多推荐
所有评论(0)