Compare commits

...

2 Commits

Author SHA1 Message Date
Rene Kievits
a572a6e6fc added vocabulary audio reading 2025-10-28 06:00:15 +01:00
Rene Kievits
6dabb9c977 added sound 2025-10-28 05:44:14 +01:00
12 changed files with 237 additions and 63 deletions

BIN
assets/sfx/confirm.mp3 Normal file

Binary file not shown.

View File

@@ -96,19 +96,27 @@ class VocabSrsItem {
}) : lastAsked = lastAsked ?? DateTime.now(); }) : lastAsked = lastAsked ?? DateTime.now();
} }
class PronunciationAudio {
final String url;
final String gender;
PronunciationAudio({required this.url, required this.gender});
}
class VocabularyItem { class VocabularyItem {
final int id; final int id;
final String characters; final String characters;
final List<String> meanings; final List<String> meanings;
final List<String> readings; final List<String> readings;
final List<PronunciationAudio> pronunciationAudios;
final Map<String, VocabSrsItem> srsItems = {}; final Map<String, VocabSrsItem> srsItems = {};
VocabularyItem({ VocabularyItem(
required this.id, {required this.id,
required this.characters, required this.characters,
required this.meanings, required this.meanings,
required this.readings, required this.readings,
}); required this.pronunciationAudios});
factory VocabularyItem.fromSubject(Map<String, dynamic> subj) { factory VocabularyItem.fromSubject(Map<String, dynamic> subj) {
final int id = subj['id'] as int; final int id = subj['id'] as int;
@@ -116,6 +124,7 @@ class VocabularyItem {
final String characters = (data['characters'] ?? '') as String; final String characters = (data['characters'] ?? '') as String;
final List<String> meanings = <String>[]; final List<String> meanings = <String>[];
final List<String> readings = <String>[]; final List<String> readings = <String>[];
final List<PronunciationAudio> pronunciationAudios = <PronunciationAudio>[];
if (data['meanings'] != null) { if (data['meanings'] != null) {
for (final m in data['meanings'] as List) { for (final m in data['meanings'] as List) {
@@ -129,11 +138,26 @@ class VocabularyItem {
} }
} }
if (data['pronunciation_audios'] != null) {
for (final audio in data['pronunciation_audios'] as List) {
final url = audio['url'] as String?;
final metadata = audio['metadata'] as Map<String, dynamic>?;
final gender = metadata?['gender'] as String?;
if (url != null && gender != null) {
pronunciationAudios.add(PronunciationAudio(
url: url,
gender: gender,
));
}
}
}
return VocabularyItem( return VocabularyItem(
id: id, id: id,
characters: characters, characters: characters,
meanings: meanings, meanings: meanings,
readings: readings, readings: readings,
); pronunciationAudios: pronunciationAudios);
} }
} }

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(
@@ -346,4 +360,4 @@ class _HomeScreenState extends State<HomeScreen> {
backgroundColor: const Color(0xFF1E1E1E), backgroundColor: const Color(0xFF1E1E1E),
); );
} }
} }

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;
@@ -55,7 +57,8 @@ class _VocabScreenState extends State<VocabScreen> {
} }
var items = await repo.loadVocabulary(); var items = await repo.loadVocabulary();
if (items.isEmpty) { if (items.isEmpty ||
items.every((item) => item.pronunciationAudios.isEmpty)) {
setState(() { setState(() {
_status = 'Fetching deck...'; _status = 'Fetching deck...';
}); });
@@ -138,17 +141,18 @@ 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; 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;
@@ -179,7 +183,23 @@ class _VocabScreenState extends State<VocabScreen> {
ScaffoldMessenger.of(context).showSnackBar(snack); ScaffoldMessenger.of(context).showSnackBar(snack);
} }
Future.delayed(const Duration(milliseconds: 900), _nextQuestion); if (isCorrect) {
await _audioPlayer.play(AssetSource('sfx/confirm.mp3'));
final maleAudios =
current.pronunciationAudios.where((a) => a.gender == 'male');
if (maleAudios.isNotEmpty) {
try {
await _audioPlayer.play(UrlSource(maleAudios.first.url));
} catch (e) {
// Ignore player errors
}
}
await Future.delayed(const Duration(milliseconds: 400));
} else {
await Future.delayed(const Duration(milliseconds: 900));
}
_nextQuestion();
} }
@override @override

