Move all navigation from bloc to Navigator

adapt-navigator
Oystein Kristoffer Tveit 2021-09-07 00:05:33 +02:00
parent feee76c2b6
commit 9d8c7b8ebb
61 changed files with 503 additions and 667 deletions

View File

@ -1,20 +0,0 @@
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';
class HistoryBloc extends Bloc<HistoryEvent, HistoryState> {
HistoryBloc() : super(HistoryInitial());
@override
Stream<HistoryState> mapEventToState(
HistoryEvent event,
) async* {
// TODO: implement mapEventToState
}
}

View File

@ -1,4 +0,0 @@
part of 'history_bloc.dart';
@immutable
abstract class HistoryEvent {}

View File

@ -1,6 +0,0 @@
part of 'history_bloc.dart';
@immutable
abstract class HistoryState {}
class HistoryInitial extends HistoryState {}

View File

@ -1,59 +0,0 @@
import 'dart:async';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/kanji_query.dart';
import 'package:jisho_study_tool/models/history/search.dart';
import './kanji_event.dart';
import './kanji_state.dart';
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';
class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
DatabaseBloc _databaseBloc;
KanjiBloc(this._databaseBloc) : super(KanjiSearch(KanjiSearchType.Initial));
void addSearchToDB(kanji) {
if (_databaseBloc.state is DatabaseDisconnected)
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<Search>()
.put(Search(timestamp: DateTime.now())
..kanjiQuery.target = KanjiQuery(
kanji: kanji,
));
}
@override
Stream<KanjiState> mapEventToState(KanjiEvent event) async* {
if (event is GetKanji) {
yield KanjiSearchLoading();
try {
addSearchToDB(event.kanjiSearchString);
final kanji = await fetchKanji(event.kanjiSearchString);
if (kanji.found)
yield KanjiSearchFinished(kanji: kanji);
else
yield KanjiSearchError('Something went wrong');
} on Exception {
yield KanjiSearchError('Something went wrong');
}
} else if (event is GetKanjiSuggestions) {
final suggestions = kanjiSuggestions(event.searchString);
yield KanjiSearchKeyboard(KanjiSearchType.Keyboard, suggestions);
} else if (event is ReturnToInitialState) {
yield KanjiSearch(KanjiSearchType.Initial);
}
}
}

View File

@ -1,17 +0,0 @@
abstract class KanjiEvent {
const KanjiEvent();
}
class GetKanjiSuggestions extends KanjiEvent {
final String searchString;
const GetKanjiSuggestions(this.searchString);
}
class GetKanji extends KanjiEvent {
final String kanjiSearchString;
const GetKanji(this.kanjiSearchString);
}
class ReturnToInitialState extends KanjiEvent {
const ReturnToInitialState();
}

View File

@ -1,43 +0,0 @@
import 'package:unofficial_jisho_api/api.dart';
abstract class KanjiState {
const KanjiState();
}
enum KanjiSearchType {
Initial,
Keyboard,
Drawing,
Radical,
Grade
}
class KanjiSearch extends KanjiState {
final KanjiSearchType type;
const KanjiSearch(this.type);
}
class KanjiSearchKeyboard extends KanjiSearch {
final List<String> kanjiSuggestions;
const KanjiSearchKeyboard(KanjiSearchType type, this.kanjiSuggestions) : super(type);
}
class KanjiSearchLoading extends KanjiState {
const KanjiSearchLoading();
}
class KanjiSearchFinished extends KanjiState {
final KanjiResult kanji;
final bool starred;
const KanjiSearchFinished({
required this.kanji,
this.starred = false,
});
}
class KanjiSearchError extends KanjiState {
final String message;
const KanjiSearchError(this.message);
}

View File

@ -1,20 +0,0 @@
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';
class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
NavigationBloc() : super(NavigationPage(0));
@override
Stream<NavigationState> mapEventToState(NavigationEvent event) async* {
if (event is ChangePage)
yield NavigationPage(event.pageNum);
}
}

View File

@ -1,8 +0,0 @@
abstract class NavigationEvent {
const NavigationEvent();
}
class ChangePage extends NavigationEvent {
final int pageNum;
const ChangePage(this.pageNum);
}

View File

@ -1,9 +0,0 @@
abstract class NavigationState {
const NavigationState();
}
class NavigationPage extends NavigationState {
final int pageNum;
const NavigationPage(this.pageNum);
}

