diff --git a/lib/core/network/failure.dart b/lib/core/network/failure.dart new file mode 100644 index 0000000..ebc4e8b --- /dev/null +++ b/lib/core/network/failure.dart @@ -0,0 +1,12 @@ +sealed class Failure { + const Failure(); +} + +final class SomeFailure extends Failure { + final String error; + + const SomeFailure(this.error); + + @override + String toString() => error; +} diff --git a/lib/core/network/result.dart b/lib/core/network/result.dart new file mode 100644 index 0000000..c3cfc9f --- /dev/null +++ b/lib/core/network/result.dart @@ -0,0 +1,29 @@ +import 'package:bloc_example/core/network/failure.dart'; + +sealed class Result { + const Result(); + + factory Result.ok(T value) = Ok; + + factory Result.err(Failure failure) = Err; + + R fold({ + required R Function(Failure) onErr, + required R Function(T) onOk, + }) => switch (this) { + Err(:final failure) => onErr(failure), + Ok(:final value) => onOk(value), + }; +} + +final class Ok extends Result { + final T value; + + const Ok(this.value); +} + +final class Err extends Result { + final Failure failure; + + const Err(this.failure); +} diff --git a/lib/features/users/bloc/users_bloc.dart b/lib/features/users/bloc/users_bloc.dart index 4f2f4c6..344aa2a 100644 --- a/lib/features/users/bloc/users_bloc.dart +++ b/lib/features/users/bloc/users_bloc.dart @@ -1,5 +1,7 @@ import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:bloc_example/core/bloc/transformers/measure_time.dart'; +import 'package:bloc_example/core/network/failure.dart'; +import 'package:bloc_example/core/network/result.dart'; import 'package:bloc_example/features/users/bloc/users_bloc_event.dart'; import 'package:bloc_example/features/users/bloc/users_bloc_state.dart'; import 'package:bloc_example/features/users/model/user.dart'; @@ -27,6 +29,7 @@ class UsersBloc extends HydratedBloc { } } + // --- RECORDS --- Future _onFetch( UsersBlocEventFetch event, Emitter emit, @@ -35,6 +38,7 @@ class UsersBloc extends HydratedBloc { limit: 30, page: 0, ); + if (usersRes.$2 == null) { final users = usersRes.$1!; @@ -50,6 +54,61 @@ class UsersBloc extends HydratedBloc { } } + // --- EITHER (dartz) --- + // Future _onFetch( + // UsersBlocEventFetch event, + // Emitter emit, + // ) async { + // final usersRes = await usersRepository.fetchUsers( + // limit: 30, + // page: 0, + // ); + + // usersRes.fold( + // (error) => emit( + // UsersBlocStateError( + // error.toString(), + // ), + // ), + // (users) { + // emit( + // UsersBlocStateLoaded( + // users: users, + // canLoadMore: users.length == 30, + // page: 1, + // ), + // ); + // }, + // ); + // } + + // --- SEALED CLASSES --- + // Future _onFetch( + // UsersBlocEventFetch event, + // Emitter emit, + // ) async { + // final usersRes = await usersRepository.fetchUsers( + // limit: 30, + // page: 0, + // ); + + // usersRes.fold( + // onErr: (failure) => emit( + // UsersBlocStateError( + // failure.toString(), + // ), + // ), + // onOk: (users) => emit( + // UsersBlocStateLoaded( + // users: users, + // canLoadMore: users.length == 30, + // page: 1, + // ), + // ), + // ); + // } + + // --- RECORDS --- Future _onFetchMore( UsersBlocEventFetchMore event, Emitter emit, @@ -70,6 +129,7 @@ class UsersBloc extends HydratedBloc { limit: 30, page: page, ); + if (usersRes.$2 == null) { final users = usersRes.$1!; @@ -88,6 +148,97 @@ class UsersBloc extends HydratedBloc { } } + // --- EITHER (dartz) --- + // Future _onFetchMore( + // UsersBlocEventFetchMore event, + // Emitter emit, + // ) async { + // if (state is! UsersBlocStateLoaded) { + // return; + // } + + // final currState = state as UsersBlocStateLoaded; + + // if (!currState.canLoadMore) { + // return; + // } + + // final page = currState.page; + + // final usersRes = await usersRepository.fetchUsers( + // limit: 30, + // page: page, + // ); + + // usersRes.fold( + // (error) => emit( + // UsersBlocStateError( + // error.toString(), + // ), + // ), + // (users) { + // emit( + // UsersBlocStateLoaded( + // users: [ + // ...currState.users, + // ...users, + // ], + // canLoadMore: users.length == 30, + // page: page + 1, + // ), + // ); + // }, + // ); + // } + + // --- SEALED CLASSES --- + // Future _onFetchMore( + // UsersBlocEventFetchMore event, + // Emitter emit, + // ) async { + // if (state is! UsersBlocStateLoaded) { + // return; + // } + + // final currState = state as UsersBlocStateLoaded; + + // if (!currState.canLoadMore) { + // return; + // } + + // final page = currState.page; + + // final usersRes = await usersRepository.fetchUsers( + // limit: 30, + // page: page, + // ); + + // usersRes.fold( + // onOk: (users) { + // emit( + // UsersBlocStateLoaded( + // users: [ + // ...currState.users, + // ...users, + // ], + // canLoadMore: users.length == 30, + // page: page + 1, + // ), + // ); + // }, + // onErr: (failure) { + // switch (failure) { + // case SomeFailure(:final error): + // emit( + // UsersBlocStateError( + // error, + // ), + // ); + // } + // }, + // ); + // } + Future _onRefresh( UsersBlocEventRefresh event, Emitter emit, diff --git a/lib/features/users/repository/users_repository.dart b/lib/features/users/repository/users_repository.dart index e5e0213..40707f9 100644 --- a/lib/features/users/repository/users_repository.dart +++ b/lib/features/users/repository/users_repository.dart @@ -1,8 +1,28 @@ +import 'package:bloc_example/core/network/failure.dart'; +import 'package:bloc_example/core/network/result.dart'; import 'package:bloc_example/features/users/model/user.dart'; +import 'package:dartz/dartz.dart'; +// --- RECORDS --- abstract class UsersRepository { Future<(List?, String?)> fetchUsers({ int limit, int page, }); } + +// --- EITHER (dartz) --- +// abstract class UsersRepository { +// Future>> fetchUsers({ +// int limit, +// int page, +// }); +// } + +// --- SEALED CLASSES --- +// abstract class UsersRepository { +// Future>> fetchUsers({ +// int limit, +// int page, +// }); +// } diff --git a/lib/features/users/repository/users_repository_impl.dart b/lib/features/users/repository/users_repository_impl.dart index 7b140cf..bfd6545 100644 --- a/lib/features/users/repository/users_repository_impl.dart +++ b/lib/features/users/repository/users_repository_impl.dart @@ -1,9 +1,13 @@ import 'dart:io'; +import 'package:bloc_example/core/network/failure.dart'; +import 'package:bloc_example/core/network/result.dart'; import 'package:bloc_example/features/users/model/user.dart'; import 'package:bloc_example/features/users/repository/users_repository.dart'; +import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; +// --- RECORDS --- class UsersRepositoryImpl extends UsersRepository { final Dio dio; @@ -35,3 +39,79 @@ class UsersRepositoryImpl extends UsersRepository { } } } + +// --- EITHER (dartz) --- +// class UsersRepositoryImpl extends UsersRepository { +// final Dio dio; + +// UsersRepositoryImpl(this.dio); + +// @override +// Future>> fetchUsers({ +// int limit = 30, +// int page = 0, +// }) async { +// try { +// final res = await dio.get('?page=$page&results=$limit'); +// if (res.statusCode == HttpStatus.ok) { +// final rawUsers = res.data['results'] as List; +// final users = rawUsers.map((rawUser) => User.fromJson(rawUser)).toList(); + +// return Right(users); +// } + +// return Left( +// SomeFailure( +// '''Network error: +// - status code: ${res.statusCode} +// - data: ${res.data} +// ''', +// ), +// ); +// } catch (e) { +// return Left( +// SomeFailure( +// 'Network error: $e', +// ), +// ); +// } +// } +// } + +// --- SEALED CLASSES --- +// class UsersRepositoryImpl extends UsersRepository { +// final Dio dio; + +// UsersRepositoryImpl(this.dio); + +// @override +// Future>> fetchUsers({ +// int limit = 30, +// int page = 0, +// }) async { +// try { +// final res = await dio.get('?page=$page&results=$limit'); +// if (res.statusCode == HttpStatus.ok) { +// final rawUsers = res.data['results'] as List; +// final users = rawUsers.map((rawUser) => User.fromJson(rawUser)).toList(); + +// return Result.ok(users); +// } + +// return Result.err( +// SomeFailure( +// '''Network error: +// - status code: ${res.statusCode} +// - data: ${res.data} +// ''', +// ), +// ); +// } catch (e) { +// return Result.err( +// SomeFailure( +// 'Network error: $e', +// ), +// ); +// } +// } +// } diff --git a/pubspec.lock b/pubspec.lock index 19d00fe..30376c2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + dartz: + dependency: "direct main" + description: + name: dartz + sha256: e6acf34ad2e31b1eb00948692468c30ab48ac8250e0f0df661e29f12dd252168 + url: "https://pub.dev" + source: hosted + version: "0.10.1" diff_match_patch: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b543dee..6abafd3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: rxdart: 0.28.0 collection: + dartz: dev_dependencies: flutter_test: