You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.1 KiB
227 lines
6.1 KiB
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/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) { |
|
// 这里需要与编辑器提供者交互来打开文件 |
|
debugPrint('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) { |
|
debugPrint('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) { |
|
debugPrint('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) { |
|
debugPrint('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) { |
|
debugPrint('Error loading dir: $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) { |
|
debugPrint('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) { |
|
debugPrint('Error refreshing file tree: $e'); |
|
_fileNodes = []; |
|
} |
|
|
|
_isLoading = false; |
|
notifyListeners(); |
|
} |
|
}
|
|
|