flutter create . --platforms=windows,macos,linux
**4. 桌面平台特有功能**
* **窗口管理**:调整窗口大小、位置、全屏等。
* **菜单栏**:创建和管理应用菜单。
* **文件操作**:读写文件、选择文件/目录。
* **通知**:发送桌面通知。
* **系统托盘**:将应用最小化到系统托盘。
* **键盘快捷键**:自定义键盘快捷键。
这些功能通常通过 Flutter 插件来实现,例如 `window_manager`、`menubar`、`file_selector`、`desktop_notifications` 等。
#### 官方文档链接
* [Flutter 桌面开发](https://docs.flutter.cn/platform-integration/desktop)
* [Flutter 桌面应用示例](https://github.com/flutter/samples/tree/main/desktop_gallery)
#### Flutter 开发中的应用案例
Flutter 桌面应用适用于各种场景,如生产力工具、数据可视化、本地文件管理、企业内部应用等。以下案例将演示如何构建一个简单的桌面文件浏览器,展示 Flutter 如何与文件系统交互。
**案例:构建一个简单的桌面文件浏览器**
我们将创建一个 Flutter 桌面应用,允许用户选择一个目录,然后显示该目录下的文件和子目录。
**步骤 1: 创建项目并添加依赖**
```bash
flutter create desktop_file_browser --platforms=windows,macos,linux
cd desktop_file_browser
在 pubspec.yaml 中添加 file_selector 依赖:
dependencies:
flutter:
sdk: flutter
file_selector: ^1.0.0 # 最新版本可能不同
运行 flutter pub get。
步骤 2: 构建 UI 和逻辑 (lib/main.dart)
import 'package:flutter/material.dart';
import 'package:file_selector/file_selector.dart';
import 'dart:io';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '桌面文件浏览器',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: const FileBrowserScreen(),
);
}
}
class FileBrowserScreen extends StatefulWidget {
const FileBrowserScreen({super.key});
@override
State<FileBrowserScreen> createState() => _FileBrowserScreenState();
}
class _FileBrowserScreenState extends State<FileBrowserScreen> {
String? _currentDirectoryPath;
List<FileSystemEntity> _filesAndFolders = [];
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadInitialDirectory();
}
Future<void> _loadInitialDirectory() async {
// 尝试加载用户文档目录作为初始目录
try {
final String? initialPath = await getDirectoryPath(); // 弹出目录选择器
if (initialPath != null) {
await _loadDirectoryContents(initialPath);
}
} catch (e) {
print('Error loading initial directory: $e');
setState(() {
_currentDirectoryPath = '无法加载初始目录';
});
}
}
Future<void> _loadDirectoryContents(String path) async {
setState(() {
_isLoading = true;
_currentDirectoryPath = path;
_filesAndFolders = [];
});
try {
final Directory directory = Directory(path);
if (await directory.exists()) {
final List<FileSystemEntity> entities = await directory.list().toList();
entities.sort((a, b) {
// 目录排在文件前面
if (a is Directory && b is File) return -1;
if (a is File && b is Directory) return 1;
return a.path.toLowerCase().compareTo(b.path.toLowerCase());
});
setState(() {
_filesAndFolders = entities;
});
} else {
setState(() {
_currentDirectoryPath = '目录不存在: $path';
});
}
} catch (e) {
setState(() {
_currentDirectoryPath = '读取目录失败: $e';
});
print('Error reading directory: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _pickDirectory() async {
final String? selectedDirectory = await getDirectoryPath();
if (selectedDirectory != null) {
await _loadDirectoryContents(selectedDirectory);
}
}
void _openItem(FileSystemEntity entity) async {
if (entity is Directory) {
await _loadDirectoryContents(entity.path);
} else if (entity is File) {
// 模拟打开文件,实际应用中可能需要调用平台特定的API
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('尝试打开文件: ${entity.path}')),
);
// 在macOS/Linux上可以使用 `open` 命令,Windows上可以使用 `start` 命令
// import 'package:process_run/process_run.dart';
// await run('open', [entity.path]); // macOS/Linux
// await run('start', [entity.path]); // Windows
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('桌面文件浏览器'),
actions: [
IconButton(
icon: const Icon(Icons.folder_open),
onPressed: _pickDirectory,
tooltip: '选择目录',
),
if (_currentDirectoryPath != null && _currentDirectoryPath != '无法加载初始目录' && _currentDirectoryPath != '读取目录失败: $e')
IconButton(
icon: const Icon(Icons.arrow_upward),
onPressed: () {
final parent = Directory(_currentDirectoryPath!).parent;
if (parent.path != _currentDirectoryPath) {
_loadDirectoryContents(parent.path);
}
},
tooltip: '返回上一级',
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'当前目录: ${_currentDirectoryPath ?? '未选择'}',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _filesAndFolders.isEmpty && _currentDirectoryPath != null && _currentDirectoryPath != '无法加载初始目录' && _currentDirectoryPath != '读取目录失败: $e'
? const Center(child: Text('此目录为空'))
: ListView.builder(
itemCount: _filesAndFolders.length,
itemBuilder: (context, index) {
final entity = _filesAndFolders[index];
final isDirectory = entity is Directory;
return ListTile(
leading: Icon(isDirectory ? Icons.folder : Icons.insert_drive_file),
title: Text(entity.path.split(Platform.pathSeparator).last),
onTap: () => _openItem(entity),
);
},
),
),
],
),
);
}
}
案例分析:
file_selector 插件:这个插件提供了跨平台的 API 来选择文件和目录。getDirectoryPath() 方法会打开一个原生的目录选择对话框。dart:io 库:Flutter 桌面应用可以直接使用 Dart 的 dart:io 库来访问文件系统,进行文件和目录的读写、遍历等操作。Directory 和 File 类:dart:io 提供了 Directory 和 File 类来表示文件系统中的目录和文件。directory.list().toList():用于获取目录下的所有文件和子目录。Platform.pathSeparator:用于获取当前操作系统的路径分隔符(例如 Windows 是 \,macOS/Linux 是 /)。IconButton 用于触发目录选择和返回上一级目录的操作。ListView.builder 用于高效地显示文件和目录列表。ListTile 用于显示每个文件或目录的图标和名称。onTap 回调用于处理用户点击事件,如果是目录则进入该目录,如果是文件则模拟打开。_isLoading 状态和错误信息显示,以提供更好的用户体验。如何运行和测试:
flutter run -d windows (或 macos, linux)。这个案例展示了 Flutter 在桌面应用开发中的强大能力,特别是与本地文件系统的深度集成。通过结合 Flutter 的 UI 能力和 dart:io 库,你可以构建功能丰富的桌面应用程序。
总结:
Flutter 桌面应用开发已经成熟,为开发者提供了一个高效、跨平台的解决方案。通过利用 Flutter 丰富的 Widget 库和强大的平台集成能力,你可以构建出高性能、美观且功能完善的桌面应用程序。