View File

@@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
@@ -23,7 +24,7 @@ class DeckRepository {
_db = await openDatabase( _db = await openDatabase(
path, path,
version: 5, version: 6,
onCreate: (db, version) async { onCreate: (db, version) async {
await db.execute( await db.execute(
'''CREATE TABLE kanji (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, onyomi TEXT, kunyomi TEXT)'''); '''CREATE TABLE kanji (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, onyomi TEXT, kunyomi TEXT)''');
@@ -32,7 +33,7 @@ class DeckRepository {
await db.execute( await db.execute(
'''CREATE TABLE srs_items (kanjiId INTEGER, quizMode TEXT, readingType TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (kanjiId, quizMode, readingType))'''); '''CREATE TABLE srs_items (kanjiId INTEGER, quizMode TEXT, readingType TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (kanjiId, quizMode, readingType))''');
await db.execute( await db.execute(
'''CREATE TABLE vocabulary (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, readings TEXT)'''); '''CREATE TABLE vocabulary (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, readings TEXT, pronunciation_audios TEXT)''');
await db.execute( await db.execute(
'''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))'''); '''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))''');
}, },
@@ -56,6 +57,13 @@ class DeckRepository {
await db.execute( await db.execute(
'''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))'''); '''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))''');
} }
if (oldVersion < 6) {
try {
await db.execute('ALTER TABLE vocabulary ADD COLUMN pronunciation_audios TEXT');
} catch (_) {
// Ignore error, column might already exist
}
}
}, },
); );
@@ -257,6 +265,9 @@ class DeckRepository {
final db = await _openDb(); final db = await _openDb();
final batch = db.batch(); final batch = db.batch();
for (final it in items) { for (final it in items) {
final audios = it.pronunciationAudios
.map((a) => {'url': a.url, 'gender': a.gender})
.toList();
batch.insert( batch.insert(
'vocabulary', 'vocabulary',
{ {
@@ -264,6 +275,7 @@ class DeckRepository {
'characters': it.characters, 'characters': it.characters,
'meanings': it.meanings.join('|'), 'meanings': it.meanings.join('|'),
'readings': it.readings.join('|'), 'readings': it.readings.join('|'),
'pronunciation_audios': jsonEncode(audios),
}, },
conflictAlgorithm: ConflictAlgorithm.replace, conflictAlgorithm: ConflictAlgorithm.replace,
); );
@@ -275,18 +287,36 @@ class DeckRepository {
final db = await _openDb(); final db = await _openDb();
final rows = await db.query('vocabulary'); final rows = await db.query('vocabulary');
final vocabItems = rows final vocabItems = rows
.map((r) => VocabularyItem( .map((r) {
id: r['id'] as int, final audiosRaw = r['pronunciation_audios'] as String?;
characters: r['characters'] as String, final List<PronunciationAudio> audios = [];
meanings: (r['meanings'] as String) if (audiosRaw != null && audiosRaw.isNotEmpty) {
.split('|') try {
.where((s) => s.isNotEmpty) final decoded = jsonDecode(audiosRaw) as List;
.toList(), for (final audioData in decoded) {
readings: (r['readings'] as String) audios.add(PronunciationAudio(
.split('|') url: audioData['url'] as String,
.where((s) => s.isNotEmpty) gender: audioData['gender'] as String,
.toList(), ));
)) }
} catch (e) {
// Error decoding, so we'll just have no audio for this item
}
}
return VocabularyItem(
id: r['id'] as int,
characters: r['characters'] as String,
meanings: (r['meanings'] as String)
.split('|')
.where((s) => s.isNotEmpty)
.toList(),
readings: (r['readings'] as String)
.split('|')
.where((s) => s.isNotEmpty)
.toList(),
pronunciationAudios: audios,
);
})
.toList(); .toList();
for (final item in vocabItems) { for (final item in vocabItems) {
@@ -336,4 +366,4 @@ class DeckRepository {
await saveVocabulary(items); await saveVocabulary(items);
return items; return items;
} }
} }

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