Flutter目录书签管理插件directory_bookmarks的使用
Flutter目录书签管理插件directory_bookmarks的使用
目录书签
directory_bookmarks
是一个用于跨平台目录书签管理和安全文件操作的 Flutter 插件。此插件提供了一致的 API 来处理目录访问和文件操作,并且特别支持平台特定的安全特性。
平台支持
平台 | 状态 | 实现细节 |
---|---|---|
macOS | 支持 | 安全范围书签用于持久目录访问 |
Android | 开发中 | 存储访问框架(部分实现) |
iOS | 计划中 | 将使用安全范围书签 |
Windows | 计划中 | 未来实现 |
Linux | 计划中 | 未来实现 |
注意:目前,该包主要集中在 macOS 支持上。在其他平台上使用会导致不支持平台错误。我们正在积极开发扩展平台支持。
特性
- 安全目录访问:平台特定的安全目录访问机制
- macOS:安全范围书签
- Android:存储访问框架
- 目录书签:保存并恢复对用户选择目录的访问
- 文件操作:在书签目录中读取、写入和列出文件
- 持久访问:跨应用重启保持对目录的访问
- 权限管理:内置权限管理和验证
- 资源管理:自动清理系统资源
入门指南
将插件添加到你的 pubspec.yaml
文件:
dependencies:
directory_bookmarks: ^0.1.0
平台特定设置
macOS (支持)
- 在你的 macOS 应用中启用 App Sandbox 和所需的权限。在你的权限文件中添加以下内容:
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
- 在你的
AppDelegate.swift
中注册插件:
import directory_bookmarks
class AppDelegate: FlutterAppDelegate {
override func applicationDidFinishLaunching(_ notification: Notification) {
guard let mainWindow = mainFlutterWindow else { return }
guard let controller = mainWindow.contentViewController as? FlutterViewController else { return }
DirectoryBookmarksPlugin.register(with: controller.registrar(forPlugin: "DirectoryBookmarksPlugin"))
super.applicationDidFinishLaunching(notification)
}
}
Android (开发中)
注意:Android 支持目前处于开发阶段。实现是部分的,可能不会按预期工作。
在你的 AndroidManifest.xml
中添加以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
其他平台 (计划中)
iOS、Windows 和 Linux 的支持正在计划中。目前在这些平台上使用此插件会导致 UnsupportedError
。
API 参考
目录书签操作
saveBookmark(String directoryPath, {Map<String, dynamic>? metadata})
: 保存目录书签,可选元数据resolveBookmark()
: 解析并返回当前目录书签信息
文件操作
saveFile(String fileName, List<int> data)
: 将原始数据保存到书签目录中的文件saveStringToFile(String fileName, String content)
: 将文本内容保存到文件saveBytesToFile(String fileName, Uint8List bytes)
: 将二进制数据保存到文件readFile(String fileName)
: 从文件中读取原始数据readStringFromFile(String fileName)
: 从文件中读取文本内容readBytesFromFile(String fileName)
: 从文件中读取二进制数据listFiles()
: 列出书签目录中的所有文件
目录操作
// 在书签位置创建目录
final success = await DirectoryBookmarkHandler.createDirectory('images');
// 创建嵌套目录
await DirectoryBookmarkHandler.createDirectory('images/thumbnails');
// 在子目录中保存文件(如果不存在则创建)
final imageBytes = await File('path/to/image.jpg').readAsBytes();
await DirectoryBookmarkHandler.saveBytesToFileInPath(
'images/thumbnails/image1.jpg',
imageBytes,
);
// 在子目录中保存文本文件
await DirectoryBookmarkHandler.saveStringToFileInPath(
'docs/notes/note1.txt',
'Hello, World!',
);
// 列出特定子目录中的文件
final imageFiles = await DirectoryBookmarkHandler.listFilesInPath('images');
if (imageFiles != null) {
for (final file in imageFiles) {
print('Image file: $file');
}
}
示例:带子目录的图像库
class ImageGalleryWithFolders extends StatefulWidget {
const ImageGalleryWithFolders({super.key});
@override
State<ImageGalleryWithFolders> createState() => _ImageGalleryWithFoldersState();
}
class _ImageGalleryWithFoldersState extends State<ImageGalleryWithFolders> {
String _currentPath = '';
List<String> _items = [];
String? _errorMessage;
@override
void initState() {
super.initState();
_loadCurrentDirectory();
}
Future<void> _loadCurrentDirectory() async {
try {
final files = await DirectoryBookmarkHandler.listFilesInPath(_currentPath);
if (files != null) {
setState(() {
_items = files;
_errorMessage = null;
});
}
} catch (e) {
setState(() {
_errorMessage = 'Error loading directory: $e';
});
}
}
Future<void> _createNewFolder(String folderName) async {
try {
final path = _currentPath.isEmpty
? folderName
: '$_currentPath/$folderName';
final success = await DirectoryBookmarkHandler.createDirectory(path);
if (success) {
_loadCurrentDirectory();
}
} catch (e) {
setState(() {
_errorMessage = 'Error creating folder: $e';
});
}
}
Future<void> _navigateToFolder(String folderName) async {
setState(() {
_currentPath = _currentPath.isEmpty
? folderName
: '$_currentPath/$folderName';
});
_loadCurrentDirectory();
}
Future<void> _navigateUp() async {
if (_currentPath.isEmpty) return;
final lastSlash = _currentPath.lastIndexOf('/');
setState(() {
_currentPath = lastSlash == -1 ? '' : _currentPath.substring(0, lastSlash);
});
_loadCurrentDirectory();
}
@override
Widget build(BuildContext context) {
if (_errorMessage != null) {
return Center(child: Text(_errorMessage!));
}
return Column(
children: [
// 目录导航栏
Container(
padding: const EdgeInsets.all(8),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_upward),
onPressed: _currentPath.isEmpty ? null : _navigateUp,
),
Text('Current path: ${_currentPath.isEmpty ? '/' : _currentPath}'),
IconButton(
icon: const Icon(Icons.create_new_folder),
onPressed: () async {
final name = await showDialog<String>(
context: context,
builder: (context) => NewFolderDialog(),
);
if (name != null) {
_createNewFolder(name);
}
},
),
],
),
),
// 目录内容
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
final isDirectory = !item.contains('.');
return ListTile(
leading: Icon(isDirectory ? Icons.folder : Icons.image),
title: Text(item),
onTap: isDirectory
? () => _navigateToFolder(item)
: null,
);
},
),
),
],
);
}
}
class NewFolderDialog extends StatelessWidget {
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Create New Folder'),
content: TextField(
controller: controller,
decoration: const InputDecoration(
labelText: 'Folder Name',
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, controller.text),
child: const Text('Create'),
),
],
);
}
}
写入文件
// 保存任何类型的文件(基本方法)
final fileData = await File('path/to/source/file').readAsBytes();
final success = await DirectoryBookmarkHandler.saveFile(
'destination.file',
fileData,
);
// 保存文本文件
final textSuccess = await DirectoryBookmarkHandler.saveStringToFile(
'example.txt',
'Hello, World!',
);
// 保存二进制文件(如图像、PDF等)
final imageBytes = await File('path/to/image.jpg').readAsBytes();
final imageSuccess = await DirectoryBookmarkHandler.saveBytesToFile(
'image.jpg',
imageBytes,
);
读取文件
// 读取任何类型的文件(基本方法)
final fileData = await DirectoryBookmarkHandler.readFile('myfile.dat');
if (fileData != null) {
// 使用文件数据(List<int>)
}
// 读取文本文件
final textContent = await DirectoryBookmarkHandler.readStringFromFile('example.txt');
if (textContent != null) {
print('File content: $textContent');
}
// 读取二进制文件
final imageBytes = await DirectoryBookmarkHandler.readBytesFromFile('image.jpg');
if (imageBytes != null) {
// 使用图像字节(Uint8List)
final image = Image.memory(imageBytes);
}
示例:将文件复制到书签目录
import 'package:directory_bookmarks/directory_bookmarks.dart';
import 'package:file_picker/file_picker.dart';
Future<void> copyFileToBookmark() async {
try {
// 选择要复制的文件
final result = await FilePicker.platform.pickFiles();
if (result == null) return;
final file = result.files.first;
if (file.bytes == null) return;
// 保存到书签目录
final success = await DirectoryBookmarkHandler.saveBytesToFile(
file.name,
file.bytes!,
);
if (success) {
print('File copied successfully');
} else {
print('Failed to copy file');
}
} catch (e) {
print('Error copying file: $e');
}
}
权限管理
hasWritePermission()
: 检查书签目录是否授予写权限requestWritePermission()
: 请求书签目录的写权限
使用
基本示例
import 'package:directory_bookmarks/directory_bookmarks.dart';
void main() async {
// 检查平台支持
if (!(defaultTargetPlatform == TargetPlatform.macOS ||
defaultTargetPlatform == TargetPlatform.android)) {
print('Platform not supported');
return;
}
try {
// 选择并书签一个目录
final path = await FilePicker.platform.getDirectoryPath(
dialogTitle: 'Select a directory to bookmark',
);
if (path == null) {
print('No directory selected');
return;
}
// 保存书签
final success = await DirectoryBookmarkHandler.saveBookmark(
path,
metadata: {'lastAccessed': DateTime.now().toIso8601String()},
);
if (success) {
print('Directory bookmarked successfully');
} else {
print('Failed to bookmark directory');
return;
}
// 解析书签
final bookmark = await DirectoryBookmarkHandler.resolveBookmark();
if (bookmark != null) {
print('Bookmarked directory: ${bookmark.path}');
// 检查写权限
final hasPermission = await DirectoryBookmarkHandler.hasWritePermission();
if (!hasPermission) {
print('No write permission');
return;
}
// 列出文件
final files = await DirectoryBookmarkHandler.listFiles();
if (files != null) {
print('Files in directory: $files');
}
// 写文件
final writeSuccess = await DirectoryBookmarkHandler.saveStringToFile(
'test.txt',
'Hello, World!',
);
if (writeSuccess) {
print('File written successfully');
}
// 读文件
final content = await DirectoryBookmarkHandler.readStringFromFile('test.txt');
if (content != null) {
print('File content: $content');
}
}
} catch (e) {
print('Error: $e');
}
}
错误处理
插件包括全面的错误处理:
try {
// 首先检查平台支持
if (!(defaultTargetPlatform == TargetPlatform.macOS ||
defaultTargetPlatform == TargetPlatform.android)) {
print('Platform ${defaultTargetPlatform.name} is not supported yet');
return;
}
// 尝试解析现有的书签
final bookmark = await DirectoryBookmarkHandler.resolveBookmark();
if (bookmark == null) {
print('No bookmark found, selecting new directory...');
final path = await FilePicker.platform.getDirectoryPath();
if (path == null) {
print('No directory selected');
return;
}
final success = await DirectoryBookmarkHandler.saveBookmark(path);
if (!success) {
print('Failed to bookmark directory');
return;
}
}
// 检查权限
if (!await DirectoryBookmarkHandler.hasWritePermission()) {
print('No write permission for bookmarked directory');
return;
}
// 执行文件操作...
} on PlatformException catch (e) {
print('Platform error: ${e.message}');
} catch (e) {
print('Unexpected error: $e');
}
更多关于Flutter目录书签管理插件directory_bookmarks的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter目录书签管理插件directory_bookmarks的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用directory_bookmarks
插件来管理目录书签的一个基本示例。请注意,directory_bookmarks
插件可能是一个虚构的插件名,因为Flutter官方插件集合中并没有直接名为directory_bookmarks
的插件。但我会基于一个假设的API来展示如何使用它。
首先,确保你已经将directory_bookmarks
插件添加到你的pubspec.yaml
文件中:
dependencies:
flutter:
sdk: flutter
directory_bookmarks: ^x.y.z # 替换为实际的版本号
然后,运行flutter pub get
来安装插件。
接下来,在你的Flutter项目中,你可以按照以下步骤使用directory_bookmarks
插件来管理目录书签。
- 导入插件
在你的Dart文件中导入插件:
import 'package:directory_bookmarks/directory_bookmarks.dart';
- 请求权限(如果需要)
如果你的应用需要访问文件系统,你可能需要请求相应的权限。这通常涉及Android和iOS的权限管理。
// Android权限请求示例(在实际应用中,你可能需要使用更复杂的权限管理逻辑)
if (Platform.isAndroid) {
// 检查并请求权限(这里只是一个示例,实际权限名称可能会有所不同)
Permission.storage.request().then((status) {
if (!status.isGranted) {
// 处理权限被拒绝的情况
}
});
}
- 使用插件API
以下是一个使用directory_bookmarks
插件来添加、获取和删除书签的示例代码:
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<String> bookmarks = [];
@override
void initState() {
super.initState();
// 获取现有的书签
_getBookmarks();
}
Future<void> _addBookmark(String path) async {
try {
await DirectoryBookmarks.addBookmark(path);
// 更新书签列表
_getBookmarks();
} catch (e) {
// 处理错误
print("Error adding bookmark: $e");
}
}
Future<void> _getBookmarks() async {
try {
List<String> result = await DirectoryBookmarks.getBookmarks();
setState(() {
bookmarks = result;
});
} catch (e) {
// 处理错误
print("Error getting bookmarks: $e");
}
}
Future<void> _deleteBookmark(String path) async {
try {
await DirectoryBookmarks.deleteBookmark(path);
// 更新书签列表
_getBookmarks();
} catch (e) {
// 处理错误
print("Error deleting bookmark: $e");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Directory Bookmarks'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
// 显示书签列表
Expanded(
child: ListView.builder(
itemCount: bookmarks.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(bookmarks[index]),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteBookmark(bookmarks[index]),
),
);
},
),
),
// 添加书签按钮
ElevatedButton(
onPressed: () async {
// 这里应该有一个UI来选择目录,这里只是一个示例
var path = "/path/to/directory"; // 替换为实际选择的路径
await _addBookmark(path);
},
child: Text('Add Bookmark'),
),
],
),
),
),
);
}
}
请注意,这个示例代码假设DirectoryBookmarks
插件提供了addBookmark
, getBookmarks
, 和 deleteBookmark
方法。如果实际的插件API有所不同,你需要根据实际的API文档进行调整。
此外,选择目录的UI部分(在示例中用/path/to/directory
表示)需要你自己实现,可以使用Flutter的文件选择器插件来完成。