1
0
mirror of https://github.com/h7x4/Jisho-Study-Tool.git synced 2024-12-22 05:57:28 +01:00

Update code style

This commit is contained in:
Oystein Kristoffer Tveit 2021-12-01 23:09:53 +01:00
parent 9d8c7b8ebb
commit 8811cef864
58 changed files with 955 additions and 700 deletions

169
analysis_options.yaml Normal file
View File

@ -0,0 +1,169 @@
analyzer:
exclude:
- "lib/objectbox.g.dart"
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
# - always_put_control_body_on_new_line # Allow return statements on same line as ifs
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
# - always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_classes_with_only_static_members
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
- avoid_js_rounded_ints
- avoid_multiple_declarations_per_line
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_print
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null
- avoid_returning_null_for_future
- avoid_returning_null_for_void
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
- avoid_types_on_closure_parameters
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
- cast_nullable_to_non_nullable
- control_flow_in_finally
# - curly_braces_in_flow_control_structures
- depend_on_referenced_packages
- deprecated_consistency
- directives_ordering
- do_not_use_environment
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
# - flutter_style_todos # This is mostly a single person project for the time being
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- library_private_types_in_public_api
# - lines_longer_than_80_chars
- list_remove_unrelated_type
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_default_cases
- no_duplicate_case_values
- no_logic_in_create_state
# - non_constant_identifier_names # use API names for several variables
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
- only_throw_errors
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
# - prefer_final_parameters
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
# - prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_null_aware_operators
- prefer_relative_imports
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- recursive_getters
- require_trailing_commas
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
# - sort_constructors_first
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_constructor_name
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_test_throws_matchers
- valid_regexps
- void_checks

View File

