VERSAO FINAL (core features)
This commit is contained in:
@@ -13,6 +13,7 @@ class ClassStats {
|
|||||||
final int totalContent;
|
final int totalContent;
|
||||||
final List<WeeklyStats> weeklyStats;
|
final List<WeeklyStats> weeklyStats;
|
||||||
final List<StudentNeedingSupport> studentsNeedingSupport;
|
final List<StudentNeedingSupport> studentsNeedingSupport;
|
||||||
|
final DateTime? lastUpdated;
|
||||||
|
|
||||||
const ClassStats({
|
const ClassStats({
|
||||||
required this.classId,
|
required this.classId,
|
||||||
@@ -26,6 +27,7 @@ class ClassStats {
|
|||||||
required this.totalContent,
|
required this.totalContent,
|
||||||
required this.weeklyStats,
|
required this.weeklyStats,
|
||||||
required this.studentsNeedingSupport,
|
required this.studentsNeedingSupport,
|
||||||
|
this.lastUpdated,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ClassStats.fromFirestore(Map<String, dynamic> data, String classId) {
|
factory ClassStats.fromFirestore(Map<String, dynamic> data, String classId) {
|
||||||
@@ -47,6 +49,7 @@ class ClassStats {
|
|||||||
?.map((s) => StudentNeedingSupport.fromFirestore(s))
|
?.map((s) => StudentNeedingSupport.fromFirestore(s))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
|
lastUpdated: (data['lastUpdated'] as Timestamp?)?.toDate(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +65,7 @@ class ClassStats {
|
|||||||
'totalContent': totalContent,
|
'totalContent': totalContent,
|
||||||
'weeklyStats': weeklyStats.map((w) => w.toFirestore()).toList(),
|
'weeklyStats': weeklyStats.map((w) => w.toFirestore()).toList(),
|
||||||
'studentsNeedingSupport': studentsNeedingSupport.map((s) => s.toFirestore()).toList(),
|
'studentsNeedingSupport': studentsNeedingSupport.map((s) => s.toFirestore()).toList(),
|
||||||
|
'lastUpdated': lastUpdated != null ? Timestamp.fromDate(lastUpdated!) : null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,19 +195,51 @@ class GamificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Obter estatísticas da turma
|
/// Obter estatísticas da turma
|
||||||
static Future<ClassStats?> getClassStats(String classId) async {
|
static Future<ClassStats?> getClassStats(String classId, {bool forceRefresh = false}) async {
|
||||||
try {
|
try {
|
||||||
|
if (forceRefresh) {
|
||||||
|
// Forçar recálculo completo
|
||||||
|
return await _calculateClassStats(classId);
|
||||||
|
}
|
||||||
|
|
||||||
final classStatsDoc = await _firestore.collection('classStats').doc(classId).get();
|
final classStatsDoc = await _firestore.collection('classStats').doc(classId).get();
|
||||||
if (!classStatsDoc.exists) {
|
if (!classStatsDoc.exists) {
|
||||||
return await _calculateClassStats(classId);
|
return await _calculateClassStats(classId);
|
||||||
}
|
}
|
||||||
return ClassStats.fromFirestore(classStatsDoc.data()!, classId);
|
|
||||||
|
// Verificar se os dados estão desatualizados (mais de 1 hora)
|
||||||
|
final data = classStatsDoc.data()!;
|
||||||
|
final lastUpdated = data['lastUpdated'] as Timestamp?;
|
||||||
|
if (lastUpdated == null ||
|
||||||
|
DateTime.now().difference(lastUpdated.toDate()).inHours > 1) {
|
||||||
|
return await _calculateClassStats(classId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClassStats.fromFirestore(data, classId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error('Error getting class stats: $e');
|
Logger.error('Error getting class stats: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forçar atualização de estatísticas de todas as turmas de um professor
|
||||||
|
static Future<void> refreshAllClassStats(String teacherId) async {
|
||||||
|
try {
|
||||||
|
final classesSnapshot = await _firestore
|
||||||
|
.collection('classes')
|
||||||
|
.where('teacherId', isEqualTo: teacherId)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
for (final classDoc in classesSnapshot.docs) {
|
||||||
|
await _calculateClassStats(classDoc.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.info('Refreshed stats for ${classesSnapshot.docs.length} classes');
|
||||||
|
} catch (e) {
|
||||||
|
Logger.error('Error refreshing class stats: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Obter ranking de alunos da turma
|
/// Obter ranking de alunos da turma
|
||||||
static Future<List<StudentRanking>> getClassRanking(String classId) async {
|
static Future<List<StudentRanking>> getClassRanking(String classId) async {
|
||||||
try {
|
try {
|
||||||
@@ -225,6 +257,13 @@ class GamificationService {
|
|||||||
final studentIds = enrollmentsSnapshot.docs.map((doc) => doc['studentId'] as String).toList();
|
final studentIds = enrollmentsSnapshot.docs.map((doc) => doc['studentId'] as String).toList();
|
||||||
final rankings = <StudentRanking>[];
|
final rankings = <StudentRanking>[];
|
||||||
|
|
||||||
|
// Obter número real de quizzes disponíveis na turma
|
||||||
|
final quizzesSnapshot = await _firestore
|
||||||
|
.collection('teacherQuizzes')
|
||||||
|
.where('classIds', arrayContains: classId)
|
||||||
|
.get();
|
||||||
|
final totalAvailableQuizzes = quizzesSnapshot.docs.length;
|
||||||
|
|
||||||
// Para cada aluno, obter suas estatísticas
|
// Para cada aluno, obter suas estatísticas
|
||||||
for (final studentId in studentIds) {
|
for (final studentId in studentIds) {
|
||||||
try {
|
try {
|
||||||
@@ -235,17 +274,37 @@ class GamificationService {
|
|||||||
final userData = userDoc.data() as Map<String, dynamic>?;
|
final userData = userDoc.data() as Map<String, dynamic>?;
|
||||||
|
|
||||||
// Calcular estatísticas para o ranking
|
// Calcular estatísticas para o ranking
|
||||||
// Usar contador real de quizzes completos
|
|
||||||
final completedQuizzes = userStats.completedQuizzes;
|
final completedQuizzes = userStats.completedQuizzes;
|
||||||
final totalQuizzes = completedQuizzes + 5; // Estimativa de quizzes disponíveis
|
final totalQuizzes = totalAvailableQuizzes > 0 ? totalAvailableQuizzes : 1;
|
||||||
final quizCompletionRate = totalQuizzes > 0 ? completedQuizzes / totalQuizzes : 0.0;
|
final quizCompletionRate = completedQuizzes / totalQuizzes;
|
||||||
|
|
||||||
|
Logger.info('=== RANKING SCORE DEBUG ===');
|
||||||
|
Logger.info('Student ID: $studentId');
|
||||||
|
Logger.info('Completed quizzes: $completedQuizzes');
|
||||||
|
Logger.info('Total quizzes: $totalQuizzes');
|
||||||
|
Logger.info('Quiz completion rate: $quizCompletionRate (${(quizCompletionRate * 100).toInt()}%)');
|
||||||
|
Logger.info('Current streak: ${userStats.currentStreak}');
|
||||||
|
Logger.info('Total study time: ${userStats.totalStudyTime} minutes');
|
||||||
|
Logger.info('Mastered concepts: ${userStats.masteredConcepts.length}');
|
||||||
|
Logger.info('Unlocked achievements: ${userStats.unlockedAchievements.length}');
|
||||||
|
|
||||||
// Calcular score geral baseado em múltiplos fatores
|
// Calcular score geral baseado em múltiplos fatores
|
||||||
final overallScore = _calculateOverallScore(userStats, quizCompletionRate);
|
final overallScore = _calculateOverallScore(userStats, quizCompletionRate);
|
||||||
|
|
||||||
|
Logger.info('Overall score calculated: $overallScore (${overallScore.toInt()}%)');
|
||||||
|
Logger.info('=== END RANKING SCORE DEBUG ===');
|
||||||
|
|
||||||
|
// Tentar obter um nome melhor para o aluno
|
||||||
|
String studentName = 'Aluno $studentId';
|
||||||
|
if (userData != null) {
|
||||||
|
studentName = userData['displayName'] ??
|
||||||
|
userData['email']?.split('@')[0] ??
|
||||||
|
'Aluno ${studentId.substring(0, 8)}...';
|
||||||
|
}
|
||||||
|
|
||||||
rankings.add(StudentRanking(
|
rankings.add(StudentRanking(
|
||||||
studentId: studentId,
|
studentId: studentId,
|
||||||
studentName: userData?['displayName'] ?? 'Aluno $studentId',
|
studentName: studentName,
|
||||||
studentEmail: userData?['email'] ?? '',
|
studentEmail: userData?['email'] ?? '',
|
||||||
overallScore: overallScore,
|
overallScore: overallScore,
|
||||||
completedQuizzes: completedQuizzes,
|
completedQuizzes: completedQuizzes,
|
||||||
@@ -274,21 +333,28 @@ class GamificationService {
|
|||||||
|
|
||||||
/// Calcular score geral para ranking
|
/// Calcular score geral para ranking
|
||||||
static double _calculateOverallScore(UserStats userStats, double quizCompletionRate) {
|
static double _calculateOverallScore(UserStats userStats, double quizCompletionRate) {
|
||||||
double score = 0.0;
|
// Se completou 100% dos quizzes, score é 100%
|
||||||
|
if (quizCompletionRate >= 1.0) {
|
||||||
|
return 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
// Peso de 40% para completion rate de quizzes
|
// Para completion < 100%, calcular proporcionalmente
|
||||||
score += quizCompletionRate * 40;
|
double baseScore = quizCompletionRate * 90; // 90% baseado em completion
|
||||||
|
|
||||||
// Peso de 20% para streak (máximo 20 pontos)
|
// Bônus adicionais (máximo 10% extra)
|
||||||
score += (userStats.currentStreak / 30.0 * 20).clamp(0.0, 20.0);
|
double bonusScore = 0.0;
|
||||||
|
|
||||||
// Peso de 20% para tempo de estudo (máximo 20 pontos para 10 horas)
|
// 5% para conceitos dominados
|
||||||
score += (userStats.totalStudyTime / 600.0 * 20).clamp(0.0, 20.0);
|
bonusScore += (userStats.masteredConcepts.length / 5.0 * 5).clamp(0.0, 5.0);
|
||||||
|
|
||||||
// Peso de 20% para conceitos dominados (máximo 20 pontos para 10 conceitos)
|
// 3% para streak
|
||||||
score += (userStats.masteredConcepts.length / 10.0 * 20).clamp(0.0, 20.0);
|
bonusScore += (userStats.currentStreak / 7.0 * 3).clamp(0.0, 3.0);
|
||||||
|
|
||||||
return score;
|
// 2% para conquistas
|
||||||
|
bonusScore += (userStats.unlockedAchievements.length / 10.0 * 2).clamp(0.0, 2.0);
|
||||||
|
|
||||||
|
final totalScore = baseScore + bonusScore;
|
||||||
|
return totalScore.clamp(0.0, 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Criar conquista personalizada (professor)
|
/// Criar conquista personalizada (professor)
|
||||||
@@ -444,46 +510,97 @@ class GamificationService {
|
|||||||
|
|
||||||
for (final studentId in studentIds) {
|
for (final studentId in studentIds) {
|
||||||
final userStats = await getUserStats(studentId);
|
final userStats = await getUserStats(studentId);
|
||||||
if (userStats != null) {
|
|
||||||
// Verificar se está ativo (atividade nos últimos 7 dias)
|
// Verificar se está ativo (atividade nos últimos 30 dias - mais realista)
|
||||||
int daysSinceLastActivity = 999; // Valor alto para inatividade
|
int daysSinceLastActivity = 999; // Valor alto para inatividade
|
||||||
if (userStats.lastActivityDate != null) {
|
bool hasStats = userStats != null;
|
||||||
daysSinceLastActivity = DateTime.now().difference(userStats.lastActivityDate!).inDays;
|
|
||||||
if (daysSinceLastActivity <= 7) {
|
if (hasStats && userStats!.lastActivityDate != null) {
|
||||||
activeStudents++;
|
daysSinceLastActivity = DateTime.now().difference(userStats.lastActivityDate!).inDays;
|
||||||
}
|
if (daysSinceLastActivity <= 30) {
|
||||||
|
activeStudents++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular progresso baseado em quizzes completos e conceitos dominados
|
||||||
|
double progress = 0.0;
|
||||||
|
if (hasStats) {
|
||||||
|
final completedQuizzes = userStats!.completedQuizzes;
|
||||||
|
final masteredConcepts = userStats.masteredConcepts.length;
|
||||||
|
|
||||||
|
Logger.info('=== PROGRESS CALCULATION DEBUG ===');
|
||||||
|
Logger.info('Student ID: $studentId');
|
||||||
|
Logger.info('Completed quizzes: $completedQuizzes');
|
||||||
|
Logger.info('Mastered concepts: $masteredConcepts');
|
||||||
|
|
||||||
|
// Progresso mais representativo: 60% quizzes + 40% conceitos
|
||||||
|
// Primeiro quiz já dá 30% de progresso (incentivo inicial)
|
||||||
|
final quizProgress = completedQuizzes > 0 ?
|
||||||
|
(0.3 + (completedQuizzes - 1) * 0.15).clamp(0.0, 0.6) : 0.0;
|
||||||
|
// Primeiro conceito já dá 15% de progresso
|
||||||
|
final conceptProgress = (masteredConcepts * 0.15).clamp(0.0, 0.4);
|
||||||
|
progress = quizProgress + conceptProgress;
|
||||||
|
|
||||||
|
Logger.info('Quiz progress: $quizProgress (${(quizProgress * 100).toInt()}%)');
|
||||||
|
Logger.info('Concept progress: $conceptProgress (${(conceptProgress * 100).toInt()}%)');
|
||||||
|
Logger.info('Total progress: $progress (${(progress * 100).toInt()}%)');
|
||||||
|
Logger.info('=== END PROGRESS CALCULATION DEBUG ===');
|
||||||
|
} else {
|
||||||
|
Logger.info('Student $studentId has no stats - progress = 0.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
totalProgress += progress;
|
||||||
|
|
||||||
|
// Verificar se precisa de apoio (ajustado para nova fórmula)
|
||||||
|
if (progress < 0.25 || daysSinceLastActivity > 30) {
|
||||||
|
final userDoc = await _firestore.collection('users').doc(studentId).get();
|
||||||
|
final userData = userDoc.data();
|
||||||
|
|
||||||
|
// Tentar obter um nome melhor para o aluno
|
||||||
|
String studentName = 'Aluno ${studentId.substring(0, 8)}...';
|
||||||
|
if (userData != null) {
|
||||||
|
studentName = userData['displayName'] ??
|
||||||
|
userData['email']?.split('@')[0] ??
|
||||||
|
'Aluno ${studentId.substring(0, 8)}...';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular progresso baseado em conceitos dominados
|
needingSupport.add(StudentNeedingSupport(
|
||||||
final progress = userStats.masteredConcepts.isEmpty
|
studentId: studentId,
|
||||||
? 0.0
|
studentName: studentName,
|
||||||
: userStats.masteredConcepts.map((c) => c.masteryLevel).reduce((a, b) => a + b) / userStats.masteredConcepts.length / 100;
|
reason: progress < 0.3 ? 'low_scores' : 'inactivity',
|
||||||
totalProgress += progress;
|
lastActivity: hasStats ? userStats!.lastActivityDate ?? DateTime.now() : DateTime.now().subtract(const Duration(days: 45)),
|
||||||
|
averageScore: progress * 100,
|
||||||
// Verificar se precisa de apoio
|
));
|
||||||
if (progress < 0.5 || daysSinceLastActivity > 14) {
|
|
||||||
final userDoc = await _firestore.collection('users').doc(studentId).get();
|
|
||||||
final userName = userDoc.data()?['displayName'] ?? 'Unknown';
|
|
||||||
|
|
||||||
needingSupport.add(StudentNeedingSupport(
|
|
||||||
studentId: studentId,
|
|
||||||
studentName: userName,
|
|
||||||
reason: progress < 0.5 ? 'low_scores' : 'inactivity',
|
|
||||||
lastActivity: userStats.lastActivityDate ?? DateTime.now(),
|
|
||||||
averageScore: progress * 100,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final averageProgress = studentIds.isEmpty ? 0.0 : totalProgress / studentIds.length;
|
final averageProgress = studentIds.isEmpty ? 0.0 : totalProgress / studentIds.length;
|
||||||
|
|
||||||
// Obter estatísticas de quizzes
|
Logger.info('=== AVERAGE PROGRESS DEBUG ===');
|
||||||
|
Logger.info('Total students: ${studentIds.length}');
|
||||||
|
Logger.info('Total progress sum: $totalProgress');
|
||||||
|
Logger.info('Average progress: $averageProgress (${(averageProgress * 100).toInt()}%)');
|
||||||
|
Logger.info('=== END AVERAGE PROGRESS DEBUG ===');
|
||||||
|
|
||||||
|
// Obter estatísticas de quizzes e conteúdos
|
||||||
final quizzesSnapshot = await _firestore
|
final quizzesSnapshot = await _firestore
|
||||||
.collection('teacherQuizzes')
|
.collection('teacherQuizzes')
|
||||||
.where('classIds', arrayContains: classId)
|
.where('classIds', arrayContains: classId)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
|
// Obter materiais/conteúdos da turma
|
||||||
|
final materialsSnapshot = await _firestore
|
||||||
|
.collection('materials')
|
||||||
|
.where('classId', isEqualTo: classId)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
// Contar quizzes ativos (últimos 30 dias)
|
||||||
|
final thirtyDaysAgo = DateTime.now().subtract(const Duration(days: 30));
|
||||||
|
final activeQuizzesCount = quizzesSnapshot.docs.where((doc) {
|
||||||
|
final createdAt = (doc.data()['createdAt'] as Timestamp?)?.toDate();
|
||||||
|
return createdAt != null && createdAt.isAfter(thirtyDaysAgo);
|
||||||
|
}).length;
|
||||||
|
|
||||||
final classStats = ClassStats(
|
final classStats = ClassStats(
|
||||||
classId: classId,
|
classId: classId,
|
||||||
teacherId: teacherId,
|
teacherId: teacherId,
|
||||||
@@ -492,14 +609,17 @@ class GamificationService {
|
|||||||
activeStudents: activeStudents,
|
activeStudents: activeStudents,
|
||||||
averageProgress: averageProgress,
|
averageProgress: averageProgress,
|
||||||
totalQuizzes: quizzesSnapshot.docs.length,
|
totalQuizzes: quizzesSnapshot.docs.length,
|
||||||
activeQuizzes: quizzesSnapshot.docs.length, // Simplificado
|
activeQuizzes: activeQuizzesCount,
|
||||||
totalContent: 0, // Implementar depois
|
totalContent: materialsSnapshot.docs.length,
|
||||||
weeklyStats: [],
|
weeklyStats: [],
|
||||||
studentsNeedingSupport: needingSupport,
|
studentsNeedingSupport: needingSupport,
|
||||||
|
lastUpdated: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Salvar estatísticas calculadas
|
// Limpar cache primeiro e depois salvar estatísticas calculadas
|
||||||
|
await _firestore.collection('classStats').doc(classId).delete();
|
||||||
await _firestore.collection('classStats').doc(classId).set(classStats.toFirestore());
|
await _firestore.collection('classStats').doc(classId).set(classStats.toFirestore());
|
||||||
|
Logger.info('Class stats refreshed and saved for class $classId');
|
||||||
|
|
||||||
return classStats;
|
return classStats;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ class _AnalyticsPageState extends State<AnalyticsPage>
|
|||||||
|
|
||||||
for (final classDoc in classesSnapshot.docs) {
|
for (final classDoc in classesSnapshot.docs) {
|
||||||
final classId = classDoc.id;
|
final classId = classDoc.id;
|
||||||
final stats = await GamificationService.getClassStats(classId);
|
// Forçar atualização para obter dados mais recentes
|
||||||
|
final stats = await GamificationService.getClassStats(classId, forceRefresh: true);
|
||||||
if (stats != null) {
|
if (stats != null) {
|
||||||
classStatsList.add(stats);
|
classStatsList.add(stats);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ class _TeacherAnalyticsPreviewWidgetState extends State<TeacherAnalyticsPreviewW
|
|||||||
|
|
||||||
for (final classDoc in classesSnapshot.docs) {
|
for (final classDoc in classesSnapshot.docs) {
|
||||||
final classId = classDoc.id;
|
final classId = classDoc.id;
|
||||||
final stats = await GamificationService.getClassStats(classId);
|
// Forçar atualização para obter dados mais recentes
|
||||||
|
final stats = await GamificationService.getClassStats(classId, forceRefresh: true);
|
||||||
if (stats != null) {
|
if (stats != null) {
|
||||||
classStatsList.add(stats);
|
classStatsList.add(stats);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ class _TeacherHeroWidgetState extends State<TeacherHeroWidget> {
|
|||||||
|
|
||||||
for (final classDoc in classesSnapshot.docs) {
|
for (final classDoc in classesSnapshot.docs) {
|
||||||
final classId = classDoc.id;
|
final classId = classDoc.id;
|
||||||
final stats = await GamificationService.getClassStats(classId);
|
// Forçar atualização para obter dados mais recentes
|
||||||
|
final stats = await GamificationService.getClassStats(classId, forceRefresh: true);
|
||||||
if (stats != null) {
|
if (stats != null) {
|
||||||
classStatsList.add(stats);
|
classStatsList.add(stats);
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,18 @@ class _TeacherHeroWidgetState extends State<TeacherHeroWidget> {
|
|||||||
double get classAverageProgress {
|
double get classAverageProgress {
|
||||||
if (_classStats.isEmpty) return 0.0;
|
if (_classStats.isEmpty) return 0.0;
|
||||||
final totalProgress = _classStats.fold(0.0, (sum, stats) => sum + stats.averageProgress);
|
final totalProgress = _classStats.fold(0.0, (sum, stats) => sum + stats.averageProgress);
|
||||||
return totalProgress / _classStats.length;
|
final average = totalProgress / _classStats.length;
|
||||||
|
|
||||||
|
print('=== UI PROGRESS DEBUG ===');
|
||||||
|
print('Number of classes: ${_classStats.length}');
|
||||||
|
for (int i = 0; i < _classStats.length; i++) {
|
||||||
|
print('Class ${i + 1}: ${_classStats[i].className} - ${_classStats[i].averageProgress} (${(_classStats[i].averageProgress * 100).toInt()}%)');
|
||||||
|
}
|
||||||
|
print('Total progress sum: $totalProgress');
|
||||||
|
print('Calculated average: $average (${(average * 100).toInt()}%)');
|
||||||
|
print('=== END UI PROGRESS DEBUG ===');
|
||||||
|
|
||||||
|
return average;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -184,13 +196,22 @@ class _TeacherHeroWidgetState extends State<TeacherHeroWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Builder(
|
||||||
'${(classAverageProgress * 100).toInt()}%',
|
builder: (context) {
|
||||||
style: const TextStyle(
|
final displayValue = (classAverageProgress * 100).toInt();
|
||||||
color: Colors.white,
|
print('=== RENDER DEBUG ===');
|
||||||
fontSize: 24,
|
print('classAverageProgress: $classAverageProgress');
|
||||||
fontWeight: FontWeight.bold,
|
print('displayValue: $displayValue%');
|
||||||
),
|
print('=== END RENDER DEBUG ===');
|
||||||
|
return Text(
|
||||||
|
'$displayValue%',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user