Compare commits
2 Commits
29909a07a7
...
a572a6e6fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a572a6e6fc | ||
|
|
6dabb9c977 |
BIN
assets/sfx/confirm.mp3
Normal file
BIN
assets/sfx/confirm.mp3
Normal file
Binary file not shown.
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
72
pubspec.lock
72
pubspec.lock
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user