Add kanji page
This commit is contained in:
parent
a8b0f3b737
commit
c1ccbc00b3
@ -43,8 +43,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.tangocard_reader"
|
||||
minSdkVersion 16
|
||||
applicationId "xyz.h7x4.tangocard_reader"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 30
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:tangocard_reader/models/yokutango_entry.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
|
||||
class Flashcard extends StatelessWidget {
|
||||
final YokutangoEntry? card;
|
||||
|
6
lib/models/data_entry.dart
Normal file
6
lib/models/data_entry.dart
Normal file
@ -0,0 +1,6 @@
|
||||
part 'yokutango_entry.dart';
|
||||
part 'kanji_entry.dart';
|
||||
|
||||
abstract class DataEntry {
|
||||
const DataEntry();
|
||||
}
|
53
lib/models/kanji_entry.dart
Normal file
53
lib/models/kanji_entry.dart
Normal file
@ -0,0 +1,53 @@
|
||||
part of 'data_entry.dart';
|
||||
|
||||
class KanjiEntry extends DataEntry {
|
||||
final String kanji;
|
||||
final List<WordConstruct> kana;
|
||||
|
||||
KanjiEntry.fromJson(Map<String, dynamic> json)
|
||||
: kanji = json['kanji'] as String,
|
||||
kana = [
|
||||
for (final j in json['kana'] as List) WordConstruct.fromJson(j)
|
||||
];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$kanji - ${kana.join('、')}';
|
||||
}
|
||||
}
|
||||
|
||||
class WordConstruct {
|
||||
final List<WordPiece> pieces;
|
||||
|
||||
WordConstruct({required this.pieces});
|
||||
|
||||
WordConstruct.fromJson(dynamic json)
|
||||
: pieces = (json is String)
|
||||
? [WordPiece(word: json, isActive: true)]
|
||||
: [for (final j in json as List) WordPiece.fromJson(j)];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return pieces.map((p) => p.isActive ? p.word : '(${p.word})').join('');
|
||||
}
|
||||
}
|
||||
|
||||
class WordPiece {
|
||||
final String? kana;
|
||||
final String? romaji;
|
||||
final String word;
|
||||
final bool isActive;
|
||||
|
||||
const WordPiece({
|
||||
required this.word,
|
||||
this.kana,
|
||||
this.romaji,
|
||||
this.isActive = false,
|
||||
});
|
||||
|
||||
WordPiece.fromJson(Map<String, dynamic> json)
|
||||
: kana = json['kana'] as String?,
|
||||
romaji = json['romaji'] as String?,
|
||||
word = json['text'] as String,
|
||||
isActive = json['active'] as bool? ?? false;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:tangocard_reader/models/yokutango_entry.dart';
|
||||
import 'data_entry.dart';
|
||||
|
||||
class BenkyouArgs {
|
||||
final List<YokutangoEntry> cards;
|
||||
final List<DataEntry> cards;
|
||||
final int? index;
|
||||
|
||||
const BenkyouArgs({required this.cards, this.index});
|
||||
|
@ -1,4 +1,6 @@
|
||||
class YokutangoEntry {
|
||||
part of 'data_entry.dart';
|
||||
|
||||
class YokutangoEntry extends DataEntry {
|
||||
final List<JapaneseWord> japanese;
|
||||
final List<NorwegianWord> norwegian;
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
import 'package:tangocard_reader/models/router_args.dart';
|
||||
import 'package:tangocard_reader/screens/flashcard.dart';
|
||||
import 'package:tangocard_reader/screens/practise/flashcard.dart';
|
||||
import 'package:tangocard_reader/screens/home.dart';
|
||||
import 'package:tangocard_reader/screens/tango_list.dart';
|
||||
import 'package:tangocard_reader/screens/pages/tango_list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'screens/pages/kanji_list.dart';
|
||||
|
||||
class PageRouter {
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
final args = settings.arguments;
|
||||
@ -14,22 +17,41 @@ class PageRouter {
|
||||
case '/':
|
||||
return MaterialPageRoute(builder: (_) => const Home());
|
||||
|
||||
case '/tangolist':
|
||||
case '/list/tango':
|
||||
final file = args as File;
|
||||
return MaterialPageRoute(builder: (_) => TangoList(file: file));
|
||||
|
||||
case '/benkyou':
|
||||
case '/list/kanji':
|
||||
final file = args as File;
|
||||
return MaterialPageRoute(builder: (_) => KanjiList(file: file));
|
||||
|
||||
case '/benkyou/tango':
|
||||
final benkyouArgs = args as BenkyouArgs;
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => FlashcardView(
|
||||
cards: benkyouArgs.cards,
|
||||
cards: benkyouArgs.cards as List<YokutangoEntry>,
|
||||
index: benkyouArgs.index,
|
||||
),
|
||||
);
|
||||
|
||||
case '/benkyou/kanji':
|
||||
// final benkyouArgs = args as BenkyouArgs;
|
||||
// return MaterialPageRoute(
|
||||
// builder: (_) => FlashcardView(
|
||||
// cards: benkyouArgs.cards as List<YokutangoEntry>,
|
||||
// index: benkyouArgs.index,
|
||||
// ),
|
||||
// );
|
||||
|
||||
default:
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const Text("ERROR: this route does not exist"));
|
||||
builder: (_) => Scaffold(
|
||||
appBar: AppBar(title: const Text('Error')),
|
||||
body: Center(
|
||||
child: ErrorWidget('No such route...')
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,81 +1,37 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:tangocard_reader/models/yokutango_entry.dart';
|
||||
import 'package:tangocard_reader/screens/error.dart';
|
||||
import 'package:tangocard_reader/screens/loading.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tangocard_reader/service/tangocard_files.dart';
|
||||
|
||||
class Home extends StatelessWidget {
|
||||
import 'pages/tango_set_list.dart';
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
const Home({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
State<Home> createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
int page = 0;
|
||||
|
||||
final _pages = [
|
||||
TangoSetList(files: tangocardFilePaths, route: '/list/tango',),
|
||||
TangoSetList(files: kanjicardFilePaths, route: '/list/kanji',),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("よく単語"),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: const TangocardList());
|
||||
}
|
||||
}
|
||||
|
||||
class TangocardList extends StatelessWidget {
|
||||
const TangocardList({Key? key}) : super(key: key);
|
||||
|
||||
Future<Map<File, List<YokutangoEntry>>> get tangocardFilePaths => rootBundle
|
||||
.loadString('AssetManifest.json')
|
||||
.then(
|
||||
(json) => jsonDecode(json)
|
||||
.keys
|
||||
.where((String key) =>
|
||||
key.contains('yokutango/json/') && key.contains('.json'))
|
||||
.toList(),
|
||||
)
|
||||
.then(
|
||||
(l) async => Map.fromIterables(
|
||||
l.map<File>((f) => File(f)),
|
||||
await Future.wait<List<YokutangoEntry>>(
|
||||
l
|
||||
.map<Future<List<YokutangoEntry>>>(
|
||||
(String t) => rootBundle
|
||||
.loadString(t)
|
||||
.then<List<YokutangoEntry>>((s) => jsonDecode(s)
|
||||
.map<YokutangoEntry>(
|
||||
(e) => YokutangoEntry.fromJson(e))
|
||||
.toList()),
|
||||
)
|
||||
),
|
||||
body: _pages[page],
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
onTap: (int index) => setState(() => page = index),
|
||||
currentIndex: page,
|
||||
items: const [
|
||||
BottomNavigationBarItem(label: 'Tango', icon: Icon(Icons.style)),
|
||||
BottomNavigationBarItem(label: 'Kanji', icon: Text('漢字')),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: tangocardFilePaths,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
debugPrint(snapshot.error.toString());
|
||||
return const ErrorScreen();
|
||||
} else if (!snapshot.hasData) {
|
||||
return const LoadingScreen();
|
||||
}
|
||||
|
||||
return ListView(
|
||||
children: (snapshot.data as Map<File, List<YokutangoEntry>>)
|
||||
.entries
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text(
|
||||
"${e.key.uri.pathSegments.last} - ${e.value.length} cards"),
|
||||
onTap: () => Navigator.pushNamed(context, '/tangolist',
|
||||
arguments: e.key),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,23 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:tangocard_reader/models/router_args.dart';
|
||||
import 'package:tangocard_reader/models/yokutango_entry.dart';
|
||||
import 'package:tangocard_reader/screens/error.dart';
|
||||
import 'package:tangocard_reader/screens/loading.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
import 'package:tangocard_reader/models/router_args.dart';
|
||||
import 'package:tangocard_reader/screens/misc/error.dart';
|
||||
import 'package:tangocard_reader/screens/misc/loading.dart';
|
||||
|
||||
class TangoList extends StatelessWidget {
|
||||
class KanjiList extends StatelessWidget {
|
||||
final File file;
|
||||
|
||||
const TangoList({
|
||||
required this.file,
|
||||
const KanjiList({
|
||||
Key? key,
|
||||
required this.file,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(file.uri.pathSegments.last),
|
||||
),
|
||||
@ -33,7 +32,7 @@ class TangoList extends StatelessWidget {
|
||||
}
|
||||
|
||||
final entries = (jsonSnapshot.data as List)
|
||||
.map((e) => YokutangoEntry.fromJson(e))
|
||||
.map((e) => KanjiEntry.fromJson(e))
|
||||
.toList();
|
||||
|
||||
return ListView(
|
||||
@ -44,7 +43,7 @@ class TangoList extends StatelessWidget {
|
||||
i,
|
||||
ListTile(
|
||||
title: Text(e.toString()),
|
||||
onTap: () => Navigator.pushNamed(context, '/benkyou',
|
||||
onTap: () => Navigator.pushNamed(context, '/benkyou/kanji',
|
||||
arguments: BenkyouArgs(cards: entries, index: i)),
|
||||
),
|
||||
),
|
||||
@ -53,7 +52,7 @@ class TangoList extends StatelessWidget {
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
61
lib/screens/pages/tango_list.dart
Normal file
61
lib/screens/pages/tango_list.dart
Normal file
@ -0,0 +1,61 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
import 'package:tangocard_reader/models/router_args.dart';
|
||||
import 'package:tangocard_reader/screens/misc/error.dart';
|
||||
import 'package:tangocard_reader/screens/misc/loading.dart';
|
||||
|
||||
class TangoList extends StatelessWidget {
|
||||
final File file;
|
||||
|
||||
const TangoList({
|
||||
required this.file,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(file.uri.pathSegments.last),
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future:
|
||||
rootBundle.loadString(file.path).then((data) => jsonDecode(data)),
|
||||
builder: (context, jsonSnapshot) {
|
||||
if (jsonSnapshot.hasError) {
|
||||
return const ErrorScreen();
|
||||
} else if (!jsonSnapshot.hasData) {
|
||||
return const LoadingScreen();
|
||||
}
|
||||
|
||||
final entries = (jsonSnapshot.data as List)
|
||||
.map((e) => YokutangoEntry.fromJson(e))
|
||||
.toList();
|
||||
|
||||
return ListView(
|
||||
children: entries
|
||||
.asMap()
|
||||
.map(
|
||||
(i, e) => MapEntry(
|
||||
i,
|
||||
ListTile(
|
||||
title: Text(e.toString()),
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/benkyou/tango',
|
||||
arguments: BenkyouArgs(cards: entries, index: i),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.values
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
46
lib/screens/pages/tango_set_list.dart
Normal file
46
lib/screens/pages/tango_set_list.dart
Normal file
@ -0,0 +1,46 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tangocard_reader/screens/misc/error.dart';
|
||||
import 'package:tangocard_reader/screens/misc/loading.dart';
|
||||
|
||||
class TangoSetList extends StatelessWidget {
|
||||
final String route;
|
||||
final Future<Map<File, List>> files;
|
||||
|
||||
const TangoSetList({
|
||||
Key? key,
|
||||
required this.route,
|
||||
required this.files,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => FutureBuilder(
|
||||
future: files,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
debugPrint(snapshot.error.toString());
|
||||
return const ErrorScreen();
|
||||
} else if (!snapshot.hasData) {
|
||||
return const LoadingScreen();
|
||||
}
|
||||
|
||||
return ListView(
|
||||
children: (snapshot.data as Map<File, List>)
|
||||
.entries
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text(
|
||||
"${e.key.uri.pathSegments.last} - ${e.value.length} cards"),
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
route,
|
||||
arguments: e.key,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:tangocard_reader/components/flashcard.dart';
|
||||
import 'package:tangocard_reader/models/yokutango_entry.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
|
||||
class FlashcardView extends StatefulWidget {
|
||||
final List<YokutangoEntry> cards;
|
50
lib/service/tangocard_files.dart
Normal file
50
lib/service/tangocard_files.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tangocard_reader/models/data_entry.dart';
|
||||
|
||||
// TODO: merge
|
||||
|
||||
Future<Map<File, List<YokutangoEntry>>> get tangocardFilePaths => rootBundle
|
||||
.loadString('AssetManifest.json')
|
||||
.then(
|
||||
(json) => jsonDecode(json)
|
||||
.keys
|
||||
.where((String key) =>
|
||||
key.contains('yokutango/json/') && key.contains('.json'))
|
||||
.toList(),
|
||||
)
|
||||
.then(
|
||||
(l) async => Map.fromIterables(
|
||||
l.map<File>((f) => File(f)),
|
||||
await Future.wait<List<YokutangoEntry>>(
|
||||
l.map<Future<List<YokutangoEntry>>>(
|
||||
(String t) => rootBundle.loadString(t).then<List<YokutangoEntry>>(
|
||||
(s) => jsonDecode(s)
|
||||
.map<YokutangoEntry>((e) => YokutangoEntry.fromJson(e))
|
||||
.toList()),
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
Future<Map<File, List<KanjiEntry>>> get kanjicardFilePaths => rootBundle
|
||||
.loadString('AssetManifest.json')
|
||||
.then(
|
||||
(json) => jsonDecode(json)
|
||||
.keys
|
||||
.where((String key) =>
|
||||
key.contains(RegExp(r'yokutango/kanji/kanji_\d+.json')))
|
||||
.toList(),
|
||||
)
|
||||
.then(
|
||||
(l) async => Map.fromIterables(
|
||||
l.map<File>((f) => File(f)),
|
||||
await Future.wait<List<KanjiEntry>>(l.map<Future<List<KanjiEntry>>>(
|
||||
(String t) => rootBundle.loadString(t).then<List<KanjiEntry>>((s) =>
|
||||
jsonDecode(s)
|
||||
.map<KanjiEntry>((e) => KanjiEntry.fromJson(e))
|
||||
.toList()),
|
||||
)),
|
||||
),
|
||||
);
|
@ -176,7 +176,7 @@ packages:
|
||||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.1"
|
||||
sdks:
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
dart: ">=2.14.0 <3.0.0"
|
||||
flutter: ">=1.16.0"
|
||||
|
Loading…
Reference in New Issue
Block a user