doc(arch): add fully featured example
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:architecture_example/core/dependency_injection/get_it.dart';
|
||||
import 'package:architecture_example/core/utils/app_bloc_observer.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
|
||||
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
Bloc.observer = AppBlocObserver();
|
||||
await GetItInitializer.run();
|
||||
await Hive.initFlutter();
|
||||
|
||||
runApp(
|
||||
await builder.call(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
abstract class HiveBoxes {
|
||||
static const String favorites = 'favorites';
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:architecture_example/data/data_sources/local/favorite_hive_data_source.dart';
|
||||
import 'package:architecture_example/data/data_sources/local/favorite_mock_data_source.dart';
|
||||
import 'package:architecture_example/data/data_sources/remote/album_api_data_source_impl.dart';
|
||||
import 'package:architecture_example/data/data_sources/remote/album_mock_data_source_impl.dart';
|
||||
import 'package:architecture_example/data/data_sources/remote/photo_api_data_source_impl.dart';
|
||||
import 'package:architecture_example/data/data_sources/remote/photo_mock_data_source_impl.dart';
|
||||
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:wyatt_http_client/wyatt_http_client.dart';
|
||||
|
||||
final getIt = GetIt.I;
|
||||
|
||||
abstract class GetItInitializer {
|
||||
static Future<void> _initGlobal() async {}
|
||||
static Future<void> _initMocks() async {
|
||||
getIt
|
||||
..registerLazySingleton<FavoriteLocalDataSource>(
|
||||
FavoriteMockDataSource.new,
|
||||
)
|
||||
..registerLazySingleton<PhotoRemoteDataSource>(
|
||||
PhotoMockDataSourceImpl.new,
|
||||
)
|
||||
..registerLazySingleton<AlbumRemoteDataSource>(
|
||||
AlbumMockDataSourceImpl.new,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> _initReal() async {
|
||||
getIt
|
||||
..registerLazySingleton<FavoriteLocalDataSource>(
|
||||
FavoriteHiveDataSource.new,
|
||||
)
|
||||
..registerLazySingleton<MiddlewareClient>(() {
|
||||
final Pipeline pipeline = Pipeline()
|
||||
.addMiddleware(
|
||||
UriPrefixMiddleware(
|
||||
protocol: Protocols.https,
|
||||
authority: 'jsonplaceholder.typicode.com',
|
||||
),
|
||||
)
|
||||
.addMiddleware(BodyToJsonMiddleware());
|
||||
return MiddlewareClient(pipeline: pipeline);
|
||||
})
|
||||
..registerLazySingleton<PhotoRemoteDataSource>(
|
||||
() => PhotoApiDataSourceImpl(getIt()),
|
||||
)
|
||||
..registerLazySingleton<AlbumRemoteDataSource>(
|
||||
() => AlbumApiDataSourceImpl(getIt()),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> run({bool enableMocks = false}) async {
|
||||
await _initGlobal();
|
||||
enableMocks ? await _initMocks() : await _initReal();
|
||||
|
||||
await getIt.allReady();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
enum FetchStatus {
|
||||
initial,
|
||||
success,
|
||||
failure,
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AppBlocObserver extends BlocObserver {
|
||||
@override
|
||||
void onTransition(
|
||||
Bloc<dynamic, dynamic> bloc,
|
||||
Transition<dynamic, dynamic> transition,
|
||||
) {
|
||||
super.onTransition(bloc, transition);
|
||||
debugPrint(transition.toString());
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
|
||||
debugPrint(error.toString());
|
||||
super.onError(bloc, error, stackTrace);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/constants/hive_boxes.dart';
|
||||
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
class FavoriteHiveDataSource extends FavoriteLocalDataSource {
|
||||
@override
|
||||
Future<void> addPhotoToFavorites(Photo photo) async {
|
||||
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
|
||||
if (box.containsKey(photo.id)) {
|
||||
throw Exception('Photo already in favorites');
|
||||
}
|
||||
await box.put(photo.id, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkIfPhotoIsInFavorites(int id) async {
|
||||
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
|
||||
return box.containsKey(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deletePhotoFromFavorites(int id) async {
|
||||
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
|
||||
if (!box.containsKey(id)) {
|
||||
throw Exception('Unknown photo');
|
||||
}
|
||||
await box.delete(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> getAllPhotosFromFavorites() async {
|
||||
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
|
||||
return box.keys.cast<int>().toList();
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/data/models/photo_model.dart';
|
||||
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
|
||||
class FavoriteMockDataSource extends FavoriteLocalDataSource {
|
||||
final Map<int, Photo> _mock = {
|
||||
2: const PhotoModel(
|
||||
albumId: 1,
|
||||
id: 2,
|
||||
title: 'Photo 2',
|
||||
url: 'https://via.placeholder.com/600/771796',
|
||||
thumbnailUrl: 'https://via.placeholder.com/150/771796',
|
||||
),
|
||||
};
|
||||
|
||||
@override
|
||||
Future<void> addPhotoToFavorites(Photo photo) async {
|
||||
if (_mock.containsKey(photo.id)) {
|
||||
throw Exception('Photo already in favorites');
|
||||
}
|
||||
_mock.putIfAbsent(photo.id, () => photo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkIfPhotoIsInFavorites(int id) async => _mock.containsKey(id);
|
||||
|
||||
@override
|
||||
Future<void> deletePhotoFromFavorites(int id) async {
|
||||
if (!_mock.containsKey(id)) {
|
||||
throw Exception('Unknown photo');
|
||||
}
|
||||
_mock.remove(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> getAllPhotosFromFavorites() async {
|
||||
final result = _mock.keys;
|
||||
return result.toList();
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:architecture_example/data/models/album_model.dart';
|
||||
import 'package:architecture_example/data/models/list_album_model.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:wyatt_http_client/wyatt_http_client.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
|
||||
final MiddlewareClient _client;
|
||||
|
||||
AlbumApiDataSourceImpl(this._client);
|
||||
|
||||
@override
|
||||
Future<Album> getAlbum(int id) async {
|
||||
final response = await _client.get(Uri.parse('/albums/$id'));
|
||||
final album =
|
||||
AlbumModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
|
||||
return album;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
|
||||
final startQuery = start.isNotNull ? '_start=$start' : '';
|
||||
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
|
||||
final delimiter1 =
|
||||
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
|
||||
final delimiter2 =
|
||||
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
|
||||
final url = '/albums$delimiter1$startQuery$delimiter2$limitQuery';
|
||||
final response = await _client.get(Uri.parse(url));
|
||||
final albums =
|
||||
ListAlbumModel.fromJson({'albums': jsonDecode(response.body)});
|
||||
return albums.albums;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/data/models/album_model.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class AlbumMockDataSourceImpl extends AlbumRemoteDataSource {
|
||||
final Map<int, Album> _mock = {
|
||||
1: const AlbumModel(
|
||||
id: 1,
|
||||
userId: 1,
|
||||
title: 'Album 1',
|
||||
)
|
||||
};
|
||||
|
||||
@override
|
||||
Future<Album> getAlbum(int id) async {
|
||||
final response = _mock[id];
|
||||
if (response.isNull) {
|
||||
throw Exception('Unknown album');
|
||||
}
|
||||
return response!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
|
||||
final response = _mock.values;
|
||||
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
|
||||
return response.toList().sublist(start ?? 0, nullableEnd);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:architecture_example/data/models/list_photo_model.dart';
|
||||
import 'package:architecture_example/data/models/photo_model.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:wyatt_http_client/wyatt_http_client.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
|
||||
final MiddlewareClient _client;
|
||||
|
||||
PhotoApiDataSourceImpl(this._client);
|
||||
|
||||
@override
|
||||
Future<Photo> getPhoto(int id) async {
|
||||
final response = await _client.get(Uri.parse('/photos/$id'));
|
||||
final photo =
|
||||
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
|
||||
return photo;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
|
||||
final startQuery = start.isNotNull ? '_start=$start' : '';
|
||||
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
|
||||
final delimiter1 =
|
||||
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
|
||||
final delimiter2 =
|
||||
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
|
||||
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
|
||||
final response = await _client.get(Uri.parse(url));
|
||||
final photos =
|
||||
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
|
||||
return photos.photos;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Photo>> getPhotosFromAlbum(
|
||||
int albumId, {
|
||||
int? start,
|
||||
int? limit,
|
||||
}) async {
|
||||
final startQuery = start.isNotNull ? '_start=$start' : '';
|
||||
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
|
||||
final delimiter =
|
||||
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
|
||||
final url = '/photos?albumId=$albumId&$startQuery$delimiter$limitQuery';
|
||||
final response = await _client.get(Uri.parse(url));
|
||||
final photos =
|
||||
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
|
||||
return photos.photos;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/data/models/photo_model.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class PhotoMockDataSourceImpl extends PhotoRemoteDataSource {
|
||||
final Map<int, Photo> _mock = {
|
||||
1: const PhotoModel(
|
||||
albumId: 1,
|
||||
id: 1,
|
||||
title: 'Photo 1',
|
||||
url: 'https://via.placeholder.com/600/92c952',
|
||||
thumbnailUrl: 'https://via.placeholder.com/150/92c952',
|
||||
),
|
||||
2: const PhotoModel(
|
||||
albumId: 1,
|
||||
id: 2,
|
||||
title: 'Photo 2',
|
||||
url: 'https://via.placeholder.com/600/771796',
|
||||
thumbnailUrl: 'https://via.placeholder.com/150/771796',
|
||||
),
|
||||
};
|
||||
|
||||
@override
|
||||
Future<Photo> getPhoto(int id) async {
|
||||
final response = _mock[id];
|
||||
if (response.isNull) {
|
||||
throw Exception('Unknown photo');
|
||||
}
|
||||
return response!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
|
||||
final response = _mock.values;
|
||||
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
|
||||
return response.toList().sublist(start ?? 0, nullableEnd);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Photo>> getPhotosFromAlbum(
|
||||
int albumId, {
|
||||
int? start,
|
||||
int? limit,
|
||||
}) async {
|
||||
// TODO(hpcl): use start and limit
|
||||
final response = _mock.values
|
||||
..where((element) => element.albumId == albumId);
|
||||
return response.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'album_model.freezed.dart';
|
||||
part 'album_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class AlbumModel extends Album with _$AlbumModel {
|
||||
const factory AlbumModel({
|
||||
required int userId,
|
||||
required int id,
|
||||
required String title,
|
||||
}) = _AlbumModel;
|
||||
|
||||
factory AlbumModel.fromJson(Map<String, Object?> json) =>
|
||||
_$AlbumModelFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// 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
|
||||
|
||||
part of 'album_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(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');
|
||||
|
||||
AlbumModel _$AlbumModelFromJson(Map<String, dynamic> json) {
|
||||
return _AlbumModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AlbumModel {
|
||||
int get userId => throw _privateConstructorUsedError;
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
String get title => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$AlbumModelCopyWith<AlbumModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AlbumModelCopyWith<$Res> {
|
||||
factory $AlbumModelCopyWith(
|
||||
AlbumModel value, $Res Function(AlbumModel) then) =
|
||||
_$AlbumModelCopyWithImpl<$Res, AlbumModel>;
|
||||
@useResult
|
||||
$Res call({int userId, int id, String title});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$AlbumModelCopyWithImpl<$Res, $Val extends AlbumModel>
|
||||
implements $AlbumModelCopyWith<$Res> {
|
||||
_$AlbumModelCopyWithImpl(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? userId = null,
|
||||
Object? id = null,
|
||||
Object? title = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
title: null == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_AlbumModelCopyWith<$Res>
|
||||
implements $AlbumModelCopyWith<$Res> {
|
||||
factory _$$_AlbumModelCopyWith(
|
||||
_$_AlbumModel value, $Res Function(_$_AlbumModel) then) =
|
||||
__$$_AlbumModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int userId, int id, String title});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_AlbumModelCopyWithImpl<$Res>
|
||||
extends _$AlbumModelCopyWithImpl<$Res, _$_AlbumModel>
|
||||
implements _$$_AlbumModelCopyWith<$Res> {
|
||||
__$$_AlbumModelCopyWithImpl(
|
||||
_$_AlbumModel _value, $Res Function(_$_AlbumModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? userId = null,
|
||||
Object? id = null,
|
||||
Object? title = null,
|
||||
}) {
|
||||
return _then(_$_AlbumModel(
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
title: null == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_AlbumModel implements _AlbumModel {
|
||||
const _$_AlbumModel(
|
||||
{required this.userId, required this.id, required this.title});
|
||||
|
||||
factory _$_AlbumModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_AlbumModelFromJson(json);
|
||||
|
||||
@override
|
||||
final int userId;
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlbumModel(userId: $userId, id: $id, title: $title)';
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
|
||||
__$$_AlbumModelCopyWithImpl<_$_AlbumModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_AlbumModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _AlbumModel implements AlbumModel {
|
||||
const factory _AlbumModel(
|
||||
{required final int userId,
|
||||
required final int id,
|
||||
required final String title}) = _$_AlbumModel;
|
||||
|
||||
factory _AlbumModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_AlbumModel.fromJson;
|
||||
|
||||
@override
|
||||
int get userId;
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
String get title;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'album_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_AlbumModel _$$_AlbumModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_AlbumModel(
|
||||
userId: json['userId'] as int,
|
||||
id: json['id'] as int,
|
||||
title: json['title'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_AlbumModelToJson(_$_AlbumModel instance) =>
|
||||
<String, dynamic>{
|
||||
'userId': instance.userId,
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/data/models/album_model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'list_album_model.freezed.dart';
|
||||
part 'list_album_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ListAlbumModel with _$ListAlbumModel {
|
||||
const factory ListAlbumModel({
|
||||
required List<AlbumModel> albums,
|
||||
}) = _ListAlbumModel;
|
||||
|
||||
factory ListAlbumModel.fromJson(Map<String, Object?> json) =>
|
||||
_$ListAlbumModelFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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
|
||||
|
||||
part of 'list_album_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(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');
|
||||
|
||||
ListAlbumModel _$ListAlbumModelFromJson(Map<String, dynamic> json) {
|
||||
return _ListAlbumModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ListAlbumModel {
|
||||
List<AlbumModel> get albums => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ListAlbumModelCopyWith<ListAlbumModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ListAlbumModelCopyWith<$Res> {
|
||||
factory $ListAlbumModelCopyWith(
|
||||
ListAlbumModel value, $Res Function(ListAlbumModel) then) =
|
||||
_$ListAlbumModelCopyWithImpl<$Res, ListAlbumModel>;
|
||||
@useResult
|
||||
$Res call({List<AlbumModel> albums});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ListAlbumModelCopyWithImpl<$Res, $Val extends ListAlbumModel>
|
||||
implements $ListAlbumModelCopyWith<$Res> {
|
||||
_$ListAlbumModelCopyWithImpl(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? albums = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
albums: null == albums
|
||||
? _value.albums
|
||||
: albums // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlbumModel>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_ListAlbumModelCopyWith<$Res>
|
||||
implements $ListAlbumModelCopyWith<$Res> {
|
||||
factory _$$_ListAlbumModelCopyWith(
|
||||
_$_ListAlbumModel value, $Res Function(_$_ListAlbumModel) then) =
|
||||
__$$_ListAlbumModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<AlbumModel> albums});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_ListAlbumModelCopyWithImpl<$Res>
|
||||
extends _$ListAlbumModelCopyWithImpl<$Res, _$_ListAlbumModel>
|
||||
implements _$$_ListAlbumModelCopyWith<$Res> {
|
||||
__$$_ListAlbumModelCopyWithImpl(
|
||||
_$_ListAlbumModel _value, $Res Function(_$_ListAlbumModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? albums = null,
|
||||
}) {
|
||||
return _then(_$_ListAlbumModel(
|
||||
albums: null == albums
|
||||
? _value._albums
|
||||
: albums // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlbumModel>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_ListAlbumModel implements _ListAlbumModel {
|
||||
const _$_ListAlbumModel({required final List<AlbumModel> albums})
|
||||
: _albums = albums;
|
||||
|
||||
factory _$_ListAlbumModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_ListAlbumModelFromJson(json);
|
||||
|
||||
final List<AlbumModel> _albums;
|
||||
@override
|
||||
List<AlbumModel> get albums {
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_albums);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ListAlbumModel(albums: $albums)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_ListAlbumModel &&
|
||||
const DeepCollectionEquality().equals(other._albums, _albums));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_albums));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
|
||||
__$$_ListAlbumModelCopyWithImpl<_$_ListAlbumModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_ListAlbumModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ListAlbumModel implements ListAlbumModel {
|
||||
const factory _ListAlbumModel({required final List<AlbumModel> albums}) =
|
||||
_$_ListAlbumModel;
|
||||
|
||||
factory _ListAlbumModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_ListAlbumModel.fromJson;
|
||||
|
||||
@override
|
||||
List<AlbumModel> get albums;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'list_album_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_ListAlbumModel _$$_ListAlbumModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_ListAlbumModel(
|
||||
albums: (json['albums'] as List<dynamic>)
|
||||
.map((e) => AlbumModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_ListAlbumModelToJson(_$_ListAlbumModel instance) =>
|
||||
<String, dynamic>{
|
||||
'albums': instance.albums,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import 'package:architecture_example/data/models/photo_model.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'list_photo_model.freezed.dart';
|
||||
part 'list_photo_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ListPhotoModel with _$ListPhotoModel {
|
||||
const factory ListPhotoModel({
|
||||
required List<PhotoModel> photos,
|
||||
}) = _ListPhotoModel;
|
||||
|
||||
factory ListPhotoModel.fromJson(Map<String, Object?> json) =>
|
||||
_$ListPhotoModelFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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
|
||||
|
||||
part of 'list_photo_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(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');
|
||||
|
||||
ListPhotoModel _$ListPhotoModelFromJson(Map<String, dynamic> json) {
|
||||
return _ListPhotoModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ListPhotoModel {
|
||||
List<PhotoModel> get photos => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ListPhotoModelCopyWith<ListPhotoModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ListPhotoModelCopyWith<$Res> {
|
||||
factory $ListPhotoModelCopyWith(
|
||||
ListPhotoModel value, $Res Function(ListPhotoModel) then) =
|
||||
_$ListPhotoModelCopyWithImpl<$Res, ListPhotoModel>;
|
||||
@useResult
|
||||
$Res call({List<PhotoModel> photos});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ListPhotoModelCopyWithImpl<$Res, $Val extends ListPhotoModel>
|
||||
implements $ListPhotoModelCopyWith<$Res> {
|
||||
_$ListPhotoModelCopyWithImpl(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? photos = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
photos: null == photos
|
||||
? _value.photos
|
||||
: photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PhotoModel>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_ListPhotoModelCopyWith<$Res>
|
||||
implements $ListPhotoModelCopyWith<$Res> {
|
||||
factory _$$_ListPhotoModelCopyWith(
|
||||
_$_ListPhotoModel value, $Res Function(_$_ListPhotoModel) then) =
|
||||
__$$_ListPhotoModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<PhotoModel> photos});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_ListPhotoModelCopyWithImpl<$Res>
|
||||
extends _$ListPhotoModelCopyWithImpl<$Res, _$_ListPhotoModel>
|
||||
implements _$$_ListPhotoModelCopyWith<$Res> {
|
||||
__$$_ListPhotoModelCopyWithImpl(
|
||||
_$_ListPhotoModel _value, $Res Function(_$_ListPhotoModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? photos = null,
|
||||
}) {
|
||||
return _then(_$_ListPhotoModel(
|
||||
photos: null == photos
|
||||
? _value._photos
|
||||
: photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PhotoModel>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_ListPhotoModel implements _ListPhotoModel {
|
||||
const _$_ListPhotoModel({required final List<PhotoModel> photos})
|
||||
: _photos = photos;
|
||||
|
||||
factory _$_ListPhotoModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_ListPhotoModelFromJson(json);
|
||||
|
||||
final List<PhotoModel> _photos;
|
||||
@override
|
||||
List<PhotoModel> get photos {
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_photos);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ListPhotoModel(photos: $photos)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_ListPhotoModel &&
|
||||
const DeepCollectionEquality().equals(other._photos, _photos));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_photos));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
|
||||
__$$_ListPhotoModelCopyWithImpl<_$_ListPhotoModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_ListPhotoModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ListPhotoModel implements ListPhotoModel {
|
||||
const factory _ListPhotoModel({required final List<PhotoModel> photos}) =
|
||||
_$_ListPhotoModel;
|
||||
|
||||
factory _ListPhotoModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_ListPhotoModel.fromJson;
|
||||
|
||||
@override
|
||||
List<PhotoModel> get photos;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'list_photo_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_ListPhotoModel _$$_ListPhotoModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_ListPhotoModel(
|
||||
photos: (json['photos'] as List<dynamic>)
|
||||
.map((e) => PhotoModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_ListPhotoModelToJson(_$_ListPhotoModel instance) =>
|
||||
<String, dynamic>{
|
||||
'photos': instance.photos,
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'photo_model.freezed.dart';
|
||||
part 'photo_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PhotoModel extends Photo with _$PhotoModel {
|
||||
const factory PhotoModel({
|
||||
required int albumId,
|
||||
required int id,
|
||||
required String title,
|
||||
required String url,
|
||||
required String thumbnailUrl,
|
||||
}) = _PhotoModel;
|
||||
|
||||
factory PhotoModel.fromJson(Map<String, Object?> json) =>
|
||||
_$PhotoModelFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
// 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
|
||||
|
||||
part of 'photo_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(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');
|
||||
|
||||
PhotoModel _$PhotoModelFromJson(Map<String, dynamic> json) {
|
||||
return _PhotoModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$PhotoModel {
|
||||
int get albumId => throw _privateConstructorUsedError;
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
String get title => throw _privateConstructorUsedError;
|
||||
String get url => throw _privateConstructorUsedError;
|
||||
String get thumbnailUrl => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$PhotoModelCopyWith<PhotoModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PhotoModelCopyWith<$Res> {
|
||||
factory $PhotoModelCopyWith(
|
||||
PhotoModel value, $Res Function(PhotoModel) then) =
|
||||
_$PhotoModelCopyWithImpl<$Res, PhotoModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int albumId, int id, String title, String url, String thumbnailUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PhotoModelCopyWithImpl<$Res, $Val extends PhotoModel>
|
||||
implements $PhotoModelCopyWith<$Res> {
|
||||
_$PhotoModelCopyWithImpl(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? albumId = null,
|
||||
Object? id = null,
|
||||
Object? title = null,
|
||||
Object? url = null,
|
||||
Object? thumbnailUrl = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
albumId: null == albumId
|
||||
? _value.albumId
|
||||
: albumId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
title: null == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
url: null == url
|
||||
? _value.url
|
||||
: url // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
thumbnailUrl: null == thumbnailUrl
|
||||
? _value.thumbnailUrl
|
||||
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_PhotoModelCopyWith<$Res>
|
||||
implements $PhotoModelCopyWith<$Res> {
|
||||
factory _$$_PhotoModelCopyWith(
|
||||
_$_PhotoModel value, $Res Function(_$_PhotoModel) then) =
|
||||
__$$_PhotoModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int albumId, int id, String title, String url, String thumbnailUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_PhotoModelCopyWithImpl<$Res>
|
||||
extends _$PhotoModelCopyWithImpl<$Res, _$_PhotoModel>
|
||||
implements _$$_PhotoModelCopyWith<$Res> {
|
||||
__$$_PhotoModelCopyWithImpl(
|
||||
_$_PhotoModel _value, $Res Function(_$_PhotoModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? albumId = null,
|
||||
Object? id = null,
|
||||
Object? title = null,
|
||||
Object? url = null,
|
||||
Object? thumbnailUrl = null,
|
||||
}) {
|
||||
return _then(_$_PhotoModel(
|
||||
albumId: null == albumId
|
||||
? _value.albumId
|
||||
: albumId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
title: null == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
url: null == url
|
||||
? _value.url
|
||||
: url // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
thumbnailUrl: null == thumbnailUrl
|
||||
? _value.thumbnailUrl
|
||||
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_PhotoModel implements _PhotoModel {
|
||||
const _$_PhotoModel(
|
||||
{required this.albumId,
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.url,
|
||||
required this.thumbnailUrl});
|
||||
|
||||
factory _$_PhotoModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_PhotoModelFromJson(json);
|
||||
|
||||
@override
|
||||
final int albumId;
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
final String title;
|
||||
@override
|
||||
final String url;
|
||||
@override
|
||||
final String thumbnailUrl;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PhotoModel(albumId: $albumId, id: $id, title: $title, url: $url, thumbnailUrl: $thumbnailUrl)';
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
|
||||
__$$_PhotoModelCopyWithImpl<_$_PhotoModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_PhotoModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _PhotoModel implements PhotoModel {
|
||||
const factory _PhotoModel(
|
||||
{required final int albumId,
|
||||
required final int id,
|
||||
required final String title,
|
||||
required final String url,
|
||||
required final String thumbnailUrl}) = _$_PhotoModel;
|
||||
|
||||
factory _PhotoModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_PhotoModel.fromJson;
|
||||
|
||||
@override
|
||||
int get albumId;
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
String get title;
|
||||
@override
|
||||
String get url;
|
||||
@override
|
||||
String get thumbnailUrl;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'photo_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_PhotoModel _$$_PhotoModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_PhotoModel(
|
||||
albumId: json['albumId'] as int,
|
||||
id: json['id'] as int,
|
||||
title: json['title'] as String,
|
||||
url: json['url'] as String,
|
||||
thumbnailUrl: json['thumbnailUrl'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_PhotoModelToJson(_$_PhotoModel instance) =>
|
||||
<String, dynamic>{
|
||||
'albumId': instance.albumId,
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'url': instance.url,
|
||||
'thumbnailUrl': instance.thumbnailUrl,
|
||||
};
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class PhotoRepositoryImpl extends PhotoRepository {
|
||||
final PhotoRemoteDataSource _photoRemoteDataSource;
|
||||
final AlbumRemoteDataSource _albumRemoteDataSource;
|
||||
final FavoriteLocalDataSource _favoriteLocalDataSource;
|
||||
|
||||
PhotoRepositoryImpl(
|
||||
this._photoRemoteDataSource,
|
||||
this._albumRemoteDataSource,
|
||||
this._favoriteLocalDataSource,
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync(
|
||||
() => _favoriteLocalDataSource.addPhotoToFavorites(photo),
|
||||
(error) => ClientException('Cannot add photo to favorites.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<bool> checkIfPhotoIsInFavorites(int id) => Result.tryCatchAsync(
|
||||
() => _favoriteLocalDataSource.checkIfPhotoIsInFavorites(id),
|
||||
(error) =>
|
||||
ClientException('Cannot check if photo `$id` is in favorites.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<void> deletePhotoFromFavorites(int id) => Result.tryCatchAsync(
|
||||
() => _favoriteLocalDataSource.deletePhotoFromFavorites(id),
|
||||
(error) => ClientException('Cannot delete photo `$id` from favorites.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<Album> getAlbum(int id) => Result.tryCatchAsync(
|
||||
() => _albumRemoteDataSource.getAlbum(id),
|
||||
(error) => ServerException('Cannot retrieve album $id.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<List<Album>> getAllAlbums({int? start, int? limit}) =>
|
||||
Result.tryCatchAsync(
|
||||
() => _albumRemoteDataSource.getAllAlbums(start: start, limit: limit),
|
||||
(error) => ServerException('Cannot retrieve all albums.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
|
||||
Result.tryCatchAsync(
|
||||
() => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
|
||||
(error) => ServerException('Cannot retrieve all photos.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> getAllPhotosFromFavorites() async {
|
||||
try {
|
||||
final response = <Photo>[];
|
||||
final favorites =
|
||||
await _favoriteLocalDataSource.getAllPhotosFromFavorites();
|
||||
final photos = await _photoRemoteDataSource.getAllPhotos();
|
||||
|
||||
for (final photo in photos) {
|
||||
if (favorites.any((favId) => photo.id == favId)) {
|
||||
response.add(photo);
|
||||
}
|
||||
}
|
||||
return Ok(response);
|
||||
} catch (_) {
|
||||
return Err(
|
||||
ClientException('Cannot retrieve all photos from favorites.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FutureResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
|
||||
() => _photoRemoteDataSource.getPhoto(id),
|
||||
(error) => ServerException('Cannot retrieve photo $id.'),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> getPhotosFromAlbum(
|
||||
int albumId, {
|
||||
int? start,
|
||||
int? limit,
|
||||
}) async =>
|
||||
Result.tryCatchAsync(
|
||||
() => _photoRemoteDataSource.getPhotosFromAlbum(
|
||||
albumId,
|
||||
start: start,
|
||||
limit: limit,
|
||||
),
|
||||
(error) =>
|
||||
ServerException('Cannot retrieve all photos from album $albumId'),
|
||||
);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class FavoriteLocalDataSource extends BaseLocalDataSource {
|
||||
Future<void> addPhotoToFavorites(Photo photo);
|
||||
Future<void> deletePhotoFromFavorites(int id);
|
||||
Future<List<int>> getAllPhotosFromFavorites();
|
||||
Future<bool> checkIfPhotoIsInFavorites(int id);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class AlbumRemoteDataSource extends BaseRemoteDataSource {
|
||||
Future<Album> getAlbum(int id);
|
||||
Future<List<Album>> getAllAlbums({int? start, int? limit});
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class PhotoRemoteDataSource extends BaseRemoteDataSource {
|
||||
Future<Photo> getPhoto(int id);
|
||||
Future<List<Photo>> getAllPhotos({int? start, int? limit});
|
||||
Future<List<Photo>> getPhotosFromAlbum(int albumId, {int? start, int? limit});
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
@immutable
|
||||
class Album extends Entity {
|
||||
final int userId;
|
||||
final int id;
|
||||
final String title;
|
||||
|
||||
const Album({
|
||||
required this.userId,
|
||||
required this.id,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Album other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.userId == userId && other.id == id && other.title == title;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => userId.hashCode ^ id.hashCode ^ title.hashCode;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
@immutable
|
||||
class Photo extends Entity {
|
||||
final int albumId;
|
||||
final int id;
|
||||
final String title;
|
||||
final String url;
|
||||
final String thumbnailUrl;
|
||||
|
||||
const Photo({
|
||||
required this.albumId,
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.url,
|
||||
required this.thumbnailUrl,
|
||||
});
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Photo other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.albumId == albumId &&
|
||||
other.id == id &&
|
||||
other.title == title &&
|
||||
other.url == url &&
|
||||
other.thumbnailUrl == thumbnailUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
albumId.hashCode ^
|
||||
id.hashCode ^
|
||||
title.hashCode ^
|
||||
url.hashCode ^
|
||||
thumbnailUrl.hashCode;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class PhotoRepository extends BaseRepository {
|
||||
FutureResult<Album> getAlbum(int id);
|
||||
FutureResult<List<Album>> getAllAlbums({int? start, int? limit});
|
||||
FutureResult<Photo> getPhoto(int id);
|
||||
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit});
|
||||
FutureResult<List<Photo>> getPhotosFromAlbum(
|
||||
int albumId, {
|
||||
int? start,
|
||||
int? limit,
|
||||
});
|
||||
FutureResult<void> addPhotoToFavorites(Photo photo);
|
||||
FutureResult<void> deletePhotoFromFavorites(int id);
|
||||
FutureResult<List<Photo>> getAllPhotosFromFavorites();
|
||||
FutureResult<bool> checkIfPhotoIsInFavorites(int id);
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class AddPhotoToFavorites extends UseCase<Photo, List<Photo>> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
AddPhotoToFavorites(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> call(Photo params) async {
|
||||
await _photoRepository.addPhotoToFavorites(params);
|
||||
return _photoRepository.getAllPhotosFromFavorites();
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class CheckIfPhotoIsInFavorites extends UseCase<int, bool> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
CheckIfPhotoIsInFavorites(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<bool> call(int params) async =>
|
||||
_photoRepository.checkIfPhotoIsInFavorites(params);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class DisplayFavorites extends UseCase<void, List<Photo>> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
DisplayFavorites(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> call(void params) {
|
||||
final photos = _photoRepository.getAllPhotosFromFavorites();
|
||||
return photos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class DisplayPhoto extends UseCase<int, Photo> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
DisplayPhoto(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<Photo> call(int params) {
|
||||
final photo = _photoRepository.getPhoto(params);
|
||||
return photo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class OpenAlbum extends UseCase<QueryParameters, List<Photo>> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
OpenAlbum(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> call(QueryParameters params) {
|
||||
final photos = _photoRepository.getPhotosFromAlbum(
|
||||
params.albumId,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
);
|
||||
|
||||
return photos;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
class QueryParameters {
|
||||
final int albumId;
|
||||
final int? start;
|
||||
final int? limit;
|
||||
|
||||
QueryParameters(this.start, this.limit, {this.albumId = -1});
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class RemovePhotoFromFavorites extends UseCase<int, List<Photo>> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
RemovePhotoFromFavorites(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<List<Photo>> call(int params) async {
|
||||
await _photoRepository.deletePhotoFromFavorites(params);
|
||||
return _photoRepository.getAllPhotosFromFavorites();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class RetrieveAllAlbums extends UseCase<QueryParameters, List<Album>> {
|
||||
final PhotoRepository _photoRepository;
|
||||
|
||||
RetrieveAllAlbums(this._photoRepository);
|
||||
|
||||
@override
|
||||
FutureResult<List<Album>> call(QueryParameters params) {
|
||||
final albums = _photoRepository.getAllAlbums(
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
);
|
||||
return albums;
|
||||
}
|
||||
}
|
||||
@@ -14,34 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
import 'package:architecture_example/bootstrap.dart';
|
||||
import 'package:architecture_example/presentation/features/app/app.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
bootstrap(() => const App());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Architecture Example',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Architecture Example'),
|
||||
),
|
||||
body: Center(child: Text(wyatt())),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/presentation/features/albums/state_management/albums_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Albums extends StatelessWidget {
|
||||
const Albums({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const AlbumsScreen();
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/enums/fetch_status.dart';
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
|
||||
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
part 'album_event.dart';
|
||||
part 'album_state.dart';
|
||||
|
||||
const _albumLimit = 20;
|
||||
const throttleDuration = Duration(milliseconds: 100);
|
||||
|
||||
EventTransformer<E> throttleDroppable<E>(Duration duration) =>
|
||||
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
|
||||
|
||||
class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {
|
||||
final RetrieveAllAlbums _retrieveAllAlbums;
|
||||
|
||||
AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) {
|
||||
on<AlbumFetched>(
|
||||
_onAlbumFetched,
|
||||
transformer: throttleDroppable(throttleDuration),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onAlbumFetched(
|
||||
AlbumFetched event,
|
||||
Emitter<AlbumState> emit,
|
||||
) async {
|
||||
if (state.hasReachedMax) {
|
||||
return;
|
||||
}
|
||||
if (state.status == FetchStatus.initial) {
|
||||
final albums =
|
||||
await _retrieveAllAlbums.call(QueryParameters(0, _albumLimit));
|
||||
return emit(
|
||||
albums.fold(
|
||||
(value) => state.copyWith(
|
||||
status: FetchStatus.success,
|
||||
albums: value,
|
||||
hasReachedMax: false,
|
||||
),
|
||||
(error) => state.copyWith(
|
||||
status: FetchStatus.failure,
|
||||
albums: [],
|
||||
hasReachedMax: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
final albums = await _retrieveAllAlbums
|
||||
.call(QueryParameters(state.albums.length, _albumLimit));
|
||||
return emit(
|
||||
albums.fold(
|
||||
(value) {
|
||||
if (value.isEmpty) {
|
||||
return state.copyWith(
|
||||
hasReachedMax: true,
|
||||
);
|
||||
}
|
||||
return state.copyWith(
|
||||
status: FetchStatus.success,
|
||||
albums: List.of(state.albums)..addAll(value),
|
||||
hasReachedMax: false,
|
||||
);
|
||||
},
|
||||
(error) => state.copyWith(
|
||||
status: FetchStatus.failure,
|
||||
albums: List.of(state.albums),
|
||||
hasReachedMax: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
part of 'album_bloc.dart';
|
||||
|
||||
abstract class AlbumEvent extends Equatable {
|
||||
const AlbumEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class AlbumFetched extends AlbumEvent {}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
part of 'album_bloc.dart';
|
||||
|
||||
class AlbumState extends Equatable {
|
||||
const AlbumState({
|
||||
this.status = FetchStatus.initial,
|
||||
this.albums = const <Album>[],
|
||||
this.hasReachedMax = false,
|
||||
});
|
||||
|
||||
final FetchStatus status;
|
||||
final List<Album> albums;
|
||||
final bool hasReachedMax;
|
||||
|
||||
AlbumState copyWith({
|
||||
FetchStatus? status,
|
||||
List<Album>? albums,
|
||||
bool? hasReachedMax,
|
||||
}) =>
|
||||
AlbumState(
|
||||
status: status ?? this.status,
|
||||
albums: albums ?? this.albums,
|
||||
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [status, albums, hasReachedMax];
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/enums/fetch_status.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/blocs/album/album_bloc.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/state_management/albums_wrapper_widget.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/state_management/widgets/albums_list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class AlbumsScreen extends BlocScreen<AlbumBloc, AlbumEvent, AlbumState> {
|
||||
const AlbumsScreen({super.key});
|
||||
|
||||
@override
|
||||
AlbumBloc create(BuildContext context) =>
|
||||
AlbumBloc(RetrieveAllAlbums(repo<PhotoRepository>(context)));
|
||||
|
||||
@override
|
||||
AlbumBloc init(BuildContext context, AlbumBloc bloc) =>
|
||||
bloc..add(AlbumFetched());
|
||||
|
||||
@override
|
||||
Widget onWrap(BuildContext context, Widget child) => AlbumsWrapperWidget(
|
||||
child: child,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, AlbumState state) {
|
||||
switch (state.status) {
|
||||
case FetchStatus.initial:
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
case FetchStatus.failure:
|
||||
return const Center(
|
||||
child: Text('failed to fetch albums'),
|
||||
);
|
||||
case FetchStatus.success:
|
||||
return AlbumsList(
|
||||
albums: state.albums,
|
||||
hasReachedMax: state.hasReachedMax,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AlbumsWrapperWidget extends StatelessWidget {
|
||||
const AlbumsWrapperWidget({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Albums'),
|
||||
),
|
||||
body: child,
|
||||
);
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/blocs/album/album_bloc.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/state_management/widgets/albums_list_item.dart';
|
||||
import 'package:architecture_example/presentation/shared/widgets/bottom_loader.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AlbumsList extends StatefulWidget {
|
||||
const AlbumsList(
|
||||
{required this.albums, required this.hasReachedMax, super.key,});
|
||||
|
||||
final List<Album> albums;
|
||||
final bool hasReachedMax;
|
||||
|
||||
@override
|
||||
State<AlbumsList> createState() => _AlbumsListState();
|
||||
}
|
||||
|
||||
class _AlbumsListState extends State<AlbumsList> {
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_onScroll);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController
|
||||
..removeListener(_onScroll)
|
||||
..dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (_isBottom) {
|
||||
context.read<AlbumBloc>().add(AlbumFetched());
|
||||
}
|
||||
}
|
||||
|
||||
bool get _isBottom {
|
||||
if (!_scrollController.hasClients) {
|
||||
return false;
|
||||
}
|
||||
final maxScroll = _scrollController.position.maxScrollExtent;
|
||||
final currentScroll = _scrollController.offset;
|
||||
return currentScroll >= (maxScroll * 0.9);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ListView.builder(
|
||||
itemBuilder: (context, index) => index >= widget.albums.length
|
||||
? const BottomLoader()
|
||||
: AlbumsListItem(album: widget.albums[index]),
|
||||
itemCount: widget.hasReachedMax
|
||||
? widget.albums.length
|
||||
: widget.albums.length + 1,
|
||||
controller: _scrollController,
|
||||
);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/album.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/photos.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AlbumsListItem extends StatelessWidget {
|
||||
const AlbumsListItem({required this.album, super.key});
|
||||
|
||||
final Album album;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ListTile(
|
||||
leading: Text('${album.id}'),
|
||||
title: Text(album.title),
|
||||
dense: true,
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (context) => Photos(albumId: album.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/dependency_injection/get_it.dart';
|
||||
import 'package:architecture_example/data/repositories/photo_repository_impl.dart';
|
||||
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/presentation/features/albums/albums.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
const App({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => MultiRepositoryProvider(
|
||||
providers: [
|
||||
RepositoryProvider<PhotoRepository>(
|
||||
create: (_) => PhotoRepositoryImpl(
|
||||
getIt<PhotoRemoteDataSource>(),
|
||||
getIt<AlbumRemoteDataSource>(),
|
||||
getIt<FavoriteLocalDataSource>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const MaterialApp(
|
||||
title: 'Demo',
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Albums(),
|
||||
),
|
||||
);
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/add_photo_to_favorites.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/display_photo.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/remove_photo_from_favorites.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
part 'photo_details_state.dart';
|
||||
|
||||
class PhotoDetailsCubit extends Cubit<PhotoDetailsState> {
|
||||
final DisplayPhoto _displayPhoto;
|
||||
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
|
||||
final AddPhotoToFavorites _addPhotoToFavorites;
|
||||
final RemovePhotoFromFavorites _removePhotoFromFavorites;
|
||||
|
||||
PhotoDetailsCubit(
|
||||
this._displayPhoto,
|
||||
this._checkIfPhotoIsInFavorites,
|
||||
this._addPhotoToFavorites,
|
||||
this._removePhotoFromFavorites,
|
||||
) : super(PhotoDetailsInitial());
|
||||
|
||||
FutureOr<void> load(int photoId) async {
|
||||
final photo = await _displayPhoto.call(photoId);
|
||||
final isFavorite = await _checkIfPhotoIsInFavorites.call(photoId);
|
||||
|
||||
emit(
|
||||
photo.fold(
|
||||
(value) => PhotoDetailsSuccess(
|
||||
value,
|
||||
isFavorite: isFavorite.fold((value) => value, (error) => false),
|
||||
),
|
||||
(error) => PhotoDetailsFailure(error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> addToFavorites() async {
|
||||
if (state is! PhotoDetailsSuccess) {
|
||||
return;
|
||||
}
|
||||
final actualState = state as PhotoDetailsSuccess;
|
||||
final response = await _addPhotoToFavorites.call(actualState.photo);
|
||||
if (response.isOk) {
|
||||
emit(PhotoDetailsSuccess(actualState.photo, isFavorite: true));
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> removeFromFavorites() async {
|
||||
if (state is! PhotoDetailsSuccess) {
|
||||
return;
|
||||
}
|
||||
final actualState = state as PhotoDetailsSuccess;
|
||||
final response = await _removePhotoFromFavorites.call(actualState.photo.id);
|
||||
if (response.isOk) {
|
||||
emit(PhotoDetailsSuccess(actualState.photo, isFavorite: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
part of 'photo_details_cubit.dart';
|
||||
|
||||
abstract class PhotoDetailsState extends Equatable {
|
||||
const PhotoDetailsState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class PhotoDetailsInitial extends PhotoDetailsState {}
|
||||
|
||||
class PhotoDetailsSuccess extends PhotoDetailsState {
|
||||
final Photo photo;
|
||||
final bool isFavorite;
|
||||
|
||||
const PhotoDetailsSuccess(this.photo, {required this.isFavorite});
|
||||
|
||||
@override
|
||||
List<Object> get props => [photo, isFavorite];
|
||||
}
|
||||
|
||||
class PhotoDetailsFailure extends PhotoDetailsState {
|
||||
final String error;
|
||||
|
||||
const PhotoDetailsFailure(this.error);
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/presentation/features/photo_details/state_management/photo_details_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PhotoDetails extends StatelessWidget {
|
||||
const PhotoDetails({required this.photoId, super.key});
|
||||
|
||||
final int photoId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PhotoDetailsScreen(photoId: photoId);
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/add_photo_to_favorites.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/display_photo.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/remove_photo_from_favorites.dart';
|
||||
import 'package:architecture_example/presentation/features/photo_details/blocs/photo_details/photo_details_cubit.dart';
|
||||
import 'package:architecture_example/presentation/features/photo_details/state_management/photo_details_wrapper_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class PhotoDetailsScreen
|
||||
extends CubitScreen<PhotoDetailsCubit, PhotoDetailsState> {
|
||||
const PhotoDetailsScreen({required this.photoId, super.key});
|
||||
|
||||
final int photoId;
|
||||
|
||||
@override
|
||||
PhotoDetailsCubit create(BuildContext context) => PhotoDetailsCubit(
|
||||
DisplayPhoto(repo<PhotoRepository>(context)),
|
||||
CheckIfPhotoIsInFavorites(repo<PhotoRepository>(context)),
|
||||
AddPhotoToFavorites(repo<PhotoRepository>(context)),
|
||||
RemovePhotoFromFavorites(repo<PhotoRepository>(context)),
|
||||
);
|
||||
|
||||
@override
|
||||
PhotoDetailsCubit init(BuildContext context, PhotoDetailsCubit bloc) =>
|
||||
bloc..load(photoId);
|
||||
|
||||
@override
|
||||
Widget onWrap(BuildContext context, Widget child) =>
|
||||
PhotoDetailsWrapperWidget(child: child);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, PhotoDetailsState state) {
|
||||
if (state is PhotoDetailsFailure) {
|
||||
return const Center(
|
||||
child: Text('failed to fetch photo details'),
|
||||
);
|
||||
}
|
||||
if (state is PhotoDetailsSuccess) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
expandedHeight: 400,
|
||||
stretch: true,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.network(
|
||||
state.photo.thumbnailUrl,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Image.network(
|
||||
state.photo.url,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
state.photo.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
state.isFavorite
|
||||
? bloc(context).removeFromFavorites()
|
||||
: bloc(context).addToFavorites();
|
||||
},
|
||||
icon: Icon(
|
||||
state.isFavorite
|
||||
? Icons.favorite
|
||||
: Icons.favorite_outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PhotoDetailsWrapperWidget extends StatelessWidget {
|
||||
const PhotoDetailsWrapperWidget({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
body: child,
|
||||
);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
part 'favorite_checker_state.dart';
|
||||
|
||||
class FavoriteCheckerCubit extends Cubit<FavoriteCheckerState> {
|
||||
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
|
||||
|
||||
FavoriteCheckerCubit(this._checkIfPhotoIsInFavorites)
|
||||
: super(FavoriteCheckerInitial());
|
||||
|
||||
FutureOr<void> checkIfPhotoIsInFavorites(int photoId) async {
|
||||
final response = await _checkIfPhotoIsInFavorites.call(photoId);
|
||||
emit(
|
||||
response.fold(
|
||||
(value) => FavoriteCheckerSuccess(photoId, isFavorite: value),
|
||||
(error) => FavoriteCheckerFailure(error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
part of 'favorite_checker_cubit.dart';
|
||||
|
||||
abstract class FavoriteCheckerState extends Equatable {
|
||||
const FavoriteCheckerState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class FavoriteCheckerInitial extends FavoriteCheckerState {}
|
||||
|
||||
class FavoriteCheckerSuccess extends FavoriteCheckerState {
|
||||
final int photoId;
|
||||
final bool isFavorite;
|
||||
|
||||
const FavoriteCheckerSuccess(this.photoId, {required this.isFavorite});
|
||||
|
||||
@override
|
||||
List<Object> get props => [photoId, isFavorite];
|
||||
}
|
||||
|
||||
class FavoriteCheckerFailure extends FavoriteCheckerState {
|
||||
final String error;
|
||||
|
||||
const FavoriteCheckerFailure(this.error);
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/enums/fetch_status.dart';
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/open_album.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
|
||||
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
part 'photo_event.dart';
|
||||
part 'photo_state.dart';
|
||||
|
||||
const _photoLimit = 40;
|
||||
const throttleDuration = Duration(milliseconds: 100);
|
||||
|
||||
EventTransformer<E> throttleDroppable<E>(Duration duration) =>
|
||||
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
|
||||
|
||||
class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
|
||||
final OpenAlbum _openAlbum;
|
||||
|
||||
PhotoBloc(this._openAlbum) : super(const PhotoState()) {
|
||||
on<PhotoFetched>(
|
||||
_onPhotoFetched,
|
||||
transformer: throttleDroppable(throttleDuration),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onPhotoFetched(
|
||||
PhotoFetched event,
|
||||
Emitter<PhotoState> emit,
|
||||
) async {
|
||||
if (state.hasReachedMax) {
|
||||
return;
|
||||
}
|
||||
if (state.status == FetchStatus.initial) {
|
||||
final photos = await _openAlbum.call(
|
||||
QueryParameters(
|
||||
0,
|
||||
_photoLimit,
|
||||
albumId: event.albumId,
|
||||
),
|
||||
);
|
||||
return emit(
|
||||
photos.fold(
|
||||
(value) => state.copyWith(
|
||||
status: FetchStatus.success,
|
||||
photos: value,
|
||||
hasReachedMax: false,
|
||||
),
|
||||
(error) => state.copyWith(
|
||||
status: FetchStatus.failure,
|
||||
photos: [],
|
||||
hasReachedMax: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
final photos = await _openAlbum.call(
|
||||
QueryParameters(
|
||||
state.photos.length,
|
||||
_photoLimit,
|
||||
albumId: event.albumId,
|
||||
),
|
||||
);
|
||||
return emit(
|
||||
photos.fold(
|
||||
(value) {
|
||||
if (value.isEmpty) {
|
||||
return state.copyWith(
|
||||
hasReachedMax: true,
|
||||
);
|
||||
}
|
||||
return state.copyWith(
|
||||
status: FetchStatus.success,
|
||||
photos: List.of(state.photos)..addAll(value),
|
||||
hasReachedMax: false,
|
||||
);
|
||||
},
|
||||
(error) => state.copyWith(
|
||||
status: FetchStatus.failure,
|
||||
photos: List.of(state.photos),
|
||||
hasReachedMax: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
part of 'photo_bloc.dart';
|
||||
|
||||
abstract class PhotoEvent extends Equatable {
|
||||
const PhotoEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class PhotoFetched extends PhotoEvent {
|
||||
final int albumId;
|
||||
|
||||
const PhotoFetched(this.albumId);
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
part of 'photo_bloc.dart';
|
||||
|
||||
class PhotoState extends Equatable {
|
||||
const PhotoState({
|
||||
this.status = FetchStatus.initial,
|
||||
this.photos = const <Photo>[],
|
||||
this.hasReachedMax = false,
|
||||
});
|
||||
|
||||
final FetchStatus status;
|
||||
final List<Photo> photos;
|
||||
final bool hasReachedMax;
|
||||
|
||||
PhotoState copyWith({
|
||||
FetchStatus? status,
|
||||
List<Photo>? photos,
|
||||
bool? hasReachedMax,
|
||||
}) =>
|
||||
PhotoState(
|
||||
status: status ?? this.status,
|
||||
photos: photos ?? this.photos,
|
||||
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [status, photos, hasReachedMax];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/presentation/features/photos/state_management/photos_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Photos extends StatelessWidget {
|
||||
const Photos({required this.albumId, super.key});
|
||||
|
||||
final int albumId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PhotosScreen(albumId: albumId);
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/core/enums/fetch_status.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/open_album.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/blocs/photo/photo_bloc.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/state_management/photos_wrapper_widget.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/state_management/widgets/photos_grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class PhotosScreen extends BlocScreen<PhotoBloc, PhotoEvent, PhotoState> {
|
||||
const PhotosScreen({required this.albumId, super.key});
|
||||
|
||||
final int albumId;
|
||||
|
||||
@override
|
||||
PhotoBloc create(BuildContext context) =>
|
||||
PhotoBloc(OpenAlbum(repo<PhotoRepository>(context)));
|
||||
|
||||
@override
|
||||
PhotoBloc init(BuildContext context, PhotoBloc bloc) =>
|
||||
bloc..add(PhotoFetched(albumId));
|
||||
|
||||
@override
|
||||
Widget onWrap(BuildContext context, Widget child) => PhotosWrapperWidget(
|
||||
child: child,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, PhotoState state) {
|
||||
switch (state.status) {
|
||||
case FetchStatus.initial:
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
case FetchStatus.failure:
|
||||
return const Center(
|
||||
child: Text('failed to fetch photos'),
|
||||
);
|
||||
case FetchStatus.success:
|
||||
return PhotosGrid(
|
||||
photos: state.photos,
|
||||
albumId: albumId,
|
||||
hasReachedMax: state.hasReachedMax,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PhotosWrapperWidget extends StatelessWidget {
|
||||
const PhotosWrapperWidget({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Photos'),
|
||||
),
|
||||
body: child,
|
||||
);
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/blocs/photo/photo_bloc.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/state_management/widgets/photos_grid_thumbnail.dart';
|
||||
import 'package:architecture_example/presentation/shared/widgets/bottom_loader.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class PhotosGrid extends StatefulWidget {
|
||||
const PhotosGrid({
|
||||
required this.photos,
|
||||
required this.albumId,
|
||||
required this.hasReachedMax,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<Photo> photos;
|
||||
final int albumId;
|
||||
final bool hasReachedMax;
|
||||
|
||||
@override
|
||||
State<PhotosGrid> createState() => _PhotosGridState();
|
||||
}
|
||||
|
||||
class _PhotosGridState extends State<PhotosGrid> {
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_onScroll);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController
|
||||
..removeListener(_onScroll)
|
||||
..dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (_isBottom) {
|
||||
context.read<PhotoBloc>().add(PhotoFetched(widget.albumId));
|
||||
}
|
||||
}
|
||||
|
||||
bool get _isBottom {
|
||||
if (!_scrollController.hasClients) {
|
||||
return false;
|
||||
}
|
||||
final maxScroll = _scrollController.position.maxScrollExtent;
|
||||
final currentScroll = _scrollController.offset;
|
||||
return currentScroll >= (maxScroll * 0.9);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisSpacing: 4,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisCount: 4,
|
||||
),
|
||||
itemCount: widget.hasReachedMax
|
||||
? widget.photos.length
|
||||
: widget.photos.length + 1,
|
||||
controller: _scrollController,
|
||||
itemBuilder: (context, index) => index >= widget.photos.length
|
||||
? const BottomLoader()
|
||||
: PhotosGridThumbnail(photo: widget.photos[index]),
|
||||
);
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:architecture_example/domain/entities/photo.dart';
|
||||
import 'package:architecture_example/domain/repositories/photo_repository.dart';
|
||||
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
|
||||
import 'package:architecture_example/presentation/features/photo_details/photo_details.dart';
|
||||
import 'package:architecture_example/presentation/features/photos/blocs/favorite_checker/favorite_checker_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class PhotosGridThumbnail
|
||||
extends CubitScreen<FavoriteCheckerCubit, FavoriteCheckerState> {
|
||||
const PhotosGridThumbnail({required this.photo, super.key});
|
||||
|
||||
final Photo photo;
|
||||
|
||||
@override
|
||||
FavoriteCheckerCubit create(BuildContext context) => FavoriteCheckerCubit(
|
||||
CheckIfPhotoIsInFavorites(repo<PhotoRepository>(context)),
|
||||
);
|
||||
|
||||
@override
|
||||
FavoriteCheckerCubit init(BuildContext context, FavoriteCheckerCubit bloc) =>
|
||||
bloc..checkIfPhotoIsInFavorites(photo.id);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, FavoriteCheckerState state) =>
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (context) => PhotoDetails(photoId: photo.id),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.network(
|
||||
photo.thumbnailUrl,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
if (state is FavoriteCheckerSuccess && state.isFavorite) ...[
|
||||
const Positioned(
|
||||
bottom: 10, right: 10, child: Icon(Icons.favorite),)
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomLoader extends StatelessWidget {
|
||||
const BottomLoader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const Center(
|
||||
child: SizedBox(
|
||||
height: 24,
|
||||
width: 24,
|
||||
child: CircularProgressIndicator(strokeWidth: 1.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user