import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/routes/app_routes.dart'; import '../../vocabulary/providers/vocabulary_provider.dart'; import '../../vocabulary/models/word_model.dart'; import '../../auth/providers/auth_provider.dart'; import '../../../shared/providers/notification_provider.dart'; import '../../../core/services/tts_service.dart'; /// 首页界面 class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override ConsumerState createState() => _HomeScreenState(); } class _HomeScreenState extends ConsumerState { final TTSService _ttsService = TTSService(); @override void initState() { super.initState(); // 初始化TTS服务 _ttsService.initialize(); // 页面首帧后触发数据加载(今日单词与统计) WidgetsBinding.instance.addPostFrameCallback((_) async { print('=== HomeScreen: 开始加载数据 ==='); final user = ref.read(currentUserProvider); print('当前用户: ${user?.username}, ID: ${user?.id}'); try { // 加载未读通知数量 ref.read(notificationProvider.notifier).loadUnreadCount(); // 直接调用 Notifier 的方法 final notifier = ref.read(vocabularyProvider.notifier); print('开始加载今日单词...'); await notifier.loadTodayStudyWords(userId: user?.id); print('今日单词加载完成,数量: ${ref.read(todayWordsProvider).length}'); print('开始加载统计数据...'); await notifier.loadUserVocabularyOverallStats(); await notifier.loadWeeklyStudyStats(); print('统计数据加载完成'); } catch (e, stack) { print('数据加载失败: $e'); print('堆栈: $stack'); } print('=== HomeScreen: 数据加载结束 ==='); }); } @override void dispose() { // 释放TTS资源 _ttsService.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), body: SafeArea( child: SingleChildScrollView( physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), child: Column( children: [ _buildHeader(), const SizedBox(height: 20), _buildQuickActions(context), const SizedBox(height: 20), _buildLearningStats(context), const SizedBox(height: 20), _buildTodayWords(), const SizedBox(height: 20), _buildAIRecommendation(context), const SizedBox(height: 100), // 底部导航栏空间 ], ), ), ), ); } Widget _buildHeader() { // 直接使用StateNotifierProvider获取认证状态 final authState = ref.watch(authProvider); final user = authState.user; final isAuthenticated = authState.isAuthenticated; final unreadCount = ref.watch(unreadCountProvider); final avatarUrl = user?.profile?.avatar ?? user?.avatar; final displayName = user?.profile?.realName ?? user?.username ?? '未登录用户'; final motto = user?.profile?.bio ?? '欢迎来到AI英语学习'; return GestureDetector( onTap: () { // 点击头部跳转:未登录->登录页,已登录->个人中心 if (isAuthenticated) { Navigator.pushNamed(context, Routes.profile); } else { Navigator.pushNamed(context, Routes.login); } }, child: Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF2196F3), Color(0xFF1976D2)], ), ), child: Row( children: [ CircleAvatar( radius: 25, backgroundColor: Colors.white, backgroundImage: avatarUrl != null && avatarUrl.isNotEmpty ? NetworkImage(avatarUrl) : null, child: (avatarUrl == null || avatarUrl.isEmpty) ? Icon( Icons.person, color: isAuthenticated ? const Color(0xFF2196F3) : Colors.grey, size: 24, ) : null, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( displayName, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( motto, style: const TextStyle( color: Colors.white70, fontSize: 14, ), ), ], ), ), Stack( children: [ IconButton( onPressed: () { // TODO: 跳转到通知页面 Navigator.pushNamed(context, '/notifications'); }, icon: const Icon( Icons.notifications_outlined, color: Colors.white, size: 28, ), ), if (unreadCount > 0) Positioned( right: 8, top: 8, child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), constraints: const BoxConstraints( minWidth: 18, minHeight: 18, ), child: Text( unreadCount > 99 ? '99+' : unreadCount.toString(), style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), ), ], ), ], ), ), ); } Widget _buildQuickActions(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only(left: 4, bottom: 12), child: Text( '快速功能', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), ), Row( children: [ Expanded( child: _buildQuickActionCard( context, '智能复习', '基于记忆曲线', Icons.psychology, const Color(0xFF4CAF50), () => Navigator.pushNamed(context, '/vocabulary/smart-review'), ), ), const SizedBox(width: 12), Expanded( child: _buildQuickActionCard( context, '词汇测试', '检测学习效果', Icons.quiz, const Color(0xFFFF9800), () => Navigator.pushNamed(context, '/vocabulary/test'), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildQuickActionCard( context, '生词本', '收藏重要单词', Icons.bookmark, const Color(0xFF9C27B0), () => Navigator.pushNamed(context, '/vocabulary/word-book'), ), ), const SizedBox(width: 12), Expanded( child: _buildQuickActionCard( context, '学习计划', '制定学习目标', Icons.schedule, const Color(0xFF2196F3), () => Navigator.pushNamed(context, '/vocabulary/study-plan'), ), ), ], ), ], ), ); } Widget _buildQuickActionCard( BuildContext context, String title, String subtitle, IconData icon, Color color, VoidCallback onTap, ) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(24), ), child: Icon( icon, color: color, size: 24, ), ), const SizedBox(height: 12), Text( title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), const SizedBox(height: 4), Text( subtitle, style: const TextStyle( fontSize: 12, color: Colors.grey, ), textAlign: TextAlign.center, ), ], ), ), ); } Widget _buildLearningStats(BuildContext context) { final statistics = ref.watch(todayStatisticsProvider); final dailyStats = ref.watch(dailyVocabularyStatsProvider); final weeklyStudied = ref.watch(weeklyWordsStudiedProvider); final overallStats = ref.watch(overallVocabularyStatsProvider) ?? {}; final user = ref.watch(currentUserProvider); final todayStudied = dailyStats?.wordsLearned ?? statistics?.wordsStudied ?? 0; final totalStudied = (overallStats['total_studied'] as int?) ?? 0; final weeklyGoal = ((user?.settings?.dailyWordGoal ?? 20) * 7); final progress = weeklyGoal > 0 ? (weeklyStudied / weeklyGoal).clamp(0.0, 1.0) : 0.0; final progressPercentText = '${(progress * 100).toInt()}%'; return Container( margin: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 4, bottom: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '学习统计', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), GestureDetector( onTap: () { Navigator.of(context).pushNamed(Routes.learningStatsDetail); }, child: const Text( '查看详情', style: TextStyle( fontSize: 14, color: Color(0xFF2196F3), fontWeight: FontWeight.w500, ), ), ), ], ), ), Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Row( children: [ Expanded( child: _buildStatCard(todayStudied.toString(), '今日学习', Icons.today, const Color(0xFF4CAF50)), ), const SizedBox(width: 12), Expanded( child: _buildStatCard(weeklyStudied.toString(), '本周学习', Icons.date_range, const Color(0xFF2196F3)), ), const SizedBox(width: 12), Expanded( child: _buildStatCard(totalStudied.toString(), '总词汇量', Icons.library_books, const Color(0xFFFF9800)), ), ], ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ const Icon( Icons.trending_up, color: Color(0xFF2196F3), size: 24, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '本周学习进度', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF333333), ), ), Text( progressPercentText, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF2196F3), ), ), ], ), ), Container( width: 60, height: 6, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(3), ), child: FractionallySizedBox( alignment: Alignment.centerLeft, widthFactor: progress, child: Container( decoration: BoxDecoration( color: const Color(0xFF2196F3), borderRadius: BorderRadius.circular(3), ), ), ), ), ], ), ), ], ), ), ], ), ); } Widget _buildStatCard(String value, String label, IconData icon, Color color) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Icon( icon, color: color, size: 20, ), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 11, color: Colors.grey, ), textAlign: TextAlign.center, ), ], ), ); } Widget _buildTodayWords() { final words = ref.watch(todayWordsProvider); return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '今日单词', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), TextButton( onPressed: () { Navigator.pushNamed(context, Routes.dailyWords); }, child: const Text('查看更多'), ), ], ), const SizedBox(height: 16), words.isEmpty ? Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 20.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.book_outlined, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( '暂无今日单词', style: TextStyle( fontSize: 16, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), TextButton( onPressed: () async { // 触发加载今日单词 final notifier = ref.read(vocabularyProvider.notifier); final user = ref.read(currentUserProvider); await notifier.loadTodayStudyWords(userId: user?.id); }, child: const Text('加载今日单词'), ), ], ), ), ) : Column( children: [ _buildWordItem(words[0]), if (words.length > 1) ...[ const SizedBox(height: 12), _buildWordItem(words[1]), ], ], ), ], ), ); } Widget _buildTodayWordsOld() { final vocabAsync = ref.watch(vocabularyProvider); return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '今日单词', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), TextButton( onPressed: () { Navigator.pushNamed(context, Routes.dailyWords); }, child: const Text('查看更多'), ), ], ), const SizedBox(height: 16), Builder( builder: (context) { final state = ref.watch(vocabularyProvider); final words = state.todayWords; if (words.isEmpty) { return Column( children: [ const Icon( Icons.book_outlined, size: 48, color: Colors.grey, ), const SizedBox(height: 8), const Text( '暂无今日单词', style: TextStyle(fontSize: 14, color: Colors.grey), ), const SizedBox(height: 8), TextButton( onPressed: () async { final user = ref.read(currentUserProvider); final notifier = ref.read(vocabularyProvider.notifier); await notifier.loadTodayStudyWords(userId: user?.id); }, child: const Text('刷新'), ), ], ); } return Column( children: [ _buildWordItem(words[0]), if (words.length > 1) ...[ const SizedBox(height: 12), _buildWordItem(words[1]), ], ], ); }, ), ], ), ); } Widget _buildWordItem(Word word) { final meaning = word.definitions.isNotEmpty ? word.definitions.first.definition : ''; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( word.word, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( meaning, style: const TextStyle( fontSize: 14, color: Colors.grey, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), IconButton( onPressed: () async { // 播放单词发音 await _ttsService.speak(word.word); }, icon: const Icon( Icons.volume_up, color: Color(0xFF2196F3), size: 24, ), ), ], ); } Widget _buildAIRecommendation(BuildContext context) { return GestureDetector( onTap: () { Navigator.pushNamed(context, '/ai'); }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(24), ), child: const Icon( Icons.smart_toy, color: Color(0xFF2196F3), size: 24, ), ), const SizedBox(width: 16), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'AI 智能助手', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( '写作批改 • 口语评估 • 智能推荐\n点击体验AI学习助手', style: TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ), const Icon( Icons.arrow_forward_ios, color: Colors.grey, size: 16, ), ], ), ), ); } }