feat: 诗词答题页面主题色支持与代码重构
- 创建 poetry-page.dart,提取 UI 组件(PoetryOptionItem、PoetryOptionsLayout、PoetryTag) - 修改 poetry.dart,使用新组件,添加主题色支持 - 修改 flow-anim.dart,添加主题色支持 - 修改 distinguish.dart,添加主题色支持 - 支持动态主题色切换 - 支持深色模式 - 保持页面布局不变
This commit is contained in:
@@ -11,6 +11,7 @@ import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../models/colors/app_colors.dart';
|
||||
import '../../../models/colors/theme_colors.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../../../controllers/history_controller.dart';
|
||||
import '../../../services/network_listener_service.dart';
|
||||
@@ -262,16 +263,8 @@ class _DistinguishPageState extends State<DistinguishPage> {
|
||||
child: Column(
|
||||
children: [
|
||||
_buildStatRow('已答题', '$_totalQuestions 题'),
|
||||
_buildStatRow(
|
||||
'正确',
|
||||
'$_correctAnswers 题',
|
||||
isGreen: true,
|
||||
),
|
||||
_buildStatRow(
|
||||
'错误',
|
||||
'$_wrongAnswers 题',
|
||||
isRed: true,
|
||||
),
|
||||
_buildStatRow('正确', '$_correctAnswers 题', isGreen: true),
|
||||
_buildStatRow('错误', '$_wrongAnswers 题', isRed: true),
|
||||
_buildStatRow(
|
||||
'正确率',
|
||||
'${_correctRate.toStringAsFixed(1)}%',
|
||||
@@ -288,15 +281,8 @@ class _DistinguishPageState extends State<DistinguishPage> {
|
||||
),
|
||||
_buildStatRow('提示次数', '$_hintCount 次'),
|
||||
_buildStatRow('跳过次数', '$_skipCount 次'),
|
||||
Divider(
|
||||
height: 24,
|
||||
color: AppColors.divider,
|
||||
),
|
||||
_buildStatRow(
|
||||
'诗词水平',
|
||||
_poetryLevel,
|
||||
isHighlight: true,
|
||||
),
|
||||
Divider(height: 24, color: AppColors.divider),
|
||||
_buildStatRow('诗词水平', _poetryLevel, isHighlight: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -378,10 +364,7 @@ class _DistinguishPageState extends State<DistinguishPage> {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: AppColors.secondaryText,
|
||||
),
|
||||
style: TextStyle(fontSize: 15, color: AppColors.secondaryText),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
@@ -453,7 +436,10 @@ $_poetryLevel
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: AppColors.surface,
|
||||
title: Text('确认清空', style: TextStyle(color: AppColors.primaryText)),
|
||||
content: Text('确定要清空所有答题记录吗?此操作不可恢复。', style: TextStyle(color: AppColors.secondaryText)),
|
||||
content: Text(
|
||||
'确定要清空所有答题记录吗?此操作不可恢复。',
|
||||
style: TextStyle(color: AppColors.secondaryText),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
@@ -500,6 +486,9 @@ $_poetryLevel
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
final primaryColor = ThemeColors.getThemeColor(
|
||||
_themeController.themeColorIndexRx.value,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
@@ -510,16 +499,13 @@ $_poetryLevel
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
backgroundColor: AppColors.primary,
|
||||
backgroundColor: primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(180),
|
||||
],
|
||||
colors: [primaryColor, primaryColor.withAlpha(180)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
@@ -540,7 +526,7 @@ $_poetryLevel
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
color: AppColors.background,
|
||||
color: isDark ? const Color(0xFF1A1A1A) : AppColors.background,
|
||||
child: SafeArea(
|
||||
child: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
@@ -577,10 +563,7 @@ $_poetryLevel
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'快去答题吧!',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.tertiaryText,
|
||||
),
|
||||
style: TextStyle(fontSize: 14, color: AppColors.tertiaryText),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -591,195 +574,210 @@ $_poetryLevel
|
||||
Widget _buildRecordList() {
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
final primaryColor = ThemeColors.getThemeColor(
|
||||
_themeController.themeColorIndexRx.value,
|
||||
);
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _answerRecords.length,
|
||||
itemBuilder: (context, index) {
|
||||
final record = _answerRecords[index];
|
||||
return _buildRecordCard(record, index);
|
||||
return _buildRecordCard(record, index, primaryColor, isDark);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildRecordCard(Map<String, dynamic> record, int index) {
|
||||
Widget _buildRecordCard(
|
||||
Map<String, dynamic> record,
|
||||
int index,
|
||||
Color primaryColor,
|
||||
bool isDark,
|
||||
) {
|
||||
final question = record['question'] ?? '未知题目';
|
||||
final author = record['author'] ?? '未知作者';
|
||||
final tags = (record['tags'] as List<dynamic>?)?.cast<String>() ?? [];
|
||||
final isCorrect = record['isCorrect'] ?? false;
|
||||
final answerTime = _formatTime(record['answerTime']);
|
||||
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(isDark ? 30 : 10),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${index + 1}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? const Color(0xFF2A2A2A) : AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(isDark ? 30 : 10),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${index + 1}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
question,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primaryText,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'—— $author',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.secondaryText,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isCorrect
|
||||
? AppColors.iosGreen.withAlpha(20)
|
||||
: AppColors.iosRed.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
isCorrect ? Icons.check_circle : Icons.cancel,
|
||||
size: 14,
|
||||
color: isCorrect ? AppColors.iosGreen : AppColors.iosRed,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
isCorrect ? '答对' : '答错',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isCorrect ? AppColors.iosGreen : AppColors.iosRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: tags.isNotEmpty
|
||||
? Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: tags.map((tag) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withAlpha(15),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: AppColors.primary.withAlpha(
|
||||
50,
|
||||
),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
tag,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: Text(
|
||||
'暂无标签',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.tertiaryText,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.access_time,
|
||||
size: 12,
|
||||
color: AppColors.tertiaryText,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
answerTime,
|
||||
question,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDark
|
||||
? Colors.grey[300]
|
||||
: AppColors.primaryText,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'—— $author',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.secondaryText,
|
||||
color: isDark
|
||||
? Colors.grey[500]
|
||||
: AppColors.secondaryText,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isCorrect
|
||||
? AppColors.iosGreen.withAlpha(20)
|
||||
: AppColors.iosRed.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
isCorrect ? Icons.check_circle : Icons.cancel,
|
||||
size: 14,
|
||||
color: isCorrect
|
||||
? AppColors.iosGreen
|
||||
: AppColors.iosRed,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
isCorrect ? '答对' : '答错',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isCorrect
|
||||
? AppColors.iosGreen
|
||||
: AppColors.iosRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: tags.isNotEmpty
|
||||
? Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: tags.map((tag) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor.withAlpha(15),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: primaryColor.withAlpha(50),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
tag,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: Text(
|
||||
'暂无标签',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDark
|
||||
? Colors.grey[500]
|
||||
: AppColors.tertiaryText,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.access_time,
|
||||
size: 12,
|
||||
color: isDark ? Colors.grey[500] : AppColors.tertiaryText,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
answerTime,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: AppColors.secondaryText,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 写入统计数据到笔记
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../services/get/theme_controller.dart';
|
||||
import '../../../models/colors/theme_colors.dart';
|
||||
|
||||
/// 流动边框装饰器
|
||||
class FlowingBorderDecoration extends Decoration {
|
||||
@@ -108,7 +109,10 @@ class _FlowingBorderContainerState extends State<FlowingBorderContainer>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final color = widget.color ?? _themeController.currentThemeColor;
|
||||
final color =
|
||||
widget.color ??
|
||||
ThemeColors.getThemeColor(_themeController.themeColorIndexRx.value);
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (context, child) {
|
||||
|
||||
327
lib/views/profile/level/poetry-page.dart
Normal file
327
lib/views/profile/level/poetry-page.dart
Normal file
@@ -0,0 +1,327 @@
|
||||
/// 诗词答题页面组件
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../../services/get/theme_controller.dart';
|
||||
import '../../../models/colors/theme_colors.dart';
|
||||
|
||||
/// 单个选项组件
|
||||
class PoetryOptionItem extends StatelessWidget {
|
||||
final dynamic option;
|
||||
final bool isSelected;
|
||||
final bool isCorrect;
|
||||
final bool isWrong;
|
||||
final bool isSubmitting;
|
||||
final bool showFeedback;
|
||||
final bool isAnswerCorrect;
|
||||
final Function(int) onTap;
|
||||
|
||||
const PoetryOptionItem({
|
||||
super.key,
|
||||
required this.option,
|
||||
required this.isSelected,
|
||||
required this.isCorrect,
|
||||
required this.isWrong,
|
||||
required this.isSubmitting,
|
||||
required this.showFeedback,
|
||||
required this.isAnswerCorrect,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDarkModeRx.value;
|
||||
final primaryColor = ThemeColors.getThemeColor(
|
||||
themeController.themeColorIndexRx.value,
|
||||
);
|
||||
|
||||
final optionNum = option['index'] ?? option['num'] ?? 0;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: isSelected
|
||||
? LinearGradient(
|
||||
colors: isCorrect
|
||||
? [Colors.green[400]!, Colors.green[300]!]
|
||||
: isWrong
|
||||
? [Colors.red[400]!, Colors.red[300]!]
|
||||
: [primaryColor, primaryColor.withAlpha(200)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: null,
|
||||
color: isSelected
|
||||
? null
|
||||
: isDark
|
||||
? const Color(0xFF2A2A2A)
|
||||
: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? Colors.transparent
|
||||
: primaryColor.withAlpha(50),
|
||||
width: 2,
|
||||
),
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color:
|
||||
(isCorrect
|
||||
? Colors.green
|
||||
: isWrong
|
||||
? Colors.red
|
||||
: primaryColor)
|
||||
.withAlpha(80),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [
|
||||
BoxShadow(
|
||||
color: isDark
|
||||
? Colors.white.withAlpha(5)
|
||||
: Colors.black.withAlpha(5),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: isSubmitting || (showFeedback && isAnswerCorrect)
|
||||
? null
|
||||
: () {
|
||||
if (showFeedback) {
|
||||
onTap(-1); // -1 表示重置状态
|
||||
} else {
|
||||
onTap(optionNum);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
gradient: isSelected
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
Colors.white,
|
||||
Colors.white.withAlpha(230),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: null,
|
||||
color: isSelected ? null : primaryColor.withAlpha(20),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(20),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'$optionNum',
|
||||
style: TextStyle(
|
||||
color: isSelected
|
||||
? (isCorrect
|
||||
? Colors.green
|
||||
: isWrong
|
||||
? Colors.red
|
||||
: primaryColor)
|
||||
: primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
option['content'] ?? option['text'] ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
: isDark
|
||||
? Colors.grey[300]
|
||||
: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isSelected)
|
||||
Icon(
|
||||
isCorrect
|
||||
? Icons.check_circle
|
||||
: isWrong
|
||||
? Icons.cancel
|
||||
: Icons.radio_button_checked,
|
||||
color: Colors.white,
|
||||
size: 28,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 选项布局组件
|
||||
class PoetryOptionsLayout extends StatelessWidget {
|
||||
final List<dynamic> options;
|
||||
final int? selectedAnswer;
|
||||
final bool showFeedback;
|
||||
final bool isAnswerCorrect;
|
||||
final bool isSubmitting;
|
||||
final Function(int) onTap;
|
||||
|
||||
const PoetryOptionsLayout({
|
||||
super.key,
|
||||
required this.options,
|
||||
required this.selectedAnswer,
|
||||
required this.showFeedback,
|
||||
required this.isAnswerCorrect,
|
||||
required this.isSubmitting,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (options.isEmpty) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
// 检查是否所有选项都少于等于4个字
|
||||
bool allShortOptions = options.every((option) {
|
||||
final text = option['content'] ?? '';
|
||||
return text.length <= 4;
|
||||
});
|
||||
|
||||
if (allShortOptions && options.length >= 4) {
|
||||
// 2*2布局
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildOptionItem(options[0])),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _buildOptionItem(options[1])),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildOptionItem(options[2])),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _buildOptionItem(options[3])),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
// 1*4布局
|
||||
final List<Widget> optionWidgets = [];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
optionWidgets.add(_buildOptionItem(options[i]));
|
||||
if (i < options.length - 1) {
|
||||
optionWidgets.add(const SizedBox(height: 12));
|
||||
}
|
||||
}
|
||||
return Column(children: optionWidgets);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildOptionItem(dynamic option) {
|
||||
final optionNum = option['index'] ?? option['num'] ?? 0;
|
||||
final isSelected = selectedAnswer == optionNum;
|
||||
final isCorrect =
|
||||
showFeedback && isAnswerCorrect && selectedAnswer == optionNum;
|
||||
final isWrong =
|
||||
showFeedback && !isAnswerCorrect && selectedAnswer == optionNum;
|
||||
|
||||
return PoetryOptionItem(
|
||||
option: option,
|
||||
isSelected: isSelected,
|
||||
isCorrect: isCorrect,
|
||||
isWrong: isWrong,
|
||||
isSubmitting: isSubmitting,
|
||||
showFeedback: showFeedback,
|
||||
isAnswerCorrect: isAnswerCorrect,
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 标签组件
|
||||
class PoetryTag extends StatelessWidget {
|
||||
final String label;
|
||||
final String value;
|
||||
|
||||
const PoetryTag({super.key, required this.label, required this.value});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (value.isEmpty) return const SizedBox();
|
||||
|
||||
final themeController = Get.find<ThemeController>();
|
||||
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDarkModeRx.value;
|
||||
final primaryColor = ThemeColors.getThemeColor(
|
||||
themeController.themeColorIndexRx.value,
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDark ? Colors.grey[300] : Colors.black87,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,34 @@
|
||||
/// 时间: 2026-03-28
|
||||
/// 功能: 诗词答题页面
|
||||
/// 介绍: 基于 API 接口实现的诗词答题系统,支持获取题目、提交答案、获取提示
|
||||
/// 最新变化: 添加自动加载下一题开关,隐藏提示标签,使用独立逻辑管理器,支持主题色设置
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../models/colors/app_colors.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../../../services/get/theme_controller.dart';
|
||||
import '../../../models/colors/theme_colors.dart';
|
||||
import '../guide/tongji.dart';
|
||||
import 'level-jilu.dart';
|
||||
import 'flow-anim.dart';
|
||||
import 'distinguish.dart';
|
||||
import 'poetry-page.dart';
|
||||
import '../settings/offline-data.dart';
|
||||
|
||||
/// 时间: 2026-03-28
|
||||
/// 功能: 诗词答题页面
|
||||
/// 介绍: 基于 API 接口实现的诗词答题系统,支持获取题目、提交答案、获取提示
|
||||
/// 最新变化: 添加自动加载下一题开关,隐藏提示标签,使用独立逻辑管理器
|
||||
|
||||
class PoetryLevelPage extends StatefulWidget {
|
||||
const PoetryLevelPage({super.key});
|
||||
final bool showBackButton;
|
||||
final bool showAppBar;
|
||||
|
||||
const PoetryLevelPage({
|
||||
super.key,
|
||||
this.showBackButton = true,
|
||||
this.showAppBar = true,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PoetryLevelPage> createState() => _PoetryLevelPageState();
|
||||
@@ -137,7 +146,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(initResult.message ?? '已加载离线缓存'),
|
||||
backgroundColor: AppColors.primary,
|
||||
backgroundColor: ThemeColors.getThemeColor(
|
||||
_themeController.themeColorIndexRx.value,
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
@@ -153,15 +164,14 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: AppColors.surface,
|
||||
title: Text('提示', style: TextStyle(color: AppColors.primaryText)),
|
||||
content: Text('当前无网络连接且无离线缓存数据,请先下载数据或检查网络设置。', style: TextStyle(color: AppColors.secondaryText)),
|
||||
title: const Text('提示'),
|
||||
content: const Text('当前无网络连接且无离线缓存数据,请先下载数据或检查网络设置。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text('取消', style: TextStyle(color: AppColors.primary)),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -172,7 +182,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
MaterialPageRoute(builder: (_) => const OfflineDataPage()),
|
||||
);
|
||||
},
|
||||
child: Text('去下载', style: TextStyle(color: AppColors.primary)),
|
||||
child: const Text('去下载'),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -407,317 +417,78 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建选项布局
|
||||
Widget _buildOptionsLayout() {
|
||||
if (_currentQuestion == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final options = _currentQuestion!['options'] as List?;
|
||||
if (options == null || options.isEmpty) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
// 检查是否所有选项都少于等于4个字
|
||||
bool allShortOptions = options.every((option) {
|
||||
final text = option['content'] ?? '';
|
||||
return text.length <= 4;
|
||||
});
|
||||
|
||||
if (allShortOptions && options.length >= 4) {
|
||||
// 2*2布局
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildOptionItem(options[0])),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _buildOptionItem(options[1])),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildOptionItem(options[2])),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _buildOptionItem(options[3])),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
// 1*4布局
|
||||
final List<Widget> optionWidgets = [];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
optionWidgets.add(_buildOptionItem(options[i]));
|
||||
if (i < options.length - 1) {
|
||||
optionWidgets.add(const SizedBox(height: 12));
|
||||
}
|
||||
}
|
||||
return Column(children: optionWidgets);
|
||||
}
|
||||
}
|
||||
|
||||
/// 构建标签
|
||||
Widget _buildTag(String label, String value) {
|
||||
if (value.isEmpty) return const SizedBox();
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.primaryText,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建单个选项
|
||||
Widget _buildOptionItem(dynamic option) {
|
||||
final optionNum = option['index'] ?? option['num'] ?? 0;
|
||||
final isSelected = _selectedAnswer == optionNum;
|
||||
final isCorrect =
|
||||
_showFeedback && _isAnswerCorrect && _selectedAnswer == optionNum;
|
||||
final isWrong =
|
||||
_showFeedback && !_isAnswerCorrect && _selectedAnswer == optionNum;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: isSelected
|
||||
? LinearGradient(
|
||||
colors: isCorrect
|
||||
? [AppColors.iosGreen, AppColors.iosGreen.withAlpha(200)]
|
||||
: isWrong
|
||||
? [AppColors.iosRed, AppColors.iosRed.withAlpha(200)]
|
||||
: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(200),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: null,
|
||||
color: isSelected ? null : AppColors.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? Colors.transparent
|
||||
: AppColors.primary.withAlpha(50),
|
||||
width: 2,
|
||||
),
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: (isCorrect
|
||||
? AppColors.iosGreen
|
||||
: isWrong
|
||||
? AppColors.iosRed
|
||||
: AppColors.primary)
|
||||
.withAlpha(80),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
final isDark = _themeController.isDarkModeRx.value;
|
||||
final primaryColor = ThemeColors.getThemeColor(
|
||||
_themeController.themeColorIndexRx.value,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: widget.showAppBar
|
||||
? AppBar(
|
||||
title: Text(
|
||||
'诗词答题',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
backgroundColor: primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: widget.showBackButton ? null : const SizedBox.shrink(),
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [primaryColor, primaryColor.withAlpha(180)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
]
|
||||
: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(isDark ? 10 : 5),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: _isSubmitting || (_showFeedback && _isAnswerCorrect)
|
||||
? null
|
||||
: () {
|
||||
if (_showFeedback) {
|
||||
// 重置状态,允许重新选择
|
||||
setState(() {
|
||||
_showFeedback = false;
|
||||
_selectedAnswer = null;
|
||||
_feedbackMessage = null;
|
||||
});
|
||||
}
|
||||
_submitAnswer(optionNum);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: isSelected
|
||||
? LinearGradient(
|
||||
colors: isCorrect
|
||||
? [Colors.white, Colors.white.withAlpha(230)]
|
||||
: isWrong
|
||||
? [Colors.white, Colors.white.withAlpha(230)]
|
||||
: [Colors.white, Colors.white.withAlpha(230)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: null,
|
||||
color: isSelected
|
||||
? null
|
||||
: AppColors.primary.withAlpha(20),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(20),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
color: Colors.white.withAlpha(30),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'$optionNum',
|
||||
child: ElevatedButton(
|
||||
onPressed: _openAnswerRecordPage,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'记录',
|
||||
style: TextStyle(
|
||||
color: isSelected
|
||||
? (isCorrect
|
||||
? AppColors.iosGreen
|
||||
: isWrong
|
||||
? AppColors.iosRed
|
||||
: AppColors.primary)
|
||||
: AppColors.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
option['content'] ?? option['text'] ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isSelected ? Colors.white : AppColors.primaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isSelected)
|
||||
Icon(
|
||||
isCorrect
|
||||
? Icons.check_circle
|
||||
: isWrong
|
||||
? Icons.cancel
|
||||
: Icons.radio_button_checked,
|
||||
color: Colors.white,
|
||||
size: 28,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'诗词答题',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(180),
|
||||
),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withAlpha(30),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: _openAnswerRecordPage,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'记录',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
body: Container(
|
||||
color: AppColors.background,
|
||||
color: isDark ? const Color(0xFF1A1A1A) : Colors.white,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 8.0,
|
||||
bottom: 16.0,
|
||||
bottom: widget.showAppBar ? 16.0 : 100.0, // 关怀模式下增加底部padding
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -740,8 +511,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(200),
|
||||
primaryColor,
|
||||
primaryColor.withAlpha(200),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -749,9 +520,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.primary.withAlpha(
|
||||
80,
|
||||
),
|
||||
color: primaryColor.withAlpha(80),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -844,7 +613,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
AppColors.primary,
|
||||
primaryColor,
|
||||
),
|
||||
strokeWidth: 3,
|
||||
),
|
||||
@@ -853,7 +622,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
'加载题目中...',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: AppColors.tertiaryText,
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -871,14 +642,14 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark
|
||||
? AppColors.iosRed.withAlpha(30)
|
||||
: AppColors.iosRed.withAlpha(10),
|
||||
? Colors.red[900]!.withAlpha(30)
|
||||
: Colors.red[50],
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
size: 64,
|
||||
color: AppColors.iosRed,
|
||||
color: Colors.red[400],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -887,7 +658,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: AppColors.iosRed,
|
||||
color: isDark
|
||||
? Colors.red[300]
|
||||
: Colors.red,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
@@ -895,17 +668,14 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(
|
||||
200,
|
||||
),
|
||||
primaryColor,
|
||||
primaryColor.withAlpha(200),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.primary
|
||||
.withAlpha(80),
|
||||
color: primaryColor.withAlpha(80),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -966,9 +736,15 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
BorderRadius.circular(
|
||||
16,
|
||||
),
|
||||
color: AppColors.surface,
|
||||
color: isDark
|
||||
? const Color(
|
||||
0xFF2A2A2A,
|
||||
)
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
color: AppConstants
|
||||
.primaryColor,
|
||||
width: 4,
|
||||
),
|
||||
),
|
||||
@@ -979,10 +755,19 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.surface,
|
||||
AppColors.primary
|
||||
isDark
|
||||
? const Color(
|
||||
0xFF2A2A2A,
|
||||
)
|
||||
: Colors.white,
|
||||
AppConstants
|
||||
.primaryColor
|
||||
.withAlpha(5),
|
||||
AppColors.surface,
|
||||
isDark
|
||||
? const Color(
|
||||
0xFF2A2A2A,
|
||||
)
|
||||
: Colors.white,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end:
|
||||
@@ -1007,8 +792,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
width: 4,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors
|
||||
.primary,
|
||||
color: AppConstants
|
||||
.primaryColor,
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
2,
|
||||
@@ -1025,8 +810,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w600,
|
||||
color: AppColors
|
||||
.primary,
|
||||
color: AppConstants
|
||||
.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1042,7 +827,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
height: 1.5,
|
||||
color: AppColors.primaryText,
|
||||
color: isDark
|
||||
? Colors.white
|
||||
: Colors.black87,
|
||||
),
|
||||
),
|
||||
// 标签信息
|
||||
@@ -1064,8 +851,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors
|
||||
.primary
|
||||
color: AppConstants
|
||||
.primaryColor
|
||||
.withAlpha(
|
||||
10,
|
||||
),
|
||||
@@ -1079,24 +866,28 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
_buildTag(
|
||||
'作者',
|
||||
_currentQuestion!['author'] ??
|
||||
PoetryTag(
|
||||
label: '作者',
|
||||
value:
|
||||
_currentQuestion!['author'] ??
|
||||
'',
|
||||
),
|
||||
_buildTag(
|
||||
'年代',
|
||||
_currentQuestion!['dynasty'] ??
|
||||
PoetryTag(
|
||||
label: '年代',
|
||||
value:
|
||||
_currentQuestion!['dynasty'] ??
|
||||
'',
|
||||
),
|
||||
_buildTag(
|
||||
'类型',
|
||||
_currentQuestion!['type'] ??
|
||||
PoetryTag(
|
||||
label: '类型',
|
||||
value:
|
||||
_currentQuestion!['type'] ??
|
||||
'',
|
||||
),
|
||||
_buildTag(
|
||||
'阶段',
|
||||
_currentQuestion!['grade'] ??
|
||||
PoetryTag(
|
||||
label: '阶段',
|
||||
value:
|
||||
_currentQuestion!['grade'] ??
|
||||
'',
|
||||
),
|
||||
],
|
||||
@@ -1113,7 +904,27 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// 选项
|
||||
_buildOptionsLayout(),
|
||||
PoetryOptionsLayout(
|
||||
options:
|
||||
_currentQuestion!['options']
|
||||
as List,
|
||||
selectedAnswer: _selectedAnswer,
|
||||
showFeedback: _showFeedback,
|
||||
isAnswerCorrect: _isAnswerCorrect,
|
||||
isSubmitting: _isSubmitting,
|
||||
onTap: (optionNum) {
|
||||
if (optionNum == -1) {
|
||||
// 重置状态
|
||||
setState(() {
|
||||
_showFeedback = false;
|
||||
_selectedAnswer = null;
|
||||
_feedbackMessage = null;
|
||||
});
|
||||
} else {
|
||||
_submitAnswer(optionNum);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1123,7 +934,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
@@ -1144,8 +955,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.surface,
|
||||
AppColors.background,
|
||||
Colors.white,
|
||||
Colors.grey[50]!,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -1153,8 +964,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppColors.primary
|
||||
.withAlpha(50),
|
||||
color: primaryColor.withAlpha(
|
||||
50,
|
||||
),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
@@ -1177,18 +989,18 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
children: [
|
||||
Icon(
|
||||
Icons.arrow_back,
|
||||
color: AppColors
|
||||
.primary,
|
||||
color: AppConstants
|
||||
.primaryColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
const Text(
|
||||
'上一题',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
color: AppColors.primaryText,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1203,20 +1015,23 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.primary,
|
||||
AppColors.primary.withAlpha(
|
||||
200,
|
||||
),
|
||||
Colors.white,
|
||||
Colors.grey[50]!,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: primaryColor,
|
||||
width: 2,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.primary
|
||||
.withAlpha(80),
|
||||
color: primaryColor.withAlpha(
|
||||
30,
|
||||
),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -1225,7 +1040,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
child: ElevatedButton(
|
||||
onPressed: _getHint,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
@@ -1240,9 +1056,10 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icon(
|
||||
Icons.lightbulb_outline,
|
||||
color: Colors.white,
|
||||
color: AppConstants
|
||||
.primaryColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -1251,8 +1068,8 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
color: Colors.white,
|
||||
FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1267,24 +1084,30 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.surface,
|
||||
AppColors.background,
|
||||
primaryColor,
|
||||
primaryColor.withAlpha(200),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppColors.primary
|
||||
.withAlpha(50),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppConstants
|
||||
.primaryColor
|
||||
.withAlpha(80),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: OutlinedButton(
|
||||
child: ElevatedButton(
|
||||
onPressed: _nextQuestion,
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide.none,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
@@ -1298,20 +1121,19 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
const Text(
|
||||
'下一题',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
color: AppColors.primaryText,
|
||||
FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
Icons.arrow_forward,
|
||||
color: AppColors
|
||||
.primary,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
@@ -1321,38 +1143,6 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_showFeedback && _feedbackMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: _isAnswerCorrect
|
||||
? AppColors.iosGreen
|
||||
.withAlpha(20)
|
||||
: AppColors.iosRed
|
||||
.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: _isAnswerCorrect
|
||||
? AppColors.iosGreen
|
||||
: AppColors.iosRed,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
_feedbackMessage!,
|
||||
style: TextStyle(
|
||||
color: _isAnswerCorrect
|
||||
? AppColors.iosGreen
|
||||
: AppColors.iosRed,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1361,6 +1151,65 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
),
|
||||
],
|
||||
),
|
||||
// 反馈信息气泡(不占用布局)
|
||||
if (_showFeedback && _feedbackMessage != null)
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeOut,
|
||||
transform: Matrix4.translationValues(0, 0, 0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: _isAnswerCorrect
|
||||
? [Colors.green[400]!, Colors.green[300]!]
|
||||
: [Colors.orange[400]!, Colors.orange[300]!],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
(_isAnswerCorrect
|
||||
? Colors.green
|
||||
: Colors.orange)
|
||||
.withAlpha(80),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
_isAnswerCorrect
|
||||
? Icons.celebration
|
||||
: Icons.lightbulb_outline,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_feedbackMessage!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user