Files
wushu/lib/views/profile/level/poetry-page.dart
Developer c12f26bd4d feat: 诗词答题页面主题色支持与代码重构
- 创建 poetry-page.dart,提取 UI 组件(PoetryOptionItem、PoetryOptionsLayout、PoetryTag)
- 修改 poetry.dart,使用新组件,添加主题色支持
- 修改 flow-anim.dart,添加主题色支持
- 修改 distinguish.dart,添加主题色支持
- 支持动态主题色切换
- 支持深色模式
- 保持页面布局不变
2026-04-03 00:38:17 +08:00

328 lines
10 KiB
Dart

/// 诗词答题页面组件
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,
),
),
],
),
);
});
}
}