ui细节优化
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:platform_info/platform_info.dart';
|
||||
import 'package:flutter_udid/flutter_udid.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
|
||||
/// 时间: 2026-03-26
|
||||
/// 功能: 应用信息页面
|
||||
@@ -21,11 +22,15 @@ class AppInfoPage extends StatefulWidget {
|
||||
|
||||
class _AppInfoPageState extends State<AppInfoPage> {
|
||||
String _udid = '获取中...';
|
||||
bool _isDeveloperMode = false;
|
||||
int _tapCount = 0;
|
||||
DateTime? _lastTapTime;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadUdid();
|
||||
_loadDeveloperMode();
|
||||
}
|
||||
|
||||
Future<void> _loadUdid() async {
|
||||
@@ -45,6 +50,50 @@ class _AppInfoPageState extends State<AppInfoPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadDeveloperMode() async {
|
||||
final isEnabled = await SharedPreferencesStorageController.getBool(
|
||||
'developer_mode_enabled',
|
||||
defaultValue: false,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isDeveloperMode = isEnabled;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveDeveloperMode(bool enabled) async {
|
||||
await SharedPreferencesStorageController.setBool(
|
||||
'developer_mode_enabled',
|
||||
enabled,
|
||||
);
|
||||
}
|
||||
|
||||
void _onFrameworkTap() {
|
||||
final now = DateTime.now();
|
||||
if (_lastTapTime != null && now.difference(_lastTapTime!).inSeconds > 2) {
|
||||
_tapCount = 0;
|
||||
}
|
||||
_lastTapTime = now;
|
||||
_tapCount++;
|
||||
|
||||
if (_tapCount >= 5 && !_isDeveloperMode) {
|
||||
setState(() {
|
||||
_isDeveloperMode = true;
|
||||
});
|
||||
_saveDeveloperMode(true);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('开发者模式激活'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
_tapCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -64,6 +113,20 @@ class _AppInfoPageState extends State<AppInfoPage> {
|
||||
icon: Icon(Icons.arrow_back, color: AppConstants.primaryColor),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
actions: [
|
||||
if (_isDeveloperMode)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bug_report, color: Colors.green),
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('调试信息已激活'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -177,9 +240,15 @@ class _AppInfoPageState extends State<AppInfoPage> {
|
||||
color: Colors.white.withValues(alpha: 0.9),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
const Text(
|
||||
'框架 1.3',
|
||||
style: TextStyle(fontSize: 12, color: Colors.white),
|
||||
GestureDetector(
|
||||
onTap: _onFrameworkTap,
|
||||
child: const Text(
|
||||
'框架 1.3',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
@@ -188,14 +257,21 @@ class _AppInfoPageState extends State<AppInfoPage> {
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
color: _isDeveloperMode
|
||||
? Colors.green.withValues(alpha: 0.3)
|
||||
: Colors.white.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'软件版本 1.5',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
color: _isDeveloperMode
|
||||
? Colors.green
|
||||
: Colors.white,
|
||||
fontWeight: _isDeveloperMode
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
1024
lib/views/profile/components/entire_page.dart
Normal file
1024
lib/views/profile/components/entire_page.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -148,7 +148,7 @@ class PopMenu extends StatelessWidget {
|
||||
}),
|
||||
_buildBottomSheetItem(
|
||||
context,
|
||||
'屏幕常亮',
|
||||
'使用教程',
|
||||
Icons.screen_lock_rotation,
|
||||
() => toggleScreenWake(context),
|
||||
),
|
||||
|
||||
359
lib/views/profile/components/server_info_dialog.dart
Normal file
359
lib/views/profile/components/server_info_dialog.dart
Normal file
@@ -0,0 +1,359 @@
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
|
||||
class ServerInfoDialog {
|
||||
static Future<void> show(BuildContext context, {Map<String, dynamic>? data}) {
|
||||
final server = data?['server'] as Map<String, dynamic>?;
|
||||
final network = data?['network'] as Map<String, dynamic>?;
|
||||
final timestamp = data?['timestamp'] as Map<String, dynamic>?;
|
||||
|
||||
final load = server?['load'] as Map<String, dynamic>?;
|
||||
final latency = network?['latency'] as List<dynamic>?;
|
||||
final serverResponseTime = network?['server_response_time'];
|
||||
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 340),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor,
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.cloud_outlined,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'广州 server-ls',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
'服务器信息',
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildInfoCard(
|
||||
icon: Icons.schedule,
|
||||
iconColor: const Color(0xFF007AFF),
|
||||
title: '服务器时间',
|
||||
content: timestamp?['datetime'] ?? '--',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoCard(
|
||||
icon: Icons.speed,
|
||||
iconColor: const Color(0xFF34C759),
|
||||
title: '服务器负载',
|
||||
content:
|
||||
'1分钟: ${_formatLoad(load?['1min'])}\n5分钟: ${_formatLoad(load?['5min'])}\n15分钟: ${_formatLoad(load?['15min'])}',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoCard(
|
||||
icon: Icons.bolt,
|
||||
iconColor: const Color(0xFFFF9500),
|
||||
title: '服务器响应',
|
||||
content: '${serverResponseTime ?? '--'} ms',
|
||||
trailing: _buildResponseTimeIndicator(
|
||||
serverResponseTime,
|
||||
),
|
||||
),
|
||||
if (latency != null && latency.isNotEmpty) ...[
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF2F2F7),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.public,
|
||||
size: 16,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'网络延迟',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...latency.map<Widget>((item) {
|
||||
final host = item['host'] as String?;
|
||||
final ip = item['ip'] as String?;
|
||||
final lat = item['latency'];
|
||||
final status = item['status'] as String?;
|
||||
final isOnline = status == 'online';
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: isOnline
|
||||
? const Color(0xFF34C759)
|
||||
: const Color(0xFFFF3B30),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
host ?? '--',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
ip ?? '--',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isOnline
|
||||
? const Color(
|
||||
0xFF34C759,
|
||||
).withValues(alpha: 0.1)
|
||||
: const Color(
|
||||
0xFFFF3B30,
|
||||
).withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(
|
||||
8,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
isOnline ? '$lat ms' : '离线',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isOnline
|
||||
? const Color(0xFF34C759)
|
||||
: const Color(0xFFFF3B30),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF2F2F7),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'关闭',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF007AFF),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildInfoCard({
|
||||
required IconData icon,
|
||||
required Color iconColor,
|
||||
required String title,
|
||||
required String content,
|
||||
Widget? trailing,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF2F2F7),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: iconColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(icon, color: iconColor, size: 18),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
content,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static String _formatLoad(dynamic value) {
|
||||
if (value == null) return '--';
|
||||
|
||||
double? loadValue;
|
||||
if (value is num) {
|
||||
loadValue = value.toDouble();
|
||||
} else if (value is String) {
|
||||
loadValue = double.tryParse(value);
|
||||
}
|
||||
|
||||
if (loadValue == null) return '--';
|
||||
|
||||
final percentage = (loadValue * 100).round();
|
||||
return '$percentage%';
|
||||
}
|
||||
|
||||
static Widget _buildResponseTimeIndicator(dynamic responseTime) {
|
||||
int? time;
|
||||
if (responseTime is int) {
|
||||
time = responseTime;
|
||||
} else if (responseTime is String) {
|
||||
time = int.tryParse(responseTime);
|
||||
}
|
||||
|
||||
if (time == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Color color;
|
||||
String label;
|
||||
if (time < 100) {
|
||||
color = const Color(0xFF34C759);
|
||||
label = '极快';
|
||||
} else if (time < 300) {
|
||||
color = const Color(0xFF34C759);
|
||||
label = '快速';
|
||||
} else if (time < 500) {
|
||||
color = const Color(0xFFFF9500);
|
||||
label = '正常';
|
||||
} else {
|
||||
color = const Color(0xFFFF3B30);
|
||||
label = '较慢';
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import 'expand/vote.dart';
|
||||
import 'expand/manu-script.dart';
|
||||
import 'components/bug_list_page.dart';
|
||||
import 'components/pop-menu.dart';
|
||||
import 'components/entire_page.dart';
|
||||
|
||||
class ProfilePage extends StatefulWidget {
|
||||
const ProfilePage({super.key});
|
||||
@@ -612,7 +613,10 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
_buildSettingsItem(
|
||||
'查看全站统计',
|
||||
Icons.history,
|
||||
() => _showSnackBar('查看全站统计'),
|
||||
() => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const EntirePage()),
|
||||
),
|
||||
),
|
||||
_buildSettingsItem(
|
||||
'开发计划',
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../utils/http/http_client.dart';
|
||||
import 'user-plan.dart';
|
||||
import '../components/server_info_dialog.dart';
|
||||
|
||||
/// 时间: 2026-03-29
|
||||
/// 功能: 离线数据管理页面
|
||||
@@ -504,95 +505,7 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
|
||||
}
|
||||
|
||||
void _displayServerInfoDialog(Map<String, dynamic> data) {
|
||||
final server = data['server'] as Map<String, dynamic>?;
|
||||
final network = data['network'] as Map<String, dynamic>?;
|
||||
final timestamp = data['timestamp'] as Map<String, dynamic>?;
|
||||
|
||||
final load = server?['load'] as Map<String, dynamic>?;
|
||||
final latency = network?['latency'] as List<dynamic>?;
|
||||
final serverResponseTime = network?['server_response_time'];
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.cloud, color: AppConstants.primaryColor, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
const Text('服务器信息', style: TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'广州 server-ls',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoSection('⏰ 服务器时间', timestamp?['datetime'] ?? '--'),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoSection(
|
||||
'📊 服务器负载',
|
||||
'1分钟: ${load?['1min'] ?? '--'} | 5分钟: ${load?['5min'] ?? '--'} | 15分钟: ${load?['15min'] ?? '--'}',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoSection(
|
||||
'⚡ 服务器响应',
|
||||
'${serverResponseTime ?? '--'} ms',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'🌐 网络延迟',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (latency != null)
|
||||
...latency.map<Widget>((item) {
|
||||
final host = item['host'] as String?;
|
||||
final ip = item['ip'] as String?;
|
||||
final lat = item['latency'];
|
||||
final status = item['status'] as String?;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Text(
|
||||
'• $host ($ip): ${status == 'online' ? '$lat ms' : '离线'}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('关闭'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoSection(String title, String content) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(content, style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
],
|
||||
);
|
||||
ServerInfoDialog.show(context, data: data);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user