你好!👋 欢迎来到这篇关于 file_selector 在 HarmonyOS (OpenHarmony) 上使用的实战教程。
� 1. 引入依赖:pubspec.yaml
首先,我们需要在项目的配置文件中引入适配后的 file_selector 库。
修改文件:pubspec.yaml
操作:在 dependencies 节点下添加 file_selector 的 git 依赖配置。
dependencies:
flutter:
sdk: flutter
# 👇 新增:引入 file_selector 鸿蒙适配版本
file_selector:
git:
url: https://gitcode.com/openharmony–tpc/flutter_packages.git
path: packages/file_selector/file_selector
ref: br_file_selector–v1.0.3_ohos
💡 提示:修改完成后,别忘了运行终端命令 flutter pub get 来下载依赖!
👶 新手小课堂:为什么通过 Git 引入?
通常我们使用 pub.dev 上的库,但由于鸿蒙适配版本目前还处于早期阶段或由社区维护,可能尚未合并到官方主仓库或发布到公共仓库。 因此,我们需要使用 git 方式直接指向代码仓库和特定的分支 (ref),这样就能抢先体验适配后的功能啦!
🛠️ 2. 核心功能实现:lib/file_selector_demo.dart
接下来,我们创建一个全新的文件 lib/file_selector_demo.dart,用于集中实现文件选择和预览的核心逻辑。
2.1 导入必要的包
文件:lib/file_selector_demo.dart
我们需要导入 file_selector 库以及用于处理二进制数据的 dart:typed_data。
#mermaid-svg-9bcB5HR8UczYbfYa{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9bcB5HR8UczYbfYa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9bcB5HR8UczYbfYa .error-icon{fill:#552222;}#mermaid-svg-9bcB5HR8UczYbfYa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9bcB5HR8UczYbfYa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9bcB5HR8UczYbfYa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9bcB5HR8UczYbfYa .marker.cross{stroke:#333333;}#mermaid-svg-9bcB5HR8UczYbfYa svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9bcB5HR8UczYbfYa p{margin:0;}#mermaid-svg-9bcB5HR8UczYbfYa .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9bcB5HR8UczYbfYa .cluster-label text{fill:#333;}#mermaid-svg-9bcB5HR8UczYbfYa .cluster-label span{color:#333;}#mermaid-svg-9bcB5HR8UczYbfYa .cluster-label span p{background-color:transparent;}#mermaid-svg-9bcB5HR8UczYbfYa .label text,#mermaid-svg-9bcB5HR8UczYbfYa span{fill:#333;color:#333;}#mermaid-svg-9bcB5HR8UczYbfYa .node rect,#mermaid-svg-9bcB5HR8UczYbfYa .node circle,#mermaid-svg-9bcB5HR8UczYbfYa .node ellipse,#mermaid-svg-9bcB5HR8UczYbfYa .node polygon,#mermaid-svg-9bcB5HR8UczYbfYa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9bcB5HR8UczYbfYa .rough-node .label text,#mermaid-svg-9bcB5HR8UczYbfYa .node .label text,#mermaid-svg-9bcB5HR8UczYbfYa .image-shape .label,#mermaid-svg-9bcB5HR8UczYbfYa .icon-shape .label{text-anchor:middle;}#mermaid-svg-9bcB5HR8UczYbfYa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9bcB5HR8UczYbfYa .rough-node .label,#mermaid-svg-9bcB5HR8UczYbfYa .node .label,#mermaid-svg-9bcB5HR8UczYbfYa .image-shape .label,#mermaid-svg-9bcB5HR8UczYbfYa .icon-shape .label{text-align:center;}#mermaid-svg-9bcB5HR8UczYbfYa .node.clickable{cursor:pointer;}#mermaid-svg-9bcB5HR8UczYbfYa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9bcB5HR8UczYbfYa .arrowheadPath{fill:#333333;}#mermaid-svg-9bcB5HR8UczYbfYa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9bcB5HR8UczYbfYa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9bcB5HR8UczYbfYa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9bcB5HR8UczYbfYa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9bcB5HR8UczYbfYa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9bcB5HR8UczYbfYa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9bcB5HR8UczYbfYa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9bcB5HR8UczYbfYa .cluster text{fill:#333;}#mermaid-svg-9bcB5HR8UczYbfYa .cluster span{color:#333;}#mermaid-svg-9bcB5HR8UczYbfYa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9bcB5HR8UczYbfYa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9bcB5HR8UczYbfYa rect.text{fill:none;stroke-width:0;}#mermaid-svg-9bcB5HR8UczYbfYa .icon-shape,#mermaid-svg-9bcB5HR8UczYbfYa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9bcB5HR8UczYbfYa .icon-shape p,#mermaid-svg-9bcB5HR8UczYbfYa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9bcB5HR8UczYbfYa .icon-shape rect,#mermaid-svg-9bcB5HR8UczYbfYa .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9bcB5HR8UczYbfYa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9bcB5HR8UczYbfYa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9bcB5HR8UczYbfYa :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
调用 openFile
Platform Channel
唤起
用户选择文件
返回文件信息
readAsBytes
Image.memory
用户点击选择
Flutter 应用
鸿蒙原生系统
系统文件选择器
读取文件流
展示图片
import 'package:flutter/material.dart';
import 'package:file_selector/file_selector.dart'; // 👈 核心库
import 'dart:typed_data'; // 👈 用于处理文件流
2.2 实现单图选择功能
文件:lib/file_selector_demo.dart
我们在 _FileSelectorDemoPageState 类中定义 _pickImage 方法。使用 XTypeGroup 限制文件类型,并调用 openFile 选择单个文件。
XFile? _singleImage; // 用于存储单选结果
Future<void> _pickImage() async {
// 定义允许选择的文件类型(图片)
const XTypeGroup typeGroup = XTypeGroup(
label: 'images',
extensions: <String>['jpg', 'png', 'gif'],
);
// 🚀 调用 openFile 唤起文件选择器
final XFile? file = await openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
if (file != null) {
setState(() {
_singleImage = file;
_multiImages = []; // 清除多选状态
});
}
}

