Files
wushu/lib/views/profile/level/poetry.dart
Developer d6ac0ed1e4 重构
2026-03-31 05:42:47 +08:00

1389 lines
58 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 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../constants/app_constants.dart';
import '../../../controllers/shared_preferences_storage_controller.dart';
import '../guide/tongji.dart';
import 'level-jilu.dart';
import 'flow-anim.dart';
import 'distinguish.dart';
import '../settings/offline-data.dart';
/// 时间: 2026-03-28
/// 功能: 诗词答题页面
/// 介绍: 基于 API 接口实现的诗词答题系统,支持获取题目、提交答案、获取提示
/// 最新变化: 添加自动加载下一题开关,隐藏提示标签,使用独立逻辑管理器
class PoetryLevelPage extends StatefulWidget {
const PoetryLevelPage({super.key});
@override
State<PoetryLevelPage> createState() => _PoetryLevelPageState();
}
class _PoetryLevelPageState extends State<PoetryLevelPage>
with TickerProviderStateMixin {
final PoetryLevelManager _manager = PoetryLevelManager();
// 状态管理
bool _isLoading = true;
bool _isSubmitting = false;
Map<String, dynamic>? _currentQuestion;
String? _errorMessage;
int _score = 0;
bool _autoLoadNext = true;
// 答题状态
int? _selectedAnswer;
String? _feedbackMessage;
bool _showFeedback = false;
bool _isAnswerCorrect = false;
// 动画控制器
late AnimationController _successAnimationController;
late AnimationController _shakeAnimationController;
late Animation<double> _scaleAnimation;
late Animation<double> _shakeAnimation;
// 标签显示状态
bool _showTags = false;
// 计时器
Timer? _tagTimer;
// 答题记录统计
int _totalQuestions = 0;
int _correctAnswers = 0;
int _wrongAnswers = 0;
int _totalTime = 0;
int _hintCount = 0;
int _skipCount = 0;
DateTime? _questionStartTime;
@override
void initState() {
super.initState();
_initializeAndLoadQuestion();
_successAnimationController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.2).animate(
CurvedAnimation(
parent: _successAnimationController,
curve: Curves.elasticOut,
),
);
_shakeAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_shakeAnimation = Tween<double>(begin: -5, end: 5).animate(
CurvedAnimation(
parent: _shakeAnimationController,
curve: Curves.easeInOut,
),
);
// 10秒后显示标签
Future.delayed(const Duration(seconds: 10), () {
if (mounted) {
setState(() {
_showTags = true;
});
}
});
}
@override
void dispose() {
_successAnimationController.dispose();
_shakeAnimationController.dispose();
_tagTimer?.cancel();
super.dispose();
}
/// 初始化并加载题目
Future<void> _initializeAndLoadQuestion() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
// 初始化题目列表(调用 fetch 和 refresh
final initResult = await _manager.initializeQuestions();
if (!mounted) return;
if (!initResult.success) {
setState(() {
_errorMessage = initResult.message ?? '初始化题目失败,请重试';
_isLoading = false;
});
// 如果需要下载,显示下载提示
if (initResult.needDownload) {
_showDownloadPrompt();
}
return;
}
// 显示加载模式提示
if (initResult.isOffline) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(initResult.message ?? '已加载离线缓存'),
backgroundColor: AppConstants.primaryColor,
duration: const Duration(seconds: 2),
),
);
}
// 加载第一题
await _loadQuestion();
}
/// 显示下载提示
void _showDownloadPrompt() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('当前无网络连接且无离线缓存数据,请先下载数据或检查网络设置。'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
// 跳转到离线数据下载页面
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const OfflineDataPage()),
);
},
child: const Text('去下载'),
),
],
);
},
);
}
/// 加载题目
Future<void> _loadQuestion() async {
// 开始计时
_questionStartTime = DateTime.now();
final result = await _manager.loadQuestion();
if (!mounted) return;
setState(() {
if (result.success) {
_currentQuestion = result.data;
_isLoading = false;
_errorMessage = null;
// 重置标签显示状态
_showTags = false;
// 取消之前的计时器
_tagTimer?.cancel();
// 10秒后显示标签
_tagTimer = Timer(const Duration(seconds: 10), () {
if (mounted) {
setState(() {
_showTags = true;
});
}
});
} else {
_errorMessage = result.message;
_isLoading = false;
}
_selectedAnswer = null;
_feedbackMessage = null;
_showFeedback = false;
_isAnswerCorrect = false;
});
}
/// 提交答案
Future<void> _submitAnswer(int answer) async {
if (_isSubmitting || _currentQuestion == null) return;
setState(() {
_isSubmitting = true;
_selectedAnswer = answer;
_showFeedback = false;
});
final result = await _manager.submitAnswer(_manager.currentId, answer);
if (!mounted) return;
// 记录今日答题
try {
await StatisticsManager().recordTodayQuestion();
} catch (e) {
// 忽略错误
}
setState(() {
if (result.success) {
_isAnswerCorrect = result.isCorrect;
if (result.message != null && result.message!.isNotEmpty) {
_feedbackMessage = result.message;
} else {
_feedbackMessage = _isAnswerCorrect ? '🎉 回答正确!' : '😔 回答错误,再想想吧!';
}
_showFeedback = true;
// 计算答题时间
if (_questionStartTime != null) {
final duration = DateTime.now().difference(_questionStartTime!);
_totalTime += duration.inSeconds;
}
// 更新统计数据
_totalQuestions++;
if (_isAnswerCorrect) {
_correctAnswers++;
_score++;
_successAnimationController.forward().then((_) {
_successAnimationController.reverse();
});
if (_autoLoadNext) {
Future.delayed(const Duration(seconds: 2), () {
if (mounted && _isAnswerCorrect) {
_nextQuestion();
}
});
}
} else {
_wrongAnswers++;
_shakeAnimationController.forward().then((_) {
_shakeAnimationController.reverse();
});
}
// 保存答题记录
_saveAnswerRecord(isCorrect: _isAnswerCorrect);
} else {
_feedbackMessage = result.message;
_showFeedback = true;
}
_isSubmitting = false;
});
}
/// 获取提示
Future<void> _getHint() async {
if (_isSubmitting || _currentQuestion == null) return;
setState(() {
_isSubmitting = true;
});
final result = await _manager.getHint(_manager.currentId);
if (!mounted) return;
setState(() {
if (result.success) {
_feedbackMessage = result.message;
_showFeedback = true;
// 增加提示次数
_hintCount++;
// 保存答题记录
_saveAnswerRecord();
} else {
_feedbackMessage = result.message;
_showFeedback = true;
}
_isSubmitting = false;
});
}
/// 下一题
void _nextQuestion() {
// 增加跳过次数
_skipCount++;
// 保存答题记录
_saveAnswerRecord();
_manager.nextQuestion();
_loadQuestion();
}
/// 上一题
void _previousQuestion() {
_manager.previousQuestion();
_loadQuestion();
}
/// 保存答题记录到本地存储
Future<void> _saveAnswerRecord({bool isCorrect = false}) async {
try {
// 保存统计数据
await SharedPreferencesStorageController.setInt(
'totalQuestions',
_totalQuestions,
);
await SharedPreferencesStorageController.setInt(
'correctAnswers',
_correctAnswers,
);
await SharedPreferencesStorageController.setInt(
'wrongAnswers',
_wrongAnswers,
);
await SharedPreferencesStorageController.setInt('totalTime', _totalTime);
await SharedPreferencesStorageController.setInt('hintCount', _hintCount);
await SharedPreferencesStorageController.setInt('skipCount', _skipCount);
// 保存当前题目的详细记录
if (_currentQuestion != null) {
// 构建标签列表
List<String> tags = [];
if (_currentQuestion!['type'] != null) {
tags.add(_currentQuestion!['type'].toString());
}
if (_currentQuestion!['grade'] != null) {
tags.add(_currentQuestion!['grade'].toString());
}
if (_currentQuestion!['dynasty'] != null) {
tags.add(_currentQuestion!['dynasty'].toString());
}
final record = {
'questionId': _manager.currentId,
'question': _currentQuestion!['question'] ?? '未知题目',
'author': _currentQuestion!['author'] ?? '未知作者',
'tags': tags,
'isCorrect': isCorrect,
'answerTime': DateTime.now().toIso8601String(),
};
// 获取已有的记录列表
List<String> records =
await SharedPreferencesStorageController.getStringList(
'poetryAnswerRecords',
defaultValue: [],
);
// 添加新记录JSON格式
records.add(jsonEncode(record));
// 保存更新后的列表
await SharedPreferencesStorageController.setStringList(
'poetryAnswerRecords',
records,
);
}
} catch (e) {
// 保存失败
}
}
/// 打开答题记录页面
void _openAnswerRecordPage() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DistinguishPage()),
);
}
/// 构建选项布局
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: AppConstants.primaryColor.withAlpha(20),
borderRadius: BorderRadius.circular(4),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
label,
style: TextStyle(
fontSize: 10,
color: AppConstants.primaryColor,
fontWeight: FontWeight.w600,
),
),
Text(
value,
style: TextStyle(
fontSize: 12,
color: Colors.black87,
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;
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]!]
: [
AppConstants.primaryColor,
AppConstants.primaryColor.withAlpha(200),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
color: isSelected ? null : Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
? Colors.transparent
: AppConstants.primaryColor.withAlpha(50),
width: 2,
),
boxShadow: isSelected
? [
BoxShadow(
color:
(isCorrect
? Colors.green
: isWrong
? Colors.red
: AppConstants.primaryColor)
.withAlpha(80),
blurRadius: 12,
offset: const Offset(0, 4),
),
]
: [
BoxShadow(
color: 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) {
// 重置状态,允许重新选择
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,
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
: AppConstants.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
: AppConstants.primaryColor)
: AppConstants.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 : Colors.black87,
),
),
),
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 Scaffold(
appBar: AppBar(
title: const Text(
'诗词答题',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor.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,
),
),
),
),
),
],
),
body: Container(
color: Colors.white,
child: SafeArea(
child: Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 8.0,
bottom: 16.0,
),
child: Stack(
children: [
Column(
children: [
const SizedBox(height: 8), // 减少顶部空白
// 分数显示
AnimatedBuilder(
animation: _successAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _isAnswerCorrect ? _scaleAnimation.value : 1.0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor.withAlpha(200),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor.withAlpha(
80,
),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withAlpha(30),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.quiz_outlined,
color: Colors.white,
size: 20,
),
),
const SizedBox(width: 12),
Text(
'题目: ${_manager.currentIndex + 1}/${_manager.total}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
const SizedBox(width: 12),
// 奖杯图标和分数
Row(
children: [
Icon(
Icons.emoji_events_outlined,
color: Colors.amber[300],
size: 24,
),
const SizedBox(width: 8),
Text(
'$_score',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
],
),
Row(
children: [
// 开关按钮
Tooltip(
message: _autoLoadNext
? '已开启自动下一题'
: '已关闭自动下一题',
child: Switch(
value: _autoLoadNext,
onChanged: (value) {
setState(() {
_autoLoadNext = value;
});
},
activeColor: Colors.white,
activeTrackColor: Colors.white
.withAlpha(128),
),
),
],
),
],
),
),
);
},
),
// 移除空白间距
const SizedBox(height: 10),
// 加载状态
if (_isLoading)
const Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
AppConstants.primaryColor,
),
strokeWidth: 3,
),
SizedBox(height: 20),
Text(
'加载题目中...',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
),
),
// 错误状态
if (!_isLoading && _errorMessage != null)
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.red[50],
shape: BoxShape.circle,
),
child: Icon(
Icons.error_outline,
size: 64,
color: Colors.red[400],
),
),
const SizedBox(height: 24),
Text(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
color: Colors.red,
),
),
const SizedBox(height: 32),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor.withAlpha(200),
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor
.withAlpha(80),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: ElevatedButton(
onPressed: () => _loadQuestion(),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
),
child: const Text(
'重新加载',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
],
),
),
),
// 题目内容
if (!_isLoading && _currentQuestion != null)
Expanded(
child: Column(
children: [
// 可滚动区域
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 题目信息
AnimatedBuilder(
animation: _shakeAnimationController,
builder: (context, child) {
return Transform.translate(
offset: Offset(
_isAnswerCorrect
? 0
: _shakeAnimation.value,
0,
),
child: Stack(
children: [
// 流动边框
Positioned.fill(
child: FlowingBorderContainer(
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
16,
),
color: Colors.white,
),
),
color:
AppConstants.primaryColor,
width: 4,
),
),
// 题目内容
Container(
padding: const EdgeInsets.all(
16,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
AppConstants.primaryColor
.withAlpha(5),
Colors.white,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius:
BorderRadius.circular(16),
backgroundBlendMode:
BlendMode.softLight,
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
// 装饰元素
Row(
children: [
Container(
width: 4,
height: 20,
decoration: BoxDecoration(
color: AppConstants
.primaryColor,
borderRadius:
BorderRadius.circular(
2,
),
),
),
const SizedBox(
width: 12,
),
Text(
'诗词挑战',
style: TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w600,
color: AppConstants
.primaryColor,
),
),
],
),
const SizedBox(height: 12),
// 题目
Text(
_currentQuestion!['question'] ??
'题目加载失败',
style: const TextStyle(
fontSize: 20,
fontWeight:
FontWeight.bold,
height: 1.5,
color: Colors.black87,
),
),
// 标签信息
if (_showTags)
AnimatedOpacity(
duration:
const Duration(
milliseconds: 500,
),
opacity: 1,
child: Container(
margin:
const EdgeInsets.only(
top: 12,
),
padding:
const EdgeInsets.all(
12,
),
decoration: BoxDecoration(
color: AppConstants
.primaryColor
.withAlpha(10),
borderRadius:
BorderRadius.circular(
8,
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
_buildTag(
'作者',
_currentQuestion!['author'] ??
'',
),
_buildTag(
'年代',
_currentQuestion!['dynasty'] ??
'',
),
_buildTag(
'类型',
_currentQuestion!['type'] ??
'',
),
_buildTag(
'阶段',
_currentQuestion!['grade'] ??
'',
),
],
),
),
),
],
),
),
],
),
);
},
),
const SizedBox(height: 10),
// 选项
_buildOptionsLayout(),
],
),
),
),
// 固定位置的操作按钮卡片(在底部固定,不随内容滚动)
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
// 操作按钮 - 改为一行显示
Row(
children: [
// 上一题按钮
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
Colors.grey[50]!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(
12,
),
border: Border.all(
color: AppConstants.primaryColor
.withAlpha(50),
width: 1,
),
),
child: OutlinedButton(
onPressed: _previousQuestion,
style: OutlinedButton.styleFrom(
side: BorderSide.none,
padding:
const EdgeInsets.symmetric(
vertical: 14,
),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(
Icons.arrow_back,
color:
AppConstants.primaryColor,
size: 20,
),
const SizedBox(width: 8),
const Text(
'上一题',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
],
),
),
),
),
const SizedBox(width: 12),
// 提示按钮
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
Colors.grey[50]!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(
12,
),
border: Border.all(
color: AppConstants.primaryColor,
width: 2,
),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor
.withAlpha(30),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: ElevatedButton(
onPressed: _getHint,
style: ElevatedButton.styleFrom(
backgroundColor:
Colors.transparent,
shadowColor: Colors.transparent,
padding:
const EdgeInsets.symmetric(
vertical: 14,
),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(
Icons.lightbulb_outline,
color:
AppConstants.primaryColor,
size: 20,
),
const SizedBox(width: 8),
const Text(
'提示',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
),
),
const SizedBox(width: 12),
// 下一题按钮
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor
.withAlpha(200),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(
12,
),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor
.withAlpha(80),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: ElevatedButton(
onPressed: _nextQuestion,
style: ElevatedButton.styleFrom(
backgroundColor:
Colors.transparent,
shadowColor: Colors.transparent,
padding:
const EdgeInsets.symmetric(
vertical: 14,
),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
const Text(
'下一题',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
const SizedBox(width: 8),
Icon(
Icons.arrow_forward,
color: Colors.white,
size: 20,
),
],
),
),
),
),
],
),
],
),
),
],
),
),
],
),
// 反馈信息气泡(不占用布局)
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,
),
),
),
],
),
),
),
],
),
),
),
),
);
}
}