作者链接哇是林啊 1标签:#harmonyos #flutter #开源项目 #文件管理工具 #跨设备协同

开源项目是提升技术能力、参与生态共建的重要途径。本文以「鸿蒙跨设备文件管理工具」为例,完整讲解从需求分析、架构设计、功能开发到项目开源的全流程,所有代码遵循开源规范,可直接用于二次开发或学习参考,契合 CSDN「我的第一个开源项目」故事征集活动主题。

一、开源项目需求与架构设计

1.1 核心需求

开发一款适配鸿蒙多设备的文件管理工具,支持本地文件浏览、跨设备文件传输、文件分类管理、文件搜索四大核心功能,同时具备轻量化、高性能、易扩展的特点。

1.2 项目架构设计

采用三层架构设计,分离 UI 层、业务逻辑层、数据层,便于维护和扩展:

  • UI 层:Flutter 实现跨设备统一界面,适配手机、平板、智慧屏;
  • 业务逻辑层:封装文件操作、跨设备传输、搜索等核心逻辑;
  • 数据层:整合鸿蒙本地文件 API、分布式软总线 API、文件数据库。

1.3 技术栈选型

  • 跨端框架:Flutter 3.16+;
  • 鸿蒙原生:Java(文件操作、软总线通信);
  • 状态管理:Provider 6.0+;
  • 本地存储:Hive(轻量级 NoSQL 数据库,存储文件索引);
  • 开源工具:git(版本控制)、markdown(文档编写)。

二、项目基础搭建与核心模块开发

2.1 项目结构初始化

创建 Flutter 项目并搭建标准目录结构,同时初始化 git 仓库:

bash

运行

# 创建项目
flutter create harmony_file_manager
cd harmony_file_manager
# 初始化git
git init
echo "# 鸿蒙跨设备文件管理工具" > README.md
git add .
git commit -m "初始化项目"

项目核心目录结构如下:

plaintext

harmony_file_manager/
├── flutter_module/
│   ├── lib/
│   │   ├── core/          # 核心业务逻辑
│   │   ├── model/         # 数据模型
│   │   ├── pages/         # UI页面
│   │   ├── provider/      # 状态管理
│   │   ├── utils/         # 工具类
│   │   └── main.dart      # 入口文件
├── entry/                 # 鸿蒙原生项目
├── README.md              # 开源项目文档
└── LICENSE                # 开源许可证

2.2 核心数据模型设计

创建文件数据模型,用于统一管理文件信息,代码如下

dart

// flutter_module/lib/model/file_model.dart
import 'dart:io';
import 'package:hive/hive.dart';

part 'file_model.g.dart'; // 自动生成序列化代码

@HiveType(typeId: 0)
class FileModel extends HiveObject {
  @HiveField(0)
  final String filePath; // 文件路径

  @HiveField(1)
  final String fileName; // 文件名

  @HiveField(2)
  final String fileType; // 文件类型(图片/文档/视频等)

  @HiveField(3)
  final int fileSize; // 文件大小(字节)

  @HiveField(4)
  final DateTime modifyTime; // 修改时间

  @HiveField(5)
  final String deviceId; // 所属设备ID

  FileModel({
    required this.filePath,
    required this.fileName,
    required this.fileType,
    required this.fileSize,
    required this.modifyTime,
    required this.deviceId,
  });

  // 转换文件大小为友好格式
  String get formattedSize {
    if (fileSize < 1024) return '$fileSize B';
    if (fileSize < 1024 * 1024) return '${(fileSize / 1024).toStringAsFixed(2)} KB';
    return '${(fileSize / (1024 * 1024)).toStringAsFixed(2)} MB';
  }
}

执行flutter pub run build_runner build生成序列化代码。

2.3 本地文件管理核心功能

封装本地文件浏览、分类、搜索功能,工具类代码如下

dart

// flutter_module/lib/core/file/local_file_manager.dart
import 'dart:io';
import 'package:hive/hive.dart';
import '../model/file_model.dart';
import '../../utils/channel_manager.dart';

class LocalFileManager {
  final Box<FileModel> _fileBox = Hive.box<FileModel>('file_box');
  final HarmonyChannelManager _channelManager = HarmonyChannelManager();

  // 初始化文件索引
  Future<void> initFileIndex() async {
    final deviceId = await _channelManager.invokeMethod<String>('getDeviceId');
    if (deviceId == null) return;
    // 扫描本地文件并建立索引
    await _scanLocalFiles('/sdcard', deviceId);
  }

  // 扫描本地文件
  Future<void> _scanLocalFiles(String directoryPath, String deviceId) async {
    final directory = Directory(directoryPath);
    if (!directory.existsSync()) return;
    final files = directory.listSync(recursive: false);
    for (var file in files) {
      if (file is File) {
        final fileType = _getFileType(file.path);
        final fileModel = FileModel(
          filePath: file.path,
          fileName: file.path.split('/').last,
          fileType: fileType,
          fileSize: file.lengthSync(),
          modifyTime: file.lastModifiedSync(),
          deviceId: deviceId,
        );
        _fileBox.add(fileModel);
      } else if (file is Directory) {
        // 递归扫描子目录
        await _scanLocalFiles(file.path, deviceId);
      }
    }
  }