View File

@ -1,56 +0,0 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/models/history/search.dart';
import 'package:jisho_study_tool/models/history/word_query.dart';
import 'package:meta/meta.dart';
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';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
DatabaseBloc _databaseBloc;
SearchBloc(this._databaseBloc) : super(SearchInitial());
void addSearchToDB(searchString) {
if (_databaseBloc.state is DatabaseDisconnected)
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<Search>()
.put(Search(timestamp: DateTime.now())
..wordQuery.target = WordQuery(
query: searchString,
));
}
@override
Stream<SearchState> mapEventToState(
SearchEvent event,
) async* {
if (event is GetSearchResults) {
yield SearchLoading();
try {
addSearchToDB(event.searchString);
final searchResults = await fetchJishoResults(event.searchString);
if (searchResults.meta.status == 200)
yield SearchFinished(searchResults.data!);
} on Exception {
yield SearchError('Something went wrong');
}
} else if (event is ReturnToInitialState) {
yield SearchInitial();
}
}
}

View File

@ -1,15 +0,0 @@
part of 'search_bloc.dart';
@immutable
abstract class SearchEvent {
const SearchEvent();
}
class GetSearchResults extends SearchEvent {
final String searchString;
const GetSearchResults(this.searchString);
}
class ReturnToInitialState extends SearchEvent {
const ReturnToInitialState();
}

View File

@ -1,26 +0,0 @@
part of 'search_bloc.dart';
@immutable
abstract class SearchState {
const SearchState();
}
class SearchInitial extends SearchState {
const SearchInitial();
}
class SearchLoading extends SearchState {
const SearchLoading();
}
class SearchFinished extends SearchState {
final List<JishoResult> results;
const SearchFinished(this.results);
}
class SearchError extends SearchState {
final String message;
const SearchError(this.message);
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/theme/theme_bloc.dart';
import 'package:jisho_study_tool/view/screens/splash.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:path_provider/path_provider.dart';
import 'package:path/path.dart';
@ -8,13 +9,10 @@ import 'package:path/path.dart';
import 'package:jisho_study_tool/objectbox.g.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.dart';
import 'package:jisho_study_tool/view/screens/kanji/view.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/view.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';
@ -60,20 +58,19 @@ class _MyAppState extends State<MyApp> {
Widget build(BuildContext context) {
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) {
if (!(dbConnected && themeState.prefsAreLoaded))
return SplashScreen();
return MaterialApp(
title: 'Jisho Study Tool',
theme: themeState.theme.getMaterialTheme(),
home: dbConnected && themeState.prefsAreLoaded
? Home()
: SplashScreen(),
initialRoute: '/',
onGenerateRoute: PageRouter.generateRoute,
);
},
),
@ -81,45 +78,48 @@ class _MyAppState extends State<MyApp> {
}
}
class Home extends StatelessWidget {
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int pageNum = 0;
@override
Widget build(BuildContext context) {
return BlocBuilder<NavigationBloc, NavigationState>(
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,
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, themeState) {
return Scaffold(
appBar: AppBar(
title: pages[pageNum].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,
),
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,
),
);
},
pages[pageNum].content,
],
),
bottomNavigationBar: BottomNavigationBar(
fixedColor: AppTheme.jishoGreen.background,
currentIndex: pageNum,
onTap: (int index) => setState(() {
this.pageNum = index;
}),
items: pages.map((p) => p.item).toList(),
showSelectedLabels: false,
showUnselectedLabels: false,
unselectedItemColor: themeState.theme.menuGreyDark.background,
),
);
},
);
@ -149,7 +149,7 @@ final List<_Page> pages = [
),
_Page(
content: KanjiView(),
titleBar: KanjiViewBar(),
titleBar: Text('Kanji'),
item: BottomNavigationBarItem(
label: 'Kanji', icon: Icon(Mdi.ideogramCjk, size: 30)),
),

27
lib/router.dart Normal file
View File

@ -0,0 +1,27 @@
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';
class PageRouter {
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => Home());
case '/search':
final searchTerm = args as String;
return MaterialPageRoute(builder: (_) => SearchResultsPage(searchTerm: searchTerm));
case '/kanjiSearch':
final searchTerm = args as String;
return MaterialPageRoute(builder: (_) => KanjiResultPage(kanjiSearchTerm: searchTerm));
default:
return MaterialPageRoute(builder: (_) => Text("ERROR: this route does not exist"));
}
}
}