@ -1,5 +1,5 @@
import 'package:bloc/bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import './database_event.dart'; import './database_event.dart';
import './database_state.dart'; import './database_state.dart';
@ -7,12 +7,12 @@ import './database_state.dart';
export 'package:flutter_bloc/flutter_bloc.dart'; export 'package:flutter_bloc/flutter_bloc.dart';
export './database_event.dart'; export './database_event.dart';
export './database_state.dart';
export './database_not_connected_exception.dart'; export './database_not_connected_exception.dart';
export './database_state.dart';
class DatabaseBloc extends Bloc<DatabaseEvent, DatabaseState> { class DatabaseBloc extends Bloc<DatabaseEvent, DatabaseState> {
DatabaseBloc() : super(DatabaseDisconnected()); DatabaseBloc() : super(const DatabaseDisconnected());
@override @override
Stream<DatabaseState> mapEventToState(DatabaseEvent event) Stream<DatabaseState> mapEventToState(DatabaseEvent event)
@ -20,7 +20,7 @@ class DatabaseBloc extends Bloc<DatabaseEvent, DatabaseState> {
if (event is ConnectedToDatabase) { if (event is ConnectedToDatabase) {
yield DatabaseConnected(event.database); yield DatabaseConnected(event.database);
} else { } else {
yield DatabaseDisconnected(); yield const DatabaseDisconnected();
} }
} }

View File

@ -1,4 +1,3 @@
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
abstract class DatabaseState { abstract class DatabaseState {

View File

@ -1,10 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/models/themes/theme.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../models/themes/theme.dart';
export 'package:flutter_bloc/flutter_bloc.dart'; export 'package:flutter_bloc/flutter_bloc.dart';
export 'package:jisho_study_tool/models/themes/theme.dart'; export 'package:jisho_study_tool/models/themes/theme.dart';
@ -14,10 +15,10 @@ part 'theme_state.dart';
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> { class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
bool prefsAreLoaded = false; bool prefsAreLoaded = false;
ThemeBloc() : super(LightThemeState()) { ThemeBloc() : super(const LightThemeState()) {
SharedPreferences.getInstance().then((prefs) { SharedPreferences.getInstance().then((prefs) {
this.prefsAreLoaded = true; prefsAreLoaded = true;
this.add( add(
SetTheme( SetTheme(
themeIsDark: prefs.getBool('darkThemeEnabled') ?? false, themeIsDark: prefs.getBool('darkThemeEnabled') ?? false,
), ),

View File

@ -14,6 +14,7 @@ class LightThemeState extends ThemeState {
const LightThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded); const LightThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded);
@override
AppTheme get theme => LightTheme(); AppTheme get theme => LightTheme();
} }
@ -22,5 +23,6 @@ class DarkThemeState extends ThemeState {
const DarkThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded); const DarkThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded);
@override
AppTheme get theme => DarkTheme(); AppTheme get theme => DarkTheme();
} }

View File

@ -1,27 +1,26 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/router.dart';
import 'package:jisho_study_tool/view/components/common/splash.dart';
import 'package:mdi/mdi.dart'; import 'package:mdi/mdi.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:jisho_study_tool/objectbox.g.dart'; import 'bloc/database/database_bloc.dart';
import 'bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/view/screens/search/kanji_view.dart';
import 'package:jisho_study_tool/view/screens/history.dart';
import 'package:jisho_study_tool/view/screens/search/search_view.dart';
import 'package:jisho_study_tool/view/screens/settings.dart';
import 'models/themes/theme.dart'; import 'models/themes/theme.dart';
import 'objectbox.g.dart';
import 'router.dart';
import 'view/components/common/splash.dart';
import 'view/screens/history.dart';
import 'view/screens/search/kanji_view.dart';
import 'view/screens/search/search_view.dart';
import 'view/screens/settings.dart';
void main() => runApp(MyApp()); void main() => runApp(const MyApp());
DatabaseBloc _databaseBloc = DatabaseBloc(); DatabaseBloc _databaseBloc = DatabaseBloc();
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override @override
_MyAppState createState() => _MyAppState(); _MyAppState createState() => _MyAppState();
} }
@ -50,7 +49,7 @@ class _MyAppState extends State<MyApp> {
@override @override
void dispose() { void dispose() {
_store.close(); _store.close();
_databaseBloc.add(DisconnectedFromDatabase()); _databaseBloc.add(const DisconnectedFromDatabase());
super.dispose(); super.dispose();
} }
@ -64,13 +63,13 @@ class _MyAppState extends State<MyApp> {
child: BlocBuilder<ThemeBloc, ThemeState>( child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, themeState) { builder: (context, themeState) {
if (!(dbConnected && themeState.prefsAreLoaded)) if (!(dbConnected && themeState.prefsAreLoaded))
return SplashScreen(); return const SplashScreen();
return MaterialApp( return MaterialApp(
title: 'Jisho Study Tool', title: 'Jisho Study Tool',
theme: themeState.theme.getMaterialTheme(), theme: themeState.theme.getMaterialTheme(),
initialRoute: '/', initialRoute: '/',
onGenerateRoute: PageRouter.generateRoute, onGenerateRoute: generateRoute,
); );
}, },
), ),
@ -79,6 +78,8 @@ class _MyAppState extends State<MyApp> {
} }
class Home extends StatefulWidget { class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override @override
State<StatefulWidget> createState() => _HomeState(); State<StatefulWidget> createState() => _HomeState();
} }
@ -100,11 +101,12 @@ class _HomeState extends State<Home> {
body: Stack( body: Stack(
children: [ children: [
Positioned( Positioned(
child: Image.asset(
'assets/images/denshi_jisho_background_overlay.png'),
right: 30, right: 30,
left: 100, left: 100,
bottom: 30, bottom: 30,
child: Image.asset(
'assets/images/denshi_jisho_background_overlay.png',
),
), ),
pages[pageNum].content, pages[pageNum].content,
], ],
@ -112,8 +114,8 @@ class _HomeState extends State<Home> {
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
fixedColor: AppTheme.jishoGreen.background, fixedColor: AppTheme.jishoGreen.background,
currentIndex: pageNum, currentIndex: pageNum,
onTap: (int index) => setState(() { onTap: (index) => setState(() {
this.pageNum = index; pageNum = index;
}), }),
items: pages.map((p) => p.item).toList(), items: pages.map((p) => p.item).toList(),
showSelectedLabels: false, showSelectedLabels: false,
@ -139,7 +141,7 @@ class _Page {
} }
final List<_Page> pages = [ final List<_Page> pages = [
_Page( const _Page(
content: SearchView(), content: SearchView(),
titleBar: Text('Search'), titleBar: Text('Search'),
item: BottomNavigationBarItem( item: BottomNavigationBarItem(
@ -147,15 +149,17 @@ final List<_Page> pages = [
icon: Icon(Icons.search), icon: Icon(Icons.search),
), ),
), ),
_Page( const _Page(
content: KanjiView(), content: KanjiView(),
titleBar: Text('Kanji'), titleBar: Text('Kanji'),
item: BottomNavigationBarItem( item: BottomNavigationBarItem(
label: 'Kanji', icon: Icon(Mdi.ideogramCjk, size: 30)), label: 'Kanji',
icon: Icon(Mdi.ideogramCjk, size: 30),
), ),
_Page( ),
const _Page(
content: HistoryView(), content: HistoryView(),
titleBar: Text("History"), titleBar: Text('History'),
item: BottomNavigationBarItem( item: BottomNavigationBarItem(
label: 'History', label: 'History',
icon: Icon(Icons.history), icon: Icon(Icons.history),
@ -163,15 +167,15 @@ final List<_Page> pages = [
), ),
_Page( _Page(
content: Container(), content: Container(),
titleBar: Text("Saved"), titleBar: const Text('Saved'),
item: BottomNavigationBarItem( item: const BottomNavigationBarItem(
label: 'Saved', label: 'Saved',
icon: Icon(Icons.bookmark), icon: Icon(Icons.bookmark),
), ),
), ),
_Page( const _Page(
content: SettingsView(), content: SettingsView(),
titleBar: Text("Settings"), titleBar: Text('Settings'),
item: BottomNavigationBarItem( item: BottomNavigationBarItem(
label: 'Settings', label: 'Settings',
icon: Icon(Icons.settings), icon: Icon(Icons.settings),

View File

@ -16,15 +16,15 @@ class Search {
Search({ Search({
this.id = 0, this.id = 0,
required this.timestamp required this.timestamp,
}); // { });
bool isKanji() { bool isKanji() {
// // TODO: better error message // // TODO: better error message
if (this.wordQuery.target == null && this.kanjiQuery.target == null) if (wordQuery.target == null && kanjiQuery.target == null)
throw Exception(); throw Exception();
return this.wordQuery.target == null; return wordQuery.target == null;
} }
} }

View File

@ -1,6 +1,6 @@
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:jisho_study_tool/models/history/word_query.dart'; import 'word_query.dart';
@Entity() @Entity()
class WordResult { class WordResult {

View File

@ -1,34 +1,42 @@
part of './theme.dart'; part of './theme.dart';
class DarkTheme extends AppTheme { class DarkTheme extends AppTheme {
@override
ColorSet get kanjiResultColor => const ColorSet( ColorSet get kanjiResultColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.green, background: Colors.green,
); );
@override
ColorSet get onyomiColor => const ColorSet( ColorSet get onyomiColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.orange, background: Colors.orange,
); );
@override
ColorSet get kunyomiColor => const ColorSet( ColorSet get kunyomiColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.lightBlue, background: Colors.lightBlue,
); );
@override
Color get foreground => Colors.black; Color get foreground => Colors.black;
@override
Color get background => Colors.white; Color get background => Colors.white;
@override
ColorSet get menuGreyLight => ColorSet( ColorSet get menuGreyLight => ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.grey.shade700, background: Colors.grey.shade700,
); );
ColorSet get menuGreyNormal => ColorSet( @override
ColorSet get menuGreyNormal => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.grey, background: Colors.grey,
); );
@override
ColorSet get menuGreyDark => ColorSet( ColorSet get menuGreyDark => ColorSet(
foreground: Colors.black, foreground: Colors.black,
background: Colors.grey.shade300, background: Colors.grey.shade300,

View File

@ -1,33 +1,41 @@
part of './theme.dart'; part of './theme.dart';
class LightTheme extends AppTheme { class LightTheme extends AppTheme {
@override
ColorSet get kanjiResultColor => const ColorSet( ColorSet get kanjiResultColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.blue, background: Colors.blue,
); );
@override
ColorSet get onyomiColor => const ColorSet( ColorSet get onyomiColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.orange, background: Colors.orange,
); );
@override
ColorSet get kunyomiColor => const ColorSet( ColorSet get kunyomiColor => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.lightBlue, background: Colors.lightBlue,
); );
@override
Color get foreground => Colors.black; Color get foreground => Colors.black;
@override
Color get background => Colors.white; Color get background => Colors.white;
@override
ColorSet get menuGreyLight => ColorSet( ColorSet get menuGreyLight => ColorSet(
foreground: Colors.black, foreground: Colors.black,
background: Colors.grey.shade300, background: Colors.grey.shade300,
); );
ColorSet get menuGreyNormal => ColorSet( @override
ColorSet get menuGreyNormal => const ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.grey, background: Colors.grey,
); );
@override
ColorSet get menuGreyDark => ColorSet( ColorSet get menuGreyDark => ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Colors.grey.shade700, background: Colors.grey.shade700,

View File

@ -4,7 +4,6 @@ part 'light.dart';
part 'dark.dart'; part 'dark.dart';
abstract class AppTheme { abstract class AppTheme {
static const ColorSet jishoGreen = ColorSet( static const ColorSet jishoGreen = ColorSet(
foreground: Colors.white, foreground: Colors.white,
background: Color(0xFF3EDD00), background: Color(0xFF3EDD00),
@ -49,14 +48,15 @@ class ColorSet {
/// Source: https://blog.usejournal.com/creating-a-custom-color-swatch-in-flutter-554bcdcb27f3 /// Source: https://blog.usejournal.com/creating-a-custom-color-swatch-in-flutter-554bcdcb27f3
MaterialColor createMaterialColor(Color color) { MaterialColor createMaterialColor(Color color) {
List strengths = <double>[.05]; final List<double> strengths = [.05];
final swatch = <int, Color>{}; final swatch = <int, Color>{};
final int r = color.red, g = color.green, b = color.blue; final int r = color.red, g = color.green, b = color.blue;
for (int i = 1; i < 10; i++) { for (int i = 1; i < 10; i++) {
strengths.add(0.1 * i); strengths.add(0.1 * i);
} }
strengths.forEach((strength) {
for (final strength in strengths) {
final double ds = 0.5 - strength; final double ds = 0.5 - strength;
swatch[(strength * 1000).round()] = Color.fromRGBO( swatch[(strength * 1000).round()] = Color.fromRGBO(
r + ((ds < 0 ? r : (255 - r)) * ds).round(), r + ((ds < 0 ? r : (255 - r)) * ds).round(),
@ -64,6 +64,6 @@ MaterialColor createMaterialColor(Color color) {
b + ((ds < 0 ? b : (255 - b)) * ds).round(), b + ((ds < 0 ? b : (255 - b)) * ds).round(),
1, 1,
); );
}); }
return MaterialColor(color.value, swatch); return MaterialColor(color.value, swatch);
} }

View File

@ -1,27 +1,31 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/screens/search/kanji_result_page.dart';
import 'package:jisho_study_tool/view/screens/search/search_results_page.dart';
import 'main.dart'; import 'main.dart';
import 'view/screens/search/kanji_result_page.dart';
import 'view/screens/search/search_results_page.dart';
class PageRouter { Route<dynamic> generateRoute(RouteSettings settings) {
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments; final args = settings.arguments;
switch (settings.name) { switch (settings.name) {
case '/': case '/':
return MaterialPageRoute(builder: (_) => Home()); return MaterialPageRoute(builder: (_) => const Home());
case '/search': case '/search':
final searchTerm = args as String; final searchTerm = args as String;
return MaterialPageRoute(builder: (_) => SearchResultsPage(searchTerm: searchTerm)); return MaterialPageRoute(
builder: (_) => SearchResultsPage(searchTerm: searchTerm),
);
case '/kanjiSearch': case '/kanjiSearch':
final searchTerm = args as String; final searchTerm = args as String;
return MaterialPageRoute(builder: (_) => KanjiResultPage(kanjiSearchTerm: searchTerm)); return MaterialPageRoute(
builder: (_) => KanjiResultPage(kanjiSearchTerm: searchTerm),
);
default: default:
return MaterialPageRoute(builder: (_) => Text("ERROR: this route does not exist")); return MaterialPageRoute(
} builder: (_) => const Text('ERROR: this route does not exist'),
);
} }
} }

View File

@ -2,5 +2,5 @@ import 'package:unofficial_jisho_api/api.dart' as jisho;
export 'package:unofficial_jisho_api/api.dart' show JishoAPIResult; export 'package:unofficial_jisho_api/api.dart' show JishoAPIResult;
Future<jisho.JishoAPIResult> fetchJishoResults(searchTerm) async { Future<jisho.JishoAPIResult> fetchJishoResults(searchTerm) async {
return await jisho.searchForPhrase(searchTerm); return jisho.searchForPhrase(searchTerm);
} }

View File

@ -1,18 +1,19 @@
import 'package:flutter/material.dart';
import 'package:unofficial_jisho_api/api.dart' as jisho; import 'package:unofficial_jisho_api/api.dart' as jisho;
export 'package:unofficial_jisho_api/api.dart' show KanjiResult; export 'package:unofficial_jisho_api/api.dart' show KanjiResult;
String? _convertGrade(String grade) { String? _convertGrade(String grade) {
const conversionTable = { const conversionTable = {
"grade 1": "小1", 'grade 1': '小1',
"grade 2": "小2", 'grade 2': '小2',
"grade 3": "小3", 'grade 3': '小3',
"grade 4": "小4", 'grade 4': '小4',
"grade 5": "小5", 'grade 5': '小5',
"grade 6": "小6", 'grade 6': '小6',
"junior high": "" 'junior high': ''
}; };
print('conversion run: $grade -> ${conversionTable[grade]}'); debugPrint('conversion run: $grade -> ${conversionTable[grade]}');
return conversionTable[grade]; return conversionTable[grade];
} }

View File

@ -1,12 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class LoadingScreen extends StatelessWidget { class LoadingScreen extends StatelessWidget {
const LoadingScreen({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return const Center(
child: Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
),
); );
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/models/themes/theme.dart';
import '../../../models/themes/theme.dart';
class SplashScreen extends StatelessWidget { class SplashScreen extends StatelessWidget {
const SplashScreen({Key? key}) : super(key: key); const SplashScreen({Key? key}) : super(key: key);
@ -8,8 +9,10 @@ class SplashScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
decoration: BoxDecoration(color: AppTheme.jishoGreen.background), decoration: BoxDecoration(color: AppTheme.jishoGreen.background),
child: Center( child: const Center(
child: Image(image: AssetImage('assets/images/logo/logo_icon_transparent.png'),) child: Image(
image: AssetImage('assets/images/logo/logo_icon_transparent.png'),
),
), ),
); );
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/models/themes/theme.dart'; import '../../../bloc/theme/theme_bloc.dart';
import '../../../models/themes/theme.dart';
class DateDivider extends StatelessWidget { class DateDivider extends StatelessWidget {
final String? text; final String? text;
@ -33,31 +34,31 @@ class DateDivider extends StatelessWidget {
final int day = date.day; final int day = date.day;
final String month = monthTable[date.month]!; final String month = monthTable[date.month]!;
final int year = date.year; final int year = date.year;
return "$day. $month $year"; return '$day. $month $year';
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Widget header = (this.text != null) final Widget header = (text != null)
? Text(this.text!) ? Text(text!)
: (this.date != null) : (date != null)
? Text(getHumanReadableDate(this.date!)) ? Text(getHumanReadableDate(date!))
: SizedBox.shrink(); : const SizedBox.shrink();
final ColorSet _menuColors = final ColorSet _menuColors =
BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyNormal; BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyNormal;
return Container( return Container(
decoration: BoxDecoration(color: _menuColors.background),
padding: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
margin: margin,
child: DefaultTextStyle.merge( child: DefaultTextStyle.merge(
child: header, child: header,
style: TextStyle(color: _menuColors.foreground), style: TextStyle(color: _menuColors.foreground),
), ),
decoration: BoxDecoration(color: _menuColors.background),
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
margin: this.margin,
); );
} }
} }

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/models/history/kanji_query.dart';
import 'package:jisho_study_tool/models/themes/theme.dart';
import './search_item.dart'; import './search_item.dart';
import '../../../bloc/theme/theme_bloc.dart';
import '../../../models/history/kanji_query.dart';
import '../../../models/themes/theme.dart';
class _KanjiBox extends StatelessWidget { class _KanjiBox extends StatelessWidget {
final String kanji; final String kanji;
@ -19,7 +19,7 @@ class _KanjiBox extends StatelessWidget {
child: AspectRatio( child: AspectRatio(
aspectRatio: 1, aspectRatio: 1,
child: Container( child: Container(
padding: EdgeInsets.all(5), padding: const EdgeInsets.all(5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _menuColors.background, color: _menuColors.background,
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:jisho_study_tool/models/history/word_query.dart';
import './search_item.dart'; import './search_item.dart';
import '../../../models/history/word_query.dart';
class PhraseSearchItem extends StatelessWidget { class PhraseSearchItem extends StatelessWidget {
final WordQuery search; final WordQuery search;
@ -17,15 +17,20 @@ class PhraseSearchItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Slidable( return Slidable(
actionPane: SlidableScrollActionPane(), actionPane: const SlidableScrollActionPane(),
secondaryActions: [ secondaryActions: const [
IconSlideAction( IconSlideAction(
caption: "Delete", color: Colors.red, icon: Icons.delete) caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
),
], ],
child: SearchItem( child: SearchItem(
onTap: () { onTap: () => Navigator.pushNamed(
Navigator.pushNamed(context, '/search', arguments: this.search.query); context,
}, '/search',
arguments: search.query,
),
time: timestamp, time: timestamp,
search: Text(search.query), search: Text(search.query),
), ),

View File

@ -13,27 +13,25 @@ class SearchItem extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
String getTime() { String getTime() {
final hours = this.time.hour.toString().padLeft(2, '0'); final hours = time.hour.toString().padLeft(2, '0');
final mins = this.time.minute.toString().padLeft(2, '0'); final mins = time.minute.toString().padLeft(2, '0');
return "$hours:$mins"; return '$hours:$mins';
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return ListTile(
child: ListTile(
onTap: onTap, onTap: onTap,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
title: Row( title: Row(
children: [ children: [
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(getTime()), child: Text(getTime()),
), ),
search, search,
], ],
), ),
),
); );
} }
} }

View File

@ -14,13 +14,12 @@ class KanjiResultBody extends StatelessWidget {
late final String query; late final String query;
late final jisho.KanjiResultData resultData; late final jisho.KanjiResultData resultData;
KanjiResultBody({required jisho.KanjiResult result}) { KanjiResultBody({required jisho.KanjiResult result, Key? key})
: super(key: key) {
query = result.query; query = result.query;
// TODO: Handle this kind of exception before widget is initialized // TODO: Handle this kind of exception before widget is initialized
if (result.data == null) if (result.data == null) throw Exception();
throw Exception();
resultData = result.data!; resultData = result.data!;
} }
@ -30,68 +29,72 @@ class KanjiResultBody extends StatelessWidget {
return ListView( return ListView(
children: [ children: [
Container( Container(
margin: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 30.0), margin: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 30.0),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Flexible( const Flexible(
flex: 1,
fit: FlexFit.tight, fit: FlexFit.tight,
child: Center(child: SizedBox()), child: Center(child: SizedBox()),
), ),
Flexible( Flexible(
flex: 1,
fit: FlexFit.tight, fit: FlexFit.tight,
child: Center(child: Header(query)), child: Center(child: Header(kanji: query)),
), ),
Flexible( Flexible(
flex: 1,
fit: FlexFit.tight, fit: FlexFit.tight,
child: Center( child: Center(
child: (resultData.radical != null) ? Radical(resultData.radical!) : SizedBox(), child: (resultData.radical != null)
? Radical(radical: resultData.radical!)
: const SizedBox(),
), ),
), ),
], ],
), ),
), ),
YomiChips(resultData.meaning.split(', '), YomiType.meaning), YomiChips(yomi: resultData.meaning.split(', '), type: YomiType.meaning),
resultData.onyomi.length != 0 ? YomiChips(resultData.onyomi, YomiType.onyomi) : SizedBox.shrink(), (resultData.onyomi.isNotEmpty)
resultData.kunyomi.length != 0 ? YomiChips(resultData.kunyomi, YomiType.kunyomi) : SizedBox.shrink(), ? YomiChips(yomi: resultData.onyomi, type: YomiType.onyomi)
: const SizedBox.shrink(),
(resultData.kunyomi.isNotEmpty)
? YomiChips(yomi: resultData.kunyomi, type: YomiType.kunyomi)
: const SizedBox.shrink(),
IntrinsicHeight( IntrinsicHeight(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
StrokeOrderGif(resultData.strokeOrderGifUri), StrokeOrderGif(uri: resultData.strokeOrderGifUri),
Container( Column(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Text("JLPT: ", style: TextStyle(fontSize: 20.0)), const Text('JLPT: ', style: TextStyle(fontSize: 20.0)),
JlptLevel(resultData.jlptLevel ?? ""), JlptLevel(jlptLevel: resultData.jlptLevel ?? ''),
], ],
), ),
Row( Row(
children: [ children: [
Text("Grade: ", style: TextStyle(fontSize: 20.0)), const Text('Grade: ', style: TextStyle(fontSize: 20.0)),
Grade(resultData.taughtIn ?? ""), Grade(grade: resultData.taughtIn ?? ''),
], ],
), ),
Row( Row(
children: [ children: [
Text("Rank: ", style: TextStyle(fontSize: 20.0)), const Text('Rank: ', style: TextStyle(fontSize: 20.0)),
Rank(resultData.newspaperFrequencyRank ?? -1), Rank(rank: resultData.newspaperFrequencyRank ?? -1),
],
),
], ],
), ),
], ],
), ),
), ),
], Examples(
onyomi: resultData.onyomiExamples,
kunyomi: resultData.kunyomiExamples,
), ),
),
Examples(resultData.onyomiExamples, resultData.kunyomiExamples),
], ],
); );
} }

View File

@ -1,15 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:unofficial_jisho_api/api.dart'; import 'package:unofficial_jisho_api/api.dart';
class Examples extends StatelessWidget { import '../../../../bloc/theme/theme_bloc.dart';
final List<YomiExample> onyomiExamples;
final List<YomiExample> kunyomiExamples;
const Examples( class Examples extends StatelessWidget {
this.onyomiExamples, final List<YomiExample> onyomi;
this.kunyomiExamples, final List<YomiExample> kunyomi;
);
const Examples({
required this.onyomi,
required this.kunyomi,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -17,20 +19,21 @@ class Examples extends StatelessWidget {
children: [ children: [
[ [
Container( Container(
margin: EdgeInsets.symmetric(horizontal: 10), margin: const EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: const Text(
'Examples:', 'Examples:',
style: TextStyle(fontSize: 20), style: TextStyle(fontSize: 20),
), ),
) )
], ],
onyomiExamples onyomi
.map((onyomiExample) => _Example(onyomiExample, _KanaType.onyomi)) .map((onyomiExample) => _Example(onyomiExample, _KanaType.onyomi))
.toList(), .toList(),
kunyomiExamples kunyomi
.map( .map(
(kunyomiExample) => _Example(kunyomiExample, _KanaType.kunyomi)) (kunyomiExample) => _Example(kunyomiExample, _KanaType.kunyomi),
)
.toList(), .toList(),
].expand((list) => list).toList(), ].expand((list) => list).toList(),
); );
@ -48,69 +51,67 @@ class _Example extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _themeData = BlocProvider.of<ThemeBloc>(context).state.theme; final _themeData = BlocProvider.of<ThemeBloc>(context).state.theme;
final _kanaColors = kanaType == _KanaType.kunyomi ? _themeData.kunyomiColor : _themeData.onyomiColor; final _kanaColors = kanaType == _KanaType.kunyomi
? _themeData.kunyomiColor
: _themeData.onyomiColor;
final _menuColors = _themeData.menuGreyNormal; final _menuColors = _themeData.menuGreyNormal;
return Container( return Container(
margin: EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
vertical: 5.0, vertical: 5.0,
horizontal: 10.0, horizontal: 10.0,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _menuColors.background, borderRadius: BorderRadius.circular(10.0)), color: _menuColors.background,
borderRadius: BorderRadius.circular(10.0),
),
child: Row( child: Row(
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 10.0, vertical: 10.0,
horizontal: 10.0, horizontal: 10.0,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _kanaColors.background, color: _kanaColors.background,
borderRadius: BorderRadius.only( borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10.0), topLeft: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0),
), ),
), ),
child: Column( child: Column(
children: [ children: [
Container( Text(
child: Text(
yomiExample.reading, yomiExample.reading,
style: TextStyle( style: TextStyle(
color: _kanaColors.foreground, color: _kanaColors.foreground,
fontSize: 15.0, fontSize: 15.0,
), ),
), ),
), const SizedBox(
SizedBox(
height: 5.0, height: 5.0,
), ),
Container( Text(
child: Text(
yomiExample.example, yomiExample.example,
style: TextStyle( style: TextStyle(
color: _kanaColors.foreground, color: _kanaColors.foreground,
fontSize: 20.0, fontSize: 20.0,
), ),
), ),
),
], ],
), ),
), ),
SizedBox( const SizedBox(
width: 15.0, width: 15.0,
), ),
Expanded( Expanded(
child: Wrap( child: Wrap(
children: [ children: [
Container( Text(
child: Text(
yomiExample.meaning, yomiExample.meaning,
style: TextStyle( style: TextStyle(
color: _menuColors.foreground, color: _menuColors.foreground,
), ),
),
) )
], ],
), ),

View File

@ -1,17 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
class Grade extends StatelessWidget { class Grade extends StatelessWidget {
final String grade; final String grade;
const Grade(this.grade); const Grade({required this.grade, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return Container( return Container(
padding: EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: _kanjiColors.background,
shape: BoxShape.circle,
),
child: Text( child: Text(
grade, grade,
style: TextStyle( style: TextStyle(
@ -19,10 +24,6 @@ class Grade extends StatelessWidget {
fontSize: 20.0, fontSize: 20.0,
), ),
), ),
decoration: BoxDecoration(
color: _kanjiColors.background,
shape: BoxShape.circle,
),
); );
} }
} }

View File

@ -1,14 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
class Header extends StatelessWidget { class Header extends StatelessWidget {
final String kanji; final String kanji;
const Header(this.kanji); const Header({
required this.kanji,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors =
BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return AspectRatio( return AspectRatio(
aspectRatio: 1, aspectRatio: 1,

View File

@ -1,18 +1,26 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
class JlptLevel extends StatelessWidget { class JlptLevel extends StatelessWidget {
final String jlptLevel; final String jlptLevel;
const JlptLevel(this.jlptLevel); const JlptLevel({
required this.jlptLevel,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors =
BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return Container( return Container(
padding: EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _kanjiColors.background,
),
child: Text( child: Text(
jlptLevel, jlptLevel,
style: TextStyle( style: TextStyle(
@ -20,10 +28,6 @@ class JlptLevel extends StatelessWidget {
fontSize: 20.0, fontSize: 20.0,
), ),
), ),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _kanjiColors.background,
),
); );
} }
} }

View File

@ -1,18 +1,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:unofficial_jisho_api/api.dart' as jisho; import 'package:unofficial_jisho_api/api.dart' as jisho;
import '../../../../bloc/theme/theme_bloc.dart';
class Radical extends StatelessWidget { class Radical extends StatelessWidget {
final jisho.Radical radical; final jisho.Radical radical;
const Radical(this.radical); const Radical({required this.radical, Key? key,}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return Container( return Container(
padding: EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _kanjiColors.background,
),
child: Text( child: Text(
radical.symbol, radical.symbol,
style: TextStyle( style: TextStyle(
@ -20,10 +26,6 @@ class Radical extends StatelessWidget {
fontSize: 40.0, fontSize: 40.0,
), ),
), ),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _kanjiColors.background,
),
); );
} }
} }

View File

@ -1,18 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
class Rank extends StatelessWidget { class Rank extends StatelessWidget {
final int rank; final int rank;
const Rank(this.rank); const Rank({required this.rank, Key? key,}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return Container( return Container(
padding: EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: _kanjiColors.background,
),
child: Text( child: Text(
'${rank.toString()} / 2500', '${rank.toString()} / 2500',
style: TextStyle( style: TextStyle(
@ -20,10 +25,6 @@ class Rank extends StatelessWidget {
fontSize: 20.0, fontSize: 20.0,
), ),
), ),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: _kanjiColors.background,
),
); );
} }
} }

View File

@ -1,26 +1,28 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
class StrokeOrderGif extends StatelessWidget { class StrokeOrderGif extends StatelessWidget {
final String uri; final String uri;
const StrokeOrderGif(this.uri); const StrokeOrderGif({required this.uri, Key? key,}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor; final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
return Container( return Container(
margin: EdgeInsets.symmetric(vertical: 20.0), margin: const EdgeInsets.symmetric(vertical: 20.0),
padding: EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: ClipRRect(
child: Image.network(uri),
borderRadius: BorderRadius.circular(10.0),
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _kanjiColors.background, color: _kanjiColors.background,
borderRadius: BorderRadius.circular(15.0), borderRadius: BorderRadius.circular(15.0),
), ),
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.network(uri),
),
); );
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import '../../../../bloc/theme/theme_bloc.dart';
enum YomiType { enum YomiType {
onyomi, onyomi,
@ -37,86 +38,88 @@ class YomiChips extends StatelessWidget {
final List<String> yomi; final List<String> yomi;
final YomiType type; final YomiType type;
const YomiChips(this.yomi, this.type); const YomiChips({
required this.yomi,
required this.type,
Key? key,
}) : super(key: key);
@override bool get isExpandable => yomi.length > 6;
Widget build(BuildContext context) {
return Container( Widget yomiCard({
margin: EdgeInsets.symmetric( required BuildContext context,
required String yomi,
required ColorSet colors,
}) =>
Container(
margin: const EdgeInsets.symmetric(horizontal: 10.0),
padding: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0, horizontal: 10.0,
vertical: 5.0,
), ),
alignment: Alignment.centerLeft, decoration: BoxDecoration(
child: _yomiWrapper(context), color: type.getColors(context).background,
borderRadius: BorderRadius.circular(10.0),
),
child: Text(
yomi,
style: TextStyle(
fontSize: 20.0,
color: colors.foreground,
),
),
); );
}
bool isExpandable() => yomi.length > 6; Widget yomiWrapper(BuildContext context) {
final yomiCards = yomi
Widget _yomiWrapper(BuildContext context) { .map(
final yomiCards = this (y) => yomiCard(
.yomi context: context,
.map((yomi) => _YomiCard( yomi: y,
yomi: yomi, colors: type.getColors(context),
colors: this.type.getColors(context), ),
)) )
.toList(); .toList();
if (!this.isExpandable()) if (!isExpandable)
return Wrap( return Wrap(
runSpacing: 10.0, runSpacing: 10.0,
children: yomiCards, children: yomiCards,
); );
else
return ExpansionTile( return ExpansionTile(
initiallyExpanded: false, // initiallyExpanded: false,
title: Center( title: Center(
child: _YomiCard( child: yomiCard(
yomi: this.type.title, context: context,
colors: this.type.getColors(context), yomi: type.title,
colors: type.getColors(context),
), ),
), ),
children: [ children: [
SizedBox( const SizedBox(
height: 20.0, height: 20.0,
), ),
Wrap( Wrap(
runSpacing: 10.0, runSpacing: 10.0,
children: yomiCards, children: yomiCards,
), ),
SizedBox( const SizedBox(
height: 25.0, height: 25.0,
), ),
], ],
); );
} }
}
class _YomiCard extends StatelessWidget {
final String yomi;
final ColorSet colors;
const _YomiCard({required this.yomi, required this.colors});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.symmetric(horizontal: 10.0), margin: const EdgeInsets.symmetric(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0, horizontal: 10.0,
vertical: 5.0,
), ),
child: Text( alignment: Alignment.centerLeft,
this.yomi, child: yomiWrapper(context),
style: TextStyle(
fontSize: 20.0,
color: colors.foreground,
),
),
decoration: BoxDecoration(
color: colors.background,
borderRadius: BorderRadius.circular(10.0),
),
); );
} }
} }

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:animated_size_and_fade/animated_size_and_fade.dart'; import 'package:animated_size_and_fade/animated_size_and_fade.dart';
import 'package:jisho_study_tool/services/kanji_suggestions.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_grid.dart'; import '../../../services/kanji_suggestions.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_search_bar.dart'; import 'kanji_search_body/kanji_grid.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_search_options_bar.dart'; import 'kanji_search_body/kanji_search_bar.dart';
import 'kanji_search_body/kanji_search_options_bar.dart';
class KanjiSearchBody extends StatefulWidget { class KanjiSearchBody extends StatefulWidget {
KanjiSearchBody({Key? key}) : super(key: key); const KanjiSearchBody({Key? key}) : super(key: key);
@override @override
_KanjiSearchBodyState createState() => _KanjiSearchBodyState(); _KanjiSearchBodyState createState() => _KanjiSearchBodyState();
@ -28,16 +28,18 @@ class _KanjiSearchBodyState extends State<KanjiSearchBody>
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
); );
_searchbarMovementAnimation = AlignmentTween( _searchbarMovementAnimation = AlignmentTween(
begin: Alignment.center, begin: Alignment.center,
end: Alignment.topCenter, end: Alignment.topCenter,
).animate(CurvedAnimation( ).animate(
CurvedAnimation(
parent: _controller, parent: _controller,
curve: Curves.easeInOut, curve: Curves.easeInOut,
)); ),
);
} }
@override @override
@ -60,15 +62,15 @@ class _KanjiSearchBodyState extends State<KanjiSearchBody>
child: GestureDetector( child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: Container( child: Container(
decoration: BoxDecoration(), decoration: const BoxDecoration(),
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: AnimatedBuilder( child: AnimatedBuilder(
animation: _searchbarMovementAnimation, animation: _searchbarMovementAnimation,
builder: (BuildContext context, _) { builder: (context, _) {
return Container( return Container(
alignment: _searchbarMovementAnimation.value, alignment: _searchbarMovementAnimation.value,
padding: EdgeInsets.symmetric(vertical: 10.0), padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -83,17 +85,17 @@ class _KanjiSearchBodyState extends State<KanjiSearchBody>
child: KanjiSearchBar( child: KanjiSearchBar(
key: _kanjiSearchBarState, key: _kanjiSearchBarState,
onChanged: (text) => setState(() { onChanged: (text) => setState(() {
this.suggestions = kanjiSuggestions(text); suggestions = kanjiSuggestions(text);
}), }),
), ),
), ),
AnimatedSizeAndFade( AnimatedSizeAndFade(
vsync: this, vsync: this,
child: _controller.value == 1
? KanjiGrid(this.suggestions)
: KanjiSearchOptionsBar(),
fadeDuration: const Duration(milliseconds: 200), fadeDuration: const Duration(milliseconds: 200),
sizeDuration: const Duration(milliseconds: 300), sizeDuration: const Duration(milliseconds: 300),
child: _controller.value == 1
? KanjiGrid(suggestions: suggestions)
: const KanjiSearchOptionsBar(),
), ),
], ],
), ),

View File

@ -1,15 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart'; import '../../../../bloc/theme/theme_bloc.dart';
class KanjiGrid extends StatelessWidget { class KanjiGrid extends StatelessWidget {
final List<String> suggestions; final List<String> suggestions;
const KanjiGrid(this.suggestions);
const KanjiGrid({required this.suggestions, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 20.0, vertical: 20.0,
horizontal: 40.0, horizontal: 40.0,
), ),
@ -43,7 +44,7 @@ class _GridItem extends StatelessWidget {
borderRadius: BorderRadius.circular(20.0), borderRadius: BorderRadius.circular(20.0),
), ),
child: Container( child: Container(
margin: EdgeInsets.all(10.0), margin: const EdgeInsets.all(10.0),
child: FittedBox( child: FittedBox(
child: Text( child: Text(
kanji, kanji,

View File

@ -4,16 +4,16 @@ import 'package:flutter/services.dart';
class KanjiSearchBar extends StatefulWidget { class KanjiSearchBar extends StatefulWidget {
final Function(String)? onChanged; final Function(String)? onChanged;
KanjiSearchBar({this.onChanged, Key? key}) : super(key: key); const KanjiSearchBar({this.onChanged, Key? key}) : super(key: key);
@override @override
KanjiSearchBarState createState() => new KanjiSearchBarState(this.onChanged); KanjiSearchBarState createState() => KanjiSearchBarState(this.onChanged);
} }
enum TextFieldButton { clear, paste } enum TextFieldButton { clear, paste }
class KanjiSearchBarState extends State<KanjiSearchBar> { class KanjiSearchBarState extends State<KanjiSearchBar> {
final TextEditingController textController = new TextEditingController(); final TextEditingController textController = TextEditingController();
TextFieldButton button = TextFieldButton.paste; TextFieldButton button = TextFieldButton.paste;
final Function(String)? onChanged; final Function(String)? onChanged;
@ -33,8 +33,8 @@ class KanjiSearchBarState extends State<KanjiSearchBar> {
runOnChanged(); runOnChanged();
} }
void pasteText() async { Future<void> pasteText() async {
ClipboardData? clipboardData = await Clipboard.getData('text/plain'); final ClipboardData? clipboardData = await Clipboard.getData('text/plain');
if (clipboardData != null && clipboardData.text != null) { if (clipboardData != null && clipboardData.text != null) {
textController.text = clipboardData.text!; textController.text = clipboardData.text!;
runOnChanged(); runOnChanged();
@ -43,24 +43,24 @@ class KanjiSearchBarState extends State<KanjiSearchBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
IconButton clearButton = IconButton( final IconButton clearButton = IconButton(
icon: Icon(Icons.clear), icon: const Icon(Icons.clear),
onPressed: () => clearText(), onPressed: () => clearText(),
); );
IconButton pasteButton = IconButton( final IconButton pasteButton = IconButton(
icon: Icon(Icons.content_paste), icon: const Icon(Icons.content_paste),
onPressed: () => pasteText(), onPressed: () => pasteText(),
); );
return TextField( return TextField(
controller: textController, controller: textController,
onChanged: (text) { onChanged: (text) {
if (this.onChanged != null) this.onChanged!(text); if (onChanged != null) onChanged!(text);
}, },
onSubmitted: (_) => {}, onSubmitted: (_) => {},
decoration: new InputDecoration( decoration: InputDecoration(
prefixIcon: Icon(Icons.search), prefixIcon: const Icon(Icons.search),
hintText: 'Search', hintText: 'Search',
// fillColor: Colors.white, // fillColor: Colors.white,
// filled: true, // filled: true,

View File

@ -12,22 +12,22 @@ class KanjiSearchOptionsBar extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_IconButton( _IconButton(
icon: Text( icon: const Text(
"", '',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 18, fontSize: 18,
), ),
), ),
onPressed: () {} onPressed: () {},
), ),
_IconButton( _IconButton(
icon: Icon(Icons.category), icon: const Icon(Icons.category),
onPressed: () {} onPressed: () {},
), ),
_IconButton( _IconButton(
icon: Icon(Icons.mode), icon: const Icon(Icons.mode),
onPressed: () {} onPressed: () {},
), ),
], ],
), ),

View File

@ -9,7 +9,7 @@ class OpaqueBox extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor), decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor),
child: this.child, child: child,
); );
} }
} }

View File

@ -1,12 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/models/themes/theme.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../../models/themes/theme.dart';
class LanguageSelector extends StatefulWidget { class LanguageSelector extends StatefulWidget {
const LanguageSelector(); const LanguageSelector({Key? key}) : super(key: key);
@override @override
_LanguageSelectorState createState() => new _LanguageSelectorState(); _LanguageSelectorState createState() => _LanguageSelectorState();
} }
class _LanguageSelectorState extends State<LanguageSelector> { class _LanguageSelectorState extends State<LanguageSelector> {
@ -18,8 +19,7 @@ class _LanguageSelectorState extends State<LanguageSelector> {
super.initState(); super.initState();
isSelected = [false, false, false]; isSelected = [false, false, false];
SharedPreferences.getInstance() SharedPreferences.getInstance().then((prefs) {
.then((prefs) {
this.prefs = prefs; this.prefs = prefs;
setState(() { setState(() {
isSelected = _getSelectedStatus() ?? isSelected; isSelected = _getSelectedStatus() ?? isSelected;
@ -27,11 +27,11 @@ class _LanguageSelectorState extends State<LanguageSelector> {
}); });
} }
void _updateSelectedStatus() async { void _updateSelectedStatus() {
await prefs.setStringList('languageSelectorStatus', prefs.setStringList(
isSelected 'languageSelectorStatus',
.map((b) => b ? '1' : '0') isSelected.map((b) => b ? '1' : '0').toList(),
.toList()); );
} }
List<bool>? _getSelectedStatus() { List<bool>? _getSelectedStatus() {
@ -46,14 +46,14 @@ class _LanguageSelectorState extends State<LanguageSelector> {
return ToggleButtons( return ToggleButtons(
selectedColor: AppTheme.jishoGreen.background, selectedColor: AppTheme.jishoGreen.background,
isSelected: isSelected, isSelected: isSelected,
children: <Widget> [ children: const <Widget>[
_LanguageOption("Auto"), _LanguageOption('Auto'),
_LanguageOption("日本語"), _LanguageOption('日本語'),
_LanguageOption("English") _LanguageOption('English')
], ],
onPressed: (int buttonIndex) { onPressed: (buttonIndex) {
setState(() { setState(() {
for (var i in Iterable.generate(isSelected.length)) { for (final int i in Iterable.generate(isSelected.length)) {
isSelected[i] = i == buttonIndex; isSelected[i] = i == buttonIndex;
} }
_updateSelectedStatus(); _updateSelectedStatus();
@ -61,7 +61,6 @@ class _LanguageSelectorState extends State<LanguageSelector> {
}, },
); );
} }
} }
class _LanguageOption extends StatelessWidget { class _LanguageOption extends StatelessWidget {
@ -72,7 +71,7 @@ class _LanguageOption extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
child: Center(child: Text(language)), child: Center(child: Text(language)),
); );
} }

View File

@ -1,13 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/language_selector.dart';
import 'language_selector.dart';
class SearchBar extends StatelessWidget { class SearchBar extends StatelessWidget {
const SearchBar(); const SearchBar({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column( child: Column(
children: [ children: [
TextField( TextField(
@ -21,10 +22,10 @@ class SearchBar extends StatelessWidget {
), ),
), ),
), ),
SizedBox( const SizedBox(
height: 10.0, height: 10.0,
), ),
LanguageSelector() const LanguageSelector()
], ],
), ),
); );

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_results_body/search_card.dart';
import 'package:unofficial_jisho_api/api.dart'; import 'package:unofficial_jisho_api/api.dart';
import 'search_results_body/search_card.dart';
class SearchResultsBody extends StatelessWidget { class SearchResultsBody extends StatelessWidget {
final List<JishoResult> results; final List<JishoResult> results;
@ -13,7 +14,7 @@ class SearchResultsBody extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
children: results.map((result) => SearchResultCard(result)).toList(), children: results.map((result) => SearchResultCard(result: result)).toList(),
); );
} }
} }

View File

@ -1,27 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class Badge extends StatelessWidget { class Badge extends StatelessWidget {
final Widget child; final Widget? child;
final Color color; final Color color;
const Badge(this.child, this.color); const Badge({this.child, required this.color, Key? key,}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
child: FittedBox( padding: const EdgeInsets.all(5),
child: Center(
child: this.child
),
),
padding: EdgeInsets.all(5),
width: 30, width: 30,
height: 30, height: 30,
margin: EdgeInsets.symmetric(horizontal: 2), margin: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: color color: color,
),
child: FittedBox(
child: Center(
child: child,
),
), ),
); } ); }
} }

View File

@ -4,16 +4,21 @@ import './badge.dart';
class CommonBadge extends StatelessWidget { class CommonBadge extends StatelessWidget {
final bool isCommon; final bool isCommon;
const CommonBadge(this.isCommon); const CommonBadge({
required this.isCommon,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Badge( return Badge(
Text( color: isCommon ? Colors.green : Colors.transparent,
"C", child: Text(
style: TextStyle(color: this.isCommon ? Colors.white : Colors.transparent) 'C',
style: TextStyle(
color: isCommon ? Colors.white : Colors.transparent,
),
), ),
this.isCommon ? Colors.green : Colors.transparent
); );
} }
} }

View File

@ -4,20 +4,23 @@ import 'package:unofficial_jisho_api/api.dart';
class JapaneseHeader extends StatelessWidget { class JapaneseHeader extends StatelessWidget {
final JishoJapaneseWord word; final JishoJapaneseWord word;
const JapaneseHeader(this.word); const JapaneseHeader({
required this.word,
Key? key,
}) : super(key: key);
bool get hasFurigana => word.word != null && word.reading != null;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hasFurigana = (word.word != null && word.reading != null);
return Container( return Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: Column( child: Column(
children: [ children: [
// TODO: take a look at this logic // TODO: take a look at this logic
(hasFurigana) ? Text(word.reading!) : Text(''), hasFurigana ? Text(word.reading!) : const Text(''),
(hasFurigana) ? Text(word.word!) : Text(word.reading ?? word.word!), hasFurigana ? Text(word.word!) : Text(word.reading ?? word.word!),
], ],
), ),
); );

View File

@ -4,22 +4,22 @@ import './badge.dart';
class JLPTBadge extends StatelessWidget { class JLPTBadge extends StatelessWidget {
final String jlptLevel; final String jlptLevel;
const JLPTBadge(this.jlptLevel); const JLPTBadge({
required this.jlptLevel,
Key? key,
}) : super(key: key);
String _extractJlptLevel(String jlptRaw) { String get formattedJlptLevel =>
return jlptRaw.isNotEmpty ? jlptRaw.substring(5).toUpperCase() : ''; jlptLevel.isNotEmpty ? jlptLevel.substring(5).toUpperCase() : '';
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Badge( return Badge(
Text( color: jlptLevel.isNotEmpty ? Colors.blue : Colors.transparent,
_extractJlptLevel(this.jlptLevel), child: Text(
style: TextStyle( formattedJlptLevel,
color: Colors.white style: const TextStyle(color: Colors.white),
), ),
),
this.jlptLevel.isNotEmpty ? Colors.blue : Colors.transparent
); );
} }
} }

View File

@ -1,28 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:unofficial_jisho_api/api.dart'; import 'package:unofficial_jisho_api/api.dart';
class OtherForms extends StatelessWidget { import '../../../../../bloc/theme/theme_bloc.dart';
final List<JishoJapaneseWord> otherForms;
const OtherForms(this.otherForms); class OtherForms extends StatelessWidget {
final List<JishoJapaneseWord> forms;
const OtherForms({required this.forms, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Column(
child: Column( children: forms.isNotEmpty
children: this.otherForms.isNotEmpty
? [ ? [
Text( const Text(
'Other Forms', 'Other Forms',
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
Row( Row(
children: otherForms.map((form) => _KanaBox(form)).toList(), children: forms.map((form) => _KanaBox(form)).toList(),
), ),
] ]
: [], : [],
),
); );
} }
} }
@ -32,28 +31,19 @@ class _KanaBox extends StatelessWidget {
const _KanaBox(this.word); const _KanaBox(this.word);
bool get hasFurigana => word.word != null;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hasFurigana = (word.word != null);
final _menuColors = final _menuColors =
BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyLight; BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyLight;
return Container( return Container(
child: DefaultTextStyle.merge( margin: const EdgeInsets.symmetric(
child: Column(
children: [
// TODO: take a look at this logic
(hasFurigana) ? Text(word.reading ?? '') : Text(''),
(hasFurigana) ? Text(word.word!) : Text(word.reading ?? ''),
],
),
style: TextStyle(color: _menuColors.foreground),
),
margin: EdgeInsets.symmetric(
horizontal: 5.0, horizontal: 5.0,
vertical: 5.0, vertical: 5.0,
), ),
padding: EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _menuColors.background, color: _menuColors.background,
boxShadow: [ boxShadow: [
@ -61,10 +51,20 @@ class _KanaBox extends StatelessWidget {
color: Colors.grey.withOpacity(0.5), color: Colors.grey.withOpacity(0.5),
spreadRadius: 1, spreadRadius: 1,
blurRadius: 0.5, blurRadius: 0.5,
offset: Offset(1, 1), offset: const Offset(1, 1),
), ),
], ],
), ),
child: DefaultTextStyle.merge(
child: Column(
children: [
// TODO: take a look at this logic
hasFurigana ? Text(word.reading ?? '') : const Text(''),
hasFurigana ? Text(word.word!) : Text(word.reading ?? ''),
],
),
style: TextStyle(color: _menuColors.foreground),
),
); );
} }
} }

View File

@ -4,17 +4,19 @@ import 'package:unofficial_jisho_api/parser.dart';
class Senses extends StatelessWidget { class Senses extends StatelessWidget {
final List<JishoWordSense> senses; final List<JishoWordSense> senses;
const Senses(this.senses); const Senses({
required this.senses,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<Widget> senseWidgets = final List<Widget> senseWidgets =
senses.asMap().entries.map((e) => _Sense(e.key, e.value)).toList(); senses.asMap().entries.map((e) => _Sense(e.key, e.value)).toList();
return Container( return Column(
child: Column(
children: senseWidgets, children: senseWidgets,
)); );
} }
} }
@ -26,37 +28,35 @@ class _Sense extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Column(
child: Column(
children: [ children: [
Row( Row(
children: [ children: [
Text( Text(
(index + 1).toString() + '. ', '${index + 1}. ',
style: TextStyle(color: Colors.grey), style: const TextStyle(color: Colors.grey),
), ),
Text( Text(
sense.partsOfSpeech.join(', '), sense.partsOfSpeech.join(', '),
style: TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
], ],
), ),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
margin: const EdgeInsets.fromLTRB(0, 5, 0, 15),
child: Row( child: Row(
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children:
sense.englishDefinitions.map((def) => Text(def)).toList(), sense.englishDefinitions.map((def) => Text(def)).toList(),
crossAxisAlignment: CrossAxisAlignment.start,
),
]
),
padding: EdgeInsets.symmetric(horizontal: 20),
margin: EdgeInsets.fromLTRB(0, 5, 0, 15),
), ),
], ],
), ),
),
],
); );
} }
} }