2.3 实现多图选择功能
文件:lib/file_selector_demo.dart
类似地,定义 _pickMultiImages 方法,调用 openFiles 接口实现批量选择。
List<XFile> _multiImages = []; // 用于存储多选结果
Future<void> _pickMultiImages() async {
const XTypeGroup typeGroup = XTypeGroup(
label: 'images',
extensions: <String>['jpg', 'png', 'gif'],
);
// 🚀 调用 openFiles 选择多个文件
final List<XFile> files = await openFiles(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
if (files.isNotEmpty) {
setState(() {
_multiImages = files;
_singleImage = null; // 清除单选状态
});
}
}

2.4 实现图片预览(✨ 关键点)
文件:lib/file_selector_demo.dart
在鸿蒙系统上,由于文件沙箱机制的差异,推荐使用文件流(Bytes)的方式来加载图片,而不是直接使用文件路径。
👶 新手小课堂:为什么不能直接用文件路径?
在 Android 或 iOS 上,我们有时可以直接通过文件路径(如 /storage/emulated/0/…)访问文件。 但在鸿蒙(以及现代移动系统)的严格沙箱机制下,应用只能访问自己“私有领地”内的文件。系统选择器返回的路径可能是一个虚拟路径或没有直接读取权限。 使用 readAsBytes() 就像是用一根吸管直接把文件内容“吸”出来,而不需要关心文件具体放在哪,这样既安全又通用!
我们使用 FutureBuilder 配合 file.readAsBytes() 来实现预览:
// 单图预览示例
if (_singleImage != null) ...[
const Text('Selected Image:', style: TextStyle(fontWeight: FontWeight.bold)),
FutureBuilder<Uint8List>(
future: _singleImage!.readAsBytes(), // 👈 关键:读取文件流
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) {
// 使用 Image.memory 展示图片
return Image.memory(
snapshot.data!,
height: 200,
);
} else if (snapshot.hasError) {
return const Text('Error loading image');
} else {
return const CircularProgressIndicator();
}
},
),
Text(_singleImage!.path), // 显示路径仅供参考
],

🚀 3. 配置应用入口:lib/main.dart
最后,我们在应用的主页添加入口,跳转到我们刚刚写的演示页面。
修改文件:lib/main.dart
操作:
// 1. 导入头文件
import 'file_selector_demo.dart';
// …
// 2. 在 build 方法中添加跳转按钮
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const FileSelectorDemoPage()),
);
},
child: const Text('Go to File Selector Demo'),
),
🎉 结语
通过以上三个文件的简单配置,我们就完成了 file_selector 在鸿蒙系统上的集成!🚀
- pubspec.yaml:引入“入场券”。
- lib/file_selector_demo.dart:搭建“舞台”,实现选择与预览。
- lib/main.dart:打开“大门”,连接新功能。
快去运行你的鸿蒙应用试试吧!Happy Coding! 💻⚡️ 🎉 祝你开发顺利! 🚀 欢迎加入开源鸿蒙跨平台社区
网硕互联帮助中心





评论前必须登录!
注册