云计算百科
云计算领域专业知识百科平台

我这样用鸿蒙化Flutter三方库file_selector实现单图片和多图片选择

你好!👋 欢迎来到这篇关于 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/openharmonytpc/flutter_packages.git
path: packages/file_selector/file_selector
ref: br_file_selectorv1.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 = []; // 清除多选状态
});
}
}

image-20260202232306070

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; // 清除单选状态
});
}
}

image-20260202232426485

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), // 显示路径仅供参考
],

image-20260202232459168


🚀 3. 配置应用入口:lib/main.dart

最后,我们在应用的主页添加入口,跳转到我们刚刚写的演示页面。

修改文件:lib/main.dart

操作:

  • 导入演示页面文件。
  • 在 MyHomePage 的 Column 中添加一个按钮。
  • // 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! 💻⚡️ 🎉 祝你开发顺利! 🚀 欢迎加入开源鸿蒙跨平台社区

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 我这样用鸿蒙化Flutter三方库file_selector实现单图片和多图片选择
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!