Skip to content

Commit

Permalink
Merge pull request #63 from pet-sitter/feature-61-breeds
Browse files Browse the repository at this point in the history
Feat:[펫] Pagination으로 조회한 견/묘종을 등록 상세화면에서 무한 스크롤 기능을 통해 출력 #61
  • Loading branch information
juan-rybczinski authored Jan 30, 2024
2 parents 37140a0 + 333f286 commit b953407
Show file tree
Hide file tree
Showing 22 changed files with 349 additions and 20 deletions.
2 changes: 2 additions & 0 deletions lib/app/di/app_binding.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:pets_next_door_flutter/app/di/modules/auth_di.dart';
import 'package:pets_next_door_flutter/app/di/modules/media_di.dart';
import 'package:pets_next_door_flutter/app/di/modules/pet_di.dart';
import 'package:pets_next_door_flutter/app/di/modules/user_di.dart';

final class AppBinder {
Expand All @@ -16,6 +17,7 @@ final class AppBinder {
AuthDependencyInjection(),
UserDependencyInjection(),
MediaDependencyInjection(),
PetDependencyInjection(),
]) {
di.init();
}
Expand Down
36 changes: 36 additions & 0 deletions lib/app/di/modules/pet_di.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:get_it/get_it.dart';
import 'package:pets_next_door_flutter/app/di/feature_di_interface.dart';
import 'package:pets_next_door_flutter/features/pet/data/remote/pet_remote_data_source.dart';
import 'package:pets_next_door_flutter/features/pet/data/remote/pet_remote_data_source_impl.dart';
import 'package:pets_next_door_flutter/features/pet/pet.dart';
import 'package:pets_next_door_flutter/features/pet/repository/pet_repository.dart';
import 'package:pets_next_door_flutter/features/pet/repository/pet_repository_impl.dart';
import 'package:pets_next_door_flutter/features/pet/usecases/get_breeds_use_case.dart';

final class PetDependencyInjection extends FeatureDependencyInjection {
@override
void dataSources() {
GetIt.I.registerLazySingleton<PetRemoteDataSource>(
PetRemoteDataSourceImpl.new,
);
}

@override
void repositories() {
GetIt.I.registerLazySingleton<PetRepository>(
() => PetRepositoryImpl(
petRemoteDataSource: petRemoteDataSource,
),
);
}

@override
void useCases() {
GetIt.I
..registerFactory<GetBreedsUseCase>(
() => GetBreedsUseCase(
petRepository: petRepository,
),
);
}
}
6 changes: 6 additions & 0 deletions lib/core/enums/pet_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ enum PetType {
dog(displayName: '강아지', code: 'dog');

const PetType({required this.displayName, required this.code});

final String displayName;
final String code;

factory PetType.getByCode(String code) {
return PetType.values
.firstWhere((value) => value.code == code, orElse: () => PetType.cat);
}
}
File renamed without changes.
17 changes: 17 additions & 0 deletions lib/features/pet/api/pet_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:dio/dio.dart' hide Headers;
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_response.dart';
import 'package:retrofit/http.dart';

part 'pet_api.g.dart';

@RestApi()
abstract class PetAPI {
factory PetAPI(Dio dio, {String baseUrl}) = _PetAPI;

@GET("/breeds")
@Headers({"requiresToken": false})
Future<BreedsPaginationResponse> getBreed(
@Queries() BreedsPaginationRequest breedsPaginationRequest,
);
}
80 changes: 80 additions & 0 deletions lib/features/pet/api/pet_api.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions lib/features/pet/data/remote/pet_remote_data_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_response.dart';

abstract interface class PetRemoteDataSource {
Future<BreedsPaginationResponse> getBreeds({
required BreedsPaginationRequest breedsPaginationRequest,
});
}
20 changes: 20 additions & 0 deletions lib/features/pet/data/remote/pet_remote_data_source_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:pets_next_door_flutter/app/env/flavors.dart';
import 'package:pets_next_door_flutter/core/network_handling/app_dio.dart';
import 'package:pets_next_door_flutter/features/pet/api/pet_api.dart';
import 'package:pets_next_door_flutter/features/pet/data/remote/pet_remote_data_source.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_response.dart';

