369 lines
11 KiB
Dart
369 lines
11 KiB
Dart
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);
|
||
},
|
||
);
|
||
}
|
||
}
|