diff --git a/lib/components/flashcard.dart b/lib/components/flashcard.dart index 594b8da..b1e5405 100644 --- a/lib/components/flashcard.dart +++ b/lib/components/flashcard.dart @@ -104,7 +104,10 @@ class _FlashCardPaper extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text((cardIndex != null) ? (cardIndex! + 1).toString() : "?") + Text( + (cardIndex != null) ? (cardIndex! + 1).toString() : "?", + style: const TextStyle(color: Colors.black), + ) ], ), ), @@ -140,13 +143,19 @@ class _NorwegianContent extends StatelessWidget { child: FittedBox( fit: BoxFit.fitHeight, child: DefaultTextStyle.merge( - style: const TextStyle(fontFamily: 'Heart Warming'), + style: const TextStyle( + fontFamily: 'Heart Warming', + color: Colors.black, + ), child: Column( children: card.norwegian.map((n) { final text = (n.hints == null) ? n.word : "${n.word} (${n.hints?.join(', ')})"; - return Text(text); + return Text( + text, + style: const TextStyle(color: Colors.black), + ); }).toList(), ), ), @@ -170,7 +179,10 @@ class _JapaneseContent extends StatelessWidget { child: FittedBox( fit: BoxFit.fitHeight, child: DefaultTextStyle.merge( - style: const TextStyle(fontFamily: 'K Gothic'), + style: const TextStyle( + fontFamily: 'K Gothic', + color: Colors.black, + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: card.japanese diff --git a/lib/screens/practise/navigation_buttons.dart b/lib/components/navigation_buttons.dart similarity index 71% rename from lib/screens/practise/navigation_buttons.dart rename to lib/components/navigation_buttons.dart index df27dfa..0b2ffd5 100644 --- a/lib/screens/practise/navigation_buttons.dart +++ b/lib/components/navigation_buttons.dart @@ -4,12 +4,14 @@ class NavigationButtons extends StatelessWidget { final String middleText; final void Function() onNextCard; final void Function() onPreviousCard; + final void Function()? onMiddlePressed; const NavigationButtons({ Key? key, required this.middleText, required this.onNextCard, required this.onPreviousCard, + this.onMiddlePressed, }) : super(key: key); @override @@ -29,12 +31,18 @@ class NavigationButtons extends StatelessWidget { icon: const Icon(Icons.arrow_back), ), const SizedBox(width: 10), - Text( - middleText, - style: Theme.of(context) - .textTheme - .headline6! - .merge(const TextStyle(color: Colors.white)), + InkWell( + onTap: onMiddlePressed, + child: Padding( + padding: const EdgeInsets.all(10), + child: Text( + middleText, + style: Theme.of(context) + .textTheme + .headline6! + .merge(const TextStyle(color: Colors.white)), + ), + ), ), const SizedBox(width: 10), IconButton( diff --git a/lib/main.dart b/lib/main.dart index 08659b2..a1ac00a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,21 +1,41 @@ +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tangocard_reader/router.dart'; import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +import 'service/theme_bloc.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + final prefs = await SharedPreferences.getInstance(); + runApp(MyApp(prefs: prefs)); +} class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + final SharedPreferences prefs; + + const MyApp({ + Key? key, + required this.prefs, + }) : super(key: key); @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Tangocard Reader', - theme: ThemeData( - fontFamily: 'Noto Sans CJK', - primarySwatch: Colors.blue, - ), - initialRoute: '/', - onGenerateRoute: PageRouter.generateRoute, - ); - } + Widget build(BuildContext context) => BlocProvider( + create: (context) => ThemeBloc( + prefs: prefs, + init: (prefs.getBool('darkTheme') ?? false) + ? Brightness.dark + : Brightness.light), + child: BlocBuilder( + builder: (context, state) => MaterialApp( + title: 'Tangocard Reader', + theme: ThemeData( + fontFamily: 'Noto Sans CJK', + primarySwatch: Colors.blue, + brightness: state, + ), + initialRoute: '/', + onGenerateRoute: PageRouter.generateRoute, + ), + ), + ); } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 44d1e8e..22c7198 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:tangocard_reader/service/tangocard_files.dart'; +import 'package:tangocard_reader/service/theme_bloc.dart'; import 'pages/tango_set_list.dart'; @@ -14,14 +15,31 @@ class _HomeState extends State { int page = 0; final _pages = [ - TangoSetList(files: tangocardFilePaths, route: '/list/tango',), - TangoSetList(files: kanjicardFilePaths, route: '/list/kanji',), + TangoSetList( + files: tangocardFilePaths, + route: '/list/tango', + ), + TangoSetList( + files: kanjicardFilePaths, + route: '/list/kanji', + ), ]; @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( title: const Text("よく単語"), + actions: [ + BlocBuilder( + builder: (context, state) { + return Switch( + value: state == Brightness.dark, + onChanged: (b) => BlocProvider.of(context) + .add(SetTheme(state != Brightness.dark)), + ); + }, + ) + ], centerTitle: true, ), body: _pages[page], @@ -29,8 +47,8 @@ class _HomeState extends State { onTap: (int index) => setState(() => page = index), currentIndex: page, items: const [ - BottomNavigationBarItem(label: 'Tango', icon: Icon(Icons.style)), - BottomNavigationBarItem(label: 'Kanji', icon: Text('漢字')), + BottomNavigationBarItem(label: '単語', icon: Icon(Icons.style)), + BottomNavigationBarItem(label: '漢字', icon: Icon(Icons.translate)), ], ), ); diff --git a/lib/screens/practise/flashcard.dart b/lib/screens/practise/flashcard.dart index 2636d33..e6322a7 100644 --- a/lib/screens/practise/flashcard.dart +++ b/lib/screens/practise/flashcard.dart @@ -1,7 +1,7 @@ import 'package:tangocard_reader/components/flashcard.dart'; import 'package:flutter/material.dart'; import 'package:tangocard_reader/models/data_entry.dart'; -import 'package:tangocard_reader/screens/practise/navigation_buttons.dart'; +import 'package:tangocard_reader/components/navigation_buttons.dart'; class FlashcardPage extends StatefulWidget { final YokutangoEntry card; diff --git a/lib/screens/practise/kanji.dart b/lib/screens/practise/kanji.dart index 632f5f5..c981940 100644 --- a/lib/screens/practise/kanji.dart +++ b/lib/screens/practise/kanji.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:signature/signature.dart'; import '../../models/data_entry.dart'; -import 'navigation_buttons.dart'; +import '../../components/navigation_buttons.dart'; class KanjiPage extends StatefulWidget { final KanjiEntry entry; @@ -27,19 +28,33 @@ class KanjiPage extends StatefulWidget { class _KanjiPageState extends State { bool isPressed = false; + late final controller = SignatureController( + penColor: Theme.of(context).textTheme.bodyText1?.color ?? Colors.black, + ); @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { - if (isPressed) widget.onNextCard(); + if (widget.showDrawingPanel) return; + if (isPressed) { + controller.clear(); + widget.onNextCard(); + } setState(() => isPressed = !isPressed); }, child: Stack( alignment: Alignment.center, fit: StackFit.expand, children: [ + if (widget.showDrawingPanel) ...[ + const SizedBox(width: 20), + Signature( + controller: controller, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + ) + ], Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -49,13 +64,18 @@ class _KanjiPageState extends State { ), const SizedBox(width: 20), const Divider(thickness: 5), - if (isPressed) ...[ - const SizedBox(width: 20), - Text( - widget.entry.kanji, - style: Theme.of(context).textTheme.headline1, - ), - ], + const SizedBox(width: 20), + Text( + widget.entry.kanji, + style: isPressed + ? Theme.of(context).textTheme.headline1 + : Theme.of(context) + .textTheme + .headline1 + ?.merge(const TextStyle(color: Colors.transparent)), + ), + const SizedBox(width: 20), + const Divider(thickness: 5), ], ), Positioned( @@ -63,11 +83,20 @@ class _KanjiPageState extends State { child: NavigationButtons( middleText: widget.index == null ? 'N' : (widget.index! + 1).toString(), + onMiddlePressed: () { + if (isPressed) { + controller.clear(); + widget.onNextCard(); + } + setState(() => isPressed = !isPressed); + }, onNextCard: () => setState(() { + controller.clear(); isPressed = false; widget.onNextCard(); }), onPreviousCard: () => setState(() { + controller.clear(); isPressed = false; widget.onPreviousCard(); }), diff --git a/lib/screens/practise/practise.dart b/lib/screens/practise/practise.dart index 5802f77..bc6f600 100644 --- a/lib/screens/practise/practise.dart +++ b/lib/screens/practise/practise.dart @@ -121,9 +121,9 @@ class _PractiseViewState extends State { Icon(Icons.edit), Icon(Icons.animation), ], - isSelected: _flashcardToggles, + isSelected: _kanjiToggles, onPressed: (int index) => - setState(() => _flashcardToggles[index] = !_flashcardToggles[index])), + setState(() => _kanjiToggles[index] = !_kanjiToggles[index])), ], ), centerTitle: true, diff --git a/lib/service/theme_bloc.dart b/lib/service/theme_bloc.dart new file mode 100644 index 0000000..8104f20 --- /dev/null +++ b/lib/service/theme_bloc.dart @@ -0,0 +1,26 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +export 'package:flutter_bloc/flutter_bloc.dart'; + +@immutable +class SetTheme { + final bool isDark; + const SetTheme(this.isDark); +} + +class ThemeBloc extends Bloc { + final SharedPreferences prefs; + + ThemeBloc({ + required this.prefs, + required Brightness init, + }) : super(init) { + on((event, emit) { + prefs.setBool('darkTheme', event.isDark); + emit(event.isDark ? Brightness.dark : Brightness.light); + }); + } +} diff --git a/pubspec.lock b/pubspec.lock index 1a68bf0..d34cd9a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.11" async: dependency: transitive description: @@ -8,6 +15,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" + bloc: + dependency: transitive + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.2" boolean_selector: dependency: transitive description: @@ -43,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" fake_async: dependency: transitive description: @@ -50,11 +71,32 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.1" flutter_lints: dependency: "direct dev" description: @@ -67,6 +109,25 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" lints: dependency: transitive description: @@ -88,6 +149,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -95,6 +163,125 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + signature: + dependency: "direct main" + description: + name: signature + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" sky_engine: dependency: transitive description: flutter @@ -156,5 +343,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.10" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" sdks: - dart: ">=2.14.0 <3.0.0" + dart: ">=2.15.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2f7a584..8f2557e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,9 @@ environment: dependencies: flutter: sdk: flutter + flutter_bloc: ^8.0.1 + shared_preferences: ^2.0.12 + signature: ^5.0.0 dev_dependencies: flutter_test: