Add theming functionality
This commit is contained in:
parent
0a7b71037b
commit
feee76c2b6
|
@ -1,9 +1,11 @@
|
|||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
import './database_event.dart';
|
||||
import './database_state.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
export './database_event.dart';
|
||||
export './database_state.dart';
|
||||
export './database_not_connected_exception.dart';
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'dart:async';
|
|||
import 'package:bloc/bloc.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
part 'history_event.dart';
|
||||
part 'history_state.dart';
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import 'package:bloc/bloc.dart';
|
|||
import 'package:jisho_study_tool/services/jisho_api/kanji_search.dart';
|
||||
import 'package:jisho_study_tool/services/kanji_suggestions.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
export './kanji_event.dart';
|
||||
export './kanji_state.dart';
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
import './navigation_event.dart';
|
||||
import './navigation_state.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
export './navigation_event.dart';
|
||||
export './navigation_state.dart';
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
|||
import 'package:jisho_study_tool/services/jisho_api/jisho_search.dart';
|
||||
import 'package:unofficial_jisho_api/parser.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
part 'search_event.dart';
|
||||
part 'search_state.dart';
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:jisho_study_tool/models/themes/theme.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
export 'package:flutter_bloc/flutter_bloc.dart';
|
||||
export 'package:jisho_study_tool/models/themes/theme.dart';
|
||||
|
||||
part 'theme_event.dart';
|
||||
part 'theme_state.dart';
|
||||
|
||||
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
|
||||
bool prefsAreLoaded = false;
|
||||
|
||||
ThemeBloc() : super(LightThemeState()) {
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
this.prefsAreLoaded = true;
|
||||
this.add(
|
||||
SetTheme(
|
||||
themeIsDark: prefs.getBool('darkThemeEnabled') ?? false,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<ThemeState> mapEventToState(ThemeEvent event) async* {
|
||||
if (event is SetTheme)
|
||||
yield event.themeIsDark
|
||||
? DarkThemeState(prefsAreLoaded: prefsAreLoaded)
|
||||
: LightThemeState(prefsAreLoaded: prefsAreLoaded);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
part of 'theme_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ThemeEvent {
|
||||
const ThemeEvent();
|
||||
}
|
||||
|
||||
class SetTheme extends ThemeEvent {
|
||||
final bool themeIsDark;
|
||||
|
||||
const SetTheme({required this.themeIsDark});
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
part of 'theme_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ThemeState {
|
||||
final bool prefsAreLoaded;
|
||||
|
||||
const ThemeState(this.prefsAreLoaded);
|
||||
|
||||
AppTheme get theme;
|
||||
}
|
||||
|
||||
class LightThemeState extends ThemeState {
|
||||
final bool prefsAreLoaded;
|
||||
|
||||
const LightThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded);
|
||||
|
||||
AppTheme get theme => LightTheme();
|
||||
}
|
||||
|
||||
class DarkThemeState extends ThemeState {
|
||||
final bool prefsAreLoaded;
|
||||
|
||||
const DarkThemeState({this.prefsAreLoaded = false}) : super(prefsAreLoaded);
|
||||
|
||||
AppTheme get theme => DarkTheme();
|
||||
}
|
106
lib/main.dart
106
lib/main.dart
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/screens/loading.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/screens/splash.dart';
|
||||
import 'package:mdi/mdi.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
@ -17,6 +17,8 @@ import 'package:jisho_study_tool/view/screens/history.dart';
|
|||
import 'package:jisho_study_tool/view/screens/search/view.dart';
|
||||
import 'package:jisho_study_tool/view/screens/settings.dart';
|
||||
|
||||
import 'models/themes/theme.dart';
|
||||
|
||||
void main() => runApp(MyApp());
|
||||
|
||||
DatabaseBloc _databaseBloc = DatabaseBloc();
|
||||
|
@ -28,6 +30,7 @@ class MyApp extends StatefulWidget {
|
|||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
late final Store _store;
|
||||
bool dbConnected = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -40,6 +43,9 @@ class _MyAppState extends State<MyApp> {
|
|||
);
|
||||
|
||||
_databaseBloc.add(ConnectedToDatabase(_store));
|
||||
setState(() {
|
||||
dbConnected = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,30 +58,24 @@ class _MyAppState extends State<MyApp> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Jisho Study Tool',
|
||||
|
||||
// TODO: Add color theme
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (context) => SearchBloc(_databaseBloc)),
|
||||
BlocProvider(create: (context) => KanjiBloc(_databaseBloc)),
|
||||
BlocProvider(create: (context) => _databaseBloc),
|
||||
BlocProvider(create: (context) => NavigationBloc()),
|
||||
],
|
||||
child:
|
||||
BlocBuilder<DatabaseBloc, DatabaseState>(builder: (context, state) {
|
||||
if (state is DatabaseDisconnected)
|
||||
return Container(
|
||||
child: LoadingScreen(),
|
||||
decoration: BoxDecoration(color: Colors.white),
|
||||
);
|
||||
|
||||
return Home();
|
||||
}),
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (context) => SearchBloc(_databaseBloc)),
|
||||
BlocProvider(create: (context) => KanjiBloc(_databaseBloc)),
|
||||
BlocProvider(create: (context) => _databaseBloc),
|
||||
BlocProvider(create: (context) => NavigationBloc()),
|
||||
BlocProvider(create: (context) => ThemeBloc()),
|
||||
],
|
||||
child: BlocBuilder<ThemeBloc, ThemeState>(
|
||||
builder: (context, themeState) {
|
||||
return MaterialApp(
|
||||
title: 'Jisho Study Tool',
|
||||
theme: themeState.theme.getMaterialTheme(),
|
||||
home: dbConnected && themeState.prefsAreLoaded
|
||||
? Home()
|
||||
: SplashScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -85,25 +85,41 @@ class Home extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<NavigationBloc, NavigationState>(
|
||||
builder: (context, state) {
|
||||
int selectedPage = (state as NavigationPage).pageNum;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: pages[selectedPage].titleBar,
|
||||
centerTitle: true,
|
||||
),
|
||||
body: pages[selectedPage].content,
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
currentIndex: selectedPage,
|
||||
onTap: (int index) =>
|
||||
BlocProvider.of<NavigationBloc>(context).add(ChangePage(index)),
|
||||
items: pages.map((p) => p.item).toList(),
|
||||
showSelectedLabels: false,
|
||||
showUnselectedLabels: false,
|
||||
unselectedItemColor: Colors.blue,
|
||||
selectedItemColor: Colors.green,
|
||||
),
|
||||
builder: (context, navigationState) {
|
||||
int selectedPage = (navigationState as NavigationPage).pageNum;
|
||||
return BlocBuilder<ThemeBloc, ThemeState>(
|
||||
builder: (context, themeState) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: pages[selectedPage].titleBar,
|
||||
centerTitle: true,
|
||||
backgroundColor: AppTheme.jishoGreen.background,
|
||||
foregroundColor: AppTheme.jishoGreen.foreground,
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
child: Image.asset(
|
||||
'assets/images/denshi_jisho_background_overlay.png'),
|
||||
right: 30,
|
||||
left: 100,
|
||||
bottom: 30,
|
||||
),
|
||||
pages[selectedPage].content,
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
fixedColor: AppTheme.jishoGreen.background,
|
||||
currentIndex: selectedPage,
|
||||
onTap: (int index) => BlocProvider.of<NavigationBloc>(context)
|
||||
.add(ChangePage(index)),
|
||||
items: pages.map((p) => p.item).toList(),
|
||||
showSelectedLabels: false,
|
||||
showUnselectedLabels: false,
|
||||
unselectedItemColor: themeState.theme.menuGreyDark.background,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
part of './theme.dart';
|
||||
|
||||
class DarkTheme extends AppTheme {
|
||||
ColorSet get kanjiResultColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.green,
|
||||
);
|
||||
|
||||
ColorSet get onyomiColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.orange,
|
||||
);
|
||||
|
||||
ColorSet get kunyomiColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.lightBlue,
|
||||
);
|
||||
|
||||
Color get foreground => Colors.black;
|
||||
Color get background => Colors.white;
|
||||
|
||||
ColorSet get menuGreyLight => ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.grey.shade700,
|
||||
);
|
||||
|
||||
ColorSet get menuGreyNormal => ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.grey,
|
||||
);
|
||||
|
||||
ColorSet get menuGreyDark => ColorSet(
|
||||
foreground: Colors.black,
|
||||
background: Colors.grey.shade300,
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeData getMaterialTheme() {
|
||||
return ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: createMaterialColor(AppTheme.jishoGreen.background),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
part of './theme.dart';
|
||||
|
||||
class LightTheme extends AppTheme {
|
||||
ColorSet get kanjiResultColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.blue,
|
||||
);
|
||||
|
||||
ColorSet get onyomiColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.orange,
|
||||
);
|
||||
|
||||
ColorSet get kunyomiColor => const ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.lightBlue,
|
||||
);
|
||||
|
||||
Color get foreground => Colors.black;
|
||||
Color get background => Colors.white;
|
||||
|
||||
ColorSet get menuGreyLight => ColorSet(
|
||||
foreground: Colors.black,
|
||||
background: Colors.grey.shade300,
|
||||
);
|
||||
ColorSet get menuGreyNormal => ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.grey,
|
||||
);
|
||||
|
||||
ColorSet get menuGreyDark => ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Colors.grey.shade700,
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeData getMaterialTheme() {
|
||||
return ThemeData(
|
||||
brightness: Brightness.light,
|
||||
primarySwatch: createMaterialColor(AppTheme.jishoGreen.background),
|
||||
// primarySwatch: Colors.green,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
part 'light.dart';
|
||||
part 'dark.dart';
|
||||
|
||||
abstract class AppTheme {
|
||||
|
||||
static const ColorSet jishoGreen = ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Color(0xFF3EDD00),
|
||||
);
|
||||
|
||||
static const Color jishoGrey = Color(0xFF5A5A5B);
|
||||
|
||||
static const ColorSet jishoLabel = ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Color(0xFF909DC0),
|
||||
);
|
||||
|
||||
static const ColorSet jishoCommon = ColorSet(
|
||||
foreground: Colors.white,
|
||||
background: Color(0xFF8ABC83),
|
||||
);
|
||||
|
||||
ColorSet get kanjiResultColor;
|
||||
|
||||
ColorSet get onyomiColor;
|
||||
ColorSet get kunyomiColor;
|
||||
|
||||
Color get foreground;
|
||||
Color get background;
|
||||
|
||||
ColorSet get menuGreyLight;
|
||||
ColorSet get menuGreyNormal;
|
||||
ColorSet get menuGreyDark;
|
||||
|
||||
ThemeData getMaterialTheme();
|
||||
}
|
||||
|
||||
class ColorSet {
|
||||
final Color foreground;
|
||||
final Color background;
|
||||
|
||||
const ColorSet({
|
||||
required this.foreground,
|
||||
required this.background,
|
||||
});
|
||||
}
|
||||
|
||||
/// Source: https://blog.usejournal.com/creating-a-custom-color-swatch-in-flutter-554bcdcb27f3
|
||||
MaterialColor createMaterialColor(Color color) {
|
||||
List strengths = <double>[.05];
|
||||
final swatch = <int, Color>{};
|
||||
final int r = color.red, g = color.green, b = color.blue;
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
strengths.add(0.1 * i);
|
||||
}
|
||||
strengths.forEach((strength) {
|
||||
final double ds = 0.5 - strength;
|
||||
swatch[(strength * 1000).round()] = Color.fromRGBO(
|
||||
r + ((ds < 0 ? r : (255 - r)) * ds).round(),
|
||||
g + ((ds < 0 ? g : (255 - g)) * ds).round(),
|
||||
b + ((ds < 0 ? b : (255 - b)) * ds).round(),
|
||||
1,
|
||||
);
|
||||
});
|
||||
return MaterialColor(color.value, swatch);
|
||||
}
|
|
@ -1,10 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
import 'package:jisho_study_tool/models/themes/theme.dart';
|
||||
|
||||
class DateDivider extends StatelessWidget {
|
||||
final String? text;
|
||||
final DateTime? date;
|
||||
final EdgeInsets? margin;
|
||||
|
||||
const DateDivider({this.text, this.date, Key? key}) : super(key: key);
|
||||
const DateDivider({
|
||||
this.text,
|
||||
this.date,
|
||||
this.margin = const EdgeInsets.symmetric(vertical: 10),
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
String getHumanReadableDate(DateTime date) {
|
||||
const Map<int, String> monthTable = {
|
||||
|
@ -22,30 +30,34 @@ class DateDivider extends StatelessWidget {
|
|||
12: 'Dec',
|
||||
};
|
||||
|
||||
int day = date.day;
|
||||
String month = monthTable[date.month]!;
|
||||
int year = date.year;
|
||||
final int day = date.day;
|
||||
final String month = monthTable[date.month]!;
|
||||
final int year = date.year;
|
||||
return "$day. $month $year";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget header = (this.text != null)
|
||||
final Widget header = (this.text != null)
|
||||
? Text(this.text!)
|
||||
: (this.date != null)
|
||||
? Text(getHumanReadableDate(this.date!))
|
||||
: SizedBox.shrink();
|
||||
|
||||
final ColorSet _menuColors =
|
||||
BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyNormal;
|
||||
|
||||
return Container(
|
||||
child: DefaultTextStyle.merge(
|
||||
child: header,
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: _menuColors.foreground),
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.grey),
|
||||
decoration: BoxDecoration(color: _menuColors.background),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 10,
|
||||
),
|
||||
margin: this.margin,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.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';
|
||||
|
||||
|
@ -14,13 +15,15 @@ class _KanjiBox extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ColorSet _menuColors = BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyLight;
|
||||
|
||||
return IntrinsicHeight(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
color: _menuColors.background,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
child: Center(
|
||||
|
@ -28,7 +31,7 @@ class _KanjiBox extends StatelessWidget {
|
|||
child: Text(
|
||||
kanji,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: _menuColors.foreground,
|
||||
fontSize: 25,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class KanjiGrid extends StatelessWidget {
|
||||
final List<String> suggestions;
|
||||
|
@ -35,9 +35,13 @@ class _GridItem extends StatelessWidget {
|
|||
onTap: () {
|
||||
BlocProvider.of<KanjiBloc>(context).add(GetKanji(kanji));
|
||||
},
|
||||
child: Container(
|
||||
child: BlocBuilder<ThemeBloc, ThemeState>(
|
||||
builder: (context, state) {
|
||||
final _menuColors = state.theme.menuGreyLight;
|
||||
return
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
color: _menuColors.background,
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Container(
|
||||
|
@ -45,10 +49,12 @@ class _GridItem extends StatelessWidget {
|
|||
child: FittedBox(
|
||||
child: Text(
|
||||
kanji,
|
||||
style: TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: _menuColors.foreground),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
|
||||
class KanjiSearchBar extends StatefulWidget {
|
||||
|
@ -59,9 +58,8 @@ class _KanjiSearchBarState extends State<KanjiSearchBar> {
|
|||
decoration: new InputDecoration(
|
||||
prefixIcon: Icon(Icons.search),
|
||||
hintText: 'Search',
|
||||
fillColor: Colors.white,
|
||||
filled: true,
|
||||
|
||||
// fillColor: Colors.white,
|
||||
// filled: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
|
||||
//TODO: Make buttons have an effect
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
import 'package:unofficial_jisho_api/api.dart';
|
||||
|
||||
class Examples extends StatelessWidget {
|
||||
|
@ -12,32 +13,27 @@ class Examples extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ExpansionTile(
|
||||
title: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
return Column(
|
||||
children: [
|
||||
[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'Examples',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
'Examples:',
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
onyomiExamples
|
||||
.map((onyomiExample) => _Example(onyomiExample, _KanaType.onyomi))
|
||||
.toList(),
|
||||
kunyomiExamples
|
||||
.map((kunyomiExample) =>
|
||||
_Example(kunyomiExample, _KanaType.kunyomi))
|
||||
.toList(),
|
||||
].expand((list) => list).toList());
|
||||
)
|
||||
],
|
||||
onyomiExamples
|
||||
.map((onyomiExample) => _Example(onyomiExample, _KanaType.onyomi))
|
||||
.toList(),
|
||||
kunyomiExamples
|
||||
.map(
|
||||
(kunyomiExample) => _Example(kunyomiExample, _KanaType.kunyomi))
|
||||
.toList(),
|
||||
].expand((list) => list).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,75 +47,75 @@ class _Example extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
final _themeData = BlocProvider.of<ThemeBloc>(context).state.theme;
|
||||
final _kanaColors = kanaType == _KanaType.kunyomi ? _themeData.kunyomiColor : _themeData.onyomiColor;
|
||||
final _menuColors = _themeData.menuGreyNormal;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
vertical: 5.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey, borderRadius: BorderRadius.circular(10.0)),
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 10.0,
|
||||
color: _menuColors.background, borderRadius: BorderRadius.circular(10.0)),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: _kanaColors.background,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10.0),
|
||||
bottomLeft: Radius.circular(10.0),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: (kanaType == _KanaType.kunyomi)
|
||||
? Colors.lightBlue
|
||||
: Colors.orange,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10.0),
|
||||
bottomLeft: Radius.circular(10.0),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.reading,
|
||||
style: TextStyle(
|
||||
color: _kanaColors.foreground,
|
||||
fontSize: 15.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.reading,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15.0,
|
||||
),
|
||||
SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.example,
|
||||
style: TextStyle(
|
||||
color: _kanaColors.foreground,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.example,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 15.0,
|
||||
),
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
children: [
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.meaning,
|
||||
style: TextStyle(
|
||||
color: _menuColors.foreground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
width: 15.0,
|
||||
),
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
children: [
|
||||
Container(
|
||||
child: Text(
|
||||
yomiExample.meaning,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class Grade extends StatelessWidget {
|
||||
final String grade;
|
||||
|
@ -7,17 +8,19 @@ class Grade extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
grade,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: _kanjiColors.foreground,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class Header extends StatelessWidget {
|
||||
final String kanji;
|
||||
|
@ -7,17 +8,19 @@ class Header extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
kanji,
|
||||
style: TextStyle(fontSize: 70.0, color: Colors.white),
|
||||
style: TextStyle(fontSize: 70.0, color: _kanjiColors.foreground),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class JlptLevel extends StatelessWidget {
|
||||
final String jlptLevel;
|
||||
|
@ -8,18 +9,20 @@ class JlptLevel extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
jlptLevel,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: _kanjiColors.foreground,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class Kunyomi extends StatelessWidget {
|
||||
final List<String> kunyomi;
|
||||
late final List<_KunyomiCard> kunyomiCards;
|
||||
late final bool expandable;
|
||||
|
||||
Kunyomi(this.kunyomi) {
|
||||
kunyomiCards = kunyomi.map((kunyomi) => _KunyomiCard(kunyomi)).toList();
|
||||
expandable = (kunyomi.length > 6);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0,
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _kunyomiWrapper(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _kunyomiWrapper(BuildContext context) {
|
||||
if (expandable) {
|
||||
return ExpansionTile(
|
||||
initiallyExpanded: false,
|
||||
title: Center(child: _KunyomiCard('Kunyomi')),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: kunyomiCards,
|
||||
),
|
||||
SizedBox(
|
||||
height: 25.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: kunyomiCards,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _KunyomiCard extends StatelessWidget {
|
||||
final String kunyomi;
|
||||
const _KunyomiCard(this.kunyomi);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
child: Text(
|
||||
kunyomi,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightBlue,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class Meaning extends StatelessWidget {
|
||||
late final List<String> meanings;
|
||||
late final List<_MeaningCard> meaningCards;
|
||||
late final bool expandable;
|
||||
|
||||
Meaning(meaning) {
|
||||
this.meanings = meaning.split(', ');
|
||||
this.meaningCards =
|
||||
meanings.map((m) => _MeaningCard(m)).toList();
|
||||
this.expandable = (this.meanings.length > 6);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0,
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _meaningWrapper(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _meaningWrapper(BuildContext context) {
|
||||
if (expandable) {
|
||||
return ExpansionTile(
|
||||
initiallyExpanded: false,
|
||||
title: Center(child: _MeaningCard('Meanings')),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: meaningCards,
|
||||
),
|
||||
SizedBox(
|
||||
height: 25.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: meaningCards,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _MeaningCard extends StatelessWidget {
|
||||
final String meaning;
|
||||
|
||||
const _MeaningCard(this.meaning);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 10.0,
|
||||
),
|
||||
child: Text(
|
||||
meaning,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class Onyomi extends StatelessWidget {
|
||||
final List<String> onyomi;
|
||||
late final List<_OnyomiCard> onyomiCards;
|
||||
late final bool expandable;
|
||||
|
||||
Onyomi(this.onyomi) {
|
||||
onyomiCards = onyomi.map((onyomi) => _OnyomiCard(onyomi)).toList();
|
||||
expandable = (onyomi.length > 6);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0,
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _onyomiWrapper(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _onyomiWrapper(BuildContext context) {
|
||||
if (expandable) {
|
||||
return ExpansionTile(
|
||||
initiallyExpanded: false,
|
||||
title: Center(child: _OnyomiCard('Onyomi')),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: onyomiCards,
|
||||
),
|
||||
SizedBox(
|
||||
height: 25.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: onyomiCards,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _OnyomiCard extends StatelessWidget {
|
||||
final String onyomi;
|
||||
|
||||
const _OnyomiCard(this.onyomi);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
child: Text(
|
||||
onyomi,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
import 'package:unofficial_jisho_api/api.dart' as jisho;
|
||||
|
||||
class Radical extends StatelessWidget {
|
||||
|
@ -8,18 +9,20 @@ class Radical extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
radical.symbol,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: _kanjiColors.foreground,
|
||||
fontSize: 40.0,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class Rank extends StatelessWidget {
|
||||
final int rank;
|
||||
|
@ -8,18 +9,20 @@ class Rank extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
'${rank.toString()} / 2500',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: _kanjiColors.foreground,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
class StrokeOrderGif extends StatelessWidget {
|
||||
final String uri;
|
||||
|
@ -7,6 +8,8 @@ class StrokeOrderGif extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _kanjiColors = BlocProvider.of<ThemeBloc>(context).state.theme.kanjiResultColor;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 20.0),
|
||||
padding: EdgeInsets.all(5.0),
|
||||
|
@ -15,7 +18,7 @@ class StrokeOrderGif extends StatelessWidget {
|
|||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
color: _kanjiColors.background,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
|
||||
enum YomiType {
|
||||
onyomi,
|
||||
kunyomi,
|
||||
meaning,
|
||||
}
|
||||
|
||||
extension on YomiType {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case YomiType.onyomi:
|
||||
return 'Onyomi';
|
||||
case YomiType.kunyomi:
|
||||
return 'Kunyomi';
|
||||
case YomiType.meaning:
|
||||
return 'Meanings';
|
||||
}
|
||||
}
|
||||
|
||||
ColorSet getColors(BuildContext context) {
|
||||
final theme = BlocProvider.of<ThemeBloc>(context).state.theme;
|
||||
|
||||
switch (this) {
|
||||
case YomiType.onyomi:
|
||||
return theme.onyomiColor;
|
||||
case YomiType.kunyomi:
|
||||
return theme.kunyomiColor;
|
||||
case YomiType.meaning:
|
||||
return theme.menuGreyNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class YomiChips extends StatelessWidget {
|
||||
final List<String> yomi;
|
||||
final YomiType type;
|
||||
|
||||
const YomiChips(this.yomi, this.type);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0,
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _yomiWrapper(context),
|
||||
);
|
||||
}
|
||||
|
||||
bool isExpandable() => yomi.length > 6;
|
||||
|
||||
Widget _yomiWrapper(BuildContext context) {
|
||||
final yomiCards = this
|
||||
.yomi
|
||||
.map((yomi) => _YomiCard(
|
||||
yomi: yomi,
|
||||
colors: this.type.getColors(context),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (!this.isExpandable())
|
||||
return Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: yomiCards,
|
||||
);
|
||||
|
||||
return ExpansionTile(
|
||||
initiallyExpanded: false,
|
||||
title: Center(
|
||||
child: _YomiCard(
|
||||
yomi: this.type.title,
|
||||
colors: this.type.getColors(context),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 10.0,
|
||||
children: yomiCards,
|
||||
),
|
||||
SizedBox(
|
||||
height: 25.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _YomiCard extends StatelessWidget {
|
||||
final String yomi;
|
||||
final ColorSet colors;
|
||||
|
||||
const _YomiCard({required this.yomi, required this.colors});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
child: Text(
|
||||
this.yomi,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: colors.foreground,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.background,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class OpaqueBox extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const OpaqueBox({required this.child, Key? key,}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor),
|
||||
child: this.child,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/models/themes/theme.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LanguageSelector extends StatefulWidget {
|
||||
|
@ -43,13 +44,13 @@ class _LanguageSelectorState extends State<LanguageSelector> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ToggleButtons(
|
||||
selectedColor: AppTheme.jishoGreen.background,
|
||||
isSelected: isSelected,
|
||||
children: <Widget> [
|
||||
_LanguageOption("Auto"),
|
||||
_LanguageOption("日本語"),
|
||||
_LanguageOption("English")
|
||||
],
|
||||
selectedColor: Colors.blue,
|
||||
onPressed: (int buttonIndex) {
|
||||
setState(() {
|
||||
for (var i in Iterable.generate(isSelected.length)) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/components/search/language_selector.dart';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
|
||||
import 'package:unofficial_jisho_api/api.dart';
|
||||
|
||||
class OtherForms extends StatelessWidget {
|
||||
|
@ -10,18 +11,17 @@ class OtherForms extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children:
|
||||
this.otherForms.isNotEmpty
|
||||
? [
|
||||
Text(
|
||||
'Other Forms',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Row(
|
||||
children: otherForms.map((form) => _KanaBox(form)).toList(),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
children: this.otherForms.isNotEmpty
|
||||
? [
|
||||
Text(
|
||||
'Other Forms',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Row(
|
||||
children: otherForms.map((form) => _KanaBox(form)).toList(),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -35,14 +35,19 @@ class _KanaBox extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasFurigana = (word.word != null);
|
||||
final _menuColors =
|
||||
BlocProvider.of<ThemeBloc>(context).state.theme.menuGreyLight;
|
||||
|
||||
return Container(
|
||||
child: Column(
|
||||
children: [
|
||||
// TODO: take a look at this logic
|
||||
(hasFurigana) ? Text(word.reading ?? '') : Text(''),
|
||||
(hasFurigana) ? Text(word.word!) : Text(word.reading ?? ''),
|
||||
],
|
||||
child: DefaultTextStyle.merge(
|
||||
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,
|
||||
|
@ -50,7 +55,7 @@ class _KanaBox extends StatelessWidget {
|
|||
),
|
||||
padding: EdgeInsets.all(5.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: _menuColors.background,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
|
|
|
@ -21,7 +21,10 @@ class SearchResultCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = Theme.of(context).scaffoldBackgroundColor;
|
||||
return ExpansionTile(
|
||||
collapsedBackgroundColor: backgroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
title:
|
||||
IntrinsicWidth(
|
||||
child: Row(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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';
|
||||
|
@ -7,6 +6,7 @@ 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 'package:jisho_study_tool/view/components/opaque_box.dart';
|
||||
|
||||
class HistoryView extends StatelessWidget {
|
||||
@override
|
||||
|
@ -23,50 +23,54 @@ class HistoryView extends StatelessWidget {
|
|||
.map((query) => query.find()),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (!snapshot.hasData) return Container();
|
||||
return ListView.separated(
|
||||
itemCount: snapshot.data.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) return Container();
|
||||
Search search = snapshot.data[index - 1];
|
||||
if (search.isKanji()) {
|
||||
return KanjiSearchItem(
|
||||
result: search.kanjiQuery.target!,
|
||||
return OpaqueBox(
|
||||
child: ListView.separated(
|
||||
itemCount: snapshot.data.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) return Container();
|
||||
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,
|
||||
);
|
||||
}
|
||||
return PhraseSearchItem(
|
||||
search: search.wordQuery.target!,
|
||||
timestamp: search.timestamp,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
Function roundToDay = (DateTime date) =>
|
||||
DateTime(date.year, date.month, date.day);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
Function roundToDay = (DateTime date) =>
|
||||
DateTime(date.year, date.month, date.day);
|
||||
|
||||
Search search = snapshot.data[index];
|
||||
DateTime searchDate = roundToDay(search.timestamp);
|
||||
Search search = snapshot.data[index];
|
||||
DateTime searchDate = roundToDay(search.timestamp);
|
||||
|
||||
bool newDate = true;
|
||||
bool newDate = true;
|
||||
|
||||
if (index != 0) {
|
||||
Search prevSearch = snapshot.data[index - 1];
|
||||
EdgeInsets? margin;
|
||||
if (index != 0) {
|
||||
Search prevSearch = snapshot.data[index - 1];
|
||||
|
||||
DateTime prevSearchDate = roundToDay(prevSearch.timestamp);
|
||||
newDate = prevSearchDate != searchDate;
|
||||
}
|
||||
DateTime prevSearchDate = roundToDay(prevSearch.timestamp);
|
||||
newDate = prevSearchDate != searchDate;
|
||||
margin = EdgeInsets.only(bottom: 10);
|
||||
}
|
||||
|
||||
if (newDate) {
|
||||
if (searchDate == roundToDay(DateTime.now()))
|
||||
return DateDivider(text: "Today");
|
||||
else if (searchDate ==
|
||||
roundToDay(
|
||||
DateTime.now().subtract(const Duration(days: 1))))
|
||||
return DateDivider(text: "Yesterday");
|
||||
return DateDivider(date: searchDate);
|
||||
}
|
||||
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();
|
||||
},
|
||||
return Divider();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:unofficial_jisho_api/api.dart' as jisho;
|
||||
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/examples.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/grade.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/header.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/jlpt_level.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/meaning.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/radical.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/rank.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/stroke_order_gif.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/onyomi.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/kunyomi.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/examples.dart';
|
||||
import 'package:jisho_study_tool/view/components/kanji/result/yomi_chips.dart';
|
||||
|
||||
class KanjiResultCard extends StatelessWidget {
|
||||
late final String query;
|
||||
late final jisho.KanjiResultData resultData;
|
||||
|
||||
KanjiResultCard({required jisho.KanjiResult result}) {
|
||||
|
||||
query = result.query;
|
||||
|
||||
// TODO: Handle this kind of exception before widget is initialized
|
||||
if (result.data == null)
|
||||
throw Exception();
|
||||
|
||||
resultData = result.data!;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
|
@ -47,9 +54,9 @@ class KanjiResultCard extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
Meaning(resultData.meaning),
|
||||
resultData.onyomi.length != 0 ? Onyomi(resultData.onyomi) : SizedBox.shrink(),
|
||||
resultData.kunyomi.length != 0 ? Kunyomi(resultData.kunyomi) : SizedBox.shrink(),
|
||||
YomiChips(resultData.meaning.split(', '), YomiType.meaning),
|
||||
resultData.onyomi.length != 0 ? YomiChips(resultData.onyomi, YomiType.onyomi) : SizedBox.shrink(),
|
||||
resultData.kunyomi.length != 0 ? YomiChips(resultData.kunyomi, YomiType.kunyomi) : SizedBox.shrink(),
|
||||
IntrinsicHeight(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
|
@ -88,15 +95,4 @@ class KanjiResultCard extends StatelessWidget {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
KanjiResultCard({required jisho.KanjiResult result}) {
|
||||
|
||||
query = result.query;
|
||||
|
||||
// TODO: Handle this kind of exception before widget is initialized
|
||||
if (result.data == null)
|
||||
throw Exception();
|
||||
|
||||
resultData = result.data!;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
import 'package:animated_size_and_fade/animated_size_and_fade.dart';
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/screens/loading.dart';
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/components/search/search_result_page/search_card.dart';
|
||||
import 'package:unofficial_jisho_api/api.dart';
|
||||
|
||||
class SearchResults extends StatelessWidget {
|
||||
final List<JishoResult> results;
|
||||
|
||||
const SearchResults({
|
||||
required this.results,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
child: ListView(
|
||||
children: results.map((result) => SearchResultCard(result)).toList(),
|
||||
),
|
||||
onWillPop: () async {
|
||||
BlocProvider.of<SearchBloc>(context).add(ReturnToInitialState());
|
||||
return false;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||
import 'package:jisho_study_tool/view/components/search/search_bar.dart';
|
||||
import 'package:jisho_study_tool/view/screens/loading.dart';
|
||||
import 'package:jisho_study_tool/view/components/search/search_result_page/search_card.dart';
|
||||
import 'package:jisho_study_tool/view/screens/search/results.dart';
|
||||
|
||||
class SearchView extends StatelessWidget {
|
||||
@override
|
||||
|
@ -16,18 +15,7 @@ class SearchView extends StatelessWidget {
|
|||
else if (state is SearchLoading)
|
||||
return LoadingScreen();
|
||||
else if (state is SearchFinished) {
|
||||
return WillPopScope(
|
||||
child: ListView(
|
||||
children: state.results
|
||||
.map((result) => SearchResultCard(result))
|
||||
.toList(),
|
||||
),
|
||||
onWillPop: () async {
|
||||
BlocProvider.of<SearchBloc>(context).add(ReturnToInitialState());
|
||||
print('Popped');
|
||||
return false;
|
||||
},
|
||||
);
|
||||
return SearchResults(results: state.results);
|
||||
}
|
||||
throw 'No such event found';
|
||||
},
|
||||
|
|
|
@ -1,61 +1,150 @@
|
|||
import 'package:confirm_dialog/confirm_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SettingsView extends StatefulWidget {
|
||||
SettingsView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SettingsViewState createState() => _SettingsViewState();
|
||||
}
|
||||
|
||||
class _SettingsViewState extends State<SettingsView> {
|
||||
late final SharedPreferences prefs;
|
||||
|
||||
bool darkThemeEnabled = false;
|
||||
bool autoThemeEnabled = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
this.prefs = prefs;
|
||||
_getPrefs();
|
||||
});
|
||||
}
|
||||
|
||||
/// Get stored preferences and set setting page state accordingly
|
||||
void _getPrefs() {
|
||||
setState(() {
|
||||
darkThemeEnabled = prefs.getBool('darkThemeEnabled') ?? darkThemeEnabled;
|
||||
autoThemeEnabled = prefs.getBool('autoThemeEnabled') ?? autoThemeEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
/// Update stored preferences with values from setting page state
|
||||
void _updatePrefs() async {
|
||||
await prefs.setBool('darkThemeEnabled', darkThemeEnabled);
|
||||
await prefs.setBool('autoThemeEnabled', autoThemeEnabled);
|
||||
}
|
||||
|
||||
class SettingsView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final TextStyle _titleTextStyle = TextStyle(
|
||||
color: BlocProvider.of<ThemeBloc>(context).state is DarkThemeState
|
||||
? AppTheme.jishoGreen.background
|
||||
: null);
|
||||
|
||||
return SettingsList(
|
||||
backgroundColor: Colors.transparent,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 10),
|
||||
sections: [
|
||||
SettingsSection(
|
||||
title: 'Cache',
|
||||
title: 'Theme',
|
||||
titleTextStyle: _titleTextStyle,
|
||||
tiles: [
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache grade 1-7 kanji (N/A)',
|
||||
switchValue: false,
|
||||
onToggle: (v) {},
|
||||
title: 'Automatically determine theme',
|
||||
onToggle: (b) {
|
||||
setState(() {
|
||||
autoThemeEnabled = b;
|
||||
});
|
||||
_updatePrefs();
|
||||
},
|
||||
switchValue: autoThemeEnabled,
|
||||
enabled: false,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache grade standard kanji (N/A)',
|
||||
title: 'Dark Theme',
|
||||
onToggle: (b) {
|
||||
BlocProvider.of<ThemeBloc>(context).add(SetTheme(themeIsDark: b));
|
||||
setState(() {
|
||||
darkThemeEnabled = b;
|
||||
});
|
||||
_updatePrefs();
|
||||
},
|
||||
switchValue: darkThemeEnabled,
|
||||
enabled: !autoThemeEnabled,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
],
|
||||
),
|
||||
SettingsSection(
|
||||
title: 'Cache',
|
||||
titleTextStyle: _titleTextStyle,
|
||||
tiles: [
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache grade 1-7 kanji',
|
||||
switchValue: false,
|
||||
onToggle: (v) {},
|
||||
enabled: false,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache all favourites (N/A)',
|
||||
title: 'Cache grade standard kanji',
|
||||
switchValue: false,
|
||||
onToggle: (v) {},
|
||||
enabled: false,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache all searches (N/A)',
|
||||
title: 'Cache all favourites',
|
||||
switchValue: false,
|
||||
onToggle: (v) {},
|
||||
enabled: false,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: 'Cache all searches',
|
||||
switchValue: false,
|
||||
onToggle: (v) {},
|
||||
enabled: false,
|
||||
switchActiveColor: AppTheme.jishoGreen.background,
|
||||
),
|
||||
],
|
||||
),
|
||||
SettingsSection(
|
||||
title: 'Data',
|
||||
titleTextStyle: _titleTextStyle,
|
||||
tiles: [
|
||||
SettingsTile(
|
||||
leading: Icon(Icons.file_download),
|
||||
title: 'Export Data (N/A)',
|
||||
title: 'Export Data',
|
||||
enabled: false,
|
||||
),
|
||||
SettingsTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: 'Clear History (N/A)',
|
||||
title: 'Clear History',
|
||||
onPressed: _clearHistory,
|
||||
titleTextStyle: TextStyle(color: Colors.red),
|
||||
enabled: false,
|
||||
),
|
||||
SettingsTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: 'Clear Favourites (N/A)',
|
||||
title: 'Clear Favourites',
|
||||
onPressed: (c) {},
|
||||
titleTextStyle: TextStyle(color: Colors.red),
|
||||
enabled: false,
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:jisho_study_tool/models/themes/theme.dart';
|
||||
|
||||
class SplashScreen extends StatelessWidget {
|
||||
const SplashScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: AppTheme.jishoGreen.background),
|
||||
child: Center(
|
||||
child: Image.asset('assets/images/logo/logo_icon_transparent.png'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue