diff --git a/android/app/build.gradle b/android/app/build.gradle index d34325c..87e149d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -57,7 +57,7 @@ android { applicationId "co.yellowtoast.pets_next_door_flutter" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/app/google-services.json b/android/app/google-services.json index d9c3ffd..38368aa 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -14,7 +14,23 @@ }, "oauth_client": [ { - "client_id": "231574029969-5chgj3uaqlh2fg9pr9qpc0diuhdhbook.apps.googleusercontent.com", + "client_id": "231574029969-17e6o5jt8pkcjvbudocmqt1pj01804a2.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "co.yellowtoast.pets_next_door_flutter.dev", + "certificate_hash": "d8d5bd779560ccae8f343662db21ac302aa8cdad" + } + }, + { + "client_id": "231574029969-9oaog3g1361tvs2mj0pj22n8ijgp4tr4.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "co.yellowtoast.pets_next_door_flutter.dev", + "certificate_hash": "1c575dfabec64a99466b5f1ddb60018e1c41fdf9" + } + }, + { + "client_id": "231574029969-hjvfj85727ar4jpbt4iemi0i6nf545bj.apps.googleusercontent.com", "client_type": 3 } ], diff --git a/android/build.gradle b/android/build.gradle index 0391438..4da596e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.3.0' // START: FlutterFire Configuration - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.3.14' // END: FlutterFire Configuration classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } diff --git a/lib/app/di/app_binding.dart b/lib/app/di/app_binding.dart index b5567b3..0f9a3a3 100644 --- a/lib/app/di/app_binding.dart +++ b/lib/app/di/app_binding.dart @@ -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 { @@ -16,6 +17,7 @@ final class AppBinder { AuthDependencyInjection(), UserDependencyInjection(), MediaDependencyInjection(), + PetDependencyInjection(), ]) { di.init(); } diff --git a/lib/app/di/modules/pet_di.dart b/lib/app/di/modules/pet_di.dart new file mode 100644 index 0000000..de20778 --- /dev/null +++ b/lib/app/di/modules/pet_di.dart @@ -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( + PetRemoteDataSourceImpl.new, + ); + } + + @override + void repositories() { + GetIt.I.registerLazySingleton( + () => PetRepositoryImpl( + petRemoteDataSource: petRemoteDataSource, + ), + ); + } + + @override + void useCases() { + GetIt.I + ..registerFactory( + () => GetBreedsUseCase( + petRepository: petRepository, + ), + ); + } +} diff --git a/lib/core/enums/pet_type.dart b/lib/core/enums/pet_type.dart index ad2d85f..ef2b25a 100644 --- a/lib/core/enums/pet_type.dart +++ b/lib/core/enums/pet_type.dart @@ -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); + } } diff --git a/lib/features/pet/domain/pagination_request.dart b/lib/core/pagination/pagination_request.dart similarity index 100% rename from lib/features/pet/domain/pagination_request.dart rename to lib/core/pagination/pagination_request.dart diff --git a/lib/features/pet/domain/pagination_response.dart b/lib/core/pagination/pagination_response.dart similarity index 100% rename from lib/features/pet/domain/pagination_response.dart rename to lib/core/pagination/pagination_response.dart diff --git a/lib/features/pet/api/pet_api.dart b/lib/features/pet/api/pet_api.dart new file mode 100644 index 0000000..6fbe822 --- /dev/null +++ b/lib/features/pet/api/pet_api.dart @@ -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 getBreed( + @Queries() BreedsPaginationRequest breedsPaginationRequest, + ); +} diff --git a/lib/features/pet/api/pet_api.g.dart b/lib/features/pet/api/pet_api.g.dart new file mode 100644 index 0000000..51952f5 --- /dev/null +++ b/lib/features/pet/api/pet_api.g.dart @@ -0,0 +1,80 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'pet_api.dart'; + +// ************************************************************************** +// RetrofitGenerator +// ************************************************************************** + +// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers + +class _PetAPI implements PetAPI { + _PetAPI( + this._dio, { + this.baseUrl, + }); + + final Dio _dio; + + String? baseUrl; + + @override + Future getBreed( + BreedsPaginationRequest breedsPaginationRequest) async { + const _extra = {}; + final queryParameters = {}; + queryParameters.addAll(breedsPaginationRequest.toJson()); + final _headers = {r'requiresToken': false}; + _headers.removeWhere((k, v) => v == null); + final Map? _data = null; + final _result = await _dio.fetch>( + _setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/breeds', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final value = BreedsPaginationResponse.fromJson(_result.data!); + return value; + } + + RequestOptions _setStreamType(RequestOptions requestOptions) { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + return requestOptions; + } + + String _combineBaseUrls( + String dioBaseUrl, + String? baseUrl, + ) { + if (baseUrl == null || baseUrl.trim().isEmpty) { + return dioBaseUrl; + } + + final url = Uri.parse(baseUrl); + + if (url.isAbsolute) { + return url.toString(); + } + + return Uri.parse(dioBaseUrl).resolveUri(url).toString(); + } +} diff --git a/lib/features/pet/data/remote/pet_remote_data_source.dart b/lib/features/pet/data/remote/pet_remote_data_source.dart new file mode 100644 index 0000000..5fbde8f --- /dev/null +++ b/lib/features/pet/data/remote/pet_remote_data_source.dart @@ -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 getBreeds({ + required BreedsPaginationRequest breedsPaginationRequest, + }); +} diff --git a/lib/features/pet/data/remote/pet_remote_data_source_impl.dart b/lib/features/pet/data/remote/pet_remote_data_source_impl.dart new file mode 100644 index 0000000..9ef7eff --- /dev/null +++ b/lib/features/pet/data/remote/pet_remote_data_source_impl.dart @@ -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 getBreeds({ + required BreedsPaginationRequest breedsPaginationRequest, + }) { + return _petAPI.getBreed(breedsPaginationRequest); + } +} diff --git a/lib/features/pet/domain/breed_pagination_request.dart b/lib/features/pet/domain/breeds_pagination_request.dart similarity index 86% rename from lib/features/pet/domain/breed_pagination_request.dart rename to lib/features/pet/domain/breeds_pagination_request.dart index a367b25..cd006be 100644 --- a/lib/features/pet/domain/breed_pagination_request.dart +++ b/lib/features/pet/domain/breeds_pagination_request.dart @@ -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 { diff --git a/lib/features/pet/domain/breeds_pagination_response.dart b/lib/features/pet/domain/breeds_pagination_response.dart index f90f205..f51f41e 100644 --- a/lib/features/pet/domain/breeds_pagination_response.dart +++ b/lib/features/pet/domain/breeds_pagination_response.dart @@ -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 { BreedsPaginationResponse({ required this.page, required this.size, - required this.petType, required this.items, }); + + BreedsPaginationResponse.fromJson( + Map json, + ) : page = json['page'] as int, + size = json['size'] as int, + items = (json['items'] as List) + .map((e) => Breed.fromJson(e)) + .toList(); + @override final int page; @@ -19,20 +27,17 @@ class BreedsPaginationResponse implements PaginationResponse { @override final List 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 toJson() { diff --git a/lib/features/pet/pet.dart b/lib/features/pet/pet.dart new file mode 100644 index 0000000..1c589dd --- /dev/null +++ b/lib/features/pet/pet.dart @@ -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(); +final petRepository = locator(); +final getBreedsUseCase = locator(); diff --git a/lib/features/pet/repository/pet_repository.dart b/lib/features/pet/repository/pet_repository.dart new file mode 100644 index 0000000..adf3f7a --- /dev/null +++ b/lib/features/pet/repository/pet_repository.dart @@ -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>> getBreeds({ + required BreedsPaginationRequest breedsPaginationRequest, + }); +} diff --git a/lib/features/pet/repository/pet_repository_impl.dart b/lib/features/pet/repository/pet_repository_impl.dart new file mode 100644 index 0000000..818174b --- /dev/null +++ b/lib/features/pet/repository/pet_repository_impl.dart @@ -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>> 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); + } + } +} \ No newline at end of file diff --git a/lib/features/pet/usecases/get_breeds_use_case.dart b/lib/features/pet/usecases/get_breeds_use_case.dart new file mode 100644 index 0000000..2f41681 --- /dev/null +++ b/lib/features/pet/usecases/get_breeds_use_case.dart @@ -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>> call({ + required BreedsPaginationRequest breedsPaginationRequest, + }) async { + return _petRepository.getBreeds(breedsPaginationRequest: breedsPaginationRequest); + } +} \ No newline at end of file diff --git a/lib/features/sign_up/data/dto/sign_up_data_dto.dart b/lib/features/sign_up/data/dto/sign_up_data_dto.dart index 244d0b7..cf02469 100644 --- a/lib/features/sign_up/data/dto/sign_up_data_dto.dart +++ b/lib/features/sign_up/data/dto/sign_up_data_dto.dart @@ -2,21 +2,31 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -part 'sign_up_data_dto.freezed.dart'; part 'sign_up_data_dto.g.dart'; /// api 통신을 통해 가져오는 유저 데이터 모델 -@freezed -class SignUpDataDto with _$SignUpDataDto { - const factory SignUpDataDto({ - required String email, - @JsonKey(name: 'fbProviderType') required String firebaseProviderType, - @JsonKey(name: 'fbUid') required String firebaseUid, - required String fullname, - required String nickname, - required int profileImageId, - }) = _SignUpDataDto; +@JsonSerializable() +class SignUpDataDto { + final String email; + @JsonKey(name: 'fbProviderType') + final String firebaseProviderType; + @JsonKey(name: 'fbUid') + final String firebaseUid; + final String fullname; + final String nickname; + final int profileImageId; + + SignUpDataDto({ + required this.email, + required this.firebaseProviderType, + required this.firebaseUid, + required this.fullname, + required this.nickname, + required this.profileImageId, + }); factory SignUpDataDto.fromJson(Map json) => _$SignUpDataDtoFromJson(json); + + Map toJson() => _$SignUpDataDtoToJson(this); } diff --git a/lib/features/sign_up/data/dto/sign_up_data_dto.freezed.dart b/lib/features/sign_up/data/dto/sign_up_data_dto.freezed.dart deleted file mode 100644 index 6ae7e2c..0000000 --- a/lib/features/sign_up/data/dto/sign_up_data_dto.freezed.dart +++ /dev/null @@ -1,268 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'sign_up_data_dto.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -SignUpDataDto _$SignUpDataDtoFromJson(Map json) { - return _SignUpDataDto.fromJson(json); -} - -/// @nodoc -mixin _$SignUpDataDto { - String get email => throw _privateConstructorUsedError; - @JsonKey(name: 'fbProviderType') - String get firebaseProviderType => throw _privateConstructorUsedError; - @JsonKey(name: 'fbUid') - String get firebaseUid => throw _privateConstructorUsedError; - String get fullname => throw _privateConstructorUsedError; - String get nickname => throw _privateConstructorUsedError; - int get profileImageId => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $SignUpDataDtoCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $SignUpDataDtoCopyWith<$Res> { - factory $SignUpDataDtoCopyWith( - SignUpDataDto value, $Res Function(SignUpDataDto) then) = - _$SignUpDataDtoCopyWithImpl<$Res, SignUpDataDto>; - @useResult - $Res call( - {String email, - @JsonKey(name: 'fbProviderType') String firebaseProviderType, - @JsonKey(name: 'fbUid') String firebaseUid, - String fullname, - String nickname, - int profileImageId}); -} - -/// @nodoc -class _$SignUpDataDtoCopyWithImpl<$Res, $Val extends SignUpDataDto> - implements $SignUpDataDtoCopyWith<$Res> { - _$SignUpDataDtoCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? email = null, - Object? firebaseProviderType = null, - Object? firebaseUid = null, - Object? fullname = null, - Object? nickname = null, - Object? profileImageId = null, - }) { - return _then(_value.copyWith( - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as String, - firebaseProviderType: null == firebaseProviderType - ? _value.firebaseProviderType - : firebaseProviderType // ignore: cast_nullable_to_non_nullable - as String, - firebaseUid: null == firebaseUid - ? _value.firebaseUid - : firebaseUid // ignore: cast_nullable_to_non_nullable - as String, - fullname: null == fullname - ? _value.fullname - : fullname // ignore: cast_nullable_to_non_nullable - as String, - nickname: null == nickname - ? _value.nickname - : nickname // ignore: cast_nullable_to_non_nullable - as String, - profileImageId: null == profileImageId - ? _value.profileImageId - : profileImageId // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_SignUpDataDtoCopyWith<$Res> - implements $SignUpDataDtoCopyWith<$Res> { - factory _$$_SignUpDataDtoCopyWith( - _$_SignUpDataDto value, $Res Function(_$_SignUpDataDto) then) = - __$$_SignUpDataDtoCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String email, - @JsonKey(name: 'fbProviderType') String firebaseProviderType, - @JsonKey(name: 'fbUid') String firebaseUid, - String fullname, - String nickname, - int profileImageId}); -} - -/// @nodoc -class __$$_SignUpDataDtoCopyWithImpl<$Res> - extends _$SignUpDataDtoCopyWithImpl<$Res, _$_SignUpDataDto> - implements _$$_SignUpDataDtoCopyWith<$Res> { - __$$_SignUpDataDtoCopyWithImpl( - _$_SignUpDataDto _value, $Res Function(_$_SignUpDataDto) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? email = null, - Object? firebaseProviderType = null, - Object? firebaseUid = null, - Object? fullname = null, - Object? nickname = null, - Object? profileImageId = null, - }) { - return _then(_$_SignUpDataDto( - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as String, - firebaseProviderType: null == firebaseProviderType - ? _value.firebaseProviderType - : firebaseProviderType // ignore: cast_nullable_to_non_nullable - as String, - firebaseUid: null == firebaseUid - ? _value.firebaseUid - : firebaseUid // ignore: cast_nullable_to_non_nullable - as String, - fullname: null == fullname - ? _value.fullname - : fullname // ignore: cast_nullable_to_non_nullable - as String, - nickname: null == nickname - ? _value.nickname - : nickname // ignore: cast_nullable_to_non_nullable - as String, - profileImageId: null == profileImageId - ? _value.profileImageId - : profileImageId // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_SignUpDataDto implements _SignUpDataDto { - const _$_SignUpDataDto( - {required this.email, - @JsonKey(name: 'fbProviderType') required this.firebaseProviderType, - @JsonKey(name: 'fbUid') required this.firebaseUid, - required this.fullname, - required this.nickname, - required this.profileImageId}); - - factory _$_SignUpDataDto.fromJson(Map json) => - _$$_SignUpDataDtoFromJson(json); - - @override - final String email; - @override - @JsonKey(name: 'fbProviderType') - final String firebaseProviderType; - @override - @JsonKey(name: 'fbUid') - final String firebaseUid; - @override - final String fullname; - @override - final String nickname; - @override - final int profileImageId; - - @override - String toString() { - return 'SignUpDataDto(email: $email, firebaseProviderType: $firebaseProviderType, firebaseUid: $firebaseUid, fullname: $fullname, nickname: $nickname, profileImageId: $profileImageId)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SignUpDataDto && - (identical(other.email, email) || other.email == email) && - (identical(other.firebaseProviderType, firebaseProviderType) || - other.firebaseProviderType == firebaseProviderType) && - (identical(other.firebaseUid, firebaseUid) || - other.firebaseUid == firebaseUid) && - (identical(other.fullname, fullname) || - other.fullname == fullname) && - (identical(other.nickname, nickname) || - other.nickname == nickname) && - (identical(other.profileImageId, profileImageId) || - other.profileImageId == profileImageId)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, email, firebaseProviderType, - firebaseUid, fullname, nickname, profileImageId); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SignUpDataDtoCopyWith<_$_SignUpDataDto> get copyWith => - __$$_SignUpDataDtoCopyWithImpl<_$_SignUpDataDto>(this, _$identity); - - @override - Map toJson() { - return _$$_SignUpDataDtoToJson( - this, - ); - } -} - -abstract class _SignUpDataDto implements SignUpDataDto { - const factory _SignUpDataDto( - {required final String email, - @JsonKey(name: 'fbProviderType') - required final String firebaseProviderType, - @JsonKey(name: 'fbUid') required final String firebaseUid, - required final String fullname, - required final String nickname, - required final int profileImageId}) = _$_SignUpDataDto; - - factory _SignUpDataDto.fromJson(Map json) = - _$_SignUpDataDto.fromJson; - - @override - String get email; - @override - @JsonKey(name: 'fbProviderType') - String get firebaseProviderType; - @override - @JsonKey(name: 'fbUid') - String get firebaseUid; - @override - String get fullname; - @override - String get nickname; - @override - int get profileImageId; - @override - @JsonKey(ignore: true) - _$$_SignUpDataDtoCopyWith<_$_SignUpDataDto> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/sign_up/data/dto/sign_up_data_dto.g.dart b/lib/features/sign_up/data/dto/sign_up_data_dto.g.dart index 04f917f..1a8fdd8 100644 --- a/lib/features/sign_up/data/dto/sign_up_data_dto.g.dart +++ b/lib/features/sign_up/data/dto/sign_up_data_dto.g.dart @@ -6,8 +6,8 @@ part of 'sign_up_data_dto.dart'; // JsonSerializableGenerator // ************************************************************************** -_$_SignUpDataDto _$$_SignUpDataDtoFromJson(Map json) => - _$_SignUpDataDto( +SignUpDataDto _$SignUpDataDtoFromJson(Map json) => + SignUpDataDto( email: json['email'] as String, firebaseProviderType: json['fbProviderType'] as String, firebaseUid: json['fbUid'] as String, @@ -16,7 +16,7 @@ _$_SignUpDataDto _$$_SignUpDataDtoFromJson(Map json) => profileImageId: json['profileImageId'] as int, ); -Map _$$_SignUpDataDtoToJson(_$_SignUpDataDto instance) => +Map _$SignUpDataDtoToJson(SignUpDataDto instance) => { 'email': instance.email, 'fbProviderType': instance.firebaseProviderType, diff --git a/lib/features/user/api/user_api.dart b/lib/features/user/api/user_api.dart index ecdf62f..9b37a9c 100644 --- a/lib/features/user/api/user_api.dart +++ b/lib/features/user/api/user_api.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart' hide Headers; import 'package:pets_next_door_flutter/features/sign_up/data/dto/sign_up_data_dto.dart'; import 'package:pets_next_door_flutter/features/user/data/dto/user_data_dto.dart'; -import 'package:retrofit/retrofit.dart'; +import 'package:retrofit/http.dart'; part 'user_api.g.dart'; diff --git a/lib/features/user/api/user_api.g.dart b/lib/features/user/api/user_api.g.dart index 3dac8f6..e6e8458 100644 --- a/lib/features/user/api/user_api.g.dart +++ b/lib/features/user/api/user_api.g.dart @@ -52,7 +52,8 @@ class _UserAPI implements UserAPI { final queryParameters = {}; final _headers = {r'requiresToken': false}; _headers.removeWhere((k, v) => v == null); - final _data = signUpData; + final _data = {}; + _data.addAll(signUpData.toJson()); final _result = await _dio .fetch>(_setStreamType(Options( method: 'POST', diff --git a/lib/presentation/pages/home/home_event.dart b/lib/presentation/pages/home/home_event.dart index 23aeae7..57a76e1 100644 --- a/lib/presentation/pages/home/home_event.dart +++ b/lib/presentation/pages/home/home_event.dart @@ -29,8 +29,8 @@ mixin class HomeEvent implements _HomeEvent { void onSearchTextSubmitted(WidgetRef ref, String text) { final tabType = ref.read(currentTabTypeProvider); final filter = switch (tabType) { - HomeTabType.petMate => ref.read(petMateSearchFilterProvider), - HomeTabType.petSos => ref.read(petSosSearchFilterProvider), + HomeTabType.petMate => ref.read(petMateFilterProvider), + HomeTabType.petSos => ref.read(petSosFilterProvider), }; // TODO: 필터 정보 가지고 게시물 불러오기 기능 구현 필요 diff --git a/lib/presentation/pages/home/home_view.dart b/lib/presentation/pages/home/home_view.dart index 5170f3d..f4f36a8 100644 --- a/lib/presentation/pages/home/home_view.dart +++ b/lib/presentation/pages/home/home_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:pets_next_door_flutter/core/constants/colors.dart'; -import 'package:pets_next_door_flutter/core/constants/images.dart'; import 'package:pets_next_door_flutter/core/constants/sizes.dart'; import 'package:pets_next_door_flutter/core/constants/text_style.dart'; import 'package:pets_next_door_flutter/presentation/pages/home/home_event.dart'; diff --git a/lib/presentation/pages/home/widgets/home_location_button.dart b/lib/presentation/pages/home/widgets/home_location_button.dart index ceb1135..2a679d8 100644 --- a/lib/presentation/pages/home/widgets/home_location_button.dart +++ b/lib/presentation/pages/home/widgets/home_location_button.dart @@ -12,11 +12,7 @@ class _HomeLocationButton extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.asset( - PNDImages.locationPin, - width: 24, - height: 24, - ), + Icon(Icons.location_on_sharp), gapW4, Row( children: [ diff --git a/lib/presentation/pages/home/widgets/home_tab_view_body.dart b/lib/presentation/pages/home/widgets/home_tab_view_body.dart index 520129a..5f013ca 100644 --- a/lib/presentation/pages/home/widgets/home_tab_view_body.dart +++ b/lib/presentation/pages/home/widgets/home_tab_view_body.dart @@ -9,13 +9,14 @@ class _HomeTabViewBody extends HookConsumerWidget with HomeEvent { initialLength: HomeTabType.values.length, initialIndex: 0); useEffect(() { - tabController.addListener(() { - // addListener를 통해 탭 변경 감지 + void _listener() { ref.read(currentTabTypeProvider.notifier).tabType = HomeTabType.values[tabController.index]; - }); - // useEffect 내부에서 dispose 함수를 반환하여 리소스 정리 - return () => tabController.dispose(); + } + + tabController.addListener(_listener); + + return () => tabController.removeListener(_listener); }, [tabController]); return Expanded( diff --git a/lib/presentation/pages/pet/layouts/pet_type_selection_button_group.dart b/lib/presentation/pages/pet/layouts/pet_type_selection_button_group.dart index f29f955..b62fc7c 100644 --- a/lib/presentation/pages/pet/layouts/pet_type_selection_button_group.dart +++ b/lib/presentation/pages/pet/layouts/pet_type_selection_button_group.dart @@ -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( @@ -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, @@ -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, ), ), ], diff --git a/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.dart b/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.dart new file mode 100644 index 0000000..27c0b98 --- /dev/null +++ b/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.dart @@ -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 build() { + final controller = PagingController(firstPageKey: 1); + + controller.addPageRequestListener((pageKey) => fetchPage(pageKey)); + + ref.onDispose(() => controller.dispose()); + + return controller; + } + + Future 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'); + } + } +} diff --git a/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.g.dart b/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.g.dart new file mode 100644 index 0000000..9cc79c3 --- /dev/null +++ b/lib/presentation/pages/pet/providers/breeds_paging_controller_provider.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'breeds_paging_controller_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$breedsPagingControllerHash() => + r'f14f38dc38e0a32b977387e5ee90d9aaae12cd88'; + +/// See also [BreedsPagingController]. +@ProviderFor(BreedsPagingController) +final breedsPagingControllerProvider = AutoDisposeNotifierProvider< + BreedsPagingController, PagingController>.internal( + BreedsPagingController.new, + name: r'breedsPagingControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$breedsPagingControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$BreedsPagingController + = AutoDisposeNotifier>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/presentation/pages/pet/register_pet_page.dart b/lib/presentation/pages/pet/register_pet_page.dart index 95d6fc0..bb2898e 100644 --- a/lib/presentation/pages/pet/register_pet_page.dart +++ b/lib/presentation/pages/pet/register_pet_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:pets_next_door_flutter/features/pet/domain/pet.dart'; import 'package:pets_next_door_flutter/presentation/pages/pet/providers/register_pet_step_controller_provider.dart'; import 'package:pets_next_door_flutter/presentation/pages/pet/register_pet_event.dart'; import 'package:pets_next_door_flutter/presentation/pages/pet/steps/register_pet_detail_view.dart'; @@ -63,12 +64,13 @@ class _Body extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final pageController = ref.watch(registerPetStepControllerProvider); + ref.watch(petStateProvider); return SafeArea( child: PageView( controller: pageController, physics: const NeverScrollableScrollPhysics(), - children: const [ + children: [ RegisterPetInitialView(), RegisterPetDetailView(), ], diff --git a/lib/presentation/pages/pet/steps/register_pet_detail_view.dart b/lib/presentation/pages/pet/steps/register_pet_detail_view.dart index 458a402..d614299 100644 --- a/lib/presentation/pages/pet/steps/register_pet_detail_view.dart +++ b/lib/presentation/pages/pet/steps/register_pet_detail_view.dart @@ -1,10 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +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/presentation/pages/pet/providers/breeds_paging_controller_provider.dart'; +import 'package:pets_next_door_flutter/presentation/widgets/pagination/infinite_paged_list.dart'; -class RegisterPetDetailView extends StatelessWidget { +class RegisterPetDetailView extends ConsumerWidget { const RegisterPetDetailView({super.key}); @override - Widget build(BuildContext context) { - return const Placeholder(); + Widget build(BuildContext context, WidgetRef ref) { + final pagingController = ref.watch(breedsPagingControllerProvider); + + return PNDInfinitePagedList( + pagingController: pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, breed, index) => Text(breed.name), + ), + ); } } diff --git a/lib/presentation/pages/pet/steps/register_pet_initial_view.dart b/lib/presentation/pages/pet/steps/register_pet_initial_view.dart index 35b348a..45938b9 100644 --- a/lib/presentation/pages/pet/steps/register_pet_initial_view.dart +++ b/lib/presentation/pages/pet/steps/register_pet_initial_view.dart @@ -30,11 +30,13 @@ const ColorFilter greyscale = ColorFilter.matrix([ ]); class RegisterPetInitialView extends StatelessWidget with RegisterPetEvent { - const RegisterPetInitialView({super.key}); + const RegisterPetInitialView({ + super.key, + }); @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Padding( padding: EdgeInsets.symmetric(horizontal: PNDSizes.p24), child: Column( @@ -45,7 +47,7 @@ class RegisterPetInitialView extends StatelessWidget with RegisterPetEvent { children: [ Text('함께하는 반려동물을 선택해주세요.'), gapH32, - PetTypeSelectionButtonGroup() + PetTypeSelectionButtonGroup(), ], ), ), @@ -67,7 +69,7 @@ class RegisterPetInitialNextButton extends ConsumerWidget Widget build(BuildContext context, WidgetRef ref) { return ActivationButton( text: '다음으로', - isActive: ref.watch(petStateProvider) != null, + isActive: ref.read(petStateProvider.notifier).state.petType != null, onTap: () => onTapRegisterPetDetailNext(ref), ); } diff --git a/lib/presentation/pages/pet_mate/pet_mate_event.dart b/lib/presentation/pages/pet_mate/pet_mate_event.dart index 8140832..c42a04b 100644 --- a/lib/presentation/pages/pet_mate/pet_mate_event.dart +++ b/lib/presentation/pages/pet_mate/pet_mate_event.dart @@ -11,11 +11,11 @@ abstract interface class _PetMateEvent { mixin class PetMateEvent implements _PetMateEvent { @override void onSortChanged(WidgetRef ref, SortTypeFilter sortType) { - ref.read(petMateSearchFilterProvider.notifier).setSortingFilter(sortType); + ref.read(petMateFilterProvider.notifier).setSortingFilter(sortType); } @override void onPetTypeChanged(WidgetRef ref, PetTypeFilter petType) { - ref.read(petMateSearchFilterProvider.notifier).setPetFilter(petType); + ref.read(petMateFilterProvider.notifier).setPetFilter(petType); } } diff --git a/lib/presentation/pages/pet_mate/pet_mate_view.dart b/lib/presentation/pages/pet_mate/pet_mate_view.dart index 3f611c0..c8e9a33 100644 --- a/lib/presentation/pages/pet_mate/pet_mate_view.dart +++ b/lib/presentation/pages/pet_mate/pet_mate_view.dart @@ -118,10 +118,7 @@ class _SortFilter extends ConsumerWidget with PetMateEvent { height: 45, child: Row( children: [ - Text(ref - .watch(petMateSearchFilterProvider) - .sortFilter - .displayName), + Text(ref.watch(petMateFilterProvider).sortFilter.displayName), Icon(Icons.keyboard_arrow_down_rounded) ], )), @@ -158,7 +155,7 @@ class _PetFilter extends ConsumerWidget with PetMateEvent { height: 30, child: Row( children: [ - (ref.watch(petMateSearchFilterProvider).petTypeFilter == + (ref.watch(petMateFilterProvider).petTypeFilter == petType) ? Icon( Icons.check_box_sharp, diff --git a/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.dart b/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.dart index 157719a..a23b852 100644 --- a/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.dart +++ b/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.dart @@ -10,13 +10,13 @@ typedef PetMateFilters = ({ }); /// 돌봄메이트 필터 프로바이더 -@Riverpod(keepAlive: true) -class PetMateSearchFilter extends _$PetMateSearchFilter { +@Riverpod() +class PetMateFilter extends _$PetMateFilter { @override PetMateFilters build() { return ( sortFilter: SortTypeFilter.newest, - petTypeFilter: PetTypeFilter.dog + petTypeFilter: PetTypeFilter.cat ); } diff --git a/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.g.dart b/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.g.dart index 3f823b1..06c8ae2 100644 --- a/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.g.dart +++ b/lib/presentation/pages/pet_mate/providers/pet_mate_filter_provider.g.dart @@ -6,25 +6,24 @@ part of 'pet_mate_filter_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$petMateSearchFilterHash() => - r'949eecf8cfeb82a82538c2bbb96235aa22ef5991'; +String _$petMateFilterHash() => r'f972067a8cbe4261995af6d3646cb71ba4fb1c28'; /// 돌봄메이트 필터 프로바이더 /// -/// Copied from [PetMateSearchFilter]. -@ProviderFor(PetMateSearchFilter) -final petMateSearchFilterProvider = NotifierProvider.internal( - PetMateSearchFilter.new, - name: r'petMateSearchFilterProvider', + PetMateFilter.new, + name: r'petMateFilterProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null - : _$petMateSearchFilterHash, + : _$petMateFilterHash, dependencies: null, allTransitiveDependencies: null, ); -typedef _$PetMateSearchFilter - = Notifier<({PetTypeFilter petTypeFilter, SortTypeFilter sortFilter})>; +typedef _$PetMateFilter = AutoDisposeNotifier< + ({PetTypeFilter petTypeFilter, SortTypeFilter sortFilter})>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/presentation/pages/pet_sos/pet_sos_event.dart b/lib/presentation/pages/pet_sos/pet_sos_event.dart index a0f017c..a75c7b5 100644 --- a/lib/presentation/pages/pet_sos/pet_sos_event.dart +++ b/lib/presentation/pages/pet_sos/pet_sos_event.dart @@ -1,7 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pets_next_door_flutter/core/enums/pet_type_filter.enum.dart'; import 'package:pets_next_door_flutter/core/enums/sort_type_filter.enum.dart'; - import 'package:pets_next_door_flutter/presentation/pages/pet_sos/providers/pet_sos_filter_provider.dart'; abstract interface class _PetSosEvent { @@ -12,11 +11,11 @@ abstract interface class _PetSosEvent { mixin class PetSosEvent implements _PetSosEvent { @override void onSortChanged(WidgetRef ref, SortTypeFilter sortType) { - ref.read(petSosSearchFilterProvider.notifier).setSortingFilter(sortType); + ref.read(petSosFilterProvider.notifier).setSortingFilter(sortType); } @override void onPetTypeChanged(WidgetRef ref, PetTypeFilter petType) { - ref.read(petSosSearchFilterProvider.notifier).setPetFilter(petType); + ref.read(petSosFilterProvider.notifier).setPetFilter(petType); } } diff --git a/lib/presentation/pages/pet_sos/pet_sos_view.dart b/lib/presentation/pages/pet_sos/pet_sos_view.dart index 551488e..f8703ff 100644 --- a/lib/presentation/pages/pet_sos/pet_sos_view.dart +++ b/lib/presentation/pages/pet_sos/pet_sos_view.dart @@ -127,8 +127,7 @@ class _SortFilter extends ConsumerWidget with PetSosEvent { height: 45, child: Row( children: [ - Text( - ref.watch(petSosSearchFilterProvider).sortFilter.displayName), + Text(ref.watch(petSosFilterProvider).sortFilter.displayName), Icon(Icons.keyboard_arrow_down_rounded) ], )), @@ -154,7 +153,7 @@ class _PetFilter extends ConsumerWidget with PetSosEvent { @override Widget build(BuildContext context, WidgetRef ref) { - final filter = ref.watch(petSosSearchFilterProvider); + final filter = ref.watch(petSosFilterProvider); return Wrap( spacing: 8, children: [ diff --git a/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.dart b/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.dart index 755a3a4..e2e5803 100644 --- a/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.dart +++ b/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.dart @@ -10,23 +10,21 @@ typedef PetSosFilters = ({ }); /// 돌봄급구 필터 프로바이더 -@Riverpod(keepAlive: true) -class PetSosSearchFilter extends _$PetSosSearchFilter { +@Riverpod() +class PetSosFilter extends _$PetSosFilter { @override PetSosFilters build() { return ( sortFilter: SortTypeFilter.newest, - petTypeFilter: PetTypeFilter.dog + petTypeFilter: PetTypeFilter.cat ); } - void setSortingFilter(SortTypeFilter? filter) { - if (filter == null) return; + void setSortingFilter(SortTypeFilter filter) { state = (sortFilter: filter, petTypeFilter: state.petTypeFilter); } - void setPetFilter(PetTypeFilter? filter) { - if (filter == null) return; + void setPetFilter(PetTypeFilter filter) { state = (sortFilter: state.sortFilter, petTypeFilter: filter); } } diff --git a/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.g.dart b/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.g.dart index 1b388f6..2122b86 100644 --- a/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.g.dart +++ b/lib/presentation/pages/pet_sos/providers/pet_sos_filter_provider.g.dart @@ -6,25 +6,23 @@ part of 'pet_sos_filter_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$petSosSearchFilterHash() => - r'305100ac7caf152d47900e0507ef1ad35fadb8f9'; +String _$petSosFilterHash() => r'a4470aedf29a6adf6204b7b1edc30029d467f2f1'; /// 돌봄급구 필터 프로바이더 /// -/// Copied from [PetSosSearchFilter]. -@ProviderFor(PetSosSearchFilter) -final petSosSearchFilterProvider = NotifierProvider.internal( - PetSosSearchFilter.new, - name: r'petSosSearchFilterProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$petSosSearchFilterHash, + PetSosFilter.new, + name: r'petSosFilterProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$petSosFilterHash, dependencies: null, allTransitiveDependencies: null, ); -typedef _$PetSosSearchFilter - = Notifier<({PetTypeFilter petTypeFilter, SortTypeFilter sortFilter})>; +typedef _$PetSosFilter = AutoDisposeNotifier< + ({PetTypeFilter petTypeFilter, SortTypeFilter sortFilter})>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/presentation/pages/sign_up/sign_up_event.dart b/lib/presentation/pages/sign_up/sign_up_event.dart index 6bb1b9c..083366b 100644 --- a/lib/presentation/pages/sign_up/sign_up_event.dart +++ b/lib/presentation/pages/sign_up/sign_up_event.dart @@ -45,7 +45,7 @@ mixin class SignUpEvent implements _SignUpEvent { Future onTapAddPetButton(WidgetRef ref) async { // TODO: 반려동물 추가 기능 구현 필요 - // final newPet = await ref.context.pushNamed(AppRoute.registerPet.name); + final newPet = await ref.context.pushNamed(AppRoute.registerPet.name); // if (newPet == null) return; diff --git a/lib/presentation/widgets/pagination/infinite_paged_list.dart b/lib/presentation/widgets/pagination/infinite_paged_list.dart new file mode 100644 index 0000000..04c47c3 --- /dev/null +++ b/lib/presentation/widgets/pagination/infinite_paged_list.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:pets_next_door_flutter/core/constants/sizes.dart'; + +class PNDInfinitePagedList extends StatelessWidget { + PNDInfinitePagedList({ + super.key, + required this.pagingController, + required this.builderDelegate, + this.separatorBuilder, + }); + + final PagingController pagingController; + final PagedChildBuilderDelegate builderDelegate; + final IndexedWidgetBuilder? separatorBuilder; + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + child: PagedListView.separated( + pagingController: pagingController, + builderDelegate: builderDelegate, + separatorBuilder: + separatorBuilder ?? (context, index) => gapH16, + ), + onRefresh: () => Future.sync( + () => pagingController.refresh(), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index d856cda..c45a8e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,9 @@ dependencies: path_provider: ^2.1.2 mime: ^1.0.4 + # 페이지네이션 + infinite_scroll_pagination: ^4.0.0 + dev_dependencies: flutter_test: sdk: flutter