View File

@ -14,13 +14,13 @@ class WKBadge extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Badge( return Badge(
Text( child: Text(
_extractWkLevel(this.wkLevel), _extractWkLevel(this.wkLevel),
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
), ),
), ),
this.wkLevel.isNotEmpty ? Colors.red : Colors.transparent color: this.wkLevel.isNotEmpty ? Colors.red : Colors.transparent
); );
} }
} }

View File

@ -13,10 +13,12 @@ class SearchResultCard extends StatelessWidget {
late final JishoJapaneseWord mainWord; late final JishoJapaneseWord mainWord;
late final List<JishoJapaneseWord> otherForms; late final List<JishoJapaneseWord> otherForms;
SearchResultCard(this.result) { SearchResultCard({
this.mainWord = result.japanese[0]; required this.result,
this.otherForms = result.japanese.sublist(1); Key? key,
} }) : mainWord = result.japanese[0],
otherForms = result.japanese.sublist(1),
super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,34 +26,40 @@ class SearchResultCard extends StatelessWidget {
return ExpansionTile( return ExpansionTile(
collapsedBackgroundColor: backgroundColor, collapsedBackgroundColor: backgroundColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
title: title: IntrinsicWidth(
IntrinsicWidth(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
JapaneseHeader(mainWord), JapaneseHeader(word: mainWord),
Row( Row(
children: [ children: [
WKBadge(result.tags.firstWhere((tag) => tag.contains("wanikani"), orElse: () => '')), WKBadge(
JLPTBadge(result.jlpt.isNotEmpty ? result.jlpt[0] : ''), result.tags.firstWhere(
CommonBadge(result.isCommon ?? false) (tag) => tag.contains('wanikani'),
orElse: () => '',
),
),
JLPTBadge(
jlptLevel: result.jlpt.isNotEmpty ? result.jlpt[0] : '',
),
CommonBadge(isCommon: result.isCommon ?? false)
], ],
) )
], ],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
), ),
), ),
children: [ children: [
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column( child: Column(
children: [ children: [
Senses(result.senses), Senses(senses: result.senses),
OtherForms(otherForms), OtherForms(forms: otherForms),
// Text(result.toJson().toString()), // Text(result.toJson().toString()),
// Text(result.attribution.toJson().toString()), // Text(result.attribution.toJson().toString()),
// Text(result.japanese.map((e) => e.toJson().toString()).toList().toString()), // Text(result.japanese.map((e) => e.toJson().toString()).toList().toString()),
], ],
), ),
padding: EdgeInsets.symmetric(horizontal: 30),
) )
], ],
); );

