416 lines
14 KiB
Dart
416 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../../core/theme/app_theme_extension.dart';
|
|
import 'package:flutter_animate/flutter_animate.dart';
|
|
import '../../../../core/services/auth_service.dart';
|
|
import '../../../../core/services/gamification_service.dart';
|
|
import '../../../../core/models/user_stats.dart';
|
|
import '../../../../core/models/achievement.dart';
|
|
|
|
/// Profile section with user info and achievements
|
|
class ProfileSectionWidget extends StatefulWidget {
|
|
const ProfileSectionWidget({super.key});
|
|
|
|
@override
|
|
State<ProfileSectionWidget> createState() => _ProfileSectionWidgetState();
|
|
}
|
|
|
|
class _ProfileSectionWidgetState extends State<ProfileSectionWidget> {
|
|
UserStats? _userStats;
|
|
List<Achievement> _recentAchievements = [];
|
|
bool _loading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadUserData();
|
|
}
|
|
|
|
Future<void> _loadUserData() async {
|
|
try {
|
|
final user = AuthService.currentUser;
|
|
if (user != null) {
|
|
final results = await Future.wait([
|
|
GamificationService.getUserStats(user.uid),
|
|
GamificationService.getAvailableAchievements(),
|
|
]);
|
|
|
|
final stats = results[0] as UserStats?;
|
|
final achievements = results[1] as List<Achievement>;
|
|
|
|
// Obter conquistas desbloqueadas recentemente
|
|
final unlockedAchievementIds =
|
|
stats?.unlockedAchievements.map((ua) => ua.achievementId).toSet() ??
|
|
{};
|
|
|
|
final recentUnlocked = achievements
|
|
.where((a) => unlockedAchievementIds.contains(a.id))
|
|
.take(4)
|
|
.toList();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_userStats = stats;
|
|
_recentAchievements = recentUnlocked;
|
|
_loading = false;
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print('Error loading user data: $e');
|
|
if (mounted) {
|
|
setState(() {
|
|
_loading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_loading) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
final user = AuthService.currentUser;
|
|
final userName = user?.displayName ?? 'Estudante';
|
|
final userEmail = user?.email ?? '';
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(top: 24),
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Profile Header
|
|
Row(
|
|
children: [
|
|
Container(
|
|
width: 48,
|
|
height: 48,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
AppThemeExtras.of(context).actionCardGradientStart,
|
|
AppThemeExtras.of(context).actionCardGradientEnd,
|
|
],
|
|
),
|
|
borderRadius: BorderRadius.circular(24),
|
|
),
|
|
child: const Icon(
|
|
Icons.person,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
userName,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
userEmail,
|
|
style: TextStyle(
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurfaceVariant,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
if (userEmail.length > 20) ...[
|
|
const SizedBox(width: 8),
|
|
Icon(
|
|
Icons.more_horiz,
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurfaceVariant,
|
|
size: 16,
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
|
|
// Achievements
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.emoji_events,
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Conquistas',
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Achievement List (Teacher-style design)
|
|
if (_recentAchievements.isNotEmpty) ...[
|
|
..._recentAchievements.map((achievement) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: _buildAchievementItem(
|
|
context,
|
|
achievement.name,
|
|
achievement.points,
|
|
_getRarityColor(achievement.rarity),
|
|
_getIconData(achievement.icon),
|
|
),
|
|
);
|
|
}),
|
|
] else ...[
|
|
// Streak item
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: _buildAchievementItem(
|
|
context,
|
|
'${_userStats?.currentStreak ?? 0} dias seguidos',
|
|
(_userStats?.currentStreak ?? 0) * 5,
|
|
Theme.of(context).colorScheme.secondary,
|
|
Icons.local_fire_department,
|
|
),
|
|
),
|
|
// Concepts item
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: _buildAchievementItem(
|
|
context,
|
|
'${_userStats?.masteredConcepts.length ?? 0} conceitos dominados',
|
|
(_userStats?.masteredConcepts.length ?? 0) * 10,
|
|
Theme.of(context).colorScheme.primary,
|
|
Icons.school,
|
|
),
|
|
),
|
|
if (_userStats != null && _userStats!.totalStudyTime > 0)
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: _buildAchievementItem(
|
|
context,
|
|
'${(_userStats!.totalStudyTime / 60).toStringAsFixed(1)}h estudadas',
|
|
(_userStats!.totalStudyTime ~/ 60) * 3,
|
|
Theme.of(context).colorScheme.tertiary,
|
|
Icons.schedule,
|
|
),
|
|
),
|
|
],
|
|
const SizedBox(height: 20),
|
|
|
|
// Recent Activity Summary
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).brightness == Brightness.dark
|
|
? Theme.of(context).colorScheme.surfaceContainerHighest
|
|
: Theme.of(context).colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.outline.withOpacity(0.2),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.trending_up,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
_getProgressMessage(),
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
_getProgressComparison(),
|
|
style: TextStyle(
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurfaceVariant,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
.animate()
|
|
.fadeIn(
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.easeOut,
|
|
)
|
|
.then(delay: const Duration(milliseconds: 200));
|
|
}
|
|
|
|
Widget _buildAchievementItem(
|
|
BuildContext context,
|
|
String name,
|
|
int points,
|
|
Color color,
|
|
IconData icon,
|
|
) {
|
|
return Row(
|
|
children: [
|
|
Container(
|
|
width: 32,
|
|
height: 32,
|
|
decoration: BoxDecoration(
|
|
color: color.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Center(child: Icon(icon, color: color, size: 16)),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
name,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: color.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
'$points pts',
|
|
style: TextStyle(
|
|
color: color,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
IconData _getIconData(String iconName) {
|
|
switch (iconName) {
|
|
case 'emoji_events':
|
|
return Icons.emoji_events;
|
|
case 'school':
|
|
return Icons.school;
|
|
case 'local_fire_department':
|
|
return Icons.local_fire_department;
|
|
case 'schedule':
|
|
return Icons.schedule;
|
|
case 'trending_up':
|
|
return Icons.trending_up;
|
|
case 'military_tech':
|
|
return Icons.military_tech;
|
|
case 'workspace_premium':
|
|
return Icons.workspace_premium;
|
|
case 'psychology':
|
|
return Icons.psychology;
|
|
case 'lightbulb':
|
|
return Icons.lightbulb;
|
|
case 'star':
|
|
return Icons.star;
|
|
case 'speed':
|
|
return Icons.speed;
|
|
default:
|
|
return Icons.star;
|
|
}
|
|
}
|
|
|
|
Color _getRarityColor(String rarity) {
|
|
switch (rarity) {
|
|
case 'common':
|
|
return Colors.grey;
|
|
case 'rare':
|
|
return Colors.blue;
|
|
case 'epic':
|
|
return Colors.purple;
|
|
case 'legendary':
|
|
return Colors.orange;
|
|
default:
|
|
return Colors.grey;
|
|
}
|
|
}
|
|
|
|
String _getProgressMessage() {
|
|
if (_userStats == null) return 'Continue estudando!';
|
|
|
|
final streak = _userStats!.currentStreak;
|
|
final studyTime = _userStats!.totalStudyTime;
|
|
|
|
if (streak >= 7) return 'Incrível streak! 🔥';
|
|
if (studyTime >= 300) return 'Dedicação exemplar! 📚';
|
|
if (streak >= 3) return 'Bom progresso! 📈';
|
|
return 'Continue estudando! 💪';
|
|
}
|
|
|
|
String _getProgressComparison() {
|
|
if (_userStats == null) return 'Comece sua jornada de estudos';
|
|
|
|
final weeklyTime = _userStats!.weeklyStudyTime;
|
|
final concepts = _userStats!.masteredConcepts.length;
|
|
|
|
if (weeklyTime >= 180) return 'Você está 15% acima da média esta semana';
|
|
if (concepts >= 3) return 'Domine $concepts conceitos esta semana';
|
|
if (weeklyTime >= 60) return 'Bom tempo de estudo esta semana';
|
|
return 'Continue assim para subir no ranking!';
|
|
}
|
|
}
|