Skip to content

Commit

Permalink
add character page
Browse files Browse the repository at this point in the history
  • Loading branch information
dhc-jiangqingnan committed Oct 16, 2023
1 parent 5d42aa3 commit 5c4859b
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 61 deletions.
2 changes: 1 addition & 1 deletion lib/app/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class AnimeTrackerAppScaffold extends StatefulWidget {
}

class _AnimeTrackerAppScaffoldState extends State<AnimeTrackerAppScaffold> {
final animeTrackerRouterDelegate = AnimeTrackerRouterDelegate();
final animeTrackerRouterDelegate = AFRouterDelegate();

var currentNavigation = TopLevelNavigation.discover;
var needShowAppbar = true;
Expand Down
12 changes: 12 additions & 0 deletions lib/app/navigation/ani_flow_route_path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:anime_tracker/feature/airing_schedule/airing_schedule.dart';
import 'package:anime_tracker/feature/anime_page/anime_page.dart';
import 'package:anime_tracker/feature/anime_search/anime_search.dart';
import 'package:anime_tracker/feature/anime_track/anime_track.dart';
import 'package:anime_tracker/feature/character_page/character_page.dart';
import 'package:anime_tracker/feature/detail_anime/detail_anime.dart';
import 'package:anime_tracker/feature/discover/discover.dart';
import 'package:anime_tracker/feature/profile/profile.dart';
Expand Down Expand Up @@ -51,6 +52,12 @@ class AnimeListRoutePath extends AniFlowRoutePath {
final AnimeCategory category;
}

class CharacterListRoutePath extends AniFlowRoutePath {
CharacterListRoutePath(this.animeId);

final String animeId;
}

class DetailAnimeRoutePath extends AniFlowRoutePath {
DetailAnimeRoutePath(this.animeId);

Expand All @@ -71,6 +78,11 @@ extension AniFlowRoutePathEx on AniFlowRoutePath {
key: ValueKey('AnimeListPage_$category'),
category: category,
);
case CharacterListRoutePath(animeId: final animeId):
return CharacterListPage(
key: ValueKey('CharacterListPage_$animeId'),
animeId: animeId,
);
case DetailAnimeRoutePath(animeId: final animeId):
return DetailAnimePage(
key: ValueKey('DetailAnimeRoute_$animeId'),
Expand Down
12 changes: 9 additions & 3 deletions lib/app/navigation/ani_flow_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:anime_tracker/app/navigation/top_level_navigation.dart';
import 'package:anime_tracker/core/common/model/anime_category.dart';
import 'package:flutter/material.dart';

class AnimeTrackerRouterDelegate extends RouterDelegate<AniFlowRoutePath>
class AFRouterDelegate extends RouterDelegate<AniFlowRoutePath>
with ChangeNotifier {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey();

Expand All @@ -27,10 +27,10 @@ class AnimeTrackerRouterDelegate extends RouterDelegate<AniFlowRoutePath>

bool get needShowTopAppBar => _backStack.last is TopLevelRoutePath;

static AnimeTrackerRouterDelegate of(context) =>
static AFRouterDelegate of(context) =>
Router
.of(context)
.routerDelegate as AnimeTrackerRouterDelegate;
.routerDelegate as AFRouterDelegate;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -83,6 +83,12 @@ class AnimeTrackerRouterDelegate extends RouterDelegate<AniFlowRoutePath>
notifyListeners();
}

void navigateToCharacterList(String animeId) {
_backStack += [CharacterListRoutePath(animeId)];

notifyListeners();
}

void navigateToDetailAnime(String animeId) {
_backStack += [DetailAnimeRoutePath(animeId)];

Expand Down
17 changes: 9 additions & 8 deletions lib/core/data/media_information_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ abstract class MediaInformationRepository {

Future<LoadResult<List<CharacterAndVoiceActorModel>>>
loadCharacterPageByAnimeId({
required int animeId,
required String animeId,
required LoadType loadType,
});

Expand Down Expand Up @@ -86,22 +86,23 @@ class MediaInformationRepositoryImpl extends MediaInformationRepository {
@override
Future<LoadResult<List<CharacterAndVoiceActorModel>>>
loadCharacterPageByAnimeId(
{required int animeId, required LoadType loadType}) async {
{required String animeId, required LoadType loadType}) async {
return LoadPageUtil.loadPage<CharacterEdge, CharacterAndVoiceActor,
CharacterAndVoiceActorModel>(
type: loadType,
onGetNetworkRes: (page, perPage) => aniListDataSource.getCharacterPage(
animeId: animeId, page: page, perPage: perPage),
animeId: int.parse(animeId), page: page, perPage: perPage),
onClearDbCache: () async {},
onInsertEntityToDB: (entities) => animeDao.insertCharacterVoiceActors(
animeId: animeId, entities: entities),
animeId: int.parse(animeId), entities: entities),
onGetEntityFromDB: (page, perPage) => animeDao.getCharacterOfAnimeByPage(
animeId.toString(),
page: page,
perPage: perPage),
animeId.toString(),
page: page,
perPage: perPage,
),
mapDtoToEntity: (dto) => CharacterAndVoiceActor(
characterEntity: CharacterEntity.fromNetworkModel(dto),
voiceActorEntity: StaffEntity.fromVoiceActorDto(dto)!,
voiceActorEntity: StaffEntity.fromVoiceActorDto(dto),
),
mapEntityToModel: (entity) =>
CharacterAndVoiceActorModel.fromDatabaseEntity(entity),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'package:anime_tracker/core/data/model/character_and_voice_actor_model.da
import 'package:anime_tracker/core/design_system/widget/af_network_image.dart';
import 'package:flutter/material.dart';

class CharacterAndVoiceActor extends StatelessWidget {
const CharacterAndVoiceActor(
class CharacterAndVoiceActorWidget extends StatelessWidget {
const CharacterAndVoiceActorWidget(
{required this.model,
super.key,
this.textStyle,
Expand Down
2 changes: 1 addition & 1 deletion lib/feature/airing_schedule/airing_schedule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class _TimeLineItemState extends State<_TimeLineItem> {
child: AiringAnimeItem(
model: schedule,
onClick: () {
AnimeTrackerRouterDelegate.of(context)
AFRouterDelegate.of(context)
.navigateToDetailAnime(schedule.animeModel.id);
},
),
Expand Down
2 changes: 1 addition & 1 deletion lib/feature/anime_page/anime_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class _AnimeListPageContent extends StatelessWidget {
model: model,
textStyle: Theme.of(context).textTheme.labelMedium,
onClick: () {
AnimeTrackerRouterDelegate.of(context).navigateToDetailAnime(
AFRouterDelegate.of(context).navigateToDetailAnime(
model.id,
);
},
Expand Down
2 changes: 1 addition & 1 deletion lib/feature/anime_page/bloc/anime_page_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class AnimePageBloc extends PagingBloc<AnimeModel> {
}

@override
Future<bool> createLoadAnimePageTask({required int page}) async {
Future<bool> createLoadPageTask({required int page}) async {
final LoadResult result =
await _mediaInfoRepository.loadAnimePageByCategory(
category: category,
Expand Down
4 changes: 2 additions & 2 deletions lib/feature/anime_track/anime_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class _AnimeTrackPageContent extends StatelessWidget {
// mark watch
},
onClick: () {
AnimeTrackerRouterDelegate.of(context).navigateToDetailAnime(
AFRouterDelegate.of(context).navigateToDetailAnime(
item.animeModel!.id,
);
},
Expand Down Expand Up @@ -148,7 +148,7 @@ class _AnimeTrackPageContent extends StatelessWidget {
child: IconButton(
icon: const Icon(Icons.calendar_month_rounded),
onPressed: () {
AnimeTrackerRouterDelegate.of(context)
AFRouterDelegate.of(context)
.navigateToAiringSchedule();
},
),
Expand Down
40 changes: 40 additions & 0 deletions lib/feature/character_page/bloc/character_page_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dart:async';

import 'package:anime_tracker/core/common/util/global_static_constants.dart';
import 'package:anime_tracker/core/data/load_result.dart';
import 'package:anime_tracker/core/data/media_information_repository.dart';
import 'package:anime_tracker/core/data/model/character_and_voice_actor_model.dart';
import 'package:anime_tracker/feature/common/page_loading_state.dart';
import 'package:anime_tracker/feature/common/paging_bloc.dart';

class CharacterPageBloc extends PagingBloc<CharacterAndVoiceActorModel> {
CharacterPageBloc(
this.animeId, {
required MediaInformationRepository aniListRepository,
}) : _mediaInfoRepository = aniListRepository,
super(const PageLoading(data: [], page: 1));

final String animeId;
final MediaInformationRepository _mediaInfoRepository;

@override
Future<bool> createLoadPageTask({required int page}) async {
final LoadResult result =
await _mediaInfoRepository.loadCharacterPageByAnimeId(
animeId: animeId,
loadType: Append(page: page, perPage: Config.defaultPerPageCount),
);
switch (result) {
case LoadSuccess<List<CharacterAndVoiceActorModel>>(data: final data):
add(OnPageLoadedEvent(data, page));
return true;
case LoadError<List<CharacterAndVoiceActorModel>>(
exception: final exception
):
add(OnPageErrorEvent(exception));
return false;
default:
return false;
}
}
}
100 changes: 100 additions & 0 deletions lib/feature/character_page/character_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import 'package:anime_tracker/core/data/media_information_repository.dart';
import 'package:anime_tracker/core/data/model/character_and_voice_actor_model.dart';
import 'package:anime_tracker/core/design_system/animetion/page_transaction_animetion.dart';
import 'package:anime_tracker/core/design_system/widget/anime_character_and_voice_actor.dart';
import 'package:anime_tracker/feature/character_page/bloc/character_page_bloc.dart';
import 'package:anime_tracker/feature/common/page_loading_state.dart';
import 'package:anime_tracker/feature/common/paging_bloc.dart';
import 'package:anime_tracker/feature/common/paging_content_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CharacterListPage extends Page {
final String animeId;

const CharacterListPage({required this.animeId, super.key});

@override
Route createRoute(BuildContext context) {
return CharacterListRoute(settings: this, animeId: animeId);
}
}

class CharacterListRoute extends PageRoute with MaterialRouteTransitionMixin {
final String animeId;

CharacterListRoute({required this.animeId, super.settings});

@override
Widget buildContent(BuildContext context) {
return BlocProvider(
create: (context) => CharacterPageBloc(
animeId,
aniListRepository: context.read<MediaInformationRepository>(),
),
child: const _CharacterListPageContent(),
);
}

@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return getFistPageTransaction(
animation: animation,
child: getSecondaryPageTransaction(
animation: secondaryAnimation,
child: child,
),
);
}

@override
bool get maintainState => true;
}

class _CharacterListPageContent extends StatelessWidget {
const _CharacterListPageContent();

@override
Widget build(BuildContext context) {
return BlocBuilder<CharacterPageBloc,
PagingState<List<CharacterAndVoiceActorModel>>>(
builder: (context, state) {
final pagingState = state;
return Scaffold(
appBar: AppBar(
title: const Text('Characters'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.maybePop(context);
},
),
),
body: PagingContent(
pagingState: pagingState,
onBuildItem: (context, model) => _buildListItems(context, model),
onRequestNewPage: () {
context.read<CharacterPageBloc>().add(OnRequestLoadPageEvent());
},
onRetryLoadPage: () {
context.read<CharacterPageBloc>().add(OnRetryLoadPageEvent());
},
),
);
});
}

Widget _buildListItems(
BuildContext context, CharacterAndVoiceActorModel model) {
return SizedBox(
height: 124,
child: CharacterAndVoiceActorWidget(
model: model,
textStyle: Theme.of(context).textTheme.labelMedium,
onCharacterTap: () {},
onVoiceActorTop: () {},
),
);
}
}
18 changes: 9 additions & 9 deletions lib/feature/common/paging_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ class OnRetryLoadPageEvent<T> extends PagingEvent<T> {}
abstract class PagingBloc<T>
extends Bloc<PagingEvent<T>, PagingState<List<T>>> {
PagingBloc(super.initialState) {
on<OnPageLoadedEvent<T>>(_onAnimePageLoadedEvent);
on<OnPageErrorEvent<T>>(_onAnimePageErrorEvent);
on<OnPageLoadedEvent<T>>(_onPageLoadedEvent);
on<OnPageErrorEvent<T>>(_onPageErrorEvent);
on<OnRequestLoadPageEvent<T>>(_onRequestLoadPageEvent);
on<OnRetryLoadPageEvent<T>>(_onRetryLoadPageEvent);

/// launch event to get first page data.
unawaited(createLoadAnimePageTask(page: 1));
unawaited(createLoadPageTask(page: 1));
}

FutureOr<void> _onAnimePageLoadedEvent(
FutureOr<void> _onPageLoadedEvent(
OnPageLoadedEvent<T> event, Emitter<PagingState<List<T>>> emit) {
final pagingState = state;
final currentData = pagingState.data;
Expand All @@ -49,7 +49,7 @@ abstract class PagingBloc<T>
onEmitNewPagingState(newPagingState, emit);
}

FutureOr<void> _onAnimePageErrorEvent(
FutureOr<void> _onPageErrorEvent(
OnPageErrorEvent<T> event, Emitter<PagingState<List<T>>> emit) {
emit(state.toError(event.exception));
}
Expand All @@ -69,7 +69,7 @@ abstract class PagingBloc<T>
emit(pagingState.toLoading());

/// load new page.
createLoadAnimePageTask(page: currentPage + 1);
createLoadPageTask(page: currentPage + 1);
}

FutureOr<void> _onRetryLoadPageEvent(
Expand All @@ -81,11 +81,11 @@ abstract class PagingBloc<T>
/// change state to loading.
emit(state.toLoading());

/// post task to load anime.
createLoadAnimePageTask(page: state.page + 1);
/// post task to load page.
createLoadPageTask(page: state.page + 1);
}

Future<bool> createLoadAnimePageTask({required int page});
Future<bool> createLoadPageTask({required int page});

void onEmitNewPagingState(
PagingState<List<T>> state, Emitter<PagingState<List<T>>> emit) {
Expand Down
Loading

0 comments on commit 5c4859b

Please sign in to comment.