View File

@ -1,75 +1,37 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/search.dart';
import 'package:jisho_study_tool/view/components/history/kanji_search_item.dart';
import 'package:jisho_study_tool/view/components/history/phrase_search_item.dart';
import 'package:jisho_study_tool/view/components/history/date_divider.dart';
import 'package:jisho_study_tool/objectbox.g.dart'; import '../../bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/view/components/opaque_box.dart'; import '../../models/history/search.dart';
import '../../objectbox.g.dart';
import '../components/history/date_divider.dart';
import '../components/history/kanji_search_item.dart';
import '../components/history/phrase_search_item.dart';
import '../components/opaque_box.dart';
class HistoryView extends StatelessWidget { class HistoryView extends StatelessWidget {
const HistoryView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<DatabaseBloc, DatabaseState>( return BlocBuilder<DatabaseBloc, DatabaseState>(
builder: (context, state) { builder: (context, state) {
if (state is DatabaseDisconnected) if (state is DatabaseDisconnected) {
throw DatabaseNotConnectedException(); throw DatabaseNotConnectedException();
}
return StreamBuilder( return StreamBuilder<List<Search>>(
stream: ((state as DatabaseConnected).database.box<Search>().query() stream: getAsyncStream(state),
..order(Search_.timestamp, flags: Order.descending)) builder: (context, snapshot) {
.watch(triggerImmediately: true) if (!snapshot.hasData) {
.map((query) => query.find()), return Container();
builder: (BuildContext context, AsyncSnapshot snapshot) { }
if (!snapshot.hasData) return Container();
final List<Search> data = snapshot.data!;
return OpaqueBox( return OpaqueBox(
child: ListView.separated( child: ListView.separated(
itemCount: snapshot.data.length + 1, itemCount: data.length + 1,
itemBuilder: (context, index) { itemBuilder: historyEntryWithData(data),
if (index == 0) return Container(); separatorBuilder: historyEntrySeparatorWithData(data),
Search search = snapshot.data[index - 1];
if (search.isKanji()) {
return KanjiSearchItem(
result: search.kanjiQuery.target!,
timestamp: search.timestamp,
);
}
return PhraseSearchItem(
search: search.wordQuery.target!,
timestamp: search.timestamp,
);
},
separatorBuilder: (context, index) {
Function roundToDay = (DateTime date) =>
DateTime(date.year, date.month, date.day);
Search search = snapshot.data[index];
DateTime searchDate = roundToDay(search.timestamp);
bool newDate = true;
EdgeInsets? margin;
if (index != 0) {
Search prevSearch = snapshot.data[index - 1];
DateTime prevSearchDate = roundToDay(prevSearch.timestamp);
newDate = prevSearchDate != searchDate;
margin = EdgeInsets.only(bottom: 10);
}
if (newDate) {
if (searchDate == roundToDay(DateTime.now()))
return DateDivider(text: "Today", margin: margin);
else if (searchDate ==
roundToDay(
DateTime.now().subtract(const Duration(days: 1))))
return DateDivider(text: "Yesterday", margin: margin);
return DateDivider(date: searchDate, margin: margin);
}
return Divider();
},
), ),
); );
}, },
@ -77,4 +39,68 @@ class HistoryView extends StatelessWidget {
}, },
); );
} }
Stream<List<Search>> getAsyncStream(DatabaseState state) =>
((state as DatabaseConnected).database.box<Search>().query()
..order(Search_.timestamp, flags: Order.descending))
.watch(triggerImmediately: true)
.map((query) => query.find());
Widget Function(BuildContext, int) historyEntryWithData(List<Search> data) =>
(context, index) {
if (index == 0) {
return Container();
}
final Search search = data[index - 1];
if (search.isKanji()) {
return KanjiSearchItem(
result: search.kanjiQuery.target!,
timestamp: search.timestamp,
);
} else {
return PhraseSearchItem(
search: search.wordQuery.target!,
timestamp: search.timestamp,
);
}
};
DateTime roundToDay(DateTime date) =>
DateTime(date.year, date.month, date.day);
bool dateChangedFromLastSearch(Search prevSearch, DateTime searchDate) {
final DateTime prevSearchDate = roundToDay(prevSearch.timestamp);
return prevSearchDate != searchDate;
}
DateTime get today => roundToDay(DateTime.now());
DateTime get yesterday =>
roundToDay(DateTime.now().subtract(const Duration(days: 1)));
Widget Function(BuildContext, int) historyEntrySeparatorWithData(
List<Search> data,
) =>
(context, index) {
final Search search = data[index];
final DateTime searchDate = roundToDay(search.timestamp);
EdgeInsets? margin;
if (index != 0) {
margin = const EdgeInsets.only(bottom: 10);
}
if (index == 0 ||
dateChangedFromLastSearch(data[index - 1], searchDate)) {
if (searchDate == today)
return DateDivider(text: 'Today', margin: margin);
else if (searchDate == yesterday)
return DateDivider(text: 'Yesterday', margin: margin);
else
return DateDivider(date: searchDate, margin: margin);
}
return const Divider();
};
} }

View File

@ -1,40 +1,40 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/kanji_query.dart'; import '../../../bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/search.dart'; import '../../../models/history/kanji_query.dart';
import 'package:jisho_study_tool/view/components/common/loading.dart'; import '../../../models/history/search.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_result_body.dart'; import '../../../services/jisho_api/kanji_search.dart';
import 'package:jisho_study_tool/services/jisho_api/kanji_search.dart'; import '../../components/common/loading.dart';
import '../../components/kanji/kanji_result_body.dart';
class KanjiResultPage extends StatelessWidget { class KanjiResultPage extends StatelessWidget {
final String kanjiSearchTerm; final String kanjiSearchTerm;
bool addedToDatabase = false; bool addedToDatabase = false;
KanjiResultPage({required this.kanjiSearchTerm, Key? key}) KanjiResultPage({required this.kanjiSearchTerm, Key? key}) : super(key: key);
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: FutureBuilder( body: FutureBuilder<KanjiResult>(
future: fetchKanji(this.kanjiSearchTerm), future: fetchKanji(kanjiSearchTerm),
builder: ( context, snapshot) { builder: ( context, snapshot) {
if (!snapshot.hasData) return LoadingScreen(); if (!snapshot.hasData) return LoadingScreen();
if (snapshot.hasError) return ErrorWidget(snapshot.error!); if (snapshot.hasError) return ErrorWidget(snapshot.error!);
if (!this.addedToDatabase) { if (!addedToDatabase) {
(BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected) (BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected)
.database .database
.box<Search>() .box<Search>()
.put(Search(timestamp: DateTime.now()) .put(Search(timestamp: DateTime.now())
..kanjiQuery.target = KanjiQuery( ..kanjiQuery.target = KanjiQuery(
kanji: this.kanjiSearchTerm, kanji: kanjiSearchTerm,
)); ),);
this.addedToDatabase = true; addedToDatabase = true;
} }
return KanjiResultBody(result: (snapshot.data as KanjiResult)); return KanjiResultBody(result: snapshot.data!);
}, },
), ),
); );

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body.dart'; import '../../components/kanji/kanji_search_body.dart';
class KanjiView extends StatelessWidget { class KanjiView extends StatelessWidget {
const KanjiView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return KanjiSearchBody(); return KanjiSearchBody();

View File

@ -1,22 +1,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_grid.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_search_bar.dart'; import '../../../components/kanji/kanji_search_body/kanji_grid.dart';
import '../../../components/kanji/kanji_search_body/kanji_search_bar.dart';
class SearchGrid extends StatelessWidget { class SearchGrid extends StatelessWidget {
final List<String> suggestions; final List<String> suggestions;
const SearchGrid(this.suggestions);
const SearchGrid({
required this.suggestions,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return return Column(
Column(
children: [ children: [
SizedBox(height: 10), const SizedBox(height: 10),
KanjiSearchBar(), const KanjiSearchBar(),
SizedBox(height: 10), const SizedBox(height: 10),
Expanded( Expanded(child: KanjiGrid(suggestions: suggestions))
child: KanjiGrid(suggestions)
)
], ],
); );
} }

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/search.dart'; import '../../../bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/word_query.dart'; import '../../../models/history/search.dart';
import 'package:jisho_study_tool/view/components/common/loading.dart'; import '../../../models/history/word_query.dart';
import 'package:jisho_study_tool/view/components/search/search_result_body.dart'; import '../../../services/jisho_api/jisho_search.dart';
import 'package:jisho_study_tool/services/jisho_api/jisho_search.dart'; import '../../components/common/loading.dart';
import '../../components/search/search_result_body.dart';
class SearchResultsPage extends StatelessWidget { class SearchResultsPage extends StatelessWidget {
final String searchTerm; final String searchTerm;
@ -12,34 +13,35 @@ class SearchResultsPage extends StatelessWidget {
bool addedToDatabase = false; bool addedToDatabase = false;
SearchResultsPage({required this.searchTerm, Key? key}) SearchResultsPage({required this.searchTerm, Key? key})
: this.results = fetchJishoResults(searchTerm), : results = fetchJishoResults(searchTerm),
super(key: key); super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: FutureBuilder( body: FutureBuilder<JishoAPIResult>(
future: results, future: results,
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) return LoadingScreen(); if (!snapshot.hasData) return LoadingScreen();
if (snapshot.hasError || if (snapshot.hasError || snapshot.data!.data == null)
(snapshot.data as JishoAPIResult).data == null)
return ErrorWidget(snapshot.error!); return ErrorWidget(snapshot.error!);
if (!this.addedToDatabase) { if (!addedToDatabase) {
(BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected) (BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected)
.database .database
.box<Search>() .box<Search>()
.put(Search(timestamp: DateTime.now()) .put(
Search(timestamp: DateTime.now())
..wordQuery.target = WordQuery( ..wordQuery.target = WordQuery(
query: this.searchTerm, query: searchTerm,
)); ),
this.addedToDatabase = true; );
addedToDatabase = true;
} }
return SearchResultsBody( return SearchResultsBody(
results: (snapshot.data as JishoAPIResult).data!, results: snapshot.data!.data!,
); );
}, },
), ),

View File

@ -1,12 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_bar.dart'; import '../../components/search/search_bar.dart';
class SearchView extends StatelessWidget { class SearchView extends StatelessWidget {
const SearchView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: const <Widget>[
SearchBar(), SearchBar(),
], ],
); );

View File

@ -1,15 +1,16 @@
import 'package:confirm_dialog/confirm_dialog.dart'; import 'package:confirm_dialog/confirm_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/models/history/search.dart';
import 'package:jisho_study_tool/models/themes/theme.dart';
import 'package:jisho_study_tool/objectbox.g.dart';
import 'package:settings_ui/settings_ui.dart'; import 'package:settings_ui/settings_ui.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../bloc/database/database_bloc.dart';
import '../../bloc/theme/theme_bloc.dart';
import '../../models/history/search.dart';
import '../../models/themes/theme.dart';
import '../../objectbox.g.dart';
class SettingsView extends StatefulWidget { class SettingsView extends StatefulWidget {
SettingsView({Key? key}) : super(key: key); const SettingsView({Key? key}) : super(key: key);
@override @override
_SettingsViewState createState() => _SettingsViewState(); _SettingsViewState createState() => _SettingsViewState();
@ -40,25 +41,27 @@ class _SettingsViewState extends State<SettingsView> {
} }
/// Update stored preferences with values from setting page state /// Update stored preferences with values from setting page state
void _updatePrefs() async { void _updatePrefs() {
await prefs.setBool('darkThemeEnabled', darkThemeEnabled); prefs.setBool('darkThemeEnabled', darkThemeEnabled);
await prefs.setBool('autoThemeEnabled', autoThemeEnabled); prefs.setBool('autoThemeEnabled', autoThemeEnabled);
} }
@override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TextStyle _titleTextStyle = TextStyle( final TextStyle _titleTextStyle = TextStyle(
color: BlocProvider.of<ThemeBloc>(context).state is DarkThemeState color: BlocProvider.of<ThemeBloc>(context).state is DarkThemeState
? AppTheme.jishoGreen.background ? AppTheme.jishoGreen.background
: null); : null,
);
return SettingsList( return SettingsList(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
contentPadding: EdgeInsets.symmetric(vertical: 10), contentPadding: const EdgeInsets.symmetric(vertical: 10),
sections: [ sections: <SettingsSection>[
SettingsSection( SettingsSection(
title: 'Theme', title: 'Theme',
titleTextStyle: _titleTextStyle, titleTextStyle: _titleTextStyle,
tiles: [ tiles: <SettingsTile>[
SettingsTile.switchTile( SettingsTile.switchTile(
title: 'Automatically determine theme', title: 'Automatically determine theme',
onToggle: (b) { onToggle: (b) {
@ -74,7 +77,8 @@ class _SettingsViewState extends State<SettingsView> {
SettingsTile.switchTile( SettingsTile.switchTile(
title: 'Dark Theme', title: 'Dark Theme',
onToggle: (b) { onToggle: (b) {
BlocProvider.of<ThemeBloc>(context).add(SetTheme(themeIsDark: b)); BlocProvider.of<ThemeBloc>(context)
.add(SetTheme(themeIsDark: b));
setState(() { setState(() {
darkThemeEnabled = b; darkThemeEnabled = b;
}); });
@ -89,7 +93,7 @@ class _SettingsViewState extends State<SettingsView> {
SettingsSection( SettingsSection(
title: 'Cache', title: 'Cache',
titleTextStyle: _titleTextStyle, titleTextStyle: _titleTextStyle,
tiles: [ tiles: <SettingsTile>[
SettingsTile.switchTile( SettingsTile.switchTile(
title: 'Cache grade 1-7 kanji', title: 'Cache grade 1-7 kanji',
switchValue: false, switchValue: false,
@ -123,13 +127,13 @@ class _SettingsViewState extends State<SettingsView> {
SettingsSection( SettingsSection(
title: 'Data', title: 'Data',
titleTextStyle: _titleTextStyle, titleTextStyle: _titleTextStyle,
tiles: [ tiles: <SettingsTile>[
SettingsTile( const SettingsTile(
leading: Icon(Icons.file_download), leading: Icon(Icons.file_download),
title: 'Export Data', title: 'Export Data',
enabled: false, enabled: false,
), ),
SettingsTile( const SettingsTile(
leading: Icon(Icons.delete), leading: Icon(Icons.delete),
title: 'Clear History', title: 'Clear History',
onPressed: _clearHistory, onPressed: _clearHistory,
@ -137,10 +141,10 @@ class _SettingsViewState extends State<SettingsView> {
enabled: false, enabled: false,
), ),
SettingsTile( SettingsTile(
leading: Icon(Icons.delete), leading: const Icon(Icons.delete),
title: 'Clear Favourites', title: 'Clear Favourites',
onPressed: (c) {}, onPressed: (c) {},
titleTextStyle: TextStyle(color: Colors.red), titleTextStyle: const TextStyle(color: Colors.red),
enabled: false, enabled: false,
) )
], ],
@ -150,12 +154,14 @@ class _SettingsViewState extends State<SettingsView> {
} }
} }
void _clearHistory(context) async { void _clearHistory(context) {
if (await confirm(context)) { confirm(context).then((userIsSure) {
Store db = if (userIsSure) {
final Store db =
(BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected) (BlocProvider.of<DatabaseBloc>(context).state as DatabaseConnected)
.database; .database;
// db.box<Search>().query().build().find() // db.box<Search>().query().build().find()
db.box<Search>().removeAll(); db.box<Search>().removeAll();
} }
});
} }

View File

@ -119,7 +119,7 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.2.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:

View File

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jisho_study_tool/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}