Files
home/home_monitor/lib/screens/home_screen.dart
panw df266614e1 feat(desktop): 增加桌面端系统托盘和窗口管理功能
- 主函数中添加窗口管理初始化,支持Windows、Linux和MacOS平台
- HomeScreen中集成托盘服务和窗口服务,实现托盘菜单交互
- 实现窗口关闭时弹出对话框,支持最小化到托盘或退出程序
- 添加托盘服务TrayService,封装系统托盘图标和菜单的初始化、事件处理
- 添加窗口服务WindowService,实现窗口的显示、隐藏、关闭及事件监听
- 在界面添加最小化到托盘的按钮,仅桌面端显示
- pubspec.yaml添加assets目录,包含托盘图标资源文件
2025-12-08 09:13:59 +08:00

335 lines
9.7 KiB
Dart

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/providers.dart';
import '../services/services.dart';
import '../widgets/widgets.dart';
/// 主页面
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final TrayService _trayService = TrayService();
final WindowService _windowService = WindowService();
@override
void initState() {
super.initState();
// 初始化数据
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = context.read<MonitorProvider>();
provider.initDefaultDevices();
provider.refreshAll();
provider.startAutoRefresh(intervalSeconds: 30);
// 初始化系统托盘
_initSystemTray(provider);
});
}
/// 初始化系统托盘
Future<void> _initSystemTray(MonitorProvider provider) async {
if (!_trayService.isSupported) return;
await _trayService.init(
onShow: () => _windowService.showWindow(),
onHide: () => _windowService.hideWindow(),
onExit: () => _exitApp(),
onRefresh: () => provider.refreshAll(),
);
// 设置窗口关闭事件(最小化到托盘)
_windowService.onCloseRequested = () => _handleCloseRequest();
}
/// 处理窗口关闭请求
void _handleCloseRequest() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('关闭窗口'),
content: const Text('请选择操作:'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
_windowService.hideWindow();
},
child: const Text('最小化到托盘'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
_exitApp();
},
child: const Text('退出程序'),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
],
),
);
}
/// 退出应用
Future<void> _exitApp() async {
await _trayService.destroy();
await _windowService.closeWindow();
}
@override
void dispose() {
_trayService.destroy();
_windowService.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(),
body: _buildBody(),
floatingActionButton: _buildFAB(),
);
}
PreferredSizeWidget _buildAppBar() {
return AppBar(
title: const Text('家庭网络监控'),
centerTitle: true,
actions: [
Consumer<MonitorProvider>(
builder: (context, provider, _) {
return IconButton(
icon: provider.isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.refresh),
onPressed: provider.isLoading ? null : () => provider.refreshAll(),
tooltip: '刷新',
);
},
),
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
_showSettingsDialog();
},
tooltip: '设置',
),
// 最小化到托盘按钮(仅桌面端)
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS))
IconButton(
icon: const Icon(Icons.minimize),
onPressed: () => _windowService.hideWindow(),
tooltip: '最小化到托盘',
),
],
);
}
Widget _buildBody() {
return Consumer<MonitorProvider>(
builder: (context, provider, _) {
return RefreshIndicator(
onRefresh: () => provider.refreshAll(),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 网速仪表盘
SpeedGauge(
networkStatus: provider.networkStatus,
isLoading: provider.isTestingSpeed,
),
// 概览卡片
_buildOverviewCards(provider),
// 设备列表标题
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'监控设备',
style: Theme.of(context).textTheme.titleMedium,
),
TextButton.icon(
onPressed: () => _showAddDeviceDialog(),
icon: const Icon(Icons.add, size: 18),
label: const Text('添加'),
),
],
),
),
// 设备列表
_buildDeviceList(provider),
const SizedBox(height: 80),
],
),
),
);
},
);
}
Widget _buildOverviewCards(MonitorProvider provider) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: StatusCard(
title: '在线设备',
value: '${provider.onlineDevicesCount}',
icon: Icons.check_circle,
iconColor: Colors.green,
subtitle: '${provider.devices.length} 台设备',
),
),
const SizedBox(width: 12),
Expanded(
child: StatusCard(
title: '离线设备',
value: '${provider.offlineDevicesCount}',
icon: Icons.error,
iconColor: provider.offlineDevicesCount > 0
? Colors.red
: Colors.grey,
subtitle: provider.offlineDevicesCount > 0
? '需要检查'
: '全部正常',
),
),
],
),
);
}
Widget _buildDeviceList(MonitorProvider provider) {
if (provider.devices.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(32),
child: Column(
children: [
Icon(Icons.devices, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无监控设备'),
SizedBox(height: 8),
Text('点击右上角添加设备', style: TextStyle(color: Colors.grey)),
],
),
),
);
}
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: provider.devices.length,
itemBuilder: (context, index) {
final device = provider.devices[index];
return DeviceTile(
device: device,
onRefresh: () => provider.checkSingleDevice(device.id),
onTap: () => _showDeviceDetail(device),
);
},
);
}
Widget _buildFAB() {
return Consumer<MonitorProvider>(
builder: (context, provider, _) {
return FloatingActionButton.extended(
onPressed: provider.isTestingSpeed ? null : () => provider.runSpeedTest(),
icon: provider.isTestingSpeed
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Icon(Icons.speed),
label: Text(provider.isTestingSpeed ? '测速中...' : '测速'),
);
},
);
}
void _showSettingsDialog() {
final provider = context.read<MonitorProvider>();
int interval = provider.refreshInterval;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('设置'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('自动刷新间隔'),
subtitle: Text('$interval'),
trailing: DropdownButton<int>(
value: interval,
items: const [
DropdownMenuItem(value: 10, child: Text('10秒')),
DropdownMenuItem(value: 30, child: Text('30秒')),
DropdownMenuItem(value: 60, child: Text('1分钟')),
DropdownMenuItem(value: 300, child: Text('5分钟')),
],
onChanged: (value) {
if (value != null) {
provider.setRefreshInterval(value);
Navigator.pop(context);
}
},
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('关闭'),
),
],
),
);
}
void _showAddDeviceDialog() {
// TODO: 实现添加设备对话框
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('添加设备功能开发中...')),
);
}
void _showDeviceDetail(device) {
// TODO: 实现设备详情页
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('设备: ${device.name}')),
);
}
}