View File

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

View File

@ -1,4 +1,5 @@
import 'package:unofficial_jisho_api/api.dart' as jisho;
export 'package:unofficial_jisho_api/api.dart' show KanjiResult;
String? _convertGrade(String grade) {
const conversionTable = {

View File

@ -9,7 +9,7 @@ class SplashScreen extends StatelessWidget {
return Container(
decoration: BoxDecoration(color: AppTheme.jishoGreen.background),
child: Center(
child: Image.asset('assets/images/logo/logo_icon_transparent.png'),
child: Image(image: AssetImage('assets/images/logo/logo_icon_transparent.png'),)
),
);
}

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.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';
@ -58,8 +56,7 @@ class KanjiSearchItem extends StatelessWidget {
return Slidable(
child: SearchItem(
onTap: () {
BlocProvider.of<NavigationBloc>(context).add(ChangePage(1));
BlocProvider.of<KanjiBloc>(context).add(GetKanji(this.result.kanji));
Navigator.pushNamed(context, '/kanjiSearch', arguments: this.result.kanji);
},
time: timestamp,
search: _KanjiBox(result.kanji),

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.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';
import 'package:jisho_study_tool/models/history/word_query.dart';
import './search_item.dart';
@ -26,8 +24,7 @@ class PhraseSearchItem extends StatelessWidget {
],
child: SearchItem(
onTap: () {
BlocProvider.of<NavigationBloc>(context).add(ChangePage(0));
BlocProvider.of<SearchBloc>(context).add(GetSearchResults(this.search.query));
Navigator.pushNamed(context, '/search', arguments: this.search.query);
},
time: timestamp,
search: Text(search.query),

View File

@ -1,20 +1,20 @@
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/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/yomi_chips.dart';
import './kanji_result_body/examples.dart';
import './kanji_result_body/grade.dart';
import './kanji_result_body/header.dart';
import './kanji_result_body/jlpt_level.dart';
import './kanji_result_body/radical.dart';
import './kanji_result_body/rank.dart';
import './kanji_result_body/stroke_order_gif.dart';
import './kanji_result_body/yomi_chips.dart';
class KanjiResultCard extends StatelessWidget {
class KanjiResultBody extends StatelessWidget {
late final String query;
late final jisho.KanjiResultData resultData;
KanjiResultCard({required jisho.KanjiResult result}) {
KanjiResultBody({required jisho.KanjiResult result}) {
query = result.query;

View File

@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
import 'package:animated_size_and_fade/animated_size_and_fade.dart';
import 'package:jisho_study_tool/services/kanji_suggestions.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 'package:jisho_study_tool/view/components/kanji/kanji_search_body/kanji_search_options_bar.dart';
class KanjiSearchBody extends StatefulWidget {
KanjiSearchBody({Key? key}) : super(key: key);
@override
_KanjiSearchBodyState createState() => _KanjiSearchBodyState();
}
class _KanjiSearchBodyState extends State<KanjiSearchBody>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation _searchbarMovementAnimation;
final FocusNode focus = FocusNode();
final GlobalKey<KanjiSearchBarState> _kanjiSearchBarState =
GlobalKey<KanjiSearchBarState>();
List<String> suggestions = [];
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 200),
);
_searchbarMovementAnimation = AlignmentTween(
begin: Alignment.center,
end: Alignment.topCenter,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (_controller.value == 1) {
focus.unfocus();
_kanjiSearchBarState.currentState!.clearText();
return false;
}
return true;
},
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(),
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 20),
child: AnimatedBuilder(
animation: _searchbarMovementAnimation,
builder: (BuildContext context, _) {
return Container(
alignment: _searchbarMovementAnimation.value,
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Focus(
focusNode: focus,
onFocusChange: (hasFocus) {
if (hasFocus)
_controller.forward();
else
_controller.reverse();
},
child: KanjiSearchBar(
key: _kanjiSearchBarState,
onChanged: (text) => setState(() {
this.suggestions = kanjiSuggestions(text);
}),
),
),
AnimatedSizeAndFade(
vsync: this,
child: _controller.value == 1
? KanjiGrid(this.suggestions)
: KanjiSearchOptionsBar(),
fadeDuration: const Duration(milliseconds: 200),
sizeDuration: const Duration(milliseconds: 300),
),
],
),
);
},
),
),
),
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.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 {
@ -33,29 +32,28 @@ class _GridItem extends StatelessWidget {
Widget build(BuildContext context) {
return InkWell(
onTap: () {
BlocProvider.of<KanjiBloc>(context).add(GetKanji(kanji));
Navigator.pushNamed(context, '/kanjiSearch', arguments: kanji);
},
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
final _menuColors = state.theme.menuGreyLight;
return
Container(
decoration: BoxDecoration(
color: _menuColors.background,
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
margin: EdgeInsets.all(10.0),
child: FittedBox(
child: Text(
kanji,
style: TextStyle(color: _menuColors.foreground),
return Container(
decoration: BoxDecoration(
color: _menuColors.background,
borderRadius: BorderRadius.circular(20.0),
),
),
),
);
child: Container(
margin: EdgeInsets.all(10.0),
child: FittedBox(
child: Text(
kanji,
style: TextStyle(color: _menuColors.foreground),
),
),
),
);
},
),
);
}
}
}

View File

@ -1,41 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
class KanjiSearchBar extends StatefulWidget {
final Function(String)? onChanged;
const KanjiSearchBar();
KanjiSearchBar({this.onChanged, Key? key}) : super(key: key);
@override
_KanjiSearchBarState createState() => new _KanjiSearchBarState();
KanjiSearchBarState createState() => new 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();
TextFieldButton button = TextFieldButton.paste;
final Function(String)? onChanged;
KanjiSearchBarState(this.onChanged);
@override
void initState() {
super.initState();
}
void _getKanjiSuggestions(String text) =>
BlocProvider.of<KanjiBloc>(context).add(GetKanjiSuggestions(text));
void updateSuggestions() => _getKanjiSuggestions(textController.text);
void _clearText() {
textController.text = '';
updateSuggestions();
void runOnChanged() {
if (onChanged != null) onChanged!(textController.text);
}
void _pasteText() async {
void clearText() {
textController.text = '';
runOnChanged();
}
void pasteText() async {
ClipboardData? clipboardData = await Clipboard.getData('text/plain');
if (clipboardData != null && clipboardData.text != null) {
textController.text = clipboardData.text!;
updateSuggestions();
runOnChanged();
}
}
@ -43,29 +45,32 @@ class _KanjiSearchBarState extends State<KanjiSearchBar> {
Widget build(BuildContext context) {
IconButton clearButton = IconButton(
icon: Icon(Icons.clear),
onPressed: () => _clearText(),
onPressed: () => clearText(),
);
IconButton pasteButton = IconButton(
icon: Icon(Icons.content_paste),
onPressed: () => _pasteText(),
onPressed: () => pasteText(),
);
return TextField(
controller: textController,
onChanged: (text) => _getKanjiSuggestions(text),
onChanged: (text) {
if (this.onChanged != null) this.onChanged!(text);
},
onSubmitted: (_) => {},
decoration: new InputDecoration(
prefixIcon: Icon(Icons.search),
hintText: 'Search',
// fillColor: Colors.white,
// filled: true,
// filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
isDense: false,
suffixIcon: (button == TextFieldButton.clear) ? clearButton : pasteButton,
suffixIcon:
(button == TextFieldButton.clear) ? clearButton : pasteButton,
),
);
}
}
}

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
//TODO: Make buttons have an effect
@ -20,18 +19,15 @@ class KanjiSearchOptionsBar extends StatelessWidget {
fontSize: 18,
),
),
onPressed: () =>
BlocProvider.of<KanjiBloc>(context).add(ReturnToInitialState()),
onPressed: () {}
),
_IconButton(
icon: Icon(Icons.category),
onPressed: () =>
BlocProvider.of<KanjiBloc>(context).add(ReturnToInitialState()),
onPressed: () {}
),
_IconButton(
icon: Icon(Icons.mode),
onPressed: () =>
BlocProvider.of<KanjiBloc>(context).add(ReturnToInitialState()),
onPressed: () {}
),
],
),

