|
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import 'package:win_text_editor/app/models/file_node.dart';
|
|
|
|
import 'package:win_text_editor/app/providers/logger.dart';
|
|
|
|
import 'package:win_text_editor/app/services/file_service.dart';
|
|
|
|
|
|
|
|
class FileProvider with ChangeNotifier {
|
|
|
|
List<FileNode> _fileNodes = [];
|
|
|
|
bool _isLoading = false;
|
|
|
|
String _searchQuery = '';
|
|
|
|
String? _currentRootPath; // 跟踪当前根路径
|
|
|
|
|
|
|
|
bool get isLoading => _isLoading;
|
|
|
|
bool get hasRoot => _fileNodes.isNotEmpty && _fileNodes[0].isRoot;
|
|
|
|
|
|
|
|
// 移除构造函数的_initFileTree调用
|
|
|
|
FileProvider();
|
|
|
|
|
|
|
|
// 新增方法:手动设置根路径
|
|
|
|
Future<void> setRootPath(String path) async {
|
|
|
|
_currentRootPath = path;
|
|
|
|
await _loadRootDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
List<FileNode> get fileNodes =>
|
|
|
|
_searchQuery.isEmpty ? _fileNodes : _fileNodes.where((node) => _filterNode(node)).toList();
|
|
|
|
|
|
|
|
bool _filterNode(FileNode node) {
|
|
|
|
if (node.name.toLowerCase().contains(_searchQuery.toLowerCase())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return node.children.any(_filterNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void searchFiles(String query) {
|
|
|
|
_searchQuery = query;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
void toggleExpand(FileNode node) {
|
|
|
|
node.isExpanded = !node.isExpanded;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> pickAndOpenFile() async {
|
|
|
|
final result = await FilePicker.platform.pickFiles();
|
|
|
|
if (result != null && result.files.single.path != null) {
|
|
|
|
// 这里需要与编辑器提供者交互来打开文件
|
|
|
|
Logger().info('File selected: ${result.files.single.path}');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> loadDirectory(String path) async {
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
final directory = Directory(path);
|
|
|
|
final rootNode = FileNode(
|
|
|
|
name: directory.path.split(Platform.pathSeparator).last,
|
|
|
|
path: directory.path,
|
|
|
|
isDirectory: true,
|
|
|
|
isRoot: true, // 添加根节点标识
|
|
|
|
children: await FileService.buildFileTree(directory.path),
|
|
|
|
);
|
|
|
|
_fileNodes = [rootNode]; // 将根节点作为唯一顶层节点
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error loading directory: $e');
|
|
|
|
_fileNodes = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _loadRootDirectory() async {
|
|
|
|
if (_currentRootPath == null) return;
|
|
|
|
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
_fileNodes = [
|
|
|
|
FileNode(
|
|
|
|
name: _currentRootPath!.split(Platform.pathSeparator).last,
|
|
|
|
path: _currentRootPath!,
|
|
|
|
isDirectory: true,
|
|
|
|
isRoot: true,
|
|
|
|
depth: 0, // 根节点深度为0
|
|
|
|
children: [], // 初始为空,不加载内容
|
|
|
|
),
|
|
|
|
];
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error loading root directory: $e');
|
|
|
|
_fileNodes = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> loadRootDirectory(String path) async {
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
_fileNodes = [
|
|
|
|
FileNode(
|
|
|
|
name: path.split(Platform.pathSeparator).last,
|
|
|
|
path: path,
|
|
|
|
isDirectory: true,
|
|
|
|
isRoot: true,
|
|
|
|
children: [], // 初始为空
|
|
|
|
),
|
|
|
|
];
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error loading root: $e');
|
|
|
|
_fileNodes = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> toggleDirectory(FileNode dirNode) async {
|
|
|
|
if (dirNode.children.isEmpty) {
|
|
|
|
// 首次点击:加载内容
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
dirNode.children = await FileService.listDirectory(dirNode.path);
|
|
|
|
dirNode.isExpanded = true;
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error loading directory: $e');
|
|
|
|
dirNode.children = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
} else {
|
|
|
|
// 已加载过:只切换展开状态
|
|
|
|
dirNode.isExpanded = !dirNode.isExpanded;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> loadDirectoryContents(FileNode dirNode) async {
|
|
|
|
if (dirNode.children.isNotEmpty && dirNode.isExpanded) {
|
|
|
|
// 如果已经加载过且是展开状态,只切换展开状态
|
|
|
|
dirNode.isExpanded = !dirNode.isExpanded;
|
|
|
|
notifyListeners();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
final contents = await FileService.listDirectory(
|
|
|
|
dirNode.path,
|
|
|
|
parentDepth: dirNode.depth, // 确保传递父节点深度
|
|
|
|
);
|
|
|
|
|
|
|
|
final updatedNode = dirNode.copyWith(
|
|
|
|
children: contents,
|
|
|
|
isExpanded: true,
|
|
|
|
// 不需要设置 depth,因为 copyWith 会自动保留原值
|
|
|
|
);
|
|
|
|
|
|
|
|
_replaceNodeInTree(dirNode, updatedNode);
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error loading directory contents: $e');
|
|
|
|
final updatedNode = dirNode.copyWith(children: []);
|
|
|
|
_replaceNodeInTree(dirNode, updatedNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _replaceNodeInTree(FileNode oldNode, FileNode newNode) {
|
|
|
|
for (int i = 0; i < _fileNodes.length; i++) {
|
|
|
|
if (_fileNodes[i] == oldNode) {
|
|
|
|
_fileNodes[i] = newNode;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_replaceNodeInChildren(_fileNodes[i], oldNode, newNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _replaceNodeInChildren(FileNode parent, FileNode oldNode, FileNode newNode) {
|
|
|
|
for (int i = 0; i < parent.children.length; i++) {
|
|
|
|
if (parent.children[i] == oldNode) {
|
|
|
|
parent.children[i] = newNode;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_replaceNodeInChildren(parent.children[i], oldNode, newNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> refreshFileTree({bool loadContent = false}) async {
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
try {
|
|
|
|
final rootDir = await getApplicationDocumentsDirectory();
|
|
|
|
_fileNodes = [
|
|
|
|
FileNode(
|
|
|
|
name: rootDir.path.split(Platform.pathSeparator).last,
|
|
|
|
path: rootDir.path,
|
|
|
|
isDirectory: true,
|
|
|
|
isRoot: true,
|
|
|
|
// 初始不加载内容
|
|
|
|
children: loadContent ? await FileService.listDirectory(rootDir.path) : [],
|
|
|
|
),
|
|
|
|
];
|
|
|
|
} catch (e) {
|
|
|
|
Logger().error('Error refreshing file tree: $e');
|
|
|
|
_fileNodes = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|