import 'package:flutter/material.dart'; import '../../core/app_state.dart'; import '../../core/app_theme.dart'; import '../../core/models.dart'; import '../../widgets/app_widgets.dart'; class WelcomePage extends StatefulWidget { const WelcomePage({super.key, required this.state, required this.onContinue}); final AppState state; final VoidCallback onContinue; @override State createState() => _WelcomePageState(); } class _WelcomePageState extends State { late LearningGoal selectedGoal; late int selectedMinutes; @override void initState() { super.initState(); selectedGoal = widget.state.goal; selectedMinutes = widget.state.dailyMinutes; } @override Widget build(BuildContext context) { return AppPage( child: SpacedColumn( spacing: 20, children: [ Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.asset( 'assets/branding/logo_512.png', width: 44, height: 44, ), ), const SizedBox(width: 12), const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '芽说英语', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.ink, ), ), Text( 'SpeakSprout', style: TextStyle( fontSize: 12, color: AppColors.green, fontWeight: FontWeight.w600, ), ), ], ), ], ), const Eyebrow('欢迎'), Text( '每天 20 分钟,\n说出能用的英语。', style: Theme.of(context).textTheme.headlineMedium, ), const Text('从真实生活场景开始,先做到听得懂、说得清。'), _ChoiceGroup( title: '你的主要目标', value: selectedGoal, options: const { LearningGoal.dailyLife: '日常生活', LearningGoal.travel: '旅行', LearningGoal.workStarter: '工作起步', }, onChanged: (value) => setState(() => selectedGoal = value), ), _ChoiceGroup( title: '每天学习多久?', value: selectedMinutes, options: const {10: '10 分钟', 20: '20 分钟', 30: '30 分钟'}, onChanged: (value) => setState(() => selectedMinutes = value), ), PrimaryButton( label: '继续', onPressed: () { widget.state ..setGoal(selectedGoal) ..setDailyMinutes(selectedMinutes); widget.onContinue(); }, ), ], ), ); } } class PlacementPage extends StatefulWidget { const PlacementPage({ super.key, required this.state, required this.onStart, this.onBack, }); final AppState state; final VoidCallback onStart; final VoidCallback? onBack; @override State createState() => _PlacementPageState(); } class _PlacementPageState extends State { late PlacementLevel selected; @override void initState() { super.initState(); selected = widget.state.placement; } @override Widget build(BuildContext context) { const labels = { PlacementLevel.beginner: ('完全零基础', '从你好、自我介绍开始'), PlacementLevel.someBasics: ('能说一点', '认识常见单词或短句'), PlacementLevel.simpleConversation: ('能简单对话', '想说得更自然、更有信心'), }; return AppPage( appBar: widget.onBack != null ? AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back), tooltip: "返回", onPressed: widget.onBack, ), title: const Text("基础定位"), ) : null, child: SpacedColumn( spacing: 14, children: [ const Eyebrow('第一步 · 约 3 分钟'), Text('从哪里开始?', style: Theme.of(context).textTheme.headlineMedium), const Text('选择最接近的状态,之后随时能调整。'), for (final option in PlacementLevel.values) _PlacementChoice( option: option, labels: labels[option]!, isSelected: selected == option, onTap: () => setState(() => selected = option), ), PrimaryButton( label: '开始 3 分钟定位', onPressed: () { widget.state.setPlacement(selected); widget.onStart(); }, ), Center( child: TextButton( onPressed: widget.onStart, child: const Text('直接从第一课开始'), ), ), ], ), ); } } class _PlacementChoice extends StatelessWidget { const _PlacementChoice({ required this.option, required this.labels, required this.isSelected, required this.onTap, }); final PlacementLevel option; final (String, String) labels; final bool isSelected; final VoidCallback onTap; @override Widget build(BuildContext context) => SectionCard( tint: isSelected ? AppColors.softGreen : null, onTap: onTap, child: Row( children: [ Icon( isSelected ? Icons.radio_button_checked : Icons.radio_button_off, color: isSelected ? AppColors.green : AppColors.muted, ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( labels.$1, style: const TextStyle(fontWeight: FontWeight.w600), ), const SizedBox(height: 3), Text(labels.$2, style: Theme.of(context).textTheme.bodyMedium), ], ), ), ], ), ); } class _ChoiceGroup extends StatelessWidget { const _ChoiceGroup({ required this.title, required this.value, required this.options, required this.onChanged, }); final String title; final T value; final Map options; final ValueChanged onChanged; @override Widget build(BuildContext context) { return SectionCard( child: SpacedColumn( spacing: 10, children: [ Text(title, style: const TextStyle(fontWeight: FontWeight.w600)), Wrap( spacing: 8, runSpacing: 8, children: options.entries .map( (entry) => ChoiceChip( label: Text(entry.value), selected: value == entry.key, selectedColor: AppColors.softGreen, onSelected: (_) => onChanged(entry.key), ), ) .toList(), ), ], ), ); } }