|
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:practice_engine/practice_engine.dart';
|
|
|
|
|
import '../routes.dart';
|
|
|
|
|
import '../services/deck_storage.dart';
|
|
|
|
|
import '../widgets/attempts_chart.dart';
|
|
|
|
|
|
|
|
|
|
class DeckOverviewScreen extends StatefulWidget {
|
|
|
|
|
const DeckOverviewScreen({super.key});
|
|
|
|
|
@ -283,6 +282,105 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildProgressStats(BuildContext context) {
|
|
|
|
|
if (_deck == null) return const SizedBox.shrink();
|
|
|
|
|
|
|
|
|
|
final totalQuestions = _deck!.numberOfQuestions;
|
|
|
|
|
final knownCount = _deck!.knownCount;
|
|
|
|
|
final unknownCount = totalQuestions - knownCount;
|
|
|
|
|
final unknownPercentage = totalQuestions > 0
|
|
|
|
|
? (unknownCount / totalQuestions * 100.0)
|
|
|
|
|
: 0.0;
|
|
|
|
|
final knownPercentage = totalQuestions > 0
|
|
|
|
|
? (knownCount / totalQuestions * 100.0)
|
|
|
|
|
: 0.0;
|
|
|
|
|
|
|
|
|
|
// Get progress trend if we have history
|
|
|
|
|
// Attempt history is stored chronologically (oldest first), so first is oldest, last is newest
|
|
|
|
|
String? trendText;
|
|
|
|
|
if (_deck!.attemptHistory.isNotEmpty) {
|
|
|
|
|
final oldestAttempt = _deck!.attemptHistory.first;
|
|
|
|
|
final newestAttempt = _deck!.attemptHistory.last;
|
|
|
|
|
final oldestUnknown = oldestAttempt.unknownPercentage;
|
|
|
|
|
final newestUnknown = newestAttempt.unknownPercentage;
|
|
|
|
|
final improvement = oldestUnknown - newestUnknown;
|
|
|
|
|
|
|
|
|
|
if (improvement > 0) {
|
|
|
|
|
trendText = 'Improved by ${improvement.toStringAsFixed(1)}% since first attempt';
|
|
|
|
|
} else if (improvement < 0) {
|
|
|
|
|
trendText = '${improvement.abs().toStringAsFixed(1)}% more to learn since first attempt';
|
|
|
|
|
} else {
|
|
|
|
|
trendText = 'No change since first attempt';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
// Main stat: Unknown percentage
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
'Questions to Learn',
|
|
|
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
|
|
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
|
Text(
|
|
|
|
|
'${unknownPercentage.toStringAsFixed(1)}%',
|
|
|
|
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
'$unknownCount of $totalQuestions questions',
|
|
|
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
'Questions Known',
|
|
|
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
|
|
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
|
Text(
|
|
|
|
|
'${knownPercentage.toStringAsFixed(1)}%',
|
|
|
|
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
'$knownCount of $totalQuestions questions',
|
|
|
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
if (trendText != null) ...[
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
Text(
|
|
|
|
|
trendText,
|
|
|
|
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
|
|
|
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
@ -462,15 +560,12 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
|
|
|
|
const Divider(),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
Text(
|
|
|
|
|
'Progress Chart',
|
|
|
|
|
'Learning Progress',
|
|
|
|
|
style: Theme.of(context).textTheme.titleSmall,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
// Progress chart
|
|
|
|
|
AttemptsChart(
|
|
|
|
|
attempts: _deck!.attemptHistory,
|
|
|
|
|
maxDisplayItems: 15,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
// Current progress
|
|
|
|
|
_buildProgressStats(context),
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
|