no chart
This commit is contained in:
@@ -477,6 +477,8 @@ class _AttemptScreenState extends State<AttemptScreen> {
|
||||
// Add attempt to history
|
||||
final historyEntry = AttemptHistoryEntry.fromAttemptResult(
|
||||
result: result.result,
|
||||
totalQuestionsInDeck: result.updatedDeck.numberOfQuestions,
|
||||
knownCount: result.updatedDeck.knownCount,
|
||||
timestamp: endTime,
|
||||
);
|
||||
final updatedDeckWithHistory = result.updatedDeck.copyWith(
|
||||
|
||||
@@ -193,7 +193,7 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
void _useDefaultDeck() async {
|
||||
// Check if default deck already exists
|
||||
final baseDeck = DefaultDeck.deck;
|
||||
final deckExists = _deckStorage.hasDeckSync(baseDeck.id);
|
||||
final deckExists = await _deckStorage.hasDeck(baseDeck.id);
|
||||
|
||||
// Show dialog to ask if user wants mock data (only if deck doesn't exist)
|
||||
bool? includeMockData = false;
|
||||
@@ -214,36 +214,47 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
? DefaultDeck.deckWithMockData
|
||||
: DefaultDeck.deck;
|
||||
|
||||
if (deckExists) {
|
||||
// If it exists, create a copy with a new ID
|
||||
final clonedDeck = defaultDeck.copyWith(
|
||||
id: '${defaultDeck.id}_${DateTime.now().millisecondsSinceEpoch}',
|
||||
);
|
||||
_deckStorage.saveDeckSync(clonedDeck);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Default deck added successfully'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
try {
|
||||
if (deckExists) {
|
||||
// If it exists, create a copy with a new ID
|
||||
final clonedDeck = defaultDeck.copyWith(
|
||||
id: '${defaultDeck.id}_${DateTime.now().millisecondsSinceEpoch}',
|
||||
);
|
||||
await _deckStorage.saveDeck(clonedDeck);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Default deck added successfully'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Save default deck to storage
|
||||
await _deckStorage.saveDeck(defaultDeck);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Default deck added successfully'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Save default deck to storage
|
||||
_deckStorage.saveDeckSync(defaultDeck);
|
||||
|
||||
_loadDecks();
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Default deck added successfully'),
|
||||
backgroundColor: Colors.green,
|
||||
SnackBar(
|
||||
content: Text('Error adding deck: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_loadDecks();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user