// 成就页面 — 徽章收集展示 import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nuanji_app/core/theme/app_colors.dart'; import 'package:nuanji_app/core/theme/app_radius.dart'; import 'package:nuanji_app/data/remote/api_client.dart'; import '../bloc/achievement_bloc.dart'; /// 成就页面 — 徽章收集和展示 class AchievementPage extends StatefulWidget { const AchievementPage({super.key}); @override State createState() => _AchievementPageState(); } class _AchievementPageState extends State { late final AchievementBloc _bloc; @override void initState() { super.initState(); _bloc = AchievementBloc(api: context.read()); _bloc.load(); } @override void dispose() { _bloc.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Scaffold( appBar: AppBar(title: const Text('成就')), body: ListenableBuilder( listenable: _bloc, builder: (context, _) { final state = _bloc.state; if (state.isLoading) { return const Center(child: CircularProgressIndicator()); } if (state.errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 48, color: colorScheme.error), const SizedBox(height: 12), Text(state.errorMessage!, style: theme.textTheme.bodyMedium), const SizedBox(height: 16), FilledButton.tonal( onPressed: _bloc.load, child: const Text('重试'), ), ], ), ); } return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 进度概览 _AchievementProgressCard( unlocked: state.unlockedCount, total: state.achievements.length, colorScheme: colorScheme, ), const SizedBox(height: 24), Text( '全部成就', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 0.75, ), itemCount: state.achievements.length, itemBuilder: (context, index) { return _AchievementCard( achievement: state.achievements[index], colorScheme: colorScheme, ); }, ), ], ), ); }, ), ); } } /// 成就进度卡片 class _AchievementProgressCard extends StatelessWidget { const _AchievementProgressCard({ required this.unlocked, required this.total, required this.colorScheme, }); final int unlocked; final int total; final ColorScheme colorScheme; @override Widget build(BuildContext context) { final theme = Theme.of(context); final progress = total > 0 ? unlocked / total : 0.0; return Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: AppRadius.lgBorder, ), color: colorScheme.primaryContainer, child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '收集进度', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), Text( '$unlocked / $total', style: theme.textTheme.titleMedium?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 12), ClipRRect( borderRadius: AppRadius.xsBorder, child: LinearProgressIndicator( value: progress, minHeight: 10, backgroundColor: colorScheme.primary.withValues(alpha: 0.15), color: colorScheme.primary, ), ), ], ), ), ); } } /// 成就卡片 class _AchievementCard extends StatelessWidget { const _AchievementCard({ required this.achievement, required this.colorScheme, }); final Achievement achievement; final ColorScheme colorScheme; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: AppRadius.mdBorder, side: BorderSide( color: achievement.isUnlocked ? AppColors.accent.withValues(alpha: 0.4) : colorScheme.outlineVariant, ), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 56, height: 56, decoration: BoxDecoration( shape: BoxShape.circle, color: achievement.isUnlocked ? AppColors.accent.withValues(alpha: 0.15) : colorScheme.onSurface.withValues(alpha: 0.05), ), alignment: Alignment.center, child: achievement.isUnlocked ? Text( achievement.icon ?? '🏆', style: const TextStyle(fontSize: 28), ) : Icon( Icons.lock_outline, color: colorScheme.onSurface.withValues(alpha: 0.3), ), ), const SizedBox(height: 8), Text( achievement.name, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, color: achievement.isUnlocked ? colorScheme.onSurface : colorScheme.onSurface.withValues(alpha: 0.4), ), ), if (achievement.description != null) ...[ const SizedBox(height: 4), Text( achievement.description!, style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.onSurface.withValues( alpha: achievement.isUnlocked ? 0.6 : 0.3, ), ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ], ), ), ); } }