themes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hirameki_srs/src/themes.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/kanji_item.dart';
|
||||
@@ -18,7 +19,8 @@ class BrowseScreen extends StatefulWidget {
|
||||
State<BrowseScreen> createState() => _BrowseScreenState();
|
||||
}
|
||||
|
||||
class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderStateMixin {
|
||||
class _BrowseScreenState extends State<BrowseScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
late PageController _kanjiPageController;
|
||||
late PageController _vocabPageController;
|
||||
@@ -50,7 +52,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
_vocabPageController = PageController();
|
||||
|
||||
_tabController.addListener(() {
|
||||
setState(() {}); // Rebuild to update the level selector
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
_kanjiPageController.addListener(() {
|
||||
@@ -94,13 +96,17 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('WaniKani API key is not set.', style: TextStyle(color: Colors.white)),
|
||||
Text(
|
||||
'WaniKani API key is not set.',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => const SettingsScreen()),
|
||||
);
|
||||
if (!mounted) return;
|
||||
_loadDecks();
|
||||
},
|
||||
child: const Text('Go to Settings'),
|
||||
@@ -115,9 +121,9 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(color: Colors.blueAccent),
|
||||
CircularProgressIndicator(color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(height: 16),
|
||||
Text(_status, style: const TextStyle(color: Colors.white)),
|
||||
Text(_status, style: TextStyle(color: Theme.of(context).colorScheme.onSurface)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -128,22 +134,28 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
|
||||
Widget _buildCustomSrsTab() {
|
||||
if (_customDeck.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('No custom cards yet.', style: TextStyle(color: Colors.white)),
|
||||
return Center(
|
||||
child: Text(
|
||||
'No custom cards yet.',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
);
|
||||
}
|
||||
return _buildCustomGridView(_customDeck);
|
||||
}
|
||||
|
||||
Widget _buildPaginatedView(
|
||||
Map<int, List<dynamic>> groupedItems,
|
||||
List<int> sortedLevels,
|
||||
PageController pageController,
|
||||
Widget Function(List<dynamic>) buildPageContent) {
|
||||
Map<int, List<dynamic>> groupedItems,
|
||||
List<int> sortedLevels,
|
||||
PageController pageController,
|
||||
Widget Function(List<dynamic>) buildPageContent,
|
||||
) {
|
||||
if (sortedLevels.isEmpty) {
|
||||
return const Center(
|
||||
child:
|
||||
Text('No items to display.', style: TextStyle(color: Colors.white)),
|
||||
return Center(
|
||||
child: Text(
|
||||
'No items to display.',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,10 +172,11 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Level $level',
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: buildPageContent(levelItems)),
|
||||
@@ -183,7 +196,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
color: const Color(0xFF1F1F1F),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
height: 60,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@@ -196,11 +209,17 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
controller.animateToPage(index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
controller.animateToPage(
|
||||
index,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: isSelected ? Colors.blueAccent : const Color(0xFF333333),
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: isSelected
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
shape: const CircleBorder(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
),
|
||||
@@ -266,7 +285,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
return GestureDetector(
|
||||
onTap: () => _showVocabDetailsDialog(context, item),
|
||||
child: Card(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
@@ -275,7 +294,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.characters,
|
||||
style: const TextStyle(fontSize: 24, color: Colors.white),
|
||||
style: TextStyle(fontSize: 24, color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
@@ -286,7 +305,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
children: [
|
||||
Text(
|
||||
item.meanings.join(', '),
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -327,7 +346,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
|
||||
return Card(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
@@ -335,7 +354,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
children: [
|
||||
Text(
|
||||
item.characters,
|
||||
style: const TextStyle(fontSize: 32, color: Colors.white),
|
||||
style: TextStyle(fontSize: 32, color: Theme.of(context).colorScheme.onSurface),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -354,8 +373,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
child: SizedBox(
|
||||
height: 10,
|
||||
child: LinearProgressIndicator(
|
||||
value: level / 9.0, // Max SRS level is 9
|
||||
backgroundColor: Colors.grey[800],
|
||||
value: level / 9.0,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
_getColorForSrsLevel(level),
|
||||
),
|
||||
@@ -366,13 +385,17 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
|
||||
Color _getColorForSrsLevel(int level) {
|
||||
if (level >= 9) return Colors.purple;
|
||||
if (level >= 8) return Colors.blue;
|
||||
if (level >= 7) return Colors.lightBlue;
|
||||
if (level >= 5) return Colors.green;
|
||||
if (level >= 3) return Colors.yellow;
|
||||
if (level >= 1) return Colors.orange;
|
||||
return Colors.red;
|
||||
final srsColors = Theme.of(context).srsColors;
|
||||
if (level >= 9) return srsColors.level9;
|
||||
if (level >= 8) return srsColors.level8;
|
||||
if (level >= 7) return srsColors.level7;
|
||||
if (level >= 6) return srsColors.level6;
|
||||
if (level >= 5) return srsColors.level5;
|
||||
if (level >= 4) return srsColors.level4;
|
||||
if (level >= 3) return srsColors.level3;
|
||||
if (level >= 2) return srsColors.level2;
|
||||
if (level >= 1) return srsColors.level1;
|
||||
return Colors.grey;
|
||||
}
|
||||
|
||||
void _showReadingsDialog(KanjiItem kanji) {
|
||||
@@ -404,12 +427,12 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
backgroundColor: const Color(0xFF1E1E1E),
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
title: Text(
|
||||
'Details for ${kanji.characters}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
@@ -418,50 +441,56 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
children: [
|
||||
Text(
|
||||
'Level: ${kanji.level}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.meanings.isNotEmpty)
|
||||
Text(
|
||||
'Meanings: ${kanji.meanings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.onyomi.isNotEmpty)
|
||||
Text(
|
||||
'On\'yomi: ${kanji.onyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
if (kanji.kunyomi.isNotEmpty)
|
||||
Text(
|
||||
'Kun\'yomi: ${kanji.kunyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
if (kanji.onyomi.isEmpty && kanji.kunyomi.isEmpty)
|
||||
const Text(
|
||||
Text(
|
||||
'No readings available.',
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
Divider(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'SRS Scores:',
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontWeight: FontWeight.bold),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
...srsScores.entries.map(
|
||||
(entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
...srsScores.entries.map((entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Close',
|
||||
style: TextStyle(color: Colors.blueAccent)),
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: Text(
|
||||
'Close',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -473,8 +502,10 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
final kanjiRepo = Provider.of<DeckRepository>(context, listen: false);
|
||||
final vocabRepo =
|
||||
Provider.of<VocabDeckRepository>(context, listen: false);
|
||||
final vocabRepo = Provider.of<VocabDeckRepository>(
|
||||
context,
|
||||
listen: false,
|
||||
);
|
||||
await kanjiRepo.loadApiKey();
|
||||
final apiKey = kanjiRepo.apiKey;
|
||||
|
||||
@@ -537,9 +568,9 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar:
|
||||
_isSelectionMode ? _buildSelectionAppBar() : _buildDefaultAppBar(),
|
||||
backgroundColor: const Color(0xFF121212),
|
||||
appBar: _isSelectionMode
|
||||
? _buildSelectionAppBar()
|
||||
: _buildDefaultAppBar(),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -548,18 +579,19 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
children: [
|
||||
_buildWaniKaniTab(
|
||||
_buildPaginatedView(
|
||||
_kanjiByLevel,
|
||||
_kanjiSortedLevels,
|
||||
_kanjiPageController,
|
||||
(items) => _buildGridView(items.cast<KanjiItem>())),
|
||||
_kanjiByLevel,
|
||||
_kanjiSortedLevels,
|
||||
_kanjiPageController,
|
||||
(items) => _buildGridView(items.cast<KanjiItem>()),
|
||||
),
|
||||
),
|
||||
_buildWaniKaniTab(
|
||||
_buildPaginatedView(
|
||||
_vocabByLevel,
|
||||
_vocabSortedLevels,
|
||||
_vocabPageController,
|
||||
(items) =>
|
||||
_buildListView(items.cast<VocabularyItem>())),
|
||||
_vocabByLevel,
|
||||
_vocabSortedLevels,
|
||||
_vocabPageController,
|
||||
(items) => _buildListView(items.cast<VocabularyItem>()),
|
||||
),
|
||||
),
|
||||
_buildCustomSrsTab(),
|
||||
],
|
||||
@@ -577,9 +609,10 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
floatingActionButton: _tabController.index == 2
|
||||
? FloatingActionButton(
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => AddCardScreen()),
|
||||
);
|
||||
await Navigator.of(
|
||||
context,
|
||||
).push(MaterialPageRoute(builder: (_) => AddCardScreen()));
|
||||
if (!mounted) return;
|
||||
_loadCustomDeck();
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
@@ -615,14 +648,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
),
|
||||
title: Text('${_selectedItems.length} selected'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.select_all),
|
||||
onPressed: _selectAll,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: _deleteSelected,
|
||||
),
|
||||
IconButton(icon: const Icon(Icons.select_all), onPressed: _selectAll),
|
||||
IconButton(icon: const Icon(Icons.delete), onPressed: _deleteSelected),
|
||||
IconButton(
|
||||
icon: Icon(_toggleIntervalIcon),
|
||||
onPressed: _toggleIntervalForSelected,
|
||||
@@ -652,28 +679,30 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
void _deleteSelected() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: const Text('Delete Selected'),
|
||||
content:
|
||||
Text('Are you sure you want to delete ${_selectedItems.length} cards?'),
|
||||
content: Text(
|
||||
'Are you sure you want to delete ${_selectedItems.length} cards?',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
for (final item in _selectedItems) {
|
||||
await _customDeckRepository.deleteCard(item);
|
||||
}
|
||||
setState(() {
|
||||
_isSelectionMode = false;
|
||||
_selectedItems.clear();
|
||||
});
|
||||
await _loadCustomDeck();
|
||||
if (!mounted) return;
|
||||
navigator.pop();
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
() async {
|
||||
for (final item in _selectedItems) {
|
||||
await _customDeckRepository.deleteCard(item);
|
||||
}
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isSelectionMode = false;
|
||||
_selectedItems.clear();
|
||||
});
|
||||
_loadCustomDeck();
|
||||
}();
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
@@ -688,8 +717,9 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
final bool targetState = _selectedItems.any((item) => !item.useInterval);
|
||||
|
||||
final selectedCharacters =
|
||||
_selectedItems.map((item) => item.characters).toSet();
|
||||
final selectedCharacters = _selectedItems
|
||||
.map((item) => item.characters)
|
||||
.toSet();
|
||||
|
||||
final List<CustomKanjiItem> updatedItems = [];
|
||||
for (final item in _selectedItems) {
|
||||
@@ -765,13 +795,13 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: isSelected
|
||||
? const BorderSide(color: Colors.blue, width: 2.0)
|
||||
? BorderSide(color: Theme.of(context).colorScheme.primary, width: 2.0)
|
||||
: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
color: isSelected
|
||||
? Colors.blue.withAlpha((255 * 0.5).round())
|
||||
: const Color(0xFF1E1E1E),
|
||||
? Theme.of(context).colorScheme.primary.withAlpha((255 * 0.5).round())
|
||||
: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
@@ -785,27 +815,34 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
item.kanji?.isNotEmpty == true
|
||||
? item.kanji!
|
||||
: item.characters,
|
||||
style:
|
||||
const TextStyle(fontSize: 32, color: Colors.white),
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
item.meaning,
|
||||
style:
|
||||
const TextStyle(color: Colors.grey, fontSize: 16),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Builder(builder: (context) {
|
||||
final avgSrs = (item.srsData.japaneseToEnglish +
|
||||
item.srsData.englishToJapanese +
|
||||
item.srsData.listeningComprehension) /
|
||||
3;
|
||||
return _buildSrsIndicator(avgSrs.round());
|
||||
}),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final avgSrs =
|
||||
(item.srsData.japaneseToEnglish +
|
||||
item.srsData.englishToJapanese +
|
||||
item.srsData.listeningComprehension) /
|
||||
3;
|
||||
return _buildSrsIndicator(avgSrs.round());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -813,11 +850,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
child: Icon(
|
||||
Icons.timer,
|
||||
color: Colors.green,
|
||||
size: 16,
|
||||
),
|
||||
child: Icon(Icons.timer, color: Theme.of(context).colorScheme.tertiary, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -848,9 +881,11 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
}
|
||||
|
||||
Future<void> _fetchExampleSentences() async {
|
||||
final theme = Theme.of(context);
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
'https://jisho.org/api/v1/search/words?keyword=${Uri.encodeComponent(widget.vocab.characters)}');
|
||||
'https://jisho.org/api/v1/search/words?keyword=${Uri.encodeComponent(widget.vocab.characters)}',
|
||||
);
|
||||
final response = await http.get(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
@@ -861,15 +896,24 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
(result['japanese'] as List).isNotEmpty &&
|
||||
result['senses'] != null &&
|
||||
(result['senses'] as List).isNotEmpty) {
|
||||
final japaneseWord = result['japanese'][0]['word'] ?? result['japanese'][0]['reading'];
|
||||
final englishDefinition = result['senses'][0]['english_definitions'].join(', ');
|
||||
final japaneseWord =
|
||||
result['japanese'][0]['word'] ??
|
||||
result['japanese'][0]['reading'];
|
||||
final englishDefinition =
|
||||
result['senses'][0]['english_definitions'].join(', ');
|
||||
if (japaneseWord != null && englishDefinition != null) {
|
||||
sentences.add(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(japaneseWord, style: const TextStyle(color: Colors.white)),
|
||||
Text(englishDefinition, style: const TextStyle(color: Colors.grey)),
|
||||
Text(
|
||||
japaneseWord,
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
Text(
|
||||
englishDefinition,
|
||||
style: TextStyle(color: theme.colorScheme.onSurfaceVariant),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
@@ -879,7 +923,12 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
}
|
||||
}
|
||||
if (sentences.isEmpty) {
|
||||
sentences.add(const Text('No example sentences found.', style: TextStyle(color: Colors.white)));
|
||||
sentences.add(
|
||||
Text(
|
||||
'No example sentences found.',
|
||||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
@@ -890,7 +939,10 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_exampleSentences = [
|
||||
const Text('Failed to load example sentences.', style: TextStyle(color: Colors.red))
|
||||
Text(
|
||||
'Failed to load example sentences.',
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -899,7 +951,10 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_exampleSentences = [
|
||||
const Text('Error loading example sentences.', style: TextStyle(color: Colors.red))
|
||||
Text(
|
||||
'Error loading example sentences.',
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -908,11 +963,7 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final srsScores = <String, int>{
|
||||
'JP -> EN': 0,
|
||||
'EN -> JP': 0,
|
||||
'Audio': 0,
|
||||
};
|
||||
final srsScores = <String, int>{'JP -> EN': 0, 'EN -> JP': 0, 'Audio': 0};
|
||||
|
||||
for (final entry in widget.vocab.srsItems.entries) {
|
||||
final srsItem = entry.value;
|
||||
@@ -936,37 +987,39 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
children: [
|
||||
Text(
|
||||
'Level: ${widget.vocab.level}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (widget.vocab.meanings.isNotEmpty)
|
||||
Text(
|
||||
'Meanings: ${widget.vocab.meanings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (widget.vocab.readings.isNotEmpty)
|
||||
Text(
|
||||
'Readings: ${widget.vocab.readings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
Divider(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'SRS Scores:',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold),
|
||||
),
|
||||
...srsScores.entries.map(
|
||||
(entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
...srsScores.entries.map((entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
Divider(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Example Sentences:',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold),
|
||||
),
|
||||
..._exampleSentences,
|
||||
],
|
||||
@@ -978,21 +1031,24 @@ class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
void _showVocabDetailsDialog(BuildContext context, VocabularyItem vocab) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
backgroundColor: const Color(0xFF1E1E1E),
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
title: Text(
|
||||
'Details for ${vocab.characters}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
content: _VocabDetailsDialog(vocab: vocab),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Close', style: TextStyle(color: Colors.blueAccent)),
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: Text(
|
||||
'Close',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user