import 'package:flutter/material.dart'; import 'package:yx_net_inspector/yx_net_inspector.dart'; class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @override State createState() => _SettingsPageState(); } class _SettingsPageState extends State { final YxNetInspectorController _controller = YxNetInspectorController.instance; // 当前配置状态 bool _showFloatingBall = true; double _ballSize = 70.0; Color _ballColor = Colors.blue; bool _draggable = true; bool _showBadge = true; bool _autoHide = false; int _maxLogs = 1000; // 主题配置 Color _primaryColor = Colors.blue; Color _successColor = Colors.green; Color _errorColor = Colors.red; Color _warningColor = Colors.orange; @override void initState() { super.initState(); _loadCurrentSettings(); } void _loadCurrentSettings() { // 这里可以从持久化存储加载设置 // 目前使用默认值 } @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 当前状态卡片 _buildStatusCard(), const SizedBox(height: 20), // 悬浮球设置 _buildFloatingBallSettings(), const SizedBox(height: 20), // 日志设置 _buildLogSettings(), const SizedBox(height: 20), // 主题设置 _buildThemeSettings(), const SizedBox(height: 20), // 操作按钮 _buildActionButtons(), const SizedBox(height: 20), // 统计信息 _buildStatistics(), ], ), ); } Widget _buildStatusCard() { final stats = _controller.getStatistics(); return Card( elevation: 4, child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.dashboard, color: Colors.blue, size: 24, ), ), const SizedBox(width: 12), Text( '检查器状态', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildStatusItem( '总请求数', stats['totalRequests'].toString(), Icons.send, Colors.blue, ), ), Expanded( child: _buildStatusItem( '成功请求', stats['successRequests'].toString(), Icons.check_circle, Colors.green, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildStatusItem( '错误请求', stats['errorRequests'].toString(), Icons.error, Colors.red, ), ), Expanded( child: _buildStatusItem( '成功率', stats['successRate'], Icons.trending_up, Colors.orange, ), ), ], ), ], ), ), ); } Widget _buildStatusItem( String label, String value, IconData icon, Color color) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(8), border: Border.all(color: color.withValues(alpha: 0.2)), ), child: Column( children: [ Icon(icon, color: color, size: 20), const SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ); } Widget _buildFloatingBallSettings() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.circle, color: Colors.blue), const SizedBox(width: 8), Text( '悬浮球设置', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), // 显示悬浮球开关 SwitchListTile( title: const Text('显示悬浮球'), subtitle: const Text('在屏幕上显示网络检查器悬浮球'), value: _showFloatingBall, onChanged: (value) { setState(() { _showFloatingBall = value; }); if (value) { _controller.showFloatingBallWidget(); } else { _controller.hideFloatingBallWidget(); } }, ), // 悬浮球大小 ListTile( title: const Text('悬浮球大小'), subtitle: Text('${_ballSize.round()}px'), trailing: SizedBox( width: 150, child: Slider( value: _ballSize, min: 40, max: 100, divisions: 12, onChanged: (value) { setState(() { _ballSize = value; }); }, ), ), ), // 悬浮球颜色 ListTile( title: const Text('悬浮球颜色'), subtitle: const Text('选择悬浮球的颜色'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ _buildColorOption(Colors.blue), _buildColorOption(Colors.green), _buildColorOption(Colors.red), _buildColorOption(Colors.purple), _buildColorOption(Colors.orange), ], ), ), // 可拖拽开关 SwitchListTile( title: const Text('可拖拽'), subtitle: const Text('允许拖拽悬浮球到不同位置'), value: _draggable, onChanged: (value) { setState(() { _draggable = value; }); }, ), // 显示徽章开关 SwitchListTile( title: const Text('显示数量徽章'), subtitle: const Text('在悬浮球上显示请求数量徽章'), value: _showBadge, onChanged: (value) { setState(() { _showBadge = value; }); }, ), // 自动隐藏开关 SwitchListTile( title: const Text('自动隐藏'), subtitle: const Text('一段时间后自动隐藏悬浮球'), value: _autoHide, onChanged: (value) { setState(() { _autoHide = value; }); }, ), ], ), ), ); } Widget _buildColorOption(Color color) { final isSelected = _ballColor == color; return GestureDetector( onTap: () { setState(() { _ballColor = color; }); }, child: Container( width: 24, height: 24, margin: const EdgeInsets.only(left: 4), decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all( color: isSelected ? Colors.black : Colors.transparent, width: 2, ), ), child: isSelected ? const Icon(Icons.check, color: Colors.white, size: 14) : null, ), ); } Widget _buildLogSettings() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.list_alt, color: Colors.green), const SizedBox(width: 8), Text( '日志设置', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), // 最大日志数量 ListTile( title: const Text('最大日志数量'), subtitle: Text('保留最近 $_maxLogs 条日志记录'), trailing: SizedBox( width: 150, child: Slider( value: _maxLogs.toDouble(), min: 100, max: 5000, divisions: 49, label: _maxLogs.toString(), onChanged: (value) { setState(() { _maxLogs = value.round(); }); }, ), ), ), // 当前日志数量 ListTile( title: const Text('当前日志数量'), subtitle: Text('${_controller.logs.length} 条日志'), trailing: IconButton( icon: const Icon(Icons.refresh), onPressed: () { setState(() {}); }, tooltip: '刷新', ), ), ], ), ), ); } Widget _buildThemeSettings() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.palette, color: Colors.purple), const SizedBox(width: 8), Text( '主题设置', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), _buildThemeColorRow('主色调', _primaryColor, (color) { setState(() { _primaryColor = color; }); }), _buildThemeColorRow('成功色', _successColor, (color) { setState(() { _successColor = color; }); }), _buildThemeColorRow('错误色', _errorColor, (color) { setState(() { _errorColor = color; }); }), _buildThemeColorRow('警告色', _warningColor, (color) { setState(() { _warningColor = color; }); }), ], ), ), ); } Widget _buildThemeColorRow( String label, Color currentColor, ValueChanged onColorChanged) { final colors = [ Colors.blue, Colors.green, Colors.red, Colors.orange, Colors.purple, Colors.teal, Colors.pink, Colors.indigo, ]; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ SizedBox( width: 80, child: Text( label, style: const TextStyle(fontWeight: FontWeight.w500), ), ), const SizedBox(width: 16), Expanded( child: Row( children: colors.map((color) { final isSelected = currentColor == color; return GestureDetector( onTap: () => onColorChanged(color), child: Container( width: 28, height: 28, margin: const EdgeInsets.only(right: 8), decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all( color: isSelected ? Colors.black : Colors.transparent, width: 2, ), ), child: isSelected ? const Icon(Icons.check, color: Colors.white, size: 16) : null, ), ); }).toList(), ), ), ], ), ); } Widget _buildActionButtons() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.settings_applications, color: Colors.orange), const SizedBox(width: 8), Text( '操作', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: _applySettings, icon: const Icon(Icons.check), label: const Text('应用设置'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, ), ), ), const SizedBox(width: 8), Expanded( child: ElevatedButton.icon( onPressed: _resetSettings, icon: const Icon(Icons.restore), label: const Text('重置设置'), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, ), ), ), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _exportLogs, icon: const Icon(Icons.download), label: const Text('导出日志'), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: _clearAllLogs, icon: const Icon(Icons.clear_all), label: const Text('清空日志'), ), ), ], ), ], ), ), ); } Widget _buildStatistics() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.analytics, color: Colors.teal), const SizedBox(width: 8), Text( '详细统计', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), _buildStatItem('GET 请求', _getMethodCount('GET')), _buildStatItem('POST 请求', _getMethodCount('POST')), _buildStatItem('PUT 请求', _getMethodCount('PUT')), _buildStatItem('DELETE 请求', _getMethodCount('DELETE')), const Divider(), _buildStatItem('平均响应时间', _getAverageResponseTime()), _buildStatItem('最近请求时间', _getLastRequestTime()), ], ), ), ); } Widget _buildStatItem(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label), Text( value, style: const TextStyle(fontWeight: FontWeight.bold), ), ], ), ); } String _getMethodCount(String method) { return _controller.logs .where((log) => log.method == method) .length .toString(); } String _getAverageResponseTime() { final logsWithDuration = _controller.logs.where((log) => log.duration != null); if (logsWithDuration.isEmpty) return '0ms'; final totalMs = logsWithDuration .map((log) => log.duration!.inMilliseconds) .reduce((a, b) => a + b); final average = totalMs / logsWithDuration.length; return '${average.round()}ms'; } String _getLastRequestTime() { if (_controller.logs.isEmpty) return '无'; final lastLog = _controller.logs.first; // logs are sorted by newest first final now = DateTime.now(); final diff = now.difference(lastLog.timestamp); if (diff.inMinutes < 1) { return '${diff.inSeconds}秒前'; } else if (diff.inHours < 1) { return '${diff.inMinutes}分钟前'; } else { return '${diff.inHours}小时前'; } } void _applySettings() { // 这里可以应用设置到实际的检查器实例 // 由于演示限制,我们只显示消息 ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('设置已应用'), backgroundColor: Colors.green, duration: Duration(seconds: 2), ), ); } void _resetSettings() { setState(() { _showFloatingBall = true; _ballSize = 70.0; _ballColor = Colors.blue; _draggable = true; _showBadge = true; _autoHide = false; _maxLogs = 1000; _primaryColor = Colors.blue; _successColor = Colors.green; _errorColor = Colors.red; _warningColor = Colors.orange; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('设置已重置为默认值'), duration: Duration(seconds: 2), ), ); } void _exportLogs() { if (_controller.logs.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('没有日志可导出'), backgroundColor: Colors.orange, ), ); return; } // 这里可以实现实际的导出功能 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('已导出 ${_controller.logs.length} 条日志'), backgroundColor: Colors.blue, duration: const Duration(seconds: 2), ), ); } void _clearAllLogs() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('确认清空'), content: const Text('确定要清空所有日志吗?此操作不可撤销。'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('取消'), ), TextButton( onPressed: () { Navigator.of(context).pop(); _controller.clearLogs(); setState(() {}); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('所有日志已清空'), backgroundColor: Colors.red, ), ); }, child: const Text('确定'), ), ], ), ); } }