  // 根据文件后缀判断文件类型
  String _getFileType(String filePath) {
    final suffix = filePath.split('.').last.toLowerCase();
    if (['jpg', 'png', 'jpeg', 'gif'].contains(suffix)) return 'image';
    if (['mp4', 'avi', 'mov'].contains(suffix)) return 'video';
    if (['doc', 'docx', 'pdf', 'txt'].contains(suffix)) return 'document';
    return 'other';
  }

  // 按类型筛选文件
  List<FileModel> getFilesByType(String type) {
    return _fileBox.values.where((file) => file.fileType == type).toList();
  }

  // 搜索文件(按文件名模糊匹配)
  List<FileModel> searchFiles(String keyword) {
    return _fileBox.values
        .where((file) => file.fileName.toLowerCase().contains(keyword.toLowerCase()))
        .toList();
  }
}

2.4 跨设备文件传输功能

整合前文实现的分布式软总线能力,实现跨设备文件发送与接收,代码如下

dart

// flutter_module/lib/core/file/cross_device_file_manager.dart
import '../softbus/softbus_manager.dart';
import '../model/file_model.dart';
import '../../utils/channel_manager.dart';

class CrossDeviceFileManager {
  final SoftBusManager _softBusManager = SoftBusManager();
  final HarmonyChannelManager _channelManager = HarmonyChannelManager();

  // 发送文件到目标设备
  Future<bool> sendFileToDevice(String deviceId, FileModel file) async {
    return await _softBusManager.sendFile(
      deviceId: deviceId,
      localFilePath: file.filePath,
    );
  }

  // 接收来自其他设备的文件
  Future<void> receiveFile() async {
    // 注册文件接收回调
    _channelManager.invokeMethod('registerFileReceiver', (Map<String, dynamic> data) {
      final filePath = data['filePath'];
      final deviceId = data['deviceId'];
      // 将接收的文件添加到本地索引
      _addReceivedFileToIndex(filePath, deviceId);
    });
  }

  // 将接收的文件添加到本地索引
  Future<void> _addReceivedFileToIndex(String filePath, String deviceId) async {
    final file = File(filePath);
    final fileType = _getFileType(filePath);
    final fileModel = FileModel(
      filePath: filePath,
      fileName: file.path.split('/').last,
      fileType: fileType,
      fileSize: file.lengthSync(),
      modifyTime: file.lastModifiedSync(),
      deviceId: deviceId,
    );
    final box = Hive.box<FileModel>('file_box');
    box.add(fileModel);
  }

  String _getFileType(String filePath) {
    final suffix = filePath.split('.').last.toLowerCase();
    if (['jpg', 'png', 'jpeg', 'gif'].contains(suffix)) return 'image';
    if (['mp4', 'avi', 'mov'].contains(suffix)) return 'video';
    if (['doc', 'docx', 'pdf', 'txt'].contains(suffix)) return 'document';
    return 'other';
  }
}

2.5 状态管理与 UI 页面实现

使用 Provider 管理全局文件数据,状态管理代码如下:

dart

// flutter_module/lib/provider/file_provider.dart
import 'package:flutter/foundation.dart';
import '../model/file_model.dart';
import '../core/file/local_file_manager.dart';
import '../core/file/cross_device_file_manager.dart';

class FileProvider extends ChangeNotifier {
  final LocalFileManager _localFileManager = LocalFileManager();
  final CrossDeviceFileManager _crossDeviceManager = CrossDeviceFileManager();

  List<FileModel> _allFiles = [];
  List<FileModel> _filteredFiles = [];
  String _currentType = 'all';

  List<FileModel> get filteredFiles => _filteredFiles;
  String get currentType => _currentType;

  // 初始化
  Future<void> init() async {
    await _localFileManager.initFileIndex();
    await _crossDeviceManager.receiveFile();
    _allFiles = Hive.box<FileModel>('file_box').values.toList();
    _filteredFiles = _allFiles;
    notifyListeners();
  }

  // 切换文件类型
  void switchFileType(String type) {
    setState(() {
      _currentType = type;
      if (type == 'all') {
        _filteredFiles = _allFiles;
      } else {
        _filteredFiles = _localFileManager.getFilesByType(type);
      }
    });
  }

  // 搜索文件
  void searchFiles(String keyword) {
    setState(() {
      _filteredFiles = _localFileManager.searchFiles(keyword);
    });
  }

  // 发送文件
  Future<bool> sendFile(String deviceId, FileModel file) async {
    return await _crossDeviceManager.sendFileToDevice(deviceId, file);
  }

  void setState(VoidCallback fn) {
    fn();
    notifyListeners();
  }
}

创建文件管理主页面,UI 代码如下:

dart

