From da1d6e2b7d8d1bc64974bf461caaf5582ee7045a Mon Sep 17 00:00:00 2001 From: AyushBherwani1998 Date: Thu, 3 Aug 2023 01:03:58 +0530 Subject: [PATCH] add bloc implementation --- lib/core/utils/string_constants.dart | 1 + .../bloc/unsplash_image_bloc.dart | 27 +++++++ .../bloc/unsplash_image_event.dart | 17 ++++ .../bloc/unsplash_image_state.dart | 30 ++++++++ pubspec.lock | 8 ++ pubspec.yaml | 1 + test/core/mocks/fetch_images_mock.dart | 6 ++ .../bloc/unsplash_image_bloc_test.dart | 77 +++++++++++++++++++ 8 files changed, 167 insertions(+) create mode 100644 lib/core/utils/string_constants.dart create mode 100644 lib/features/home/presentation/bloc/unsplash_image_bloc.dart create mode 100644 lib/features/home/presentation/bloc/unsplash_image_event.dart create mode 100644 lib/features/home/presentation/bloc/unsplash_image_state.dart create mode 100644 test/core/mocks/fetch_images_mock.dart create mode 100644 test/features/home/presentation/bloc/unsplash_image_bloc_test.dart diff --git a/lib/core/utils/string_constants.dart b/lib/core/utils/string_constants.dart new file mode 100644 index 0000000..885a578 --- /dev/null +++ b/lib/core/utils/string_constants.dart @@ -0,0 +1 @@ +const String serverErrorMessage = "Server error, please try agian later!"; diff --git a/lib/features/home/presentation/bloc/unsplash_image_bloc.dart b/lib/features/home/presentation/bloc/unsplash_image_bloc.dart new file mode 100644 index 0000000..02d1393 --- /dev/null +++ b/lib/features/home/presentation/bloc/unsplash_image_bloc.dart @@ -0,0 +1,27 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unplash_sample/core/utils/string_constants.dart'; +import 'package:unplash_sample/features/home/domain/entities/image.dart'; +import 'package:unplash_sample/features/home/domain/usecases/fetch_images.dart'; + +part 'unsplash_image_event.dart'; +part 'unsplash_image_state.dart'; + +class UnsplashImageBloc extends Bloc { + final FetchImages fetchImages; + + UnsplashImageBloc(this.fetchImages) : super(UnsplashImageInitialState()) { + on((event, emit) async { + if (event is FetchImageEvent) { + emit(UnsplashImageLoadingState()); + + final imagesEither = await fetchImages(event.params); + imagesEither.fold((error) { + emit(const UnsplashImageErrorState(serverErrorMessage)); + }, (images) { + emit(UnsplashImageLoadedState(images)); + }); + } + }); + } +} diff --git a/lib/features/home/presentation/bloc/unsplash_image_event.dart b/lib/features/home/presentation/bloc/unsplash_image_event.dart new file mode 100644 index 0000000..068a5b0 --- /dev/null +++ b/lib/features/home/presentation/bloc/unsplash_image_event.dart @@ -0,0 +1,17 @@ +part of 'unsplash_image_bloc.dart'; + +abstract class UnsplashImageEvent extends Equatable { + const UnsplashImageEvent(); + + @override + List get props => []; +} + +class FetchImageEvent extends UnsplashImageEvent { + final FetchImageParams params; + + const FetchImageEvent(this.params); + + @override + List get props => [params.page, params.perPage]; +} diff --git a/lib/features/home/presentation/bloc/unsplash_image_state.dart b/lib/features/home/presentation/bloc/unsplash_image_state.dart new file mode 100644 index 0000000..c5f6bed --- /dev/null +++ b/lib/features/home/presentation/bloc/unsplash_image_state.dart @@ -0,0 +1,30 @@ +part of 'unsplash_image_bloc.dart'; + +abstract class UnsplashImageState extends Equatable { + const UnsplashImageState(); + + @override + List get props => []; +} + +class UnsplashImageInitialState extends UnsplashImageState {} + +class UnsplashImageLoadingState extends UnsplashImageState {} + +class UnsplashImageLoadedState extends UnsplashImageState { + final List images; + + const UnsplashImageLoadedState(this.images); + + @override + List get props => [images]; +} + +class UnsplashImageErrorState extends UnsplashImageState { + final String errorMessage; + + const UnsplashImageErrorState(this.errorMessage); + + @override + List get props => [errorMessage]; +} diff --git a/pubspec.lock b/pubspec.lock index e250d4e..054e147 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" + source: hosted + version: "8.1.2" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c121c62..44571ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ environment: dependencies: flutter: sdk: flutter + bloc: ^8.1.2 dartz: ^0.10.1 dio: ^5.3.0 equatable: ^2.0.5 diff --git a/test/core/mocks/fetch_images_mock.dart b/test/core/mocks/fetch_images_mock.dart new file mode 100644 index 0000000..441a361 --- /dev/null +++ b/test/core/mocks/fetch_images_mock.dart @@ -0,0 +1,6 @@ +import 'package:mocktail/mocktail.dart'; +import 'package:unplash_sample/features/home/domain/usecases/fetch_images.dart'; + +class FetchImagesMock extends Mock implements FetchImages {} + +class FakeFetchImageParams extends Fake implements FetchImageParams {} diff --git a/test/features/home/presentation/bloc/unsplash_image_bloc_test.dart b/test/features/home/presentation/bloc/unsplash_image_bloc_test.dart new file mode 100644 index 0000000..17fd844 --- /dev/null +++ b/test/features/home/presentation/bloc/unsplash_image_bloc_test.dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +import 'package:bloc/bloc.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:unplash_sample/core/error/error.dart'; +import 'package:unplash_sample/core/utils/string_constants.dart'; +import 'package:unplash_sample/features/home/data/models/image_model.dart'; +import 'package:unplash_sample/features/home/domain/usecases/fetch_images.dart'; +import 'package:unplash_sample/features/home/presentation/bloc/unsplash_image_bloc.dart'; + +import '../../../../core/mocks/fetch_images_mock.dart'; +import '../../../../fixtures/fixture_reader.dart'; + +void main() { + late final FetchImagesMock fetchImagesMock; + late final Bloc bloc; + late final FetchImageParams params; + + group("UnsplashImageBloc test", () { + setUpAll(() { + fetchImagesMock = FetchImagesMock(); + bloc = UnsplashImageBloc(fetchImagesMock); + params = FetchImageParams(1, 20); + + registerFallbackValue(FakeFetchImageParams()); + }); + + test("Initial state should be UnsplashImageInitialState", () { + expect(bloc.state, UnsplashImageInitialState()); + }); + + test( + "Emits [UnsplashImageLoadingState, UnsplashImageErrroState] on error", + () { + when(() => fetchImagesMock.call(any())).thenAnswer((invocation) async { + return Left(ServerError()); + }); + + bloc.add(FetchImageEvent(params)); + + expectLater( + bloc.stream, + emitsInOrder([ + UnsplashImageLoadingState(), + const UnsplashImageErrorState(serverErrorMessage), + ]), + ); + }, + ); + + test( + "Emits [UnsplashImageLoadingState, UnsplashImageLoadedState] on success", + () { + final imageListModel = + UnsplashImageListModel.fromJson(List>.from( + jsonDecode(fixture("image_model_fixture.json")), + )); + + when(() => fetchImagesMock.call(any())).thenAnswer((invocation) async { + return Right(imageListModel.images); + }); + + bloc.add(FetchImageEvent(params)); + + expectLater( + bloc.stream, + emitsInOrder([ + UnsplashImageLoadingState(), + UnsplashImageLoadedState(imageListModel.images), + ]), + ); + }, + ); + }); +}