no chart
This commit is contained in:
+21
-20
@@ -156,42 +156,43 @@ class DefaultDeck {
|
||||
final now = DateTime.now();
|
||||
|
||||
// Create mock attempt history (4 previous attempts)
|
||||
// Unknown percentage decreases over time as more questions become known
|
||||
final attemptHistory = [
|
||||
// First attempt - 6/10 correct (60%)
|
||||
// First attempt - 6/10 correct (60%), ~85% unknown (17/20 questions)
|
||||
AttemptHistoryEntry(
|
||||
timestamp: now.subtract(const Duration(days: 7)).millisecondsSinceEpoch,
|
||||
totalQuestions: 10,
|
||||
correctAnswers: 6,
|
||||
incorrectAnswers: 4,
|
||||
skippedAnswers: 0,
|
||||
timeSpentSeconds: 245,
|
||||
correctCount: 6,
|
||||
percentageCorrect: 60.0,
|
||||
timeSpent: 245000, // milliseconds
|
||||
unknownPercentage: 85.0, // 17 out of 20 questions unknown
|
||||
),
|
||||
// Second attempt - 7/10 correct (70%)
|
||||
// Second attempt - 7/10 correct (70%), ~70% unknown (14/20 questions)
|
||||
AttemptHistoryEntry(
|
||||
timestamp: now.subtract(const Duration(days: 5)).millisecondsSinceEpoch,
|
||||
totalQuestions: 10,
|
||||
correctAnswers: 7,
|
||||
incorrectAnswers: 3,
|
||||
skippedAnswers: 0,
|
||||
timeSpentSeconds: 198,
|
||||
correctCount: 7,
|
||||
percentageCorrect: 70.0,
|
||||
timeSpent: 198000, // milliseconds
|
||||
unknownPercentage: 70.0, // 14 out of 20 questions unknown
|
||||
),
|
||||
// Third attempt - 8/10 correct (80%)
|
||||
// Third attempt - 8/10 correct (80%), ~55% unknown (11/20 questions)
|
||||
AttemptHistoryEntry(
|
||||
timestamp: now.subtract(const Duration(days: 3)).millisecondsSinceEpoch,
|
||||
totalQuestions: 10,
|
||||
correctAnswers: 8,
|
||||
incorrectAnswers: 2,
|
||||
skippedAnswers: 0,
|
||||
timeSpentSeconds: 187,
|
||||
correctCount: 8,
|
||||
percentageCorrect: 80.0,
|
||||
timeSpent: 187000, // milliseconds
|
||||
unknownPercentage: 55.0, // 11 out of 20 questions unknown
|
||||
),
|
||||
// Fourth attempt - 9/10 correct (90%)
|
||||
// Fourth attempt - 9/10 correct (90%), ~45% unknown (9/20 questions)
|
||||
AttemptHistoryEntry(
|
||||
timestamp: now.subtract(const Duration(days: 1)).millisecondsSinceEpoch,
|
||||
totalQuestions: 10,
|
||||
correctAnswers: 9,
|
||||
incorrectAnswers: 1,
|
||||
skippedAnswers: 0,
|
||||
timeSpentSeconds: 165,
|
||||
correctCount: 9,
|
||||
percentageCorrect: 90.0,
|
||||
timeSpent: 165000, // milliseconds
|
||||
unknownPercentage: 45.0, // 9 out of 20 questions unknown
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
+4
-1
@@ -3,7 +3,10 @@ import 'routes.dart';
|
||||
import 'theme.dart';
|
||||
import 'services/deck_storage.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
// Ensure Flutter bindings are initialized before using any plugins
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize storage early
|
||||
DeckStorage().initialize();
|
||||
runApp(const DeckyApp());
|
||||
|
||||
@@ -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}',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Save default deck to storage
|
||||
_deckStorage.saveDeckSync(defaultDeck);
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_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),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@@ -14,17 +14,25 @@ class DeckStorage {
|
||||
static const String _deckIdsKey = 'deck_ids';
|
||||
bool _initialized = false;
|
||||
SharedPreferences? _prefs;
|
||||
final Future<void> _initFuture;
|
||||
Future<void>? _initFuture;
|
||||
|
||||
DeckStorage._internal() : _initFuture = SharedPreferences.getInstance().then((prefs) {
|
||||
_instance._prefs = prefs;
|
||||
_instance._initialized = true;
|
||||
});
|
||||
DeckStorage._internal();
|
||||
|
||||
/// Lazy initialization of SharedPreferences
|
||||
Future<void> _initSharedPreferences() async {
|
||||
if (_initFuture == null) {
|
||||
_initFuture = SharedPreferences.getInstance().then((prefs) {
|
||||
_instance._prefs = prefs;
|
||||
_instance._initialized = true;
|
||||
});
|
||||
}
|
||||
await _initFuture;
|
||||
}
|
||||
|
||||
/// Initialize storage with default deck if empty
|
||||
Future<void> initialize() async {
|
||||
if (_initialized) return;
|
||||
await _initFuture;
|
||||
await _initSharedPreferences();
|
||||
|
||||
// Load existing decks
|
||||
final deckIds = _prefs!.getStringList(_deckIdsKey) ?? [];
|
||||
@@ -39,7 +47,7 @@ class DeckStorage {
|
||||
/// Ensure storage is initialized
|
||||
Future<void> _ensureInitialized() async {
|
||||
if (!_initialized) {
|
||||
await _initFuture;
|
||||
await _initSharedPreferences();
|
||||
await initialize();
|
||||
}
|
||||
}
|
||||
@@ -75,10 +83,10 @@ class DeckStorage {
|
||||
'attemptHistory': deck.attemptHistory.map((entry) => {
|
||||
'timestamp': entry.timestamp,
|
||||
'totalQuestions': entry.totalQuestions,
|
||||
'correctAnswers': entry.correctAnswers,
|
||||
'incorrectAnswers': entry.incorrectAnswers,
|
||||
'skippedAnswers': entry.skippedAnswers,
|
||||
'timeSpentSeconds': entry.timeSpentSeconds,
|
||||
'correctCount': entry.correctCount,
|
||||
'percentageCorrect': entry.percentageCorrect,
|
||||
'timeSpent': entry.timeSpent,
|
||||
'unknownPercentage': entry.unknownPercentage,
|
||||
}).toList(),
|
||||
'incompleteAttempt': deck.incompleteAttempt != null ? {
|
||||
'attemptId': deck.incompleteAttempt!.attemptId,
|
||||
@@ -145,13 +153,23 @@ class DeckStorage {
|
||||
final historyJson = json['attemptHistory'] as List<dynamic>? ?? [];
|
||||
final attemptHistory = historyJson.map((entryJson) {
|
||||
final entryMap = entryJson as Map<String, dynamic>;
|
||||
// Support both old and new field names for backward compatibility
|
||||
final correctCount = entryMap['correctCount'] as int? ??
|
||||
entryMap['correctAnswers'] as int? ?? 0;
|
||||
final totalQuestions = entryMap['totalQuestions'] as int? ?? 0;
|
||||
final timeSpent = entryMap['timeSpent'] as int? ??
|
||||
(entryMap['timeSpentSeconds'] as int? ?? 0) * 1000; // Convert seconds to milliseconds
|
||||
// Calculate percentageCorrect if not present
|
||||
final percentageCorrect = entryMap['percentageCorrect'] as double? ??
|
||||
(totalQuestions > 0 ? (correctCount / totalQuestions * 100) : 0.0);
|
||||
final unknownPercentage = entryMap['unknownPercentage'] as double? ?? 0.0;
|
||||
return AttemptHistoryEntry(
|
||||
timestamp: entryMap['timestamp'] as int? ?? 0,
|
||||
totalQuestions: entryMap['totalQuestions'] as int? ?? 0,
|
||||
correctAnswers: entryMap['correctAnswers'] as int? ?? 0,
|
||||
incorrectAnswers: entryMap['incorrectAnswers'] as int? ?? 0,
|
||||
skippedAnswers: entryMap['skippedAnswers'] as int? ?? 0,
|
||||
timeSpentSeconds: entryMap['timeSpentSeconds'] as int?,
|
||||
totalQuestions: totalQuestions,
|
||||
correctCount: correctCount,
|
||||
percentageCorrect: percentageCorrect,
|
||||
timeSpent: timeSpent,
|
||||
unknownPercentage: unknownPercentage,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:practice_engine/practice_engine.dart';
|
||||
|
||||
/// A chart widget that visualizes attempt progress over time.
|
||||
/// A chart widget that visualizes learning progress over time.
|
||||
/// Shows the percentage of questions that are still unknown (not yet learned).
|
||||
/// As you learn more questions, this percentage decreases.
|
||||
class AttemptsChart extends StatelessWidget {
|
||||
final List<AttemptHistoryEntry> attempts;
|
||||
final int maxDisplayItems;
|
||||
@@ -18,14 +20,14 @@ class AttemptsChart extends StatelessWidget {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// Get recent attempts (most recent first, then reverse for display)
|
||||
final displayAttempts = attempts.reversed.take(maxDisplayItems).toList();
|
||||
// Get recent attempts (take most recent, then reverse so oldest is first for chronological display)
|
||||
final displayAttempts = attempts.reversed.take(maxDisplayItems).toList().reversed.toList();
|
||||
if (displayAttempts.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// Find min and max values for scaling
|
||||
final percentages = displayAttempts.map((e) => e.percentageCorrect).toList();
|
||||
// Find min and max values for scaling (using unknown percentage)
|
||||
final percentages = displayAttempts.map((e) => e.unknownPercentage).toList();
|
||||
final minValue = percentages.reduce((a, b) => a < b ? a : b);
|
||||
final maxValue = percentages.reduce((a, b) => a > b ? a : b);
|
||||
final range = (maxValue - minValue).clamp(10.0, 100.0); // Ensure minimum range
|
||||
@@ -148,7 +150,7 @@ class _AttemptsChartPainter extends CustomPainter {
|
||||
final points = <Offset>[];
|
||||
|
||||
for (int i = 0; i < attempts.length; i++) {
|
||||
final percentage = attempts[i].percentageCorrect;
|
||||
final percentage = attempts[i].unknownPercentage;
|
||||
final normalizedValue = chartRange > 0
|
||||
? ((percentage - chartMin) / chartRange).clamp(0.0, 1.0)
|
||||
: 0.5; // If all values are the same, center vertically
|
||||
@@ -201,7 +203,7 @@ class _AttemptsChartPainter extends CustomPainter {
|
||||
}
|
||||
|
||||
// Draw average line (dashed)
|
||||
final avgPercentage = attempts.map((e) => e.percentageCorrect).reduce((a, b) => a + b) / attempts.length;
|
||||
final avgPercentage = attempts.map((e) => e.unknownPercentage).reduce((a, b) => a + b) / attempts.length;
|
||||
final avgNormalized = ((avgPercentage - chartMin) / chartRange).clamp(0.0, 1.0);
|
||||
final avgY = padding + chartHeight - (avgNormalized * chartHeight);
|
||||
|
||||
|
||||
+135
-2
@@ -57,6 +57,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -75,6 +91,11 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -139,6 +160,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
practice_engine:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -146,6 +207,62 @@ packages:
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -215,6 +332,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.8.0-0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.35.0"
|
||||
|
||||
Reference in New Issue
Block a user