View File

@ -1,9 +1,7 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
import 'package:jisho_study_tool/view/components/search/language_selector.dart';
class SearchBar extends StatelessWidget {
const SearchBar();
@override
@ -13,10 +11,8 @@ class SearchBar extends StatelessWidget {
child: Column(
children: [
TextField(
onSubmitted: (text) {
BlocProvider.of<SearchBloc>(context)
.add(GetSearchResults(text));
},
onSubmitted: (text) =>
Navigator.pushNamed(context, '/search', arguments: text),
controller: TextEditingController(),
decoration: InputDecoration(
labelText: 'Search',
@ -33,4 +29,4 @@ class SearchBar extends StatelessWidget {
),
);
}
}
}

View File

@ -0,0 +1,19 @@
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';
class SearchResultsBody extends StatelessWidget {
final List<JishoResult> results;
const SearchResultsBody({
required this.results,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView(
children: results.map((result) => SearchResultCard(result)).toList(),
);
}
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/badge.dart';
import './badge.dart';
class CommonBadge extends StatelessWidget {
final bool isCommon;

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/badge.dart';
import './badge.dart';
class JLPTBadge extends StatelessWidget {
final String jlptLevel;

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/badge.dart';
import './badge.dart';
class WKBadge extends StatelessWidget {
final String wkLevel;

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/common_badge.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/jlpt_badge.dart';
import 'package:jisho_study_tool/view/components/search/search_result_page/parts/wanikani_badge.dart';
import 'package:unofficial_jisho_api/api.dart';
import './parts/common_badge.dart';
import './parts/header.dart';
import './parts/senses.dart';
import './parts/jlpt_badge.dart';
import './parts/other_forms.dart';
import './parts/senses.dart';
import './parts/wanikani_badge.dart';
class SearchResultCard extends StatelessWidget {
final JishoResult result;

View File

@ -1,94 +0,0 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
import 'package:animated_size_and_fade/animated_size_and_fade.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_grid.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_bar.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_options_bar.dart';
class SearchScreen extends StatefulWidget {
SearchScreen({Key? key}) : super(key: key);
@override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation _searchbarMovementAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 200),
);
_searchbarMovementAnimation = AlignmentTween(
begin: Alignment.center,
end: Alignment.topCenter,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(),
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 20),
child: AnimatedBuilder(
animation: _searchbarMovementAnimation,
builder: (BuildContext context, _) {
return Container(
alignment: _searchbarMovementAnimation.value,
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Focus(
onFocusChange: (hasFocus) {
if (hasFocus)
_controller.forward();
else
_controller.reverse();
},
child: KanjiSearchBar(),
),
BlocBuilder<KanjiBloc, KanjiState>(
builder: (context, state) {
return AnimatedSizeAndFade(
vsync: this,
child: _controller.value == 1
? KanjiGrid((state is KanjiSearchKeyboard)
? state.kanjiSuggestions
: [])
// ? Container()
: KanjiSearchOptionsBar(),
fadeDuration: const Duration(milliseconds: 200),
sizeDuration: const Duration(milliseconds: 300),
);
},
)
],
),
);
},
),
),
);
}
}

View File

@ -1,71 +0,0 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
import 'package:jisho_study_tool/view/screens/loading.dart';
import 'search.dart';
import 'result.dart';
class KanjiView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocListener<KanjiBloc, KanjiState>(
listener: (context, state) {
if (state is KanjiSearch && state.type == KanjiSearchType.Initial) {
FocusScope.of(context).unfocus();
} else if (state is KanjiSearchLoading) {
FocusScope.of(context).unfocus();
}
},
child: BlocBuilder<KanjiBloc, KanjiState>(
builder: (context, state) {
if (state is KanjiSearch) {
if (state.type == KanjiSearchType.Initial) return SearchScreen();
else if (state is KanjiSearchKeyboard) return SearchScreen();
}
else if (state is KanjiSearchLoading) return LoadingScreen();
else if (state is KanjiSearchFinished)
return WillPopScope(
child: KanjiResultCard(result: state.kanji),
onWillPop: () async {
BlocProvider.of<KanjiBloc>(context)
.add(ReturnToInitialState());
return false;
});
throw 'No such event found';
},
),
);
}
}
class KanjiViewBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () =>
BlocProvider.of<KanjiBloc>(context).add(ReturnToInitialState()),
),
// Expanded(
// child: Container(
// child: KanjiSearchBar(),
// ),
// ),
// IconButton(
// icon: Icon(Icons.star_border),
// onPressed: null,
// ),
// IconButton(
// icon: Icon(Icons.add),
// onPressed: null,
// ),
],
),
);
}
}

