doc(arch): add fully featured example

This commit is contained in:
2022-11-08 20:09:18 -05:00
parent db67491876
commit 6602db215f
109 changed files with 4499 additions and 29 deletions
@@ -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));
}
}
}
@@ -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];
}
@@ -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);
}
@@ -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(),
);
}
}
@@ -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,
);
}