
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 file_selector 文件选择组件的使用方法,带你全面掌握在应用中选择单个文件或多个文件的功能。
一、file_selector 组件概述
在 Flutter 应用开发中,文件选择是一个非常常见的需求。比如用户需要上传头像、导入文档、批量处理图片等操作,都需要与设备的文件系统进行交互。不同操作系统(Android、iOS、Windows、macOS、OpenHarmony)的文件系统各不相同,API 也完全不同。如果为每个平台单独编写文件选择代码,工作量巨大且难以维护。
file_selector 就是为了解决这个问题而诞生的插件。它为开发者提供了一个统一的 API,屏蔽了不同平台的文件系统差异,让开发者可以用相同的代码在所有平台上实现文件选择功能。
📋 file_selector 组件特点
| 特点 | 说明 |
|---|---|
| 跨平台支持 | 支持 Android、iOS、Linux、macOS、Windows、OpenHarmony 六大平台 |
| 统一 API | 不同平台使用相同的 API 进行文件操作,无需关心底层实现 |
| 文件类型过滤 | 支持按文件扩展名过滤选择,方便用户快速找到所需文件 |
| 多选支持 | 支持一次选择多个文件,提升用户体验 |
| 异步操作 | 所有文件操作都是异步的,不会阻塞 UI,保证流畅体验 |
| 类型安全 | 使用强类型的 XFile 对象封装文件信息,避免空指针错误 |
💡 核心价值:开发者只需要学习一套 API,就可以在所有平台上实现文件选择功能,大大提高了开发效率。
二、为什么需要 file_selector?
2.1 原生文件选择的痛点
在没有 file_selector 之前,开发者需要:
Android 平台:
java
// 需要编写原生代码
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE);
iOS 平台:
swift
// 需要编写 Swift/Objective-C 代码
let picker = UIImagePickerController()
picker.delegate = self
present(picker, animated: true)
OpenHarmony 平台:
typescript
// 需要编写 ArkTS 代码
let documentPicker = new picker.DocumentViewPicker()
documentPicker.select(documentSelectOptions, (err, documentSelectResult) => {
// 处理选择结果
})
2.2 file_selector 的优势
使用 file_selector 后,开发者只需要:
dart
// 所有平台通用
const XTypeGroup typeGroup = XTypeGroup(
label: '图片',
extensions: ['jpg', 'png', 'gif'],
);
final XFile? file = await openFile(acceptedTypeGroups: [typeGroup]);
优势总结:
- ✅ 开发效率高:一套代码适配所有平台
- ✅ 维护成本低:底层实现由插件团队维护
- ✅ 学习曲线平缓:只需掌握 Dart API,无需学习多门原生语言
- ✅ Bug 修复快:底层 Bug 由插件团队统一修复
三、file_selector 核心概念
3.1 XFile - 文件对象
XFile 是 file_selector 和相关插件(如 image_picker)中广泛使用的文件对象,它是对系统文件的跨平台封装。
XFile 的核心属性:
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
name |
String | 文件名 | photo.jpg |
path |
String | 文件的完整路径 | /storage/emulated/0/Download/photo.jpg |
length() |
Future | 获取文件大小(字节) | await file.length() |
readAsBytes() |
Future | 读取文件为字节数组 | await file.readAsBytes() |
readAsString() |
Future | 读取文件为字符串 | await file.readAsString() |
writeAsString() |
Future | 写入字符串内容 | await file.writeAsString('hello') |
writeAsBytes() |
Future | 写入字节数据 | await file.writeAsBytes(bytes) |
使用示例:
dart
final XFile? file = await openFile();
if (file != null) {
print('文件名: ${file.name}');
print('文件路径: ${file.path}');
// 读取文件大小
final int size = await file.length();
print('文件大小: ${size / 1024} KB');
// 读取文件内容
final String content = await file.readAsString();
print('文件内容: $content');
}
3.2 XTypeGroup - 文件类型组
XTypeGroup 用于定义一组相关的文件类型,用于在文件选择对话框中显示过滤选项。
构造函数:
dart
const XTypeGroup({
required String label, // 类型标签,如 "图片"、"文档"
required List<String> extensions, // 文件扩展名列表,如 ['jpg', 'png']
});
设计理念:
- label:显示给用户看的友好名称
- extensions:实际用于过滤的文件扩展名
不同平台的显示效果:
| 平台 | 显示方式 |
|---|---|
| Windows | 下拉菜单中显示为"图片"分组,展开后显示具体扩展名 |
| macOS | 将所有类型组的扩展名合并显示 |
| Linux | 类似 Windows,下拉菜单显示分组 |
| OpenHarmony | 类似 macOS,合并显示 |
最佳实践建议:
dart
// ✅ 推荐:一个类型组包含相关类型
const XTypeGroup images = XTypeGroup(
label: '图片文件',
extensions: ['jpg', 'png', 'gif', 'jpeg', 'bmp', 'webp'],
);
// ❌ 不推荐:多个分散的类型组
const XTypeGroup jpg = XTypeGroup(label: 'JPG', extensions: ['jpg']);
const XTypeGroup png = XTypeGroup(label: 'PNG', extensions: ['png']);
四、OpenHarmony 平台适配说明
4.1 兼容性信息
本项目基于 file_selector@1.0.3 开发,适配 Flutter 3.27.5-ohos-1.2.0。
4.2 OpenHarmony 平台支持的功能
在 OpenHarmony 平台上,file_selector 目前只支持以下功能:
| 功能 | 方法 | OpenHarmony 支持 |
|---|---|---|
| 打开单个文件 | openFile() |
✅ yes |
| 打开多个文件 | openFiles() |
✅ yes |
| 选择目录 | getDirectoryPath() |
❌ no |
| 获取保存位置 | getSaveLocation() |
❌ no |
| 选择多个目录 | getDirectoryPaths() |
❌ no |
⚠️ 重要提示 :OpenHarmony 平台目前只支持
openFile()和openFiles()两个方法。选择目录、保存文件等功能尚未实现。如果需要这些功能,可以:
- 选择目录:使用 OpenHarmony 原生 API 或等待插件更新
- 保存文件:使用
path_provider获取应用目录,然后结合dart:io进行文件写入操作
4.3 OpenHarmony 文件路径特点
- 路径分隔符使用
/(与 Linux/macOS 一致) - 应用沙盒机制限制了可访问的目录
- 需要权限才能访问某些目录(如外部存储)
五、项目配置与安装
5.1 添加依赖配置
首先,需要在你的 Flutter 项目的 pubspec.yaml 文件中添加 file_selector 依赖。
打开项目根目录下的 pubspec.yaml 文件,找到 dependencies 部分,添加以下配置:
yaml
dependencies:
flutter:
sdk: flutter
# 添加 file_selector 依赖(OpenHarmony 适配版本)
file_selector:
git:
url: "https://gitcode.com/openharmony-tpc/flutter_packages.git"
path: "packages/file_selector/file_selector"
配置说明:
- 使用 git 方式引用开源鸿蒙适配的 flutter_packages 仓库
url:指定 GitCode 托管的仓库地址path:指定 file_selector 包的具体路径- 本项目基于
file_selector@1.0.3开发,适配 Flutter 3.27.5-ohos-1.2.0
⚠️ 重要:对于 OpenHarmony 平台,必须使用 git 方式引用适配版本,不能直接使用 pub.dev 的版本号。
5.2 下载依赖
配置完成后,需要在项目根目录执行以下命令下载依赖:
bash
flutter pub get
执行成功后,你会看到类似以下的输出:
Running "flutter pub get" in my_cross_platform_app...
Resolving dependencies...
Got dependencies!
5.3 依赖自动配置说明
执行 flutter pub get 后,OpenHarmony 平台的依赖会自动配置到 ohos/entry/oh-package.json5 文件中。你不需要手动配置 OpenHarmony 端的依赖,Flutter 构建系统会自动处理。
六、file_selector 基础用法详解
6.1 导入包
在使用 file_selector 之前,首先需要导入相关包:
dart
import 'package:file_selector/file_selector.dart';
导入内容说明:
openFile、openFiles- 打开文件的方法XFile、XTypeGroup- 核心类型定义
6.2 打开单个文件 - openFile()
这是最常用的功能,用于让用户选择一个文件。
方法签名:
dart
Future<XFile?> openFile({
List<XTypeGroup> acceptedTypeGroups = const [],
String? initialDirectory,
String? confirmButtonText,
})
参数详解:
| 参数 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
acceptedTypeGroups |
List | 否 | 文件类型过滤器,不传则可选择所有类型 | [XTypeGroup(label: '图片', extensions: ['jpg', 'png'])] |
initialDirectory |
String? | 否 | 打开对话框时显示的初始目录 | '/storage/emulated/0/Download' |
confirmButtonText |
String? | 否 | 确认按钮的文字 | '选择文件' |
返回值:
- 成功:返回
XFile对象,包含文件名和路径信息 - 用户取消:返回
null
完整示例:
dart
// 示例 1:选择任意类型文件
final XFile? file = await openFile();
if (file != null) {
print('选择了: ${file.name}');
}
// 示例 2:只选择图片文件
const XTypeGroup images = XTypeGroup(
label: '图片文件',
extensions: ['jpg', 'png', 'gif', 'jpeg'],
);
final XFile? imageFile = await openFile(
acceptedTypeGroups: [images],
);
if (imageFile != null) {
print('选择了图片: ${imageFile.name}');
}
// 示例 3:指定初始目录和按钮文字
final XFile? file = await openFile(
initialDirectory: '/storage/emulated/0/Download',
confirmButtonText: '导入',
);
使用场景:
- 用户上传头像
- 导入配置文件
- 选择要打开的文档
- 读取本地数据文件
6.3 打开多个文件 - openFiles()
当需要一次选择多个文件时使用此方法。
方法签名:
dart
Future<List<XFile>> openFiles({
List<XTypeGroup> acceptedTypeGroups = const [],
String? initialDirectory,
String? confirmButtonText,
})
参数详解:
与 openFile 相同
返回值:
- 成功:返回
List<XFile>,包含所有选择的文件 - 用户取消:返回空列表
[]
完整示例:
dart
// 示例 1:选择多个图片
const XTypeGroup images = XTypeGroup(
label: '图片文件',
extensions: ['jpg', 'png', 'gif'],
);
final List<XFile> files = await openFiles(
acceptedTypeGroups: [images],
);
if (files.isNotEmpty) {
print('选择了 ${files.length} 个文件:');
for (var file in files) {
print(' - ${file.name}');
}
} else {
print('用户取消了选择');
}
// 示例 2:选择所有类型的多个文件
final List<XFile> allFiles = await openFiles();
使用场景:
- 批量上传图片
- 导入多个配置文件
- 选择多张照片
- 批量处理文档
七、常用文件类型参考
7.1 图片文件
| 类型 | 扩展名 | 说明 |
|---|---|---|
| 常见图片 | jpg, jpeg, png, gif, bmp, webp | 大多数图片格式 |
| 高清图片 | tif, tiff | 用于印刷和出版 |
| 矢阵图 | ico | 图标文件 |
示例:
dart
const XTypeGroup images = XTypeGroup(
label: '图片文件',
extensions: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
);
7.2 文档文件
| 类型 | 扩展名 | 说明 |
|---|---|---|
| 文本文件 | txt | 纯文本文件 |
| PDF 文档 | 便携式文档格式 | |
| Word 文档 | doc, docx | Microsoft Word 文档 |
| Excel 表格 | xls, xlsx, csv | Microsoft Excel 表格 |
| 演示文稿 | ppt, pptx | Microsoft PowerPoint |
| Markdown | md | Markdown 文档 |
示例:
dart
const XTypeGroup documents = XTypeGroup(
label: '文档文件',
extensions: ['txt', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'],
);
7.3 媒体文件
| 类型 | 扩展名 | 说明 |
|---|---|---|
| 视频文件 | mp4, mov, avi, mkv, flv | 常见视频格式 |
| 音频文件 | mp3, wav, aac, flac, m4a | 常见音频格式 |
示例:
dart
const XTypeGroup videos = XTypeGroup(
label: '视频文件',
extensions: ['mp4', 'mov', 'avi', 'mkv'],
);
const XTypeGroup audios = XTypeGroup(
label: '音频文件',
extensions: ['mp3', 'wav', 'aac', 'flac', 'm4a'],
);
7.4 压缩包
| 类型 | 扩展名 | 说明 |
|---|---|---|
| 压缩包 | zip, rar, 7z, tar, gz | 各种压缩格式 |
示例:
dart
const XTypeGroup archives = XTypeGroup(
label: '压缩包',
extensions: ['zip', 'rar', '7z', 'tar', 'gz'],
);
八、完整示例代码
下面是一个完整的示例应用,展示了 file_selector 在 OpenHarmony 平台上的用法:
dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:file_selector/file_selector.dart';
void main() {
runApp(const FileSelectorDemo());
}
class FileSelectorDemo extends StatelessWidget {
const FileSelectorDemo({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'File Selector 示例',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const FileSelectorPage(),
);
}
}
class FileSelectorPage extends StatefulWidget {
const FileSelectorPage({super.key});
@override
State<FileSelectorPage> createState() => _FileSelectorPageState();
}
class _FileSelectorPageState extends State<FileSelectorPage> {
XFile? _selectedFile;
List<XFile> _selectedFiles = [];
String? _error;
Future<void> _openSingleFile() async {
try {
const XTypeGroup typeGroup = XTypeGroup(
label: '图片文件',
extensions: <String>['jpg', 'png', 'gif', 'jpeg'],
);
final XFile? file = await openFile(
acceptedTypeGroups: <XTypeGroup>[typeGroup],
);
if (file != null) {
setState(() {
_selectedFile = file;
_selectedFiles.clear();
_error = null;
});
}
} catch (e) {
setState(() {
_error = '选择文件失败: $e';
});
}
}
Future<void> _openMultipleFiles() async {
try {
const XTypeGroup typeGroup = XTypeGroup(
label: '图片文件',
extensions: <String>['jpg', 'png', 'gif'],
);
final List<XFile> files = await openFiles(
acceptedTypeGroups: <XTypeGroup>[typeGroup],
);
if (files.isNotEmpty) {
setState(() {
_selectedFiles = files;
_selectedFile = null;
_error = null;
});
}
} catch (e) {
setState(() {
_error = '选择多个文件失败: $e';
});
}
}
void _clearAll() {
setState(() {
_selectedFile = null;
_selectedFiles = [];
_error = null;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('File Selector 示例'),
actions: [
if (_selectedFile != null || _selectedFiles.isNotEmpty)
IconButton(
icon: const Icon(Icons.clear),
onPressed: _clearAll,
tooltip: '清除所有',
),
],
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.blue.shade50,
Colors.indigo.shade50,
],
),
),
child: ListView(
padding: const EdgeInsets.all(16),
children: [
// 文件选择卡片
_buildSectionCard(
title: '文件选择',
icon: Icons.file_open,
color: Colors.blue,
description: '选择单个或多个文件,支持按类型过滤',
child: Column(
children: [
ElevatedButton.icon(
onPressed: _openSingleFile,
icon: const Icon(Icons.insert_drive_file),
label: const Text('选择单个文件'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _openMultipleFiles,
icon: const Icon(Icons.file_copy),
label: const Text('选择多个文件'),
),
],
),
),
const SizedBox(height: 16),
// 显示错误信息
if (_error != null)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.red.shade200),
),
child: Row(
children: [
const Icon(Icons.error, color: Colors.red),
const SizedBox(width: 8),
Expanded(
child: Text(
_error!,
style: const TextStyle(color: Colors.red),
),
),
],
),
),
const SizedBox(height: 16),
// 显示选中的单个文件
if (_selectedFile != null)
_buildSectionCard(
title: '选中的文件',
icon: Icons.description,
color: Colors.purple,
description: '显示用户选择的单个文件的详细信息',
child: Column(
children: [
const Icon(Icons.file_present, size: 48, color: Colors.purple),
const SizedBox(height: 12),
Text(
_selectedFile!.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Text(
_selectedFile!.path,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade700,
fontFamily: 'monospace',
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
// 显示选中的多个文件
if (_selectedFiles.isNotEmpty)
_buildSectionCard(
title: '选中的文件 (${_selectedFiles.length})',
icon: Icons.folder_zip,
color: Colors.teal,
description: '显示用户选择的多个文件的列表',
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _selectedFiles.length,
itemBuilder: (context, index) {
return ListTile(
leading: const Icon(Icons.insert_drive_file),
title: Text(_selectedFiles[index].name),
subtitle: Text(
_selectedFiles[index].path,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
dense: true,
);
},
),
),
const SizedBox(height: 32),
],
),
),
);
}
Widget _buildSectionCard({
required String title,
required IconData icon,
required Color color,
required Widget child,
required String description,
}) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
color: color,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
description,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
const SizedBox(height: 16),
child,
],
),
),
);
}
}
九、常见问题与最佳实践
9.1 常见问题
Q1: 为什么文件选择失败?
A: 可能的原因和解决方法:
原因 1:文件类型扩展名格式不正确
dart
// ❌ 错误:格式不一致
extensions: ['jpg', '.png', 'JPEG']
// ✅ 正确:统一格式,不需要点号
extensions: ['jpg', 'png', 'jpeg']
原因 2:设备没有对应的文件管理器
- OpenHarmony 设备需要确保有文件管理器应用
- 某些模拟器可能不支持文件选择
原因 3:权限不足
- 应用需要适当的存储权限
- 某些目录可能需要特殊权限才能访问
Q2: 如何处理用户取消选择的情况?
A: 检查返回值并给出友好提示
dart
final XFile? file = await openFile();
if (file == null) {
// 用户取消了选择
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已取消选择')),
);
return;
}
// 继续处理选择的文件
print('选择了: ${file.name}');
Q3: 如何获取文件的大小?
A: 使用 XFile.length() 方法
dart
final XFile? file = await openFile();
if (file != null) {
final int size = await file.length();
if (size < 1024) {
print('文件大小: ${size} 字节');
} else if (size < 1024 * 1024) {
print('文件大小: ${size / 1024} KB');
} else {
print('文件大小: ${size / (1024 * 1024)} MB');
}
}
Q4: 如何读取文件内容?
A: 根据文件类型使用不同的方法
dart
final XFile? file = await openFile();
if (file != null) {
// 如果是文本文件,可以直接读取为字符串
if (file.path.endsWith('.txt') || file.path.endsWith('.md')) {
final String content = await file.readAsString();
print('文件内容: $content');
} else {
// 如果是二进制文件,读取为字节数组
final Uint8List bytes = await file.readAsBytes();
print('文件大小: ${bytes.length} 字节');
}
}
Q5: OpenHarmony 平台支持哪些功能?
A: OpenHarmony 平台目前只支持:
- ✅
openFile()- 打开单个文件 - ✅
openFiles()- 打开多个文件
不支持:
- ❌
getDirectoryPath()- 选择目录 - ❌
getSaveLocation()- 获取保存位置 - ❌
getDirectoryPaths()- 选择多个目录
Q6: OpenHarmony 平台如何保存文件?
A: OpenHarmony 平台不支持 getSaveLocation(),需要使用 path_provider 获取应用目录,然后用 dart:io 写入文件。
dart
import 'package:path_provider/path_provider.dart';
import 'dart:io';
Future<void> _saveFile(String content) async {
try {
// 获取应用文档目录
final Directory appDocDir = await getApplicationDocumentsDirectory();
final String filePath = '${appDocDir.path}/my_file.txt';
// 写入文件
final File file = File(filePath);
await file.writeAsString(content);
print('文件已保存到: $filePath');
} catch (e) {
print('保存文件失败: $e');
}
}
9.2 最佳实践
1. 始终处理异常
文件操作涉及与系统交互,总是有失败的可能。良好的错误处理是必须的。
dart
Future<void> _handleFileSelection() async {
try {
final XFile? file = await openFile(
acceptedTypeGroups: [typeGroup],
);
if (file != null) {
setState(() {
_selectedFile = file;
_error = null;
});
} else {
setState(() {
_error = '用户取消了选择';
});
}
} catch (e) {
setState(() {
_error = '文件选择失败: $e';
});
// 记录错误日志用于调试
print('文件选择异常: $e');
}
}
2. 检查返回值
dart
// ❌ 错误:直接使用可能导致空指针异常
final XFile? file = await openFile();
print(file.name); // 如果 file 为 null 会报错
// ✅ 正确:先检查返回值
final XFile? file = await openFile();
if (file != null) {
print('选择了文件: ${file.name}');
} else {
print('用户取消了选择');
}
3. 提供合理的文件类型过滤
原则:
- 不要过滤得太严格,让用户无法找到文件
- 不要过滤得太宽松,让用户选择到不合适的文件
- 根据实际业务需求设置合理的过滤条件
示例:
dart
// ✅ 推荐:适度的过滤
const XTypeGroup images = XTypeGroup(
label: '图片文件',
extensions: ['jpg', 'png', 'gif', 'jpeg', 'bmp', 'webp'],
);
// ❌ 不推荐:只支持一种格式
const XTypeGroup jpgOnly = XTypeGroup(
label: 'JPG 图片',
extensions: ['jpg'],
);
// ❌ 不推荐:过滤范围太宽
const XTypeGroup allFiles = XTypeGroup(
label: '所有文件',
extensions: ['*'],
);
4. 使用异步操作避免阻塞 UI
dart
// ❌ 错误:在主线程进行耗时操作
void _openFileWrong() {
openFile().then((file) {
setState(() {
_selectedFile = file;
});
});
}
// ✅ 正确:使用 async/await 语法
Future<void> _openFileCorrect() async {
try {
final XFile? file = await openFile();
setState(() {
_selectedFile = file;
});
} catch (e) {
print('错误: $e');
}
}
5. 提供用户友好的提示
dart
Future<void> _loadFile() async {
try {
final XFile? file = await openFile(
acceptedTypeGroups: [textFiles],
);
if (file != null) {
final String content = await file.readAsString();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('文件加载成功: ${file.name}'),
duration: const Duration(seconds: 3),
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('加载失败: $e'),
backgroundColor: Colors.red,
),
);
}
}
十、API 参考速查表
11.1 OpenHarmony 平台支持的方法
| 方法 | 用途 | 返回类型 | OpenHarmony 支持 |
|---|---|---|---|
openFile() |
打开单个文件 | Future<XFile?> |
✅ yes |
openFiles() |
打开多个文件 | Future<List<XFile>> |
✅ yes |
getDirectoryPath() |
选择单个目录 | Future<String?> |
❌ no |
getSaveLocation() |
获取保存位置 | Future<FileSaveLocation?> |
❌ no |
getDirectoryPaths() |
选择多个目录 | Future<List<String?>> |
❌ no |
11.2 常用文件类型速查表
| 用途 | 推荐的扩展名 |
|---|---|
| 通用图片 | jpg, jpeg, png, gif, webp, bmp |
| 文档 | txt, pdf, doc, docx, xls, xlsx |
| 视频 | mp4, mov, avi, mkv |
| 音频 | mp3, wav, aac, flac, m4a |
| 压缩包 | zip, rar, 7z, tar, gz |