// flutter_module/lib/pages/file_manager_home.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../provider/file_provider.dart';
import '../model/file_model.dart';

class FileManagerHomePage extends StatefulWidget {
  const FileManagerHomePage({super.key});

  @override
  State<FileManagerHomePage> createState() => _FileManagerHomePageState();
}

class _FileManagerHomePageState extends State<FileManagerHomePage> {
  final TextEditingController _searchController = TextEditingController();

  @override
  void initState() {
    super.initState();
    Provider.of<FileProvider>(context, listen: false).init();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('鸿蒙跨设备文件管理器')),
      body: Consumer<FileProvider>(
        builder: (context, provider, child) {
          return Column(
            children: [
              // 搜索框
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextField(
                  controller: _searchController,
                  decoration: const InputDecoration(
                    hintText: '搜索文件...',
                    prefixIcon: Icon(Icons.search),
                    border: OutlineInputBorder(),
                  ),
                  onChanged: provider.searchFiles,
                ),
              ),
              // 文件类型筛选
              SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: ['all', 'image', 'video', 'document', 'other']
                      .map((type) => Padding(
                            padding: const EdgeInsets.symmetric(horizontal: 4),
                            child: ElevatedButton(
                              onPressed: () => provider.switchFileType(type),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: provider.currentType == type ? Colors.blue : Colors.grey,
                              ),
                              child: Text(_getTypeName(type)),
                            ),
                          ))
                      .toList(),
                ),
              ),
              // 文件列表
              Expanded(
                child: provider.filteredFiles.isEmpty
                    ? const Center(child: Text('暂无文件'))
                    : ListView.builder(
                        itemCount: provider.filteredFiles.length,
                        itemBuilder: (context, index) {
                          final file = provider.filteredFiles[index];
                          return ListTile(
                            title: Text(file.fileName),
                            subtitle: Text('${file.formattedSize} · ${file.modifyTime.toString().substring(0, 16)}'),
                            trailing: IconButton(
                              icon: const Icon(Icons.send),
                              onPressed: () => _sendFile(context, provider, file),
                            ),
                          );
                        },
                      ),
              ),
            ],
          );
        },
      ),
    );
  }

  String _getTypeName(String type) {
    switch (type) {
      case 'all': return '全部文件';
      case 'image': return '图片';
      case 'video': return '视频';
      case 'document': return '文档';
      case 'other': return '其他';
      default: return type;
    }
  }

  Future<void> _sendFile(BuildContext context, FileProvider provider, FileModel file) async {
    // 此处可弹窗选择目标设备,简化处理直接传入测试设备ID
    const testDeviceId = 'test_device_001';
    final result = await provider.sendFile(testDeviceId, file);
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(result ? '文件发送成功' : '文件发送失败')),
    );
  }
}

三、项目开源与文档编写

3.1 开源许可证选择

选择Apache License 2.0,允许商用和二次开发,同时要求保留原作者版权声明。在项目根目录创建LICENSE文件,写入许可证内容。

3.2 编写 README.md 文档

详细介绍项目功能、安装教程、使用方法、贡献指南等,吸引开发者使用和贡献代码:

markdown

# 鸿蒙跨设备文件管理工具
一款基于Flutter和鸿蒙系统开发的跨设备文件管理工具,支持本地文件浏览、分类管理、跨设备传输等功能。

## 功能特点
1.  本地文件管理:支持文件浏览、分类、搜索;
2.  跨设备传输:基于鸿蒙分布式软总线,实现高速文件传输;
3.  多设备适配:适配鸿蒙手机、平板、智慧屏等设备;
4.  轻量化设计:体积小、启动快,占用资源少。

## 环境要求
- 鸿蒙系统:HarmonyOS 6.0+
- Flutter:3.16.0+
- DevEco Studio:4.1.0+

## 安装教程
1.  克隆仓库:git clone https://github.com/yourname/harmony_file_manager.git
2.  安装依赖:cd harmony_file_manager/flutter_module && flutter pub get
3.  打开项目:用DevEco Studio打开项目,编译运行。

## 贡献指南
1.  Fork本仓库;
2.  创建特性分支:git checkout -b feature/xxx;
3.  提交代码:git commit -m 'feat: 添加xxx功能';
4.  推送分支:git push origin feature/xxx;
5.  提交Pull Request。

## 许可证
本项目基于Apache License 2.0开源,详见LICENSE文件。

3.3 提交代码并推送到开源平台

将项目推送到 GitCode、GitHub 等开源平台,完成开源发布:

bash

运行

# 添加远程仓库
git remote add origin https://github.com/yourname/harmony_file_manager.git
# 推送代码
git push -u origin main

四、开源项目维护与社区运营

  1. 问题响应:及时处理用户提交的 Issue,解决使用过程中的 bug;
  2. 版本迭代:规划后续版本功能,如文件预览、批量传输、云存储集成等;
  3. 社区交流:创建项目交流群,收集用户反馈,邀请开发者参与贡献。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