added sound

This commit is contained in:
Rene Kievits
2025-10-28 05:44:14 +01:00
parent 29909a07a7
commit 6dabb9c977
10 changed files with 139 additions and 35 deletions

BIN
assets/sfx/confirm.mp3 Normal file

Binary file not shown.

View File

@@ -6,6 +6,8 @@ import '../services/deck_repository.dart';
import '../services/distractor_generator.dart'; import '../services/distractor_generator.dart';
import '../widgets/kanji_card.dart'; import '../widgets/kanji_card.dart';
import '../widgets/options_grid.dart'; import '../widgets/options_grid.dart';
import 'package:audioplayers/audioplayers.dart';
import 'settings_screen.dart'; import 'settings_screen.dart';
class _ReadingInfo { class _ReadingInfo {
@@ -28,6 +30,7 @@ class _HomeScreenState extends State<HomeScreen> {
String _status = 'Loading deck...'; String _status = 'Loading deck...';
final DistractorGenerator _dg = DistractorGenerator(); final DistractorGenerator _dg = DistractorGenerator();
final Random _random = Random(); final Random _random = Random();
final _audioPlayer = AudioPlayer();
QuizMode _mode = QuizMode.kanjiToEnglish; QuizMode _mode = QuizMode.kanjiToEnglish;
KanjiItem? _current; KanjiItem? _current;
@@ -109,14 +112,20 @@ class _HomeScreenState extends State<HomeScreen> {
void _nextQuestion() { void _nextQuestion() {
_deck.sort((a, b) { _deck.sort((a, b) {
final aSrsItem = a.srsItems[_mode.toString()] ?? SrsItem(kanjiId: a.id, quizMode: _mode); final aSrsItem = a.srsItems[_mode.toString()];
final bSrsItem = b.srsItems[_mode.toString()] ?? SrsItem(kanjiId: b.id, quizMode: _mode); final bSrsItem = b.srsItems[_mode.toString()];
final stageComparison = aSrsItem.srsStage.compareTo(bSrsItem.srsStage); final aStage = aSrsItem?.srsStage ?? 0;
if (stageComparison != 0) { final bStage = bSrsItem?.srsStage ?? 0;
return stageComparison;
if (aStage != bStage) {
return aStage.compareTo(bStage);
} }
return aSrsItem.lastAsked.compareTo(bSrsItem.lastAsked);
final aLastAsked = aSrsItem?.lastAsked ?? DateTime.fromMillisecondsSinceEpoch(0);
final bLastAsked = bSrsItem?.lastAsked ?? DateTime.fromMillisecondsSinceEpoch(0);
return aLastAsked.compareTo(bLastAsked);
}); });
_current = _deck.first; _current = _deck.first;
@@ -128,16 +137,19 @@ class _HomeScreenState extends State<HomeScreen> {
switch (_mode) { switch (_mode) {
case QuizMode.kanjiToEnglish: case QuizMode.kanjiToEnglish:
_correctAnswers = [_current!.meanings.first]; _correctAnswers = [_current!.meanings.first];
_options = [_correctAnswers.first, ..._dg.generateMeanings(_current!, _deck, 3)] _options = [
.map(_toTitleCase) _correctAnswers.first,
.toList() ..._dg.generateMeanings(_current!, _deck, 3)
].map(_toTitleCase).toList()
..shuffle(); ..shuffle();
break; break;
case QuizMode.englishToKanji: case QuizMode.englishToKanji:
_correctAnswers = [_current!.characters]; _correctAnswers = [_current!.characters];
_options = [_correctAnswers.first, ..._dg.generateKanji(_current!, _deck, 3)] _options = [
..shuffle(); _correctAnswers.first,
..._dg.generateKanji(_current!, _deck, 3)
]..shuffle();
break; break;
case QuizMode.reading: case QuizMode.reading:
@@ -149,10 +161,15 @@ class _HomeScreenState extends State<HomeScreen> {
? _deck.expand((k) => k.onyomi) ? _deck.expand((k) => k.onyomi)
: _deck.expand((k) => k.kunyomi); : _deck.expand((k) => k.kunyomi);
final distractors = final distractors = readingsSource
readingsSource.where((r) => !_correctAnswers.contains(r)).toSet().toList() .where((r) => !_correctAnswers.contains(r))
.toSet()
.toList()
..shuffle(); ..shuffle();
_options = ([_correctAnswers[_random.nextInt(_correctAnswers.length)], ...distractors.take(3)]) _options = ([
_correctAnswers[_random.nextInt(_correctAnswers.length)],
...distractors.take(3)
])
..shuffle(); ..shuffle();
break; break;
} }
@@ -176,29 +193,31 @@ class _HomeScreenState extends State<HomeScreen> {
var srsItem = current.srsItems[srsKey]; var srsItem = current.srsItems[srsKey];
final isNew = srsItem == null; final isNew = srsItem == null;
srsItem ??= SrsItem(kanjiId: current.id, quizMode: _mode, readingType: readingType); final srsItemForUpdate = srsItem ??=
SrsItem(kanjiId: current.id, quizMode: _mode, readingType: readingType);
setState(() { setState(() {
_asked += 1; _asked += 1;
if (isCorrect) { if (isCorrect) {
_score += 1; _score += 1;
srsItem!.srsStage += 1; _audioPlayer.play(AssetSource('sfx/confirm.mp3'));
} else { } else {
srsItem!.srsStage = max(0, srsItem.srsStage - 1); srsItemForUpdate.srsStage = max(0, srsItemForUpdate.srsStage - 1);
} }
srsItem.lastAsked = DateTime.now(); srsItemForUpdate.lastAsked = DateTime.now();
current.srsItems[srsKey] = srsItem; current.srsItems[srsKey] = srsItemForUpdate;
}); });
if (isNew) { if (isNew) {
await repo.insertSrsItem(srsItem); await repo.insertSrsItem(srsItemForUpdate);
} else { } else {
await repo.updateSrsItem(srsItem); await repo.updateSrsItem(srsItemForUpdate);
} }
final correctDisplay = (_mode == QuizMode.kanjiToEnglish) final correctDisplay = (_mode == QuizMode.kanjiToEnglish)
? _toTitleCase(_correctAnswers.first) ? _toTitleCase(_correctAnswers.first)
: (_mode == QuizMode.reading ? _correctAnswers.join(', ') : _correctAnswers.first); : (_mode == QuizMode.reading
? _correctAnswers.join(', ')
: _correctAnswers.first);
final snack = SnackBar( final snack = SnackBar(
content: Text( content: Text(
@@ -271,7 +290,6 @@ class _HomeScreenState extends State<HomeScreen> {
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Wrap( Wrap(
spacing: 6, spacing: 6,
runSpacing: 4, runSpacing: 4,
@@ -282,9 +300,7 @@ class _HomeScreenState extends State<HomeScreen> {
_buildChoiceChip('Reading', QuizMode.reading), _buildChoiceChip('Reading', QuizMode.reading),
], ],
), ),
const SizedBox(height: 18), const SizedBox(height: 18),
Expanded( Expanded(
flex: 3, flex: 3,
child: Center( child: Center(
@@ -303,9 +319,7 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
SafeArea( SafeArea(
top: false, top: false,
child: Column( child: Column(

View File

@@ -6,11 +6,13 @@ import '../services/deck_repository.dart';
import '../services/distractor_generator.dart'; import '../services/distractor_generator.dart';
import '../widgets/kanji_card.dart'; import '../widgets/kanji_card.dart';
import '../widgets/options_grid.dart'; import '../widgets/options_grid.dart';
import 'package:audioplayers/audioplayers.dart';
import 'settings_screen.dart'; import 'settings_screen.dart';
class VocabScreen extends StatefulWidget { class VocabScreen extends StatefulWidget {
const VocabScreen({super.key}); const VocabScreen({super.key});
@override
State<VocabScreen> createState() => _VocabScreenState(); State<VocabScreen> createState() => _VocabScreenState();
} }
@@ -19,7 +21,7 @@ class _VocabScreenState extends State<VocabScreen> {
bool _loading = false; bool _loading = false;
String _status = 'Loading deck...'; String _status = 'Loading deck...';
final DistractorGenerator _dg = DistractorGenerator(); final DistractorGenerator _dg = DistractorGenerator();
final Random _random = Random(); final _audioPlayer = AudioPlayer();
VocabQuizMode _mode = VocabQuizMode.vocabToEnglish; VocabQuizMode _mode = VocabQuizMode.vocabToEnglish;
VocabularyItem? _current; VocabularyItem? _current;
@@ -138,17 +140,19 @@ class _VocabScreenState extends State<VocabScreen> {
final srsKey = _mode.toString(); final srsKey = _mode.toString();
var srsItem = current.srsItems[srsKey]; var srsItemNullable = current.srsItems[srsKey];
final isNew = srsItem == null; final isNew = srsItemNullable == null;
srsItem ??= VocabSrsItem(vocabId: current.id, quizMode: _mode); final srsItem =
srsItemNullable ?? VocabSrsItem(vocabId: current.id, quizMode: _mode);
setState(() { setState(() {
_asked += 1; _asked += 1;
if (isCorrect) { if (isCorrect) {
_score += 1; _score += 1;
srsItem!.srsStage += 1; _audioPlayer.play(AssetSource('sfx/confirm.mp3'));
srsItem.srsStage += 1;
} else { } else {
srsItem!.srsStage = max(0, srsItem.srsStage - 1); srsItem.srsStage = max(0, srsItem.srsStage - 1);
} }
srsItem.lastAsked = DateTime.now(); srsItem.lastAsked = DateTime.now();
current.srsItems[srsKey] = srsItem; current.srsItems[srsKey] = srsItem;

View File

@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
} }

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@@ -5,11 +5,13 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import audioplayers_darwin
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))

View File

@@ -41,6 +41,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.0" version: "2.13.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
sha256: "5441fa0ceb8807a5ad701199806510e56afde2b4913d9d17c2f19f2902cf0ae4"
url: "https://pub.dev"
source: hosted
version: "6.5.1"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
sha256: "60a6728277228413a85755bd3ffd6fab98f6555608923813ce383b190a360605"
url: "https://pub.dev"
source: hosted
version: "5.2.1"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
sha256: "0811d6924904ca13f9ef90d19081e4a87f7297ddc19fc3d31f60af1aaafee333"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
sha256: f75bce1ce864170ef5e6a2c6a61cd3339e1a17ce11e99a25bae4474ea491d001
url: "https://pub.dev"
source: hosted
version: "4.2.1"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
sha256: "0e2f6a919ab56d0fec272e801abc07b26ae7f31980f912f24af4748763e5a656"
url: "https://pub.dev"
source: hosted
version: "7.1.1"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
sha256: "1c0f17cec68455556775f1e50ca85c40c05c714a99c5eb1d2d57cc17ba5522d7"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
sha256: "4048797865105b26d47628e6abb49231ea5de84884160229251f37dfcbe52fd7"
url: "https://pub.dev"
source: hosted
version: "4.2.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@@ -693,6 +749,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.1" version: "1.10.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite: sqflite:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -821,6 +885,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:

View File

@@ -13,6 +13,7 @@ dependencies:
path: ^1.9.1 path: ^1.9.1
provider: ^6.1.5+1 provider: ^6.1.5+1
http: ^1.5.0 http: ^1.5.0
audioplayers: ^6.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -28,3 +29,5 @@ flutter_icons:
flutter: flutter:
uses-material-design: true uses-material-design: true
assets:
- assets/sfx/confirm.mp3

View File

@@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
} }

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST