chore(all): Format

This commit is contained in:
PapaTutuWawa 2023-04-12 16:16:58 +02:00
parent 67c1903bd1
commit 9dcc99ca36
22 changed files with 913 additions and 625 deletions

File diff suppressed because it is too large Load Diff

View File

@ -31,8 +31,8 @@ void main() async {
// Load animes
GetIt.I.get<AnimeListBloc>().add(
AnimesLoadedEvent(),
);
AnimesLoadedEvent(),
);
runApp(
MultiBlocProvider(
@ -81,10 +81,14 @@ class MyApp extends StatelessWidget {
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
case animeListRoute: return AnimeListPage.route;
case animeSearchRoute: return AnimeSearchPage.route;
case detailsRoute: return DetailsPage.route;
case aboutRoute: return AboutPage.route;
case animeListRoute:
return AnimeListPage.route;
case animeSearchRoute:
return AnimeSearchPage.route;
case detailsRoute:
return DetailsPage.route;
case aboutRoute:
return AboutPage.route;
}
return null;

View File

@ -10,18 +10,24 @@ class AnimeTrackingData with _$AnimeTrackingData, TrackingMedium {
factory AnimeTrackingData(
/// The ID of the anime
String id,
/// The state of the anime
@MediumTrackingStateConverter() MediumTrackingState state,
/// The title of the anime
String title,
/// Episodes in total.
int episodesWatched,
/// Episodes watched.
int? episodesTotal,
/// URL to the thumbnail/cover art for the anime.
String thumbnailUrl,
) = _AnimeTrackingData;
/// JSON
factory AnimeTrackingData.fromJson(Map<String, dynamic> json) => _$AnimeTrackingDataFromJson(json);
factory AnimeTrackingData.fromJson(Map<String, dynamic> json) =>
_$AnimeTrackingDataFromJson(json);
}

View File

@ -10,20 +10,27 @@ class MangaTrackingData with _$MangaTrackingData, TrackingMedium {
factory MangaTrackingData(
/// The ID of the manga
String id,
/// The state of the manga
@MediumTrackingStateConverter() MediumTrackingState state,
/// The title of the manga
String title,
/// Chapters read.
int chaptersRead,
/// Chapters read.
int volumesOwned,
/// Episodes watched.
int? chaptersTotal,
/// URL to the thumbnail/cover art for the manga.
String thumbnailUrl,
) = _MangaTrackingData;
/// JSON
factory MangaTrackingData.fromJson(Map<String, dynamic> json) => _$MangaTrackingDataFromJson(json);
factory MangaTrackingData.fromJson(Map<String, dynamic> json) =>
_$MangaTrackingDataFromJson(json);
}

View File

@ -32,47 +32,70 @@ abstract class TrackingMedium {
extension MediumStateExtension on MediumTrackingState {
int toInteger() {
assert(this != MediumTrackingState.all, 'MediumTrackingState.all must not be serialized');
assert(
this != MediumTrackingState.all,
'MediumTrackingState.all must not be serialized',
);
switch (this) {
case MediumTrackingState.ongoing: return 0;
case MediumTrackingState.completed: return 1;
case MediumTrackingState.planned: return 2;
case MediumTrackingState.dropped: return 3;
case MediumTrackingState.all: return -1;
case MediumTrackingState.ongoing:
return 0;
case MediumTrackingState.completed:
return 1;
case MediumTrackingState.planned:
return 2;
case MediumTrackingState.dropped:
return 3;
case MediumTrackingState.all:
return -1;
}
}
String toNameString(TrackingMediumType type) {
assert(this != MediumTrackingState.all, 'MediumTrackingState.all must not be stringified');
assert(
this != MediumTrackingState.all,
'MediumTrackingState.all must not be stringified',
);
switch (this) {
case MediumTrackingState.ongoing:
switch (type) {
case TrackingMediumType.anime: return 'Watching';
case TrackingMediumType.manga: return 'Reading';
case TrackingMediumType.anime:
return 'Watching';
case TrackingMediumType.manga:
return 'Reading';
}
case MediumTrackingState.completed: return 'Completed';
case MediumTrackingState.completed:
return 'Completed';
case MediumTrackingState.planned:
switch (type) {
case TrackingMediumType.anime: return 'Plan to watch';
case TrackingMediumType.manga: return 'Plan to read';
case TrackingMediumType.anime:
return 'Plan to watch';
case TrackingMediumType.manga:
return 'Plan to read';
}
case MediumTrackingState.dropped: return 'Dropped';
case MediumTrackingState.all: return 'All';
case MediumTrackingState.dropped:
return 'Dropped';
case MediumTrackingState.all:
return 'All';
}
}
}
class MediumTrackingStateConverter implements JsonConverter<MediumTrackingState, int> {
class MediumTrackingStateConverter
implements JsonConverter<MediumTrackingState, int> {
const MediumTrackingStateConverter();
@override
MediumTrackingState fromJson(int json) {
switch (json) {
case 0: return MediumTrackingState.ongoing;
case 1: return MediumTrackingState.completed;
case 2: return MediumTrackingState.planned;
case 3: return MediumTrackingState.dropped;
case 0:
return MediumTrackingState.ongoing;
case 1:
return MediumTrackingState.completed;
case 2:
return MediumTrackingState.planned;
case 3:
return MediumTrackingState.dropped;
}
return MediumTrackingState.planned;

View File

@ -46,18 +46,18 @@ class DatabaseService {
final animes = await _db.query(animeTable);
return animes
.cast<Map<String, dynamic>>()
.map(AnimeTrackingData.fromJson)
.toList();
.cast<Map<String, dynamic>>()
.map(AnimeTrackingData.fromJson)
.toList();
}
Future<List<MangaTrackingData>> loadMangas() async {
final mangas = await _db.query(mangaTable);
return mangas
.cast<Map<String, dynamic>>()
.map(MangaTrackingData.fromJson)
.toList();
.cast<Map<String, dynamic>>()
.map(MangaTrackingData.fromJson)
.toList();
}
Future<void> addAnime(AnimeTrackingData data) async {

View File

@ -30,30 +30,35 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
}
/// Internal anime state
final List<AnimeTrackingData> _animes = List<AnimeTrackingData>.empty(growable: true);
final List<MangaTrackingData> _mangas = List<MangaTrackingData>.empty(growable: true);
final List<AnimeTrackingData> _animes =
List<AnimeTrackingData>.empty(growable: true);
final List<MangaTrackingData> _mangas =
List<MangaTrackingData>.empty(growable: true);
List<AnimeTrackingData> _getFilteredAnime({MediumTrackingState? trackingState}) {
List<AnimeTrackingData> _getFilteredAnime({
MediumTrackingState? trackingState,
}) {
final filterState = trackingState ?? state.animeFilterState;
if (filterState == MediumTrackingState.all) return _animes;
return _animes
.where((anime) => anime.state == filterState)
.toList();
return _animes.where((anime) => anime.state == filterState).toList();
}
List<MangaTrackingData> _getFilteredManga({MediumTrackingState? trackingState}) {
List<MangaTrackingData> _getFilteredManga({
MediumTrackingState? trackingState,
}) {
final filterState = trackingState ?? state.mangaFilterState;
if (state.mangaFilterState == MediumTrackingState.all) return _mangas;
return _mangas
.where((manga) => manga.state == filterState)
.toList();
return _mangas.where((manga) => manga.state == filterState).toList();
}
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeAdded(
AnimeAddedEvent event,
Emitter<AnimeListState> emit,
) async {
// Add the anime to the database
await GetIt.I.get<DatabaseService>().addAnime(event.data);
@ -67,7 +72,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onMangaAdded(MangaAddedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaAdded(
MangaAddedEvent event,
Emitter<AnimeListState> emit,
) async {
// Add the manga to the database
await GetIt.I.get<DatabaseService>().addManga(event.data);
@ -81,12 +89,16 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onAnimeIncremented(AnimeEpisodeIncrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeIncremented(
AnimeEpisodeIncrementedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = state.animes.indexWhere((item) => item.id == event.id);
if (index == -1) return;
final anime = state.animes[index];
if (anime.episodesTotal != null && anime.episodesWatched + 1 > anime.episodesTotal!) return;
if (anime.episodesTotal != null &&
anime.episodesWatched + 1 > anime.episodesTotal!) return;
final newList = List<AnimeTrackingData>.from(state.animes);
final newAnime = anime.copyWith(
@ -103,7 +115,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
}
Future<void> _onAnimeDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeDecremented(
AnimeEpisodeDecrementedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = state.animes.indexWhere((item) => item.id == event.id);
if (index == -1) return;
@ -125,7 +140,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
}
Future<void> _onAnimesLoaded(AnimesLoadedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimesLoaded(
AnimesLoadedEvent event,
Emitter<AnimeListState> emit,
) async {
_animes.addAll(
await GetIt.I.get<DatabaseService>().loadAnimes(),
);
@ -141,7 +159,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onAnimesFiltered(AnimeFilterChangedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimesFiltered(
AnimeFilterChangedEvent event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
animeFilterState: event.filterState,
@ -150,7 +171,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onMangasFiltered(MangaFilterChangedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangasFiltered(
MangaFilterChangedEvent event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
mangaFilterState: event.filterState,
@ -159,7 +183,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onTrackingTypeChanged(AnimeTrackingTypeChanged event, Emitter<AnimeListState> emit) async {
Future<void> _onTrackingTypeChanged(
AnimeTrackingTypeChanged event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
trackingType: event.type,
@ -168,12 +195,16 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onMangaIncremented(MangaChapterIncrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaIncremented(
MangaChapterIncrementedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = state.mangas.indexWhere((item) => item.id == event.id);
assert(index != -1, 'The manga must exist');
final manga = state.mangas[index];
if (manga.chaptersTotal != null && manga.chaptersRead + 1 > manga.chaptersTotal!) return;
if (manga.chaptersTotal != null &&
manga.chaptersRead + 1 > manga.chaptersTotal!) return;
final newList = List<MangaTrackingData>.from(state.mangas);
final newManga = manga.copyWith(
@ -195,7 +226,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().updateManga(newManga);
}
Future<void> _onMangaDecremented(MangaChapterDecrementedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaDecremented(
MangaChapterDecrementedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = state.mangas.indexWhere((item) => item.id == event.id);
if (index == -1) return;
@ -222,7 +256,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().updateManga(newManga);
}
Future<void> _onAnimeUpdated(AnimeUpdatedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeUpdated(
AnimeUpdatedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = _animes.indexWhere((anime) => anime.id == event.anime.id);
assert(index != -1, 'The anime must exist');
@ -235,7 +272,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onMangaUpdated(MangaUpdatedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaUpdated(
MangaUpdatedEvent event,
Emitter<AnimeListState> emit,
) async {
final index = _mangas.indexWhere((manga) => manga.id == event.manga.id);
assert(index != -1, 'The manga must exist');
@ -248,7 +288,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
}
Future<void> _onAnimeRemoved(AnimeRemovedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onAnimeRemoved(
AnimeRemovedEvent event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
animes: List.from(
@ -266,7 +309,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().deleteAnime(event.id);
}
Future<void> _onMangaRemoved(MangaRemovedEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onMangaRemoved(
MangaRemovedEvent event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
mangas: List.from(
@ -284,7 +330,10 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
await GetIt.I.get<DatabaseService>().deleteManga(event.id);
}
Future<void> _onButtonVisibilityToggled(AddButtonVisibilitySetEvent event, Emitter<AnimeListState> emit) async {
Future<void> _onButtonVisibilityToggled(
AddButtonVisibilitySetEvent event,
Emitter<AnimeListState> emit,
) async {
emit(
state.copyWith(
buttonVisibility: event.state,

View File

@ -22,7 +22,10 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
on<ResultTappedEvent>(_onResultTapped);
}
Future<void> _onRequested(AnimeSearchRequestedEvent event, Emitter<AnimeSearchState> emit) async {
Future<void> _onRequested(
AnimeSearchRequestedEvent event,
Emitter<AnimeSearchState> emit,
) async {
emit(
state.copyWith(
searchQuery: '',
@ -33,13 +36,16 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
);
GetIt.I.get<NavigationBloc>().add(
PushedNamedEvent(
const NavigationDestination(animeSearchRoute),
),
);
PushedNamedEvent(
const NavigationDestination(animeSearchRoute),
),
);
}
Future<void> _onQueryChanged(SearchQueryChangedEvent event, Emitter<AnimeSearchState> emit) async {
Future<void> _onQueryChanged(
SearchQueryChangedEvent event,
Emitter<AnimeSearchState> emit,
) async {
emit(
state.copyWith(
searchQuery: event.query,
@ -47,7 +53,10 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
);
}
Future<void> _onQuerySubmitted(SearchQuerySubmittedEvent event, Emitter<AnimeSearchState> emit) async {
Future<void> _onQuerySubmitted(
SearchQuerySubmittedEvent event,
Emitter<AnimeSearchState> emit,
) async {
if (state.searchQuery.isEmpty) return;
emit(
@ -65,13 +74,17 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
emit(
state.copyWith(
working: false,
searchResults: result.map((Anime anime) => SearchResult(
anime.title,
anime.malId.toString(),
anime.episodes,
anime.imageUrl,
anime.synopsis ?? '',
),).toList(),
searchResults: result
.map(
(Anime anime) => SearchResult(
anime.title,
anime.malId.toString(),
anime.episodes,
anime.imageUrl,
anime.synopsis ?? '',
),
)
.toList(),
),
);
} else {
@ -83,46 +96,53 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
emit(
state.copyWith(
working: false,
searchResults: result.map((Manga manga) => SearchResult(
manga.title,
manga.malId.toString(),
manga.chapters,
manga.imageUrl,
manga.synopsis ?? '',
),).toList(),
searchResults: result
.map(
(Manga manga) => SearchResult(
manga.title,
manga.malId.toString(),
manga.chapters,
manga.imageUrl,
manga.synopsis ?? '',
),
)
.toList(),
),
);
}
}
Future<void> _onResultTapped(ResultTappedEvent event, Emitter<AnimeSearchState> emit) async {
Future<void> _onResultTapped(
ResultTappedEvent event,
Emitter<AnimeSearchState> emit,
) async {
GetIt.I.get<list.AnimeListBloc>().add(
state.trackingType == TrackingMediumType.anime ?
list.AnimeAddedEvent(
AnimeTrackingData(
event.result.id,
MediumTrackingState.ongoing,
event.result.title,
0,
event.result.total,
event.result.thumbnailUrl,
),
) :
list.MangaAddedEvent(
MangaTrackingData(
event.result.id,
MediumTrackingState.ongoing,
event.result.title,
0,
0,
event.result.total,
event.result.thumbnailUrl,
),
),
);
state.trackingType == TrackingMediumType.anime
? list.AnimeAddedEvent(
AnimeTrackingData(
event.result.id,
MediumTrackingState.ongoing,
event.result.title,
0,
event.result.total,
event.result.thumbnailUrl,
),
)
: list.MangaAddedEvent(
MangaTrackingData(
event.result.id,
MediumTrackingState.ongoing,
event.result.title,
0,
0,
event.result.total,
event.result.thumbnailUrl,
),
),
);
GetIt.I.get<NavigationBloc>().add(
PoppedRouteEvent(),
);
PoppedRouteEvent(),
);
}
}

View File

@ -21,7 +21,10 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
on<ItemRemovedEvent>(_onItemRemoved);
}
Future<void> _onAnimeRequested(AnimeDetailsRequestedEvent event, Emitter<DetailsState> emit) async {
Future<void> _onAnimeRequested(
AnimeDetailsRequestedEvent event,
Emitter<DetailsState> emit,
) async {
emit(
state.copyWith(
trackingType: TrackingMediumType.anime,
@ -30,13 +33,16 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
);
GetIt.I.get<NavigationBloc>().add(
PushedNamedEvent(
const NavigationDestination(detailsRoute),
),
);
PushedNamedEvent(
const NavigationDestination(detailsRoute),
),
);
}
Future<void> _onMangaRequested(MangaDetailsRequestedEvent event, Emitter<DetailsState> emit) async {
Future<void> _onMangaRequested(
MangaDetailsRequestedEvent event,
Emitter<DetailsState> emit,
) async {
emit(
state.copyWith(
trackingType: TrackingMediumType.manga,
@ -45,13 +51,16 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
);
GetIt.I.get<NavigationBloc>().add(
PushedNamedEvent(
const NavigationDestination(detailsRoute),
),
);
PushedNamedEvent(
const NavigationDestination(detailsRoute),
),
);
}
Future<void> _onDetailsUpdated(DetailsUpdatedEvent event, Emitter<DetailsState> emit) async {
Future<void> _onDetailsUpdated(
DetailsUpdatedEvent event,
Emitter<DetailsState> emit,
) async {
if (state.trackingType == TrackingMediumType.anime) {
emit(
state.copyWith(
@ -59,11 +68,13 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
),
);
await GetIt.I.get<DatabaseService>().updateAnime(event.data as AnimeTrackingData);
await GetIt.I
.get<DatabaseService>()
.updateAnime(event.data as AnimeTrackingData);
GetIt.I.get<AnimeListBloc>().add(
AnimeUpdatedEvent(event.data as AnimeTrackingData),
);
AnimeUpdatedEvent(event.data as AnimeTrackingData),
);
} else {
emit(
state.copyWith(
@ -71,15 +82,20 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
),
);
await GetIt.I.get<DatabaseService>().updateManga(event.data as MangaTrackingData);
await GetIt.I
.get<DatabaseService>()
.updateManga(event.data as MangaTrackingData);
GetIt.I.get<AnimeListBloc>().add(
MangaUpdatedEvent(event.data as MangaTrackingData),
);
MangaUpdatedEvent(event.data as MangaTrackingData),
);
}
}
Future<void> _onItemRemoved(ItemRemovedEvent event, Emitter<DetailsState> emit) async {
Future<void> _onItemRemoved(
ItemRemovedEvent event,
Emitter<DetailsState> emit,
) async {
emit(
state.copyWith(
data: null,

View File

@ -13,14 +13,20 @@ class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
}
final GlobalKey<NavigatorState> navigationKey;
Future<void> _onPushedNamed(PushedNamedEvent event, Emitter<NavigationState> emit) async {
Future<void> _onPushedNamed(
PushedNamedEvent event,
Emitter<NavigationState> emit,
) async {
await navigationKey.currentState!.pushNamed(
event.destination.path,
arguments: event.destination.arguments,
);
}
Future<void> _onPushedNamedAndRemoveUntil(PushedNamedAndRemoveUntilEvent event, Emitter<NavigationState> emit) async {
Future<void> _onPushedNamedAndRemoveUntil(
PushedNamedAndRemoveUntilEvent event,
Emitter<NavigationState> emit,
) async {
await navigationKey.currentState!.pushNamedAndRemoveUntil(
event.destination.path,
event.predicate,
@ -28,14 +34,20 @@ class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
);
}
Future<void> _onPushedNamedReplaceEvent(PushedNamedReplaceEvent event, Emitter<NavigationState> emit) async {
Future<void> _onPushedNamedReplaceEvent(
PushedNamedReplaceEvent event,
Emitter<NavigationState> emit,
) async {
await navigationKey.currentState!.pushReplacementNamed(
event.destination.path,
arguments: event.destination.arguments,
);
}
Future<void> _onPoppedRoute(PoppedRouteEvent event, Emitter<NavigationState> emit) async {
Future<void> _onPoppedRoute(
PoppedRouteEvent event,
Emitter<NavigationState> emit,
) async {
navigationKey.currentState!.pop();
}

View File

@ -2,11 +2,9 @@ part of 'navigation_bloc.dart';
class NavigationDestination {
const NavigationDestination(
this.path,
{
this.arguments,
}
);
this.path, {
this.arguments,
});
final String path;
final Object? arguments;
}

View File

@ -9,11 +9,11 @@ class AboutPage extends StatelessWidget {
});
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
builder: (_) => const AboutPage(),
settings: const RouteSettings(
name: aboutRoute,
),
);
builder: (_) => const AboutPage(),
settings: const RouteSettings(
name: aboutRoute,
),
);
@override
Widget build(BuildContext context) {
@ -34,7 +34,6 @@ class AboutPage extends StatelessWidget {
'AniTrack',
style: Theme.of(context).textTheme.titleLarge,
),
ElevatedButton(
onPressed: () async {
await launchUrl(

View File

@ -16,11 +16,11 @@ class AnimeListPage extends StatefulWidget {
});
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
builder: (_) => AnimeListPage(),
settings: const RouteSettings(
name: animeListRoute,
),
);
builder: (_) => const AnimeListPage(),
settings: const RouteSettings(
name: animeListRoute,
),
);
@override
AnimeListPageState createState() => AnimeListPageState();
@ -39,7 +39,8 @@ class AnimeListPageState extends State<AnimeListPage> {
void _onAnimeListScrolled() {
//print(_animeScrollController.position.maxScrollExtent);
final bloc = GetIt.I.get<AnimeListBloc>();
if (_animeScrollController.offset + 20 >= _animeScrollController.position.maxScrollExtent) {
if (_animeScrollController.offset + 20 >=
_animeScrollController.position.maxScrollExtent) {
if (bloc.state.buttonVisibility) {
bloc.add(
AddButtonVisibilitySetEvent(false),
@ -56,12 +57,16 @@ class AnimeListPageState extends State<AnimeListPage> {
String _getPageTitle(TrackingMediumType type) {
switch (type) {
case TrackingMediumType.anime: return 'Anime';
case TrackingMediumType.manga: return 'Manga';
case TrackingMediumType.anime:
return 'Anime';
case TrackingMediumType.manga:
return 'Manga';
}
}
List<PopupMenuItem<MediumTrackingState>> _getPopupButtonItems(TrackingMediumType type) {
List<PopupMenuItem<MediumTrackingState>> _getPopupButtonItems(
TrackingMediumType type,
) {
return [
PopupMenuItem<MediumTrackingState>(
value: MediumTrackingState.ongoing,
@ -96,8 +101,8 @@ class AnimeListPageState extends State<AnimeListPage> {
initialValue: state.animeFilterState,
onSelected: (filterState) {
context.read<AnimeListBloc>().add(
AnimeFilterChangedEvent(filterState),
);
AnimeFilterChangedEvent(filterState),
);
},
itemBuilder: (_) => _getPopupButtonItems(TrackingMediumType.anime),
);
@ -109,8 +114,8 @@ class AnimeListPageState extends State<AnimeListPage> {
initialValue: state.mangaFilterState,
onSelected: (filterState) {
context.read<AnimeListBloc>().add(
MangaFilterChangedEvent(filterState),
);
MangaFilterChangedEvent(filterState),
);
},
itemBuilder: (_) => _getPopupButtonItems(TrackingMediumType.manga),
);
@ -145,7 +150,6 @@ class AnimeListPageState extends State<AnimeListPage> {
),
),
),
ListTile(
leading: const Icon(Icons.info),
title: const Text('About'),
@ -178,37 +182,37 @@ class AnimeListPageState extends State<AnimeListPage> {
return GridItem(
minusCallback: () {
context.read<AnimeListBloc>().add(
AnimeEpisodeDecrementedEvent(
anime.id,
),
);
AnimeEpisodeDecrementedEvent(
anime.id,
),
);
},
plusCallback: () {
context.read<AnimeListBloc>().add(
AnimeEpisodeIncrementedEvent(
anime.id,
),
);
AnimeEpisodeIncrementedEvent(
anime.id,
),
);
},
child: AnimeCoverImage(
url: anime.thumbnailUrl,
hero: anime.id,
onTap: () {
context.read<DetailsBloc>().add(
AnimeDetailsRequestedEvent(anime),
);
},
extra: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(
'${anime.episodesWatched}/${anime.episodesTotal ?? "???"}',
style: Theme.of(context).textTheme.titleMedium,
),
url: anime.thumbnailUrl,
hero: anime.id,
onTap: () {
context.read<DetailsBloc>().add(
AnimeDetailsRequestedEvent(anime),
);
},
extra: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(
'${anime.episodesWatched}/${anime.episodesTotal ?? "???"}',
style: Theme.of(context).textTheme.titleMedium,
),
),
),
),
);
},
),
@ -228,37 +232,37 @@ class AnimeListPageState extends State<AnimeListPage> {
return GridItem(
minusCallback: () {
context.read<AnimeListBloc>().add(
MangaChapterDecrementedEvent(
manga.id,
),
);
MangaChapterDecrementedEvent(
manga.id,
),
);
},
plusCallback: () {
context.read<AnimeListBloc>().add(
MangaChapterIncrementedEvent(
manga.id,
),
);
MangaChapterIncrementedEvent(
manga.id,
),
);
},
child: AnimeCoverImage(
hero: manga.id,
url: manga.thumbnailUrl,
onTap: () {
context.read<DetailsBloc>().add(
MangaDetailsRequestedEvent(manga),
);
},
extra: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(
'${manga.chaptersRead}/${manga.chaptersTotal ?? "???"}',
style: Theme.of(context).textTheme.titleMedium,
),
url: manga.thumbnailUrl,
onTap: () {
context.read<DetailsBloc>().add(
MangaDetailsRequestedEvent(manga),
);
},
extra: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(
'${manga.chaptersRead}/${manga.chaptersTotal ?? "???"}',
style: Theme.of(context).textTheme.titleMedium,
),
),
),
),
);
},
),
@ -266,19 +270,18 @@ class AnimeListPageState extends State<AnimeListPage> {
],
),
floatingActionButton: BlocBuilder<AnimeListBloc, AnimeListState>(
buildWhen: (prev, next) => prev.buttonVisibility != next.buttonVisibility,
buildWhen: (prev, next) =>
prev.buttonVisibility != next.buttonVisibility,
builder: (context, state) {
return AnimatedScale(
duration: const Duration(milliseconds: 250),
scale: state.buttonVisibility ?
1 :
0,
scale: state.buttonVisibility ? 1 : 0,
curve: Curves.easeInOutQuint,
child: FloatingActionButton(
onPressed: () {
context.read<AnimeSearchBloc>().add(
AnimeSearchRequestedEvent(state.trackingType),
);
AnimeSearchRequestedEvent(state.trackingType),
);
},
tooltip: 'Add new item',
child: const Icon(Icons.add),
@ -287,17 +290,16 @@ class AnimeListPageState extends State<AnimeListPage> {
},
),
bottomNavigationBar: BottomBar(
selectedIndex: state.trackingType == TrackingMediumType.anime ?
0 :
1,
selectedIndex:
state.trackingType == TrackingMediumType.anime ? 0 : 1,
onTap: (int index) {
context.read<AnimeListBloc>().add(
AnimeTrackingTypeChanged(
index == 0 ?
TrackingMediumType.anime :
TrackingMediumType.manga,
),
);
AnimeTrackingTypeChanged(
index == 0
? TrackingMediumType.anime
: TrackingMediumType.manga,
),
);
_controller.jumpToPage(index);
},

View File

@ -11,11 +11,11 @@ class AnimeSearchPage extends StatelessWidget {
});
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
builder: (_) => const AnimeSearchPage(),
settings: const RouteSettings(
name: animeSearchRoute,
),
);
builder: (_) => const AnimeSearchPage(),
settings: const RouteSettings(
name: animeSearchRoute,
),
);
@override
Widget build(BuildContext context) {
@ -24,9 +24,9 @@ class AnimeSearchPage extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: Text(
state.trackingType == TrackingMediumType.anime ?
'Anime Search' :
'Manga Search',
state.trackingType == TrackingMediumType.anime
? 'Anime Search'
: 'Manga Search',
),
),
body: Column(
@ -40,17 +40,16 @@ class AnimeSearchPage extends StatelessWidget {
),
onSubmitted: (_) {
context.read<AnimeSearchBloc>().add(
SearchQuerySubmittedEvent(),
);
SearchQuerySubmittedEvent(),
);
},
onChanged: (value) {
context.read<AnimeSearchBloc>().add(
SearchQueryChangedEvent(value),
);
SearchQueryChangedEvent(value),
);
},
),
),
if (state.working)
const Expanded(
child: Align(
@ -66,8 +65,8 @@ class AnimeSearchPage extends StatelessWidget {
return InkWell(
onTap: () {
context.read<AnimeSearchBloc>().add(
ResultTappedEvent(item),
);
ResultTappedEvent(item),
);
},
child: ListItem(
title: item.title,

View File

@ -16,11 +16,11 @@ class DetailsPage extends StatelessWidget {
});
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
builder: (_) => const DetailsPage(),
settings: const RouteSettings(
name: detailsRoute,
),
);
builder: (_) => const DetailsPage(),
settings: const RouteSettings(
name: detailsRoute,
),
);
@override
Widget build(BuildContext context) {
@ -30,195 +30,213 @@ class DetailsPage extends StatelessWidget {
),
body: BlocBuilder<DetailsBloc, DetailsState>(
builder: (context, state) {
return state.data == null ?
Container() :
Padding(
padding: const EdgeInsets.all(8),
child: ListView(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [AnimeCoverImage(
url: state.data!.thumbnailUrl,
hero: state.data!.id,
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.data!.title,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.titleLarge,
maxLines: 2,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
ElevatedButton(
onPressed: () async {
final result = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Remove "${state.data!.title}"?'),
content: Text('Are you sure you want to remove "${state.data!.title}" from the list?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
},
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
child: const Text('Remove'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('Cancel'),
),
],
);
},
);
if (result != true) return;
// ignore: use_build_context_synchronously
context.read<DetailsBloc>().add(
ItemRemovedEvent(
state.data!.id,
state.trackingType,
return state.data == null
? Container()
: Padding(
padding: const EdgeInsets.all(8),
child: ListView(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimeCoverImage(
url: state.data!.thumbnailUrl,
hero: state.data!.id,
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.data!.title,
textAlign: TextAlign.left,
style:
Theme.of(context).textTheme.titleLarge,
maxLines: 2,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
);
},
child: const Icon(Icons.delete),
ElevatedButton(
onPressed: () async {
final result = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
'Remove "${state.data!.title}"?',
),
content: Text(
'Are you sure you want to remove "${state.data!.title}" from the list?',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context)
.pop(true);
},
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
child: const Text('Remove'),
),
TextButton(
onPressed: () {
Navigator.of(context)
.pop(false);
},
child: const Text('Cancel'),
),
],
);
},
);
if (result != true) return;
// ignore: use_build_context_synchronously
context.read<DetailsBloc>().add(
ItemRemovedEvent(
state.data!.id,
state.trackingType,
),
);
},
child: const Icon(Icons.delete),
),
],
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
child: DropdownSelector<MediumTrackingState>(
title: state.trackingType == TrackingMediumType.anime
? 'Watch state'
: 'Read state',
onChanged: (MediumTrackingState newState) {
if (state.trackingType ==
TrackingMediumType.anime) {
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
(state.data! as AnimeTrackingData)
.copyWith(
state: newState,
),
),
);
} else if (state.trackingType ==
TrackingMediumType.manga) {
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
(state.data! as MangaTrackingData)
.copyWith(
state: newState,
),
),
);
}
},
values: [
SelectorItem(
MediumTrackingState.ongoing,
MediumTrackingState.ongoing
.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.completed,
MediumTrackingState.completed
.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.planned,
MediumTrackingState.planned
.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.dropped,
MediumTrackingState.dropped
.toNameString(state.trackingType),
),
],
initialValue: state.data!.state,
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
child: DropdownSelector<MediumTrackingState>(
title: state.trackingType == TrackingMediumType.anime ?
'Watch state' :
'Read state',
onChanged: (MediumTrackingState newState) {
if (state.trackingType == TrackingMediumType.anime) {
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
(state.data! as AnimeTrackingData).copyWith(
state: newState,
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
child: IntegerInput(
labelText:
state.trackingType == TrackingMediumType.anime
? 'Episodes'
: 'Chapters',
onChanged: (value) {
switch (state.trackingType) {
case TrackingMediumType.anime:
final data = state.data! as AnimeTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
episodesWatched: value,
),
),
);
break;
case TrackingMediumType.manga:
final data = state.data! as MangaTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
chaptersRead: value,
),
),
);
break;
}
},
initialValue: state.trackingType ==
TrackingMediumType.anime
? (state.data! as AnimeTrackingData)
.episodesWatched
: (state.data! as MangaTrackingData).chaptersRead,
),
),
if (state.trackingType == TrackingMediumType.manga)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
);
} else if (state.trackingType == TrackingMediumType.manga) {
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
(state.data! as MangaTrackingData).copyWith(
state: newState,
),
child: IntegerInput(
labelText: 'Volumes owned',
onChanged: (value) {
final data = state.data! as MangaTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
volumesOwned: value,
),
),
);
},
initialValue: (GetIt.I
.get<DetailsBloc>()
.state
.data! as MangaTrackingData)
.volumesOwned,
),
);
}
},
values: [
SelectorItem(
MediumTrackingState.ongoing,
MediumTrackingState.ongoing.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.completed,
MediumTrackingState.completed.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.planned,
MediumTrackingState.planned.toNameString(state.trackingType),
),
SelectorItem(
MediumTrackingState.dropped,
MediumTrackingState.dropped.toNameString(state.trackingType),
),
),
],
initialValue: state.data!.state,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
child: IntegerInput(
labelText: state.trackingType == TrackingMediumType.anime ?
'Episodes' :
'Chapters',
onChanged: (value) {
switch (state.trackingType) {
case TrackingMediumType.anime:
final data = state.data! as AnimeTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
episodesWatched: value,
),
),
);
break;
case TrackingMediumType.manga:
final data = state.data! as MangaTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
chaptersRead: value,
),
),
);
break;
}
},
initialValue: state.trackingType == TrackingMediumType.anime ?
(state.data! as AnimeTrackingData).episodesWatched :
(state.data! as MangaTrackingData).chaptersRead,
),
),
if (state.trackingType == TrackingMediumType.manga)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
),
child: IntegerInput(
labelText: 'Volumes owned',
onChanged: (value) {
final data = state.data! as MangaTrackingData;
context.read<DetailsBloc>().add(
DetailsUpdatedEvent(
data.copyWith(
volumesOwned: value,
),
),
);
},
initialValue: (GetIt.I.get<DetailsBloc>().state.data! as MangaTrackingData).volumesOwned,
),
),
],
),
);
);
},
),
);

View File

@ -76,54 +76,56 @@ class DropdownSelectorState<T> extends State<DropdownSelector<T>> {
void initState() {
super.initState();
index = widget.values.indexWhere((item) => item.value == widget.initialValue);
index =
widget.values.indexWhere((item) => item.value == widget.initialValue);
}
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Card(
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
final result = await showDialog<T>(
context: context,
builder: (context) => DropdownSelectorDialog<T>(
values: widget.values.cast<SelectorItem<T>>(),
),
);
if (result == null) return;
if (result == widget.values[index].value) return;
setState(() {
index = widget.values.indexWhere((item) => item.value == result);
});
widget.onChanged(result);
},
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: Theme.of(context).textTheme.titleSmall,
),
Text(
widget.values[index].text,
style: Theme.of(context).textTheme.bodyLarge,
),
],
children: [
Expanded(
child: Card(
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
final result = await showDialog<T>(
context: context,
builder: (context) => DropdownSelectorDialog<T>(
values: widget.values.cast<SelectorItem<T>>(),
),
);
if (result == null) return;
if (result == widget.values[index].value) return;
setState(() {
index =
widget.values.indexWhere((item) => item.value == result);
});
widget.onChanged(result);
},
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: Theme.of(context).textTheme.titleSmall,
),
Text(
widget.values[index].text,
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
),
),
],
),
],
);
}
}

View File

@ -28,7 +28,7 @@ class GridItemState extends State<GridItem> {
onHorizontalDragUpdate: (details) {
setState(() {
_offset += details.delta.dx;
_translationX = 160 / (1 + exp(-1 * (1/30) * _offset)) - 80;
_translationX = 160 / (1 + exp(-1 * (1 / 30) * _offset)) - 80;
});
},
onHorizontalDragEnd: (_) {

View File

@ -48,15 +48,14 @@ class AnimeCoverImage extends StatelessWidget {
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: cached ?
CachedNetworkImageProvider(url) as ImageProvider :
NetworkImage(url),
image: cached
? CachedNetworkImageProvider(url) as ImageProvider
: NetworkImage(url),
fit: BoxFit.cover,
),
),
),
),
if (extra != null)
Positioned(
left: 0,

View File

@ -59,7 +59,6 @@ class IntegerInputState extends State<IntegerInput> {
},
child: const Icon(Icons.remove),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
@ -84,7 +83,6 @@ class IntegerInputState extends State<IntegerInput> {
),
),
),
ElevatedButton(
onPressed: () {
_value++;

View File

@ -66,9 +66,9 @@ class ListItem extends StatelessWidget {
return Container();
},
color: Theme.of(context).scaffoldBackgroundColor,
direction: onRightSwipe != null && onLeftSwipe != null ?
SwipeDirection.horizontal :
SwipeDirection.none,
direction: onRightSwipe != null && onLeftSwipe != null
? SwipeDirection.horizontal
: SwipeDirection.none,
child: Padding(
padding: const EdgeInsets.all(8),
child: Row(
@ -81,7 +81,6 @@ class ListItem extends StatelessWidget {
extra: imageExtra,
url: thumbnailUrl,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
@ -97,7 +96,6 @@ class ListItem extends StatelessWidget {
softWrap: true,
overflow: TextOverflow.ellipsis,
),
...extra,
],
),