import 'package:flutter/material.dart'; import '../models/conversation_scenario.dart'; /// 场景练习页面 class ScenarioPracticeScreen extends StatefulWidget { final ConversationScenario scenario; const ScenarioPracticeScreen({ Key? key, required this.scenario, }) : super(key: key); @override State createState() => _ScenarioPracticeScreenState(); } class _ScenarioPracticeScreenState extends State { int _currentStepIndex = 0; List _userResponses = []; bool _isCompleted = false; @override void initState() { super.initState(); _userResponses = List.filled(widget.scenario.steps.length, ''); } ScenarioStep get _currentStep => widget.scenario.steps[_currentStepIndex]; void _selectOption(String option) { setState(() { _userResponses[_currentStepIndex] = option; }); } void _nextStep() { if (_currentStepIndex < widget.scenario.steps.length - 1) { setState(() { _currentStepIndex++; }); } else { setState(() { _isCompleted = true; }); _showCompletionDialog(); } } void _previousStep() { if (_currentStepIndex > 0) { setState(() { _currentStepIndex--; }); } } void _showCompletionDialog() { showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text('🎉 恭喜完成!'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text('您已成功完成「${widget.scenario.title}」场景练习!'), const SizedBox(height: 16), const Text('继续练习可以提高您的英语口语水平。'), ], ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); // 关闭对话框 Navigator.of(context).pop(); // 返回主页 }, child: const Text('返回主页'), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); // 关闭对话框 _restartScenario(); }, child: const Text('重新练习'), ), ], ), ); } void _restartScenario() { setState(() { _currentStepIndex = 0; _userResponses = List.filled(widget.scenario.steps.length, ''); _isCompleted = false; }); } void _showScenarioInfo() { showModalBottomSheet( context: context, isScrollControlled: true, builder: (context) => DraggableScrollableSheet( initialChildSize: 0.7, maxChildSize: 0.9, minChildSize: 0.5, builder: (context, scrollController) => Container( padding: const EdgeInsets.all(20), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: SingleChildScrollView( controller: scrollController, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( widget.scenario.type.icon, style: const TextStyle(fontSize: 32), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.scenario.title, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), Text( widget.scenario.subtitle, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ), ], ), const SizedBox(height: 20), Text( widget.scenario.description, style: const TextStyle(fontSize: 16), ), const SizedBox(height: 20), const Text( '学习目标', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), ...widget.scenario.objectives.map( (objective) => Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon(Icons.check_circle, color: Colors.green, size: 16), const SizedBox(width: 8), Expanded(child: Text(objective)), ], ), ), ), const SizedBox(height: 20), const Text( '关键短语', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: widget.scenario.keyPhrases.map( (phrase) => Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.blue.withOpacity(0.3)), ), child: Text( phrase, style: const TextStyle( fontSize: 12, color: Colors.blue, ), ), ), ).toList(), ), ], ), ), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.scenario.title, style: const TextStyle(fontSize: 16), ), Text( '步骤 ${_currentStepIndex + 1}/${widget.scenario.steps.length}', style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), actions: [ IconButton( onPressed: _showScenarioInfo, icon: const Icon(Icons.info_outline), ), ], ), body: Column( children: [ // 进度条 LinearProgressIndicator( value: (_currentStepIndex + 1) / widget.scenario.steps.length, backgroundColor: Colors.grey[300], valueColor: const AlwaysStoppedAnimation(Colors.blue), ), Expanded( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 步骤标题 Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _currentStep.title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.blue, ), ), const SizedBox(height: 4), Text( _currentStep.description, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ), const SizedBox(height: 20), // 对话内容 Expanded( child: Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _currentStep.role == 'npc' ? Colors.grey[100] : Colors.blue[50], borderRadius: BorderRadius.circular(12), border: Border.all( color: _currentStep.role == 'npc' ? Colors.grey.withOpacity(0.3) : Colors.blue.withOpacity(0.3), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ CircleAvatar( radius: 16, backgroundColor: _currentStep.role == 'npc' ? Colors.grey : Colors.blue, child: Text( _currentStep.role == 'npc' ? 'NPC' : 'YOU', style: const TextStyle( fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 12), Text( _currentStep.role == 'npc' ? '对方说:' : '您说:', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 12), Text( _currentStep.content, style: const TextStyle(fontSize: 16), ), ], ), ), ), const SizedBox(height: 20), // 选项 if (_currentStep.options.isNotEmpty) ...[ const Text( '请选择您的回应:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), ...(_currentStep.options.asMap().entries.map( (entry) { final index = entry.key; final option = entry.value; final isSelected = _userResponses[_currentStepIndex] == option; return Padding( padding: const EdgeInsets.only(bottom: 8), child: GestureDetector( onTap: () => _selectOption(option), child: Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isSelected ? Colors.blue.withOpacity(0.1) : Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? Colors.blue : Colors.grey.withOpacity(0.3), width: isSelected ? 2 : 1, ), ), child: Row( children: [ Container( width: 24, height: 24, decoration: BoxDecoration( shape: BoxShape.circle, color: isSelected ? Colors.blue : Colors.grey[300], ), child: Center( child: Text( String.fromCharCode(65 + index), // A, B, C style: TextStyle( color: isSelected ? Colors.white : Colors.grey[600], fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), const SizedBox(width: 12), Expanded( child: Text( option, style: TextStyle( fontSize: 14, color: isSelected ? Colors.blue : Colors.black, ), ), ), ], ), ), ), ); }, )), ], ], ), ), ), // 底部按钮 Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, -2), ), ], ), child: Row( children: [ if (_currentStepIndex > 0) Expanded( child: OutlinedButton( onPressed: _previousStep, child: const Text('上一步'), ), ), if (_currentStepIndex > 0) const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: _userResponses[_currentStepIndex].isNotEmpty || _currentStep.options.isEmpty ? _nextStep : null, child: Text( _currentStepIndex == widget.scenario.steps.length - 1 ? '完成练习' : '下一步', ), ), ), ], ), ), ], ), ); } }