final class PetRemoteDataSourceImpl implements PetRemoteDataSource {
final PetAPI _petAPI = PetAPI(
AppDio.instance,
baseUrl: Flavor.apiUrl,
);

@override
Future<BreedsPaginationResponse> getBreeds({
required BreedsPaginationRequest breedsPaginationRequest,
}) {
return _petAPI.getBreed(breedsPaginationRequest);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:pets_next_door_flutter/core/enums/pet_type.dart';
import 'package:pets_next_door_flutter/features/pet/domain/pagination_request.dart';
import 'package:pets_next_door_flutter/core/pagination/pagination_request.dart';

/// Metadata used when fetching movies with the paginated search API.
class BreedsPaginationRequest implements PaginationRequest {
Expand Down
17 changes: 11 additions & 6 deletions lib/features/pet/domain/breeds_pagination_response.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import 'package:pets_next_door_flutter/core/enums/pet_type.dart';
import 'package:pets_next_door_flutter/core/pagination/pagination_response.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breed.dart';
import 'package:pets_next_door_flutter/features/pet/domain/pagination_response.dart';

/// Metadata used when fetching movies with the paginated search API.
class BreedsPaginationResponse implements PaginationResponse<Breed> {
BreedsPaginationResponse({
required this.page,
required this.size,
required this.petType,
required this.items,
});

BreedsPaginationResponse.fromJson(
Map<String, dynamic> json,
) : page = json['page'] as int,
size = json['size'] as int,
items = (json['items'] as List<dynamic>)
.map((e) => Breed.fromJson(e))
.toList();

@override
final int page;

Expand All @@ -19,20 +27,17 @@ class BreedsPaginationResponse implements PaginationResponse<Breed> {
@override
final List<Breed> items;

final PetType petType;

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is BreedsPaginationResponse &&
other.petType == petType &&
other.page == page &&
other.size == size;
}

@override
int get hashCode => petType.hashCode ^ page.hashCode ^ size.hashCode;
int get hashCode => page.hashCode ^ size.hashCode;

@override
Map<String, dynamic> toJson() {
Expand Down
12 changes: 12 additions & 0 deletions lib/features/pet/pet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:pets_next_door_flutter/app/di/locator.dart';
import 'package:pets_next_door_flutter/features/pet/data/remote/pet_remote_data_source.dart';
import 'package:pets_next_door_flutter/features/pet/repository/pet_repository.dart';
import 'package:pets_next_door_flutter/features/pet/usecases/get_breeds_use_case.dart';

// export 'data/remote/pet_remote_data_source.dart';
// export 'repository/pet_repository.dart';
// export 'usecases/get_breeds_use_case.dart';

final petRemoteDataSource = locator<PetRemoteDataSource>();
final petRepository = locator<PetRepository>();
final getBreedsUseCase = locator<GetBreedsUseCase>();
9 changes: 9 additions & 0 deletions lib/features/pet/repository/pet_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:pets_next_door_flutter/core/utils/result.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breed.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';

abstract interface class PetRepository {
Future<Result<List<Breed>>> getBreeds({
required BreedsPaginationRequest breedsPaginationRequest,
});
}
23 changes: 23 additions & 0 deletions lib/features/pet/repository/pet_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:pets_next_door_flutter/core/utils/result.dart';
import 'package:pets_next_door_flutter/features/pet/data/remote/pet_remote_data_source.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breed.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/repository/pet_repository.dart';

final class PetRepositoryImpl implements PetRepository {
const PetRepositoryImpl({
required PetRemoteDataSource petRemoteDataSource,
}) : _petRemoteDataSource = petRemoteDataSource;

final PetRemoteDataSource _petRemoteDataSource;

@override
Future<Result<List<Breed>>> getBreeds({required BreedsPaginationRequest breedsPaginationRequest}) async {
try {
final breedsData = await _petRemoteDataSource.getBreeds(breedsPaginationRequest: breedsPaginationRequest);
return Result.success(breedsData.items);
} on Exception catch (e) {
return Result.failure(e);
}
}
}
18 changes: 18 additions & 0 deletions lib/features/pet/usecases/get_breeds_use_case.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:pets_next_door_flutter/core/utils/result.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breed.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/repository/pet_repository.dart';

final class GetBreedsUseCase {
const GetBreedsUseCase({
required PetRepository petRepository,
}) : _petRepository = petRepository;

final PetRepository _petRepository;

Future<Result<List<Breed>>> call({
required BreedsPaginationRequest breedsPaginationRequest,
}) async {
return _petRepository.getBreeds(breedsPaginationRequest: breedsPaginationRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ class PetTypeSelectionButtonGroup extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedPetType = ref.watch(petStateProvider).petType;

return Row(
children: [
Expanded(
Expand All @@ -22,7 +20,7 @@ class PetTypeSelectionButtonGroup extends ConsumerWidget {
onTap: () => ref.read(petStateProvider.notifier).update(
(state) => state.copyWith(petType: PetType.cat),
),
active: selectedPetType == PetType.cat,
active: ref.read(petStateProvider.notifier).state.petType == PetType.cat,
),
),
gapW12,
Expand All @@ -32,7 +30,7 @@ class PetTypeSelectionButtonGroup extends ConsumerWidget {
onTap: () => ref.read(petStateProvider.notifier).update(
(state) => state.copyWith(petType: PetType.dog),
),
active: selectedPetType == PetType.dog,
active: ref.read(petStateProvider.notifier).state.petType == PetType.dog,
),
),
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breed.dart';
import 'package:pets_next_door_flutter/features/pet/domain/breeds_pagination_request.dart';
import 'package:pets_next_door_flutter/features/pet/domain/pet.dart';
import 'package:pets_next_door_flutter/features/pet/pet.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'breeds_paging_controller_provider.g.dart';

const breedsPagingSize = 20;

@riverpod
class BreedsPagingController extends _$BreedsPagingController {
@override
Raw<PagingController> build() {
final controller = PagingController<int, Breed>(firstPageKey: 1);

controller.addPageRequestListener((pageKey) => fetchPage(pageKey));

ref.onDispose(() => controller.dispose());

return controller;
}

Future<void> fetchPage(int pageKey) async {
try {
final newBreeds = await getBreedsUseCase.call(
breedsPaginationRequest: new BreedsPaginationRequest(
page: pageKey,
size: breedsPagingSize,
petType: ref.read(petStateProvider.notifier).state.petType!,
),
);

newBreeds.fold(
onSuccess: (breeds) {
final isLastPage = breeds.length < breedsPagingSize;

if (isLastPage) {
state.appendLastPage(breeds);
} else {
state.appendPage(breeds, pageKey + 1);
}
},
onFailure: (e) => print('::: Fold Error ::: $e'),
);
} catch (e) {
print('::: Catch Error ::: $e');
}
}
}
Loading

0 comments on commit b953407

Please sign in to comment.