yx_net_inspector_flutter/example/lib/pages/settings_page.dart

735 lines
21 KiB
Dart

import 'package:flutter/material.dart';
import 'package:yx_net_inspector/yx_net_inspector.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
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<Color> 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('确定'),
),
],
),
);
}
}