Files
AutoJudge-Flutter/lib/screens/settings_screen.dart
2025-11-13 09:14:49 +08:00

369 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import '../providers/theme_provider.dart';
import '../providers/evaluation_provider.dart';
import '../widgets/confirm_dialog.dart';
import 'login_screen.dart';
/// Settings screen for app configuration
///
/// Provides theme customization, logout, and data management
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
Future<void> _handleLogout(BuildContext context) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => const ConfirmDialog(
title: '退出登录',
content: '确定要退出登录吗?',
confirmText: '退出',
cancelText: '取消',
),
);
if (confirmed != true || !context.mounted) return;
final authProvider = Provider.of<AuthProvider>(context, listen: false);
final evaluationProvider = Provider.of<EvaluationProvider>(
context,
listen: false,
);
await authProvider.logout();
evaluationProvider.reset();
if (!context.mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => const LoginScreen()),
(route) => false,
);
}
Future<void> _handleClearData(BuildContext context) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => const ConfirmDialog(
title: '清除数据',
content: '确定要清除所有本地数据吗?\n这将删除评教历史记录。',
confirmText: '清除',
cancelText: '取消',
),
);
if (confirmed != true || !context.mounted) return;
final evaluationProvider = Provider.of<EvaluationProvider>(
context,
listen: false,
);
await evaluationProvider.clearEvaluationHistory();
if (!context.mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('数据已清除')));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: ListView(
children: [
// Theme section
_buildSectionHeader(context, '外观设置'),
_buildThemeModeSelector(context),
_buildColorSchemeSelector(context),
const Divider(height: 32),
// Account section
_buildSectionHeader(context, '账号管理'),
_buildAccountInfo(context),
_buildLogoutTile(context),
const Divider(height: 32),
// Data section
_buildSectionHeader(context, '数据管理'),
_buildClearDataTile(context),
const Divider(height: 32),
// About section
_buildSectionHeader(context, '关于'),
_buildAboutTile(context),
_buildFontLicenseTile(context),
],
),
);
}
Widget _buildSectionHeader(BuildContext context, String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
);
}
Widget _buildThemeModeSelector(BuildContext context) {
return Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return ListTile(
leading: const Icon(Icons.brightness_6),
title: const Text('主题模式'),
subtitle: Text(
themeProvider.getThemeModeName(themeProvider.themeMode),
),
onTap: () {
showDialog(
context: context,
builder: (context) => _ThemeModeDialog(
currentMode: themeProvider.themeMode,
onModeSelected: (mode) {
themeProvider.setThemeMode(mode);
Navigator.of(context).pop();
},
),
);
},
);
},
);
}
Widget _buildColorSchemeSelector(BuildContext context) {
return Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return ListTile(
leading: const Icon(Icons.palette),
title: const Text('颜色方案'),
subtitle: Text(
themeProvider.getColorSchemeName(themeProvider.colorScheme),
),
onTap: () {
showDialog(
context: context,
builder: (context) => _ColorSchemeDialog(
currentScheme: themeProvider.colorScheme,
onSchemeSelected: (scheme) {
themeProvider.setColorScheme(scheme);
Navigator.of(context).pop();
},
),
);
},
);
},
);
}
Widget _buildAccountInfo(BuildContext context) {
return Consumer<AuthProvider>(
builder: (context, authProvider, child) {
final credentials = authProvider.credentials;
return ListTile(
leading: const Icon(Icons.account_circle),
title: const Text('当前账号'),
subtitle: Text(credentials?.userId ?? '未登录'),
);
},
);
}
Widget _buildLogoutTile(BuildContext context) {
return ListTile(
leading: Icon(Icons.logout, color: Theme.of(context).colorScheme.error),
title: Text(
'退出登录',
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
onTap: () => _handleLogout(context),
);
}
Widget _buildClearDataTile(BuildContext context) {
return ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text('清除本地数据'),
subtitle: const Text('删除评教历史记录'),
onTap: () => _handleClearData(context),
);
}
Widget _buildAboutTile(BuildContext context) {
return ListTile(
leading: const Icon(Icons.info_outline),
title: const Text('关于应用'),
subtitle: const Text('版本 1.0.0'),
onTap: () {
showAboutDialog(
context: context,
applicationName: '自动评教系统',
applicationVersion: '1.0.0',
applicationIcon: Icon(
Icons.school,
size: 48,
color: Theme.of(context).colorScheme.primary,
),
children: [
const Text('AUFE自动评教工具'),
const SizedBox(height: 8),
const Text('帮助学生快速完成课程评教任务'),
],
);
},
);
}
Widget _buildFontLicenseTile(BuildContext context) {
return ListTile(
leading: const Icon(Icons.font_download),
title: const Text('字体许可'),
subtitle: const Text('MiSans 字体使用说明'),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('MiSans 字体知识产权许可协议'),
content: const SingleChildScrollView(
child: Text(
'本应用在 Windows 平台使用 MiSans 字体。\n\n'
'根据小米科技有限责任公司的授权MiSans 字体可免费用于个人和商业用途。\n\n'
'使用条件:\n'
'• 应特别注明使用了 MiSans 字体\n'
'• 不得对字体进行改编或二次开发\n'
'• 不得单独分发或售卖字体文件\n'
'• 可自由分发使用该字体创作的作品\n\n'
'本应用遵守以上使用条款。',
style: TextStyle(fontSize: 14),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('关闭'),
),
],
),
);
},
);
}
}
class _ThemeModeDialog extends StatelessWidget {
final ThemeMode currentMode;
final Function(ThemeMode) onModeSelected;
const _ThemeModeDialog({
required this.currentMode,
required this.onModeSelected,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('选择主题模式'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<ThemeMode>(
title: const Text('浅色模式'),
value: ThemeMode.light,
groupValue: currentMode,
onChanged: (mode) {
if (mode != null) onModeSelected(mode);
},
),
RadioListTile<ThemeMode>(
title: const Text('深色模式'),
value: ThemeMode.dark,
groupValue: currentMode,
onChanged: (mode) {
if (mode != null) onModeSelected(mode);
},
),
RadioListTile<ThemeMode>(
title: const Text('跟随系统'),
value: ThemeMode.system,
groupValue: currentMode,
onChanged: (mode) {
if (mode != null) onModeSelected(mode);
},
),
],
),
);
}
}
class _ColorSchemeDialog extends StatelessWidget {
final AppColorScheme currentScheme;
final Function(AppColorScheme) onSchemeSelected;
const _ColorSchemeDialog({
required this.currentScheme,
required this.onSchemeSelected,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('选择颜色方案'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildColorOption(context, AppColorScheme.blue, '蓝色', Colors.blue),
_buildColorOption(context, AppColorScheme.green, '绿色', Colors.green),
_buildColorOption(
context,
AppColorScheme.purple,
'紫色',
Colors.purple,
),
_buildColorOption(
context,
AppColorScheme.orange,
'橙色',
Colors.orange,
),
],
),
);
}
Widget _buildColorOption(
BuildContext context,
AppColorScheme scheme,
String name,
Color color,
) {
return RadioListTile<AppColorScheme>(
title: Row(
children: [
Container(
width: 24,
height: 24,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SizedBox(width: 12),
Text(name),
],
),
value: scheme,
groupValue: currentScheme,
onChanged: (scheme) {
if (scheme != null) onSchemeSelected(scheme);
},
);
}
}