import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fl_chart/fl_chart.dart'; import '../theme/app_theme.dart'; import '../models/daily_stats.dart'; import '../models/app_usage.dart'; import '../providers/statistics_provider.dart'; import '../widgets/empty_state_widget.dart'; import '../widgets/error_state_widget.dart'; class TodayScreen extends ConsumerWidget { const TodayScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final todayStatsAsync = ref.watch(todayStatsProvider); final topAppsAsync = ref.watch(todayTopAppsProvider); return Scaffold( appBar: AppBar( title: const Text('AutoTime Tracker'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () { ref.invalidate(todayStatsProvider); ref.invalidate(todayTopAppsProvider); }, ), IconButton( icon: const Icon(Icons.settings), onPressed: () { // 设置页面通过底部导航栏访问 }, ), ], ), body: todayStatsAsync.when( data: (stats) { // 检查是否为空数据(总时长为0且没有应用数据) final isEmpty = stats.totalTime == 0; if (isEmpty) { return _buildEmptyContent(context, ref); } return _buildContent(context, ref, stats, topAppsAsync); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => ErrorStateWidget.dataLoad( message: _getErrorMessage(error), onRetry: () { ref.invalidate(todayStatsProvider); ref.invalidate(todayTopAppsProvider); }, ), ), ); } Widget _buildContent( BuildContext context, WidgetRef ref, DailyStats stats, AsyncValue> topAppsAsync, ) { final theme = Theme.of(context); return RefreshIndicator( onRefresh: () async { ref.invalidate(todayStatsProvider); ref.invalidate(todayTopAppsProvider); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 总时长显示 _buildTotalTimeSection(stats, theme), const SizedBox(height: 24), // 效率评分 _buildEfficiencySection(stats, theme), const SizedBox(height: 24), // 分类时间分布(饼图) _buildCategoryChart(stats, theme), const SizedBox(height: 24), // 分类标签 _buildCategoryTags(theme), const SizedBox(height: 24), // Top 应用列表 _buildTopAppsSection(context, ref, theme, topAppsAsync), ], ), ), ); } Widget _buildTotalTimeSection(DailyStats stats, ThemeData theme) { return Center( child: Column( children: [ Text( stats.formattedTotalTime, style: theme.textTheme.displayLarge?.copyWith( fontSize: 48, fontWeight: FontWeight.bold, color: AppTheme.primaryColor, ), ), const SizedBox(height: 8), Text( '今日总时长', style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), ], ), ); } Widget _buildEfficiencySection(DailyStats stats, ThemeData theme) { final score = stats.efficiencyScore ?? 0; final color = stats.efficiencyColor; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '效率评分', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), const SizedBox(height: 4), Row( children: [ Text( '$score%', style: theme.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(width: 8), ...List.generate(5, (index) { return Icon( index < (score / 20).floor() ? Icons.star : Icons.star_border, size: 20, color: color, ); }), ], ), ], ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( _getEfficiencyText(score), style: TextStyle( color: color, fontWeight: FontWeight.w600, ), ), ), ], ), ); } String _getEfficiencyText(int score) { if (score >= 80) return '优秀'; if (score >= 60) return '良好'; if (score >= 40) return '一般'; return '需改进'; } Widget _buildCategoryChart(DailyStats stats, ThemeData theme) { final categoryData = [ {'category': 'work', 'time': stats.workTime, 'color': AppTheme.workColor}, {'category': 'study', 'time': stats.studyTime, 'color': AppTheme.studyColor}, {'category': 'entertainment', 'time': stats.entertainmentTime, 'color': AppTheme.entertainmentColor}, {'category': 'social', 'time': stats.socialTime, 'color': AppTheme.socialColor}, {'category': 'tool', 'time': stats.toolTime, 'color': AppTheme.toolColor}, ].where((item) => item['time'] as int > 0).toList(); return Container( height: 300, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '分类时间分布', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), Expanded( child: PieChart( PieChartData( sections: categoryData.map((item) { final time = item['time'] as int; final total = stats.totalTime; final percentage = (time / total * 100); return PieChartSectionData( value: time.toDouble(), title: '${percentage.toStringAsFixed(1)}%', color: item['color'] as Color, radius: 80, titleStyle: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.white, ), ); }).toList(), sectionsSpace: 2, centerSpaceRadius: 60, ), ), ), ], ), ); } Widget _buildCategoryTags(ThemeData theme) { final categories = ['work', 'study', 'entertainment', 'social', 'tool']; return Wrap( spacing: 8, runSpacing: 8, children: categories.map((category) { return FilterChip( label: Text(AppTheme.getCategoryName(category)), selected: false, onSelected: (selected) { // 筛选该分类 }, backgroundColor: AppTheme.getCategoryColor(category).withOpacity(0.1), selectedColor: AppTheme.getCategoryColor(category), labelStyle: TextStyle( color: AppTheme.getCategoryColor(category), fontWeight: FontWeight.w500, ), ); }).toList(), ); } Widget _buildTopAppsSection(BuildContext context, WidgetRef ref, ThemeData theme, AsyncValue> topAppsAsync) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Top Apps Today', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 12), topAppsAsync.when( data: (apps) { if (apps.isEmpty) { return EmptyStateWidget.noApps( onAction: () { ref.invalidate(todayTopAppsProvider); }, ); } return Column( children: apps.map((app) => _buildAppItem(app, theme)).toList(), ); }, loading: () => const Center( child: Padding( padding: EdgeInsets.all(16), child: CircularProgressIndicator(), ), ), error: (error, stack) => Padding( padding: const EdgeInsets.all(16), child: ErrorStateWidget.dataLoad( message: _getErrorMessage(error), onRetry: () { ref.invalidate(todayTopAppsProvider); }, ), ), ), ], ); } Widget _buildEmptyContent(BuildContext context, WidgetRef ref) { return RefreshIndicator( onRefresh: () async { ref.invalidate(todayStatsProvider); ref.invalidate(todayTopAppsProvider); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: EmptyStateWidget.firstTime( onAction: () { // 可以导航到权限设置页面 Navigator.of(context).pushNamed('/permission'); }, ), ), ); } String _getErrorMessage(Object error) { final errorString = error.toString().toLowerCase(); if (errorString.contains('permission') || errorString.contains('权限')) { return '需要授予应用使用权限'; } else if (errorString.contains('network') || errorString.contains('网络')) { return '网络连接失败,请检查网络'; } else if (errorString.contains('database') || errorString.contains('数据库')) { return '数据库操作失败'; } return '加载失败,请稍后重试'; } Widget _buildAppItem(AppUsage app, ThemeData theme) { return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: AppTheme.getCategoryColor(app.category).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.phone_android, color: AppTheme.getCategoryColor(app.category), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( app.appName, style: theme.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), Text( AppTheme.getCategoryName(app.category), style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), ], ), ), Text( app.formattedDuration, style: theme.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w600, color: AppTheme.primaryColor, ), ), ], ), ); } }