View File

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

View File

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

View File

@ -1,26 +0,0 @@
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;
},
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_grid.dart';
import 'package:jisho_study_tool/view/components/kanji/kanji_search_bar.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';
class SearchGrid extends StatelessWidget {
final List<String> suggestions;

View File

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

View File

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

View File

@ -1,33 +0,0 @@
import 'package:flutter/material.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/screens/search/results.dart';
class SearchView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocConsumer<SearchBloc, SearchState>(
listener: (context, state) {},
builder: (context, state) {
if (state is SearchInitial)
return _InitialView();
else if (state is SearchLoading)
return LoadingScreen();
else if (state is SearchFinished) {
return SearchResults(results: state.results);
}
throw 'No such event found';
},
);
}
}
class _InitialView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
SearchBar(),
]);
}
}

View File

@ -15,20 +15,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.2"
animated_size_and_fade:
dependency: "direct main"
description:
name: animated_size_and_fade
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.2.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.7.0"
version: "2.8.2"
bloc:
dependency: transitive
description:
@ -56,14 +70,14 @@ packages:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.7"
version: "1.0.0"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.10"
version: "3.0.0"
build_resolvers:
dependency: transitive
description:
@ -77,14 +91,14 @@ packages:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.12.2"
version: "2.0.6"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.12"
version: "7.0.1"
built_collection:
dependency: transitive
description:
@ -112,7 +126,7 @@ packages:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
checked_yaml:
dependency: transitive
description:
@ -140,7 +154,7 @@ packages:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
version: "4.1.0"
collection:
dependency: transitive
description:
@ -148,6 +162,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
confirm_dialog:
dependency: "direct main"
description:
name: confirm_dialog
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
convert:
dependency: transitive
description:
@ -168,7 +189,7 @@ packages:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.2"
version: "0.17.0"
dart_style:
dependency: transitive
description:
@ -182,7 +203,7 @@ packages:
name: division
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.8"
version: "0.9.0"
fake_async:
dependency: transitive
description:
@ -222,7 +243,28 @@ packages:
name: flutter_bloc
url: "https://pub.dartlang.org"
source: hosted
version: "7.0.0"
version: "7.2.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.2"
flutter_native_splash:
dependency: "direct dev"
description:
name: flutter_native_splash
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
flutter_slidable:
dependency: "direct main"
description:
name: flutter_slidable
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -233,6 +275,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
glob:
dependency: transitive
description:
@ -246,28 +295,28 @@ packages:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
version: "2.0.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+4"
version: "0.15.0"
html_unescape:
dependency: transitive
description:
name: html_unescape
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "2.0.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
version: "0.13.3"
http_multi_server:
dependency: transitive
description:
@ -281,7 +330,14 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
version: "4.0.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
io:
dependency: transitive
description:
@ -316,21 +372,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
mdi:
dependency: "direct main"
description:
name: mdi
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "5.0.0-nullsafety.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
version: "1.7.0"
mime:
dependency: transitive
description:
@ -422,6 +478,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
platform:
dependency: transitive
description:
@ -435,7 +498,7 @@ packages:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "2.0.1"
pool:
dependency: transitive
description:
@ -456,7 +519,7 @@ packages:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
version: "6.0.0"
pub_semver:
dependency: transitive
description:
@ -471,13 +534,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
settings_ui:
dependency: "direct main"
description:
name: settings_ui
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.7"
shared_preferences_linux:
dependency: transitive
description:
@ -519,14 +589,14 @@ packages:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
version: "1.2.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.4+1"
version: "1.0.1"
sky_engine:
dependency: transitive
description: flutter
@ -587,7 +657,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
version: "0.4.3"
timing:
dependency: transitive
description:
@ -602,20 +672,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
universal_io:
dependency: transitive
description:
name: universal_io
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
unofficial_jisho_api:
dependency: "direct main"
description:
name: unofficial_jisho_api
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "2.0.3"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.3"
version: "6.0.10"
url_launcher_linux:
dependency: transitive
description:
@ -636,7 +713,7 @@ packages:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "2.0.4"
url_launcher_web:
dependency: transitive
description:
@ -686,6 +763,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.0"
yaml:
dependency: transitive
description:
@ -694,5 +778,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
dart: ">=2.13.0 <3.0.0"
flutter: ">=2.0.0"

View File

@ -41,6 +41,7 @@ flutter:
assets:
- assets/images/denshi_jisho_background_overlay.png
- assets/images/logo/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg