Compare commits

...

10 Commits

150 changed files with 2010 additions and 1812 deletions

View File

@ -1,2 +0,0 @@
new_version.sh
.latest_version

View File

@ -0,0 +1 @@
../../.pubignore

View File

@ -1,4 +1,4 @@
# Copyright (C) 2022 WYATT GROUP # Copyright (C) 2023 WYATT GROUP
# Please see the AUTHORS file for details. # Please see the AUTHORS file for details.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -14,8 +14,4 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
include: package:wyatt_analysis/analysis_options.yaml
include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"

View File

@ -52,13 +52,13 @@ abstract class GetItInitializer {
) )
..registerLazySingleton<MiddlewareClient>(() { ..registerLazySingleton<MiddlewareClient>(() {
final Pipeline pipeline = Pipeline() final Pipeline pipeline = Pipeline()
.addMiddleware( ..addMiddleware(
UriPrefixMiddleware( const UriPrefixMiddleware(
protocol: Protocols.https, protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com', authority: 'jsonplaceholder.typicode.com',
), ),
) )
.addMiddleware(BodyToJsonMiddleware()); ..addMiddleware(const BodyToJsonMiddleware());
return MiddlewareClient(pipeline: pipeline); return MiddlewareClient(pipeline: pipeline);
}) })
..registerLazySingleton<PhotoRemoteDataSource>( ..registerLazySingleton<PhotoRemoteDataSource>(

View File

@ -1,2 +0,0 @@
firebase_options.dart
.vscode

View File

@ -0,0 +1 @@
../../.pubignore

View File

@ -1,4 +1 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"

View File

@ -16,57 +16,46 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Flutter - Wyatt Bloc Layout # Wyatt Bloc Layout
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
</a>
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" /> <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
</p> </p>
Bloc Layout for Flutter. Bloc Layout for Flutter.
Wyatt Bloc Layout is a Flutter package that builds on the Wyatt UI Layout package and the Wyatt Bloc Helper package. It provides a way to link multiple packages in order to create intelligent layouts that combine both layout and logic. The package allows developers to use the available layouts in the Wyatt UI Layout package along with the block state logic available in the Wyatt Bloc Helper package. It also uses the Wyatt Crud Bloc package to make it easier to implement CRUD logic. Wyatt Bloc Layout is a Flutter package that is built on the Wyatt UI Layout package and the Wyatt Bloc Helper package.
It provides a way to link multiple packages in order to create intelligent layouts that combine both layout and logic. The package allows developers to use the available layouts in the Wyatt UI Layout package along with the block state logic available in the Wyatt Bloc Helper package.
It also uses the Wyatt Crud Bloc package to make it easier to implement CRUD logic.
### Features ### Features
- Allows developers to use available layouts from Wyatt UI Layout package. * Allows developers to use available layouts from Wyatt UI Layout package.
- Links with the Wyatt Bloc Helper package to combine layout and block state logic. * Links with the Wyatt Bloc Helper package to combine layout and block state logic.
- Uses the Wyatt Crud Bloc package to easily implement CRUD logic. * Uses the Wyatt Crud Bloc package to easily implement CRUD logic.
#### Available bloc layouts #### Available bloc layouts
- BottomNavigationBarGridLayoutCubitScreenCrudList * BottomNavigationBarGridLayoutCubitScreenCrudList
- BottomNavigationBarLayoutCubitScreen * BottomNavigationBarLayoutCubitScreen
- BottomNavigationBarLayoutCubitScreenCrud * BottomNavigationBarLayoutCubitScreenCrud
- BottomNavigationBarLayoutCubitScreenCrudItem * BottomNavigationBarLayoutCubitScreenCrudItem
- BottomNavigationBarLayoutCubitScreenCrudList * BottomNavigationBarLayoutCubitScreenCrudList
- FrameGridLayoutCubitScreenCrudList * FrameGridLayoutCubitScreenCrudList
- FrameLayoutCubitScreen * FrameLayoutCubitScreen
- FrameLayoutCubitScreenCrud * FrameLayoutCubitScreenCrud
- FrameLayoutCubitScreenCrudItem * FrameLayoutCubitScreenCrudItem
- FrameLayoutCubitScreenCrudList * FrameLayoutCubitScreenCrudList
- TopAppBarGridLayoutCubitScreenCrudList * TopAppBarGridLayoutCubitScreenCrudList
- TopAppBarLayoutCubitScreen * TopAppBarLayoutCubitScreen
- TopAppBarLayoutCubitScreenCrud * TopAppBarLayoutCubitScreenCrud
- TopAppBarLayoutCubitScreenCrudItem * TopAppBarLayoutCubitScreenCrudItem
- TopAppBarLayoutCubitScreenCrudList * TopAppBarLayoutCubitScreenCrudList
- TopNavigationBarGridLayoutCubitScreenCrudList * TopNavigationBarGridLayoutCubitScreenCrudList
- TopNavigationBarLayoutCubitScreen * TopNavigationBarLayoutCubitScreen
- TopNavigationBarLayoutCubitScreenCrud * TopNavigationBarLayoutCubitScreenCrud
- TopNavigationBarLayoutCubitScreenCrudItem * TopNavigationBarLayoutCubitScreenCrudItem
- TopNavigationBarLayoutCubitScreenCrudList * TopNavigationBarLayoutCubitScreenCrudList
### Installation
To use Wyatt Bloc Layout in your Flutter project, add the following dependency to your pubspec.yaml file:
```yaml
wyatt_bloc_layout:
git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
path: packages/wyatt_bloc_layout
```
Then, run flutter pub get to download the package.

View File

@ -1,17 +1 @@
# Copyright (C) 2023 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/>.
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml

View File

@ -1,27 +1,28 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
class ExampleCubit extends Cubit<CrudState> { class ExampleCubit extends Cubit<CrudState> {
ExampleCubit() : super(CrudInitial()); ExampleCubit() : super(const CrudInitial());
FutureOr<void> run() async { FutureOr<void> run() async {
while (true) { while (true) {
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(CrudLoading()); emit(const CrudLoading());
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(const CrudError('Cubit Error')); emit(const CrudError('Cubit Error'));
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(const CrudLoaded<String>('DATA LOADED')); emit(const CrudLoaded<String>('DATA LOADED'));
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(CrudInitial()); emit(const CrudInitial());
} }
} }
FutureOr<void> runList() async { FutureOr<void> runList() async {
while (true) { while (true) {
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(CrudLoading()); emit(const CrudLoading());
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(const CrudError('Cubit Error')); emit(const CrudError('Cubit Error'));
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
@ -34,7 +35,7 @@ class ExampleCubit extends Cubit<CrudState> {
]), ]),
); );
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
emit(CrudInitial()); emit(const CrudInitial());
} }
} }
} }

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
part 'custom_app_bar.g.dart'; part 'custom_app_bar.g.dart';

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
part 'custom_bottom_bar.g.dart'; part 'custom_bottom_bar.g.dart';

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
part 'custom_error_widget.g.dart'; part 'custom_error_widget.g.dart';

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
part 'custom_loading_widget.g.dart'; part 'custom_loading_widget.g.dart';

View File

@ -2,7 +2,7 @@ import 'package:bloc_layout_example/components/custom_app_bar.dart';
import 'package:bloc_layout_example/components/custom_bottom_bar.dart'; import 'package:bloc_layout_example/components/custom_bottom_bar.dart';
import 'package:bloc_layout_example/components/custom_error_widget.dart'; import 'package:bloc_layout_example/components/custom_error_widget.dart';
import 'package:bloc_layout_example/components/custom_loading_widget.dart'; import 'package:bloc_layout_example/components/custom_loading_widget.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_ui_components/wyatt_ui_components.dart';
class AppThemeComponent { class AppThemeComponent {
static ComponentThemeData get components => ComponentThemeData.raw( static ComponentThemeData get components => ComponentThemeData.raw(

View File

@ -17,7 +17,10 @@
import 'package:bloc_layout_example/bloc/example_cubit.dart'; import 'package:bloc_layout_example/bloc/example_cubit.dart';
import 'package:bloc_layout_example/components/theme_components.dart'; import 'package:bloc_layout_example/components/theme_components.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
void main() { void main() {
runApp(const MyApp()); runApp(const MyApp());

View File

@ -30,9 +30,27 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_bloc: ^8.1.2
wyatt_bloc_layout: wyatt_bloc_layout:
path: "../" path: "../"
wyatt_bloc_helper:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^2.0.0
wyatt_ui_layout:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^0.0.1
wyatt_crud_bloc:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^0.1.0+2
wyatt_ui_components:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^0.0.1
wyatt_component_copy_with_extension: wyatt_component_copy_with_extension:
git: git:
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git

View File

@ -15,7 +15,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
import 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
mixin GridLayoutMixin<SuccessType extends Object?> { mixin GridLayoutMixin<SuccessType extends Object?> {
Widget gridChild(BuildContext context, SuccessType? successType); Widget gridChild(BuildContext context, SuccessType? successType);

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class BottomNavigationBarGridLayoutCubitScreenCrudList< abstract class BottomNavigationBarGridLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -18,6 +18,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
abstract class BottomNavigationBarLayoutCubitScreen< abstract class BottomNavigationBarLayoutCubitScreen<
Cubit extends bloc_base.Cubit<State>, Cubit extends bloc_base.Cubit<State>,

View File

@ -17,6 +17,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class BottomNavigationBarLayoutCubitScreenCrud< abstract class BottomNavigationBarLayoutCubitScreenCrud<
Cubit extends bloc_base.Cubit<CrudState>, Cubit extends bloc_base.Cubit<CrudState>,

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class BottomNavigationBarLayoutCubitScreenCrudItem< abstract class BottomNavigationBarLayoutCubitScreenCrudItem<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class BottomNavigationBarLayoutCubitScreenCrudList< abstract class BottomNavigationBarLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
abstract class CubitScreenBase<Cubit extends bloc_base.Cubit<State>, abstract class CubitScreenBase<Cubit extends bloc_base.Cubit<State>,
State extends Object> extends CubitScreen<Cubit, State> { State extends Object> extends CubitScreen<Cubit, State> {

View File

@ -17,6 +17,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class CubitScreenCrudBase<Cubit extends bloc_base.Cubit<CrudState>, abstract class CubitScreenCrudBase<Cubit extends bloc_base.Cubit<CrudState>,
CrudSuccessState extends CrudSuccess> CrudSuccessState extends CrudSuccess>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class CubitScreenCrudItemBase<Cubit extends bloc_base.Cubit<CrudState>, abstract class CubitScreenCrudItemBase<Cubit extends bloc_base.Cubit<CrudState>,
T extends Object?> extends CubitScreenCrudBase<Cubit, CrudLoaded<T>> { T extends Object?> extends CubitScreenCrudBase<Cubit, CrudLoaded<T>> {

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class CubitScreenCrudListBase<Cubit extends bloc_base.Cubit<CrudState>, abstract class CubitScreenCrudListBase<Cubit extends bloc_base.Cubit<CrudState>,
T extends Object?> extends CubitScreenCrudBase<Cubit, CrudListLoaded<T>> { T extends Object?> extends CubitScreenCrudBase<Cubit, CrudListLoaded<T>> {

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class FrameLayoutGridCubitScreenCrudList< abstract class FrameLayoutGridCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -18,6 +18,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
abstract class FrameLayoutCubitScreen<Cubit extends bloc_base.Cubit<State>, abstract class FrameLayoutCubitScreen<Cubit extends bloc_base.Cubit<State>,
State extends Object> extends CubitScreenBase<Cubit, State> { State extends Object> extends CubitScreenBase<Cubit, State> {

View File

@ -17,6 +17,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class FrameLayoutCubitScreenCrud< abstract class FrameLayoutCubitScreenCrud<
Cubit extends bloc_base.Cubit<CrudState>, Cubit extends bloc_base.Cubit<CrudState>,

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class FrameLayoutCubitScreenCrudItem< abstract class FrameLayoutCubitScreenCrudItem<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class FrameLayoutCubitScreenCrudList< abstract class FrameLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class GridCubitScreenCrudListBase< abstract class GridCubitScreenCrudListBase<
Cubit extends bloc_base.Cubit<CrudState>, T extends Object?> Cubit extends bloc_base.Cubit<CrudState>, T extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopAppBarGridLayoutCubitScreenCrudList< abstract class TopAppBarGridLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -18,6 +18,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
abstract class TopAppBarLayoutCubitScreen<Cubit extends bloc_base.Cubit<State>, abstract class TopAppBarLayoutCubitScreen<Cubit extends bloc_base.Cubit<State>,
State extends Object> extends CubitScreenBase<Cubit, State> { State extends Object> extends CubitScreenBase<Cubit, State> {

View File

@ -17,6 +17,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopAppBarLayoutCubitScreenCrud< abstract class TopAppBarLayoutCubitScreenCrud<
Cubit extends bloc_base.Cubit<CrudState>, Cubit extends bloc_base.Cubit<CrudState>,

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopAppBarLayoutCubitScreenCrudItem< abstract class TopAppBarLayoutCubitScreenCrudItem<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopAppBarLayoutCubitScreenCrudList< abstract class TopAppBarLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopNavigationBarGridLayoutCubitScreenCrudList< abstract class TopNavigationBarGridLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -18,6 +18,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
abstract class TopNavigationBarLayoutCubitScreen< abstract class TopNavigationBarLayoutCubitScreen<
Cubit extends bloc_base.Cubit<State>, Cubit extends bloc_base.Cubit<State>,

View File

@ -17,6 +17,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopNavigationBarLayoutCubitScreenCrud< abstract class TopNavigationBarLayoutCubitScreenCrud<
Cubit extends bloc_base.Cubit<CrudState>, Cubit extends bloc_base.Cubit<CrudState>,

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopNavigationBarLayoutCubitScreenCrudItem< abstract class TopNavigationBarLayoutCubitScreenCrudItem<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -16,6 +16,7 @@
import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_base;
import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart'; import 'package:wyatt_bloc_layout/wyatt_bloc_layout.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
abstract class TopNavigationBarLayoutCubitScreenCrudList< abstract class TopNavigationBarLayoutCubitScreenCrudList<
Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?> Cubit extends bloc_base.Cubit<CrudState>, SuccessType extends Object?>

View File

@ -14,11 +14,5 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'package:flutter_bloc/flutter_bloc.dart';
export 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
export 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
export 'package:wyatt_ui_components/wyatt_ui_components.dart';
export 'package:wyatt_ui_layout/wyatt_ui_layout.dart';
export 'core/core.dart'; export 'core/core.dart';
export 'presentation/presentation.dart'; export 'presentation/presentation.dart';

View File

@ -3,43 +3,35 @@ description: Layouts based on bloc helper library
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_bloc_layout repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_bloc_layout
version: 0.0.1 version: 0.0.1
publish_to: "none" publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
dependencies: dependencies:
flutter: flutter: { sdk: flutter }
sdk: flutter
flutter_bloc: ^8.1.2 flutter_bloc: ^8.1.2
wyatt_bloc_helper: wyatt_bloc_helper:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: ^2.0.0
path: packages/wyatt_bloc_helper
wyatt_ui_layout: wyatt_ui_layout:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: ^0.0.1
path: packages/wyatt_ui_layout
wyatt_crud_bloc: wyatt_crud_bloc:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: ^0.1.0+2
path: packages/wyatt_crud_bloc
wyatt_ui_components: wyatt_ui_components:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: ^0.0.1
path: packages/wyatt_ui_components
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: { sdk: flutter }
sdk: flutter
wyatt_analysis: wyatt_analysis:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: ^2.4.1
ref: wyatt_analysis-v2.4.1
path: packages/wyatt_analysis

View File

@ -16,15 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Flutter - Component Copy With Extension # Component Copy With Extension
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" /> <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" />
</p> </p>
This package provides annotations for generating code and simplifying the use of an UI kit within a Flutter application. **The package contains only the annotation classes**. This package provides annotations for generating code and simplifying the use of an UI kit within a Flutter application. **The package contains only the annotation classes**.
> This package does not contain Flutter specific code, but there is no sense in using it without Flutter.
## Summary
* `ComponentProxyExtension` - Annotation class for generating a proxy of a component of an UI kit.
* `ComponentCopyWithExtension` - Annotation class for generating the copyWith method of a component implementation.
For example, let's say we have a component of an UI kit that we want to use in our application.
1) We create the component in the `wyatt_ui_components` , add the `ComponentProxyExtension` annotation and generate the code. This component does not have any specific implementation, and only have a set of properties.
2) Now we implement the component in our ui kit, `wyatt_ui_kit` , add the `ComponentCopyWithExtension` annotation and generate the code. This component has a specific implementation and can be used in the application.
3) But we can implement multiple ui kits, and each of them can have their own implementation of the same component. And at the top of the application tree we can use the `wyatt_ui_components` package, which contains only the proxy of the component.
> In that way, the **application is UI kit agnostic**, and we can easily change the UI kit without changing the code of the application.
We will use the `LoaderComponent` component as an example in this documentation.
## Annotation Classes ## Annotation Classes
#### `ComponentProxyExtension` #### `ComponentProxyExtension`
@ -32,31 +49,30 @@ This package provides annotations for generating code and simplifying the use of
This annotation class is used to annotate a new component of an UI kit in the `wyatt_ui_components` package. It generates the abstract proxy of the component and allows access to all its properties in different packages and throughout the application. This annotation class is used to annotate a new component of an UI kit in the `wyatt_ui_components` package. It generates the abstract proxy of the component and allows access to all its properties in different packages and throughout the application.
```dart ```dart
part 'text_field_component.g.dart'; part 'loader_component.g.dart';
@ComponentProxyExtension() @ComponentProxyExtension()
abstract class TextFieldComponent extends Component { abstract class LoaderComponent extends Component
with CopyWithMixin<$TextFieldComponentCWProxy> { with CopyWithMixin<$LoaderComponentCWProxy> {
const TextFieldComponent({ const LoaderComponent({
... ...
}); });
} }
``` ```
#### `ComponentCopyWithExtension` #### `ComponentCopyWithExtension`
This annotation class is used to annotate the implementation of components directly in the application. It generates the implementation of the proxy and the mixin to ensure that the component meets the specifications defined in the `wyatt_ui_components package`. This annotation class is used to annotate the implementation of components directly in the application. It generates the implementation of the proxy and the mixin to ensure that the component meets the specifications defined in the `wyatt_ui_components package` .
```dart ```dart
part 'text_field_component.g.dart'; part 'loader.g.dart';
@ComponentProxyExtension() @ComponentCopyWithExtension()
abstract class TextFieldComponent extends Component { class Loader extends LoaderComponent with $LoaderCWMixin {
with CopyWithMixin<$TextFieldComponentCWProxy> {
const TextFieldComponent({ const Loader({
... ...
}); });
} }
``` ```

View File

@ -1,5 +1,5 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.yaml

View File

@ -17,9 +17,29 @@
import 'package:meta/meta_meta.dart'; import 'package:meta/meta_meta.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
/// Annotation used to indicate that the `copyWith` extension /// {@template component_copy_with_extension}
/// should be generated for the compononent. /// This annotation class is used to annotate the implementation of components
/// directly in the application. It generates the implementation of the proxy
/// and the mixin to ensure that the component meets the specifications
/// defined in the UI Kit.
///
/// Basically it indicate that the `copyWith` extension
/// should be generated for the component.
///
/// ```dart
/// part 'loader.g.dart';
///
/// @ComponentCopyWithExtension()
/// class Loader extends LoaderComponent with $LoaderCWMixin {
///
/// const Loader({
/// ...
/// });
/// }
/// ```
/// {@endtemplate}
@Target({TargetKind.classType}) @Target({TargetKind.classType})
class ComponentCopyWithExtension extends ComponentAnnotation { class ComponentCopyWithExtension extends ComponentAnnotation {
/// {@macro component_copy_with_extension}
const ComponentCopyWithExtension({super.skipFields}); const ComponentCopyWithExtension({super.skipFields});
} }

View File

@ -14,12 +14,32 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:meta/meta_meta.dart'; import 'package:meta/meta_meta.dart';
import 'package:wyatt_component_copy_with_extension/src/domain/component_annotation.dart';
/// {@template component_proxy_extension}
/// This annotation class is used to annotate a new component of an UI kit.
/// It generates the abstract proxy of the component and allows access to
/// all its properties in different packages and throughout the application.
///
/// ```dart
/// part 'loader_component.g.dart';
///
/// @ComponentProxyExtension()
/// abstract class LoaderComponent extends Component
/// with CopyWithMixin<$LoaderComponentCWProxy> {
///
/// const LoaderComponent({
/// ...
/// });
/// }
/// ```
///
/// The [skipFields] option allows you to directly and specifically change
/// a field. This makes it easier to use.
///
/// {@endtemplate}
@Target({TargetKind.classType}) @Target({TargetKind.classType})
class ComponentProxyExtension { class ComponentProxyExtension extends ComponentAnnotation {
const ComponentProxyExtension({ /// {@macro component_proxy_extension}
this.skipFields = true, const ComponentProxyExtension({super.skipFields});
});
final bool? skipFields;
} }

View File

@ -14,7 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
/// {@template component_annotation}
/// Abstract class that is used as a base for all annotations
/// {@endtemplate}
abstract class ComponentAnnotation { abstract class ComponentAnnotation {
/// {@macro component_annotation}
const ComponentAnnotation({this.skipFields = true}); const ComponentAnnotation({this.skipFields = true});
/// Prevent the library from generating `copyWith` functions for individual /// Prevent the library from generating `copyWith` functions for individual

View File

@ -3,18 +3,15 @@ description: Extension for component copy with feature.
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/component_copy_with_extension repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/component_copy_with_extension
version: 1.0.0 version: 1.0.0
publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"
dependencies: dependencies:
meta: ^1.8.0 meta: ^1.8.0
path: ^1.8.0
dev_dependencies: dev_dependencies:
wyatt_analysis: wyatt_analysis:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: ^2.4.1 version: ^2.4.1
# The following section is specific to Flutter.
flutter:
uses-material-design: true

View File

@ -16,30 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Dart - Component Copy With Gen # Component Copy With Gen
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" />
</p> </p>
A Dart package for generating code from annotations to ease the use of a UIKit in Flutter applications. The generated code is based on the annotation classes present in the 'wyatt_component_copy_with_extension' package. A Dart package for generating code from annotations to ease the use of a UIKit in Flutter applications. The generated code is based on the annotation classes present in the [ `wyatt_component_copy_with_extension` package](https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_component_copy_with_extension)
> This package does not contain Flutter specific code, but there is no sense in using it without Flutter.
## Features ## Features
- Supports the generation of abstract proxies in the `wyatt_ui_components` package. * Supports the generation of abstract proxies in the `wyatt_ui_components` package.
- Supports direct use in Flutter applications. * Supports direct use in Flutter applications.
## Usage ## Usage
### In the 'wyatt_ui_components' package ### In the 'wyatt_ui_components' package
- Add the appropriate annotation when addicdng a new component. * Add the appropriate annotation when adding a new component.
- Run the build runner command to generate the proxy. * Run the build runner command to generate the proxy.
### In Flutter applications ### In Flutter applications (or UI Kit implementations)
- Add the following dependencies to your pubspec.yaml: * Add the following dependencies to your pubspec.yaml:
```yaml ```yaml
dependencies: dependencies:
@ -51,7 +53,7 @@ dev_dependencies:
build_runner: ^2.3.3 build_runner: ^2.3.3
``` ```
- In your UIKit, extend the desired component class and add the appropriate annotation. * In your UIKit, extend the desired component class and add the appropriate annotation.
- Run the code generation command via the build runner. * Run the code generation command via the build runner.
For further details and additional features on class annotation, see the 'wyatt_component_copy_with_extension' package's README. For further details and additional features on class annotation, see the 'wyatt_component_copy_with_extension' package's README.

View File

@ -3,21 +3,19 @@ description: Generator for copywith method for components.
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/component_copy_with_gen repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/component_copy_with_gen
version: 1.0.0 version: 1.0.0
publish_to: "none" publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"
dependencies: dependencies:
path: ^1.8.0
build: ^2.3.1 build: ^2.3.1
source_gen: ^1.2.7 source_gen: ^1.2.7
analyzer: ^5.4.0 analyzer: ^5.4.0
wyatt_component_copy_with_extension: wyatt_component_copy_with_extension:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git version: ^1.0.0
path: packages/wyatt_component_copy_with_extension
dev_dependencies: dev_dependencies:
test: ^1.21.0 test: ^1.21.0

View File

@ -1,2 +0,0 @@
google-services.json
.vscode

View File

@ -0,0 +1 @@
../../.pubignore

View File

@ -1,39 +1,167 @@
<!-- <!--
This README describes the package. If you publish this package to pub.dev, * Copyright (C) 2023 WYATT GROUP
this README's contents appear on the landing page for your package. * Please see the AUTHORS file for details.
*
For information about how to write a good package README, see the guide for * This program is free software: you can redistribute it and/or modify
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages). * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
For general information about developing packages, see the Dart guide for * any later version.
[creating packages](https://dart.dev/guides/libraries/create-library-packages) *
and the Flutter guide for * This program is distributed in the hope that it will be useful,
[developing packages and plugins](https://flutter.dev/developing-packages). * 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/>.
--> -->
TODO: Put a short description of the package here that helps potential users # CRUD BloC
know whether this package might be useful for them.
## Features <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
</p>
TODO: List what your package can do. Maybe include images, gifs, or videos. CRUD Bloc Pattern utilities for Flutter.
## Getting started This package defines a set of classes that can be used to implement the CRUD Bloc Pattern.
TODO: List prerequisites and provide or point to information on how to * Model
start using the package. * Data Source
+ In Memory
+ Firestore
* Repository
* Use Case
+ Create
+ Get
+ Get All
+ Update
+ Update All
+ Delete
+ Delete All
+ Query
* Bloc
+ Standard (C R U D), you have to choose the responsiblity of the bloc for each use case. For example, you can have a cubit that only handles the creation of an entity, and another cubit that only handles the deletion of an entity. Each cubit can only have one operation responsibility of each type, for example you can't have a cubit that handles get and get all.
+ Advanced, you can set every use case to be handled by the bloc. This is useful if you want to have a single bloc that handles all the operations of an entity.
## Usage ## Usage
TODO: Include short and useful examples for package users. Add longer examples Create a model class that extends the `ObjectModel` class.
to `/example` folder.
```dart ```dart
const like = 'sample'; class User extends ObjectModel {
@override
final String? id;
final String? name;
const User({
required this.name,
this.id,
});
Map<String, Object> toMap() {
return {
'name': name ?? '',
};
}
@override
String toString() => 'User(id: $id, name: $name)';
}
``` ```
## Additional information You have to implement a bloc.
TODO: Tell users more about the package: where to find more information, how to ```dart
contribute to the package, how to file issues, what response they can expect /// A [CrudCubit] for [User].
from the package authors, and more. class UserCubit extends CrudCubit<User> {
final CrudRepository<User> _crudRepository;
UserCubit(this._crudRepository);
@override
CreateOperation<User, dynamic>? get createOperation =>
Create(_crudRepository);
@override
DeleteOperation<User, dynamic>? get deleteOperation =>
Delete(_crudRepository);
@override
ReadOperation<User, dynamic, dynamic>? get readOperation =>
GetAll(_crudRepository);
@override
UpdateOperation<User, dynamic>? get updateOperation =>
Update(_crudRepository);
}
```
> You can also use the `CrudAdvancedCubit` class to implement a bloc that handles all the use cases.
Then you can use the bloc in your widget with a data source and a repository.
```dart
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final CrudDataSource<User> userLocalDataSource =
CrudInMemoryDataSourceImpl<User>(toMap: (user) => user.toMap());
final CrudRepository<User> userRepository =
CrudRepositoryImpl(crudDataSource: userLocalDataSource);
return RepositoryProvider<CrudRepository<User>>.value(
value: userRepository,
child: BlocProvider<UserCubit>(
create: (context) => UserCubit(userRepository)..read(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
),
),
);
}
}
```
And anywhere in your widget tree you can use the BlocBuilder to build your widget.
```dart
...
BlocBuilder<UserCubit, CrudState>(
builder: (context, state) {
return CrudBuilder.typed<CrudListLoaded<User?>>(
state: state,
builder: ((context, state) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.data.length,
itemBuilder: (context, index) {
final user = state.data.elementAt(index);
return ListTile(
title: Text(user?.name ?? 'Error'),
subtitle: Text(user?.id ?? 'Error'),
onTap: () {
context.read<UserCubit>().delete(id: (user?.id)!);
},
);
},
);
}),
initialBuilder: (context, state) => const Text("Loading..."),
loadingBuilder: (context, state) => const Text("Loading..."),
errorBuilder: (context, state) => Text("Error: $state"),
);
},
),
...
```

View File

@ -1,4 +1 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"

View File

@ -36,8 +36,12 @@ class MyApp extends StatelessWidget {
return RepositoryProvider<CrudRepository<User>>.value( return RepositoryProvider<CrudRepository<User>>.value(
value: userRepository, value: userRepository,
child: BlocProvider<UserCubit>( child: MultiBlocProvider(
create: (context) => UserCubit(userRepository)..getAll(), providers: [
BlocProvider<UserCubit>(
create: (context) => UserCubit(userRepository)..read(),
),
],
child: MaterialApp( child: MaterialApp(
title: 'Flutter Demo', title: 'Flutter Demo',
theme: ThemeData( theme: ThemeData(
@ -78,9 +82,7 @@ class MyHomePage extends StatelessWidget {
title: Text(user?.name ?? 'Error'), title: Text(user?.name ?? 'Error'),
subtitle: Text(user?.id ?? 'Error'), subtitle: Text(user?.id ?? 'Error'),
onTap: () { onTap: () {
context.read<UserCubit>().delete( context.read<UserCubit>().delete(id: (user?.id)!);
(user?.id)!,
);
}, },
); );
}, },
@ -108,22 +110,10 @@ class MyHomePage extends StatelessWidget {
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
context.read<UserCubit>().deleteAll(); context.read<UserCubit>().read();
},
child: const Text("DeleteAll"),
),
ElevatedButton(
onPressed: () {
context.read<UserCubit>().getAll();
}, },
child: const Text("GetAll"), child: const Text("GetAll"),
), ),
ElevatedButton(
onPressed: () {
context.read<UserCubit>().query([LimitQuery(2)]);
},
child: const Text("Query"),
),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
), ),

View File

@ -18,37 +18,18 @@ import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
class User extends ObjectModel { class User extends ObjectModel {
@override @override
String? id; final String? id;
String? name; final String? name;
String? email; final String? email;
String? phone; final String? phone;
User({ const User({
required this.name, required this.name,
required this.email, required this.email,
required this.phone, required this.phone,
this.id, this.id,
}); });
// User._();
// factory User.parser() {
// return User._();
// }
// @override
// User? from(DocumentSnapshot? object) {
// if (object == null) return null;
// if (object.exists) {
// return User(
// id: object.id,
// name: (object.data() as Map<String, dynamic>?)!['name'] as String,
// email: (object.data() as Map<String, dynamic>?)!['email'] as String,
// phone: (object.data() as Map<String, dynamic>?)!['phone'] as String,
// );
// }
// return null;
// }
Map<String, Object> toMap() { Map<String, Object> toMap() {
return { return {

View File

@ -17,32 +17,25 @@
import 'package:crud_bloc_example/models.dart'; import 'package:crud_bloc_example/models.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart'; import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
/// A [CrudCubit] for [User].
class UserCubit extends CrudCubit<User> { class UserCubit extends CrudCubit<User> {
final CrudRepository<User> _crudRepository; final CrudRepository<User> _crudRepository;
UserCubit(this._crudRepository); UserCubit(this._crudRepository);
@override @override
Create<User>? get crudCreate => Create(_crudRepository); CreateOperation<User, dynamic>? get createOperation =>
Create(_crudRepository);
@override @override
Delete<User>? get crudDelete => Delete(_crudRepository); DeleteOperation<User, dynamic>? get deleteOperation =>
Delete(_crudRepository);
@override @override
DeleteAll<User>? get crudDeleteAll => DeleteAll(_crudRepository); ReadOperation<User, dynamic, dynamic>? get readOperation =>
GetAll(_crudRepository);
@override @override
Get<User>? get crudGet => Get(_crudRepository); UpdateOperation<User, dynamic>? get updateOperation =>
Update(_crudRepository);
@override
GetAll<User>? get crudGetAll => GetAll(_crudRepository);
@override
Query<User>? get crudQuery => Query(_crudRepository);
@override
Update<User>? get crudUpdate => Update(_crudRepository);
@override
UpdateAll<User>? get crudUpdateAll => UpdateAll(_crudRepository);
} }

View File

@ -15,3 +15,4 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'enums/where_query_type.dart'; export 'enums/where_query_type.dart';
export 'mixins/operation.dart';

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines different query types for WhereQuery.
enum WhereQueryType { enum WhereQueryType {
isEqualTo, isEqualTo,
isNotEqualTo, isNotEqualTo,

View File

@ -0,0 +1,30 @@
// Copyright (C) 2023 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:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
/// Defines every write operation in CRUD.
mixin CreateOperation<Model extends ObjectModel, Out> on AsyncUseCase<Model, Out> {}
/// Defines every read operation in CRUD.
mixin ReadOperation<Model extends ObjectModel, In, Out> on AsyncUseCase<In, Out> {}
/// Defines every update operation in CRUD.
mixin UpdateOperation<Model extends ObjectModel, In> on AsyncUseCase<In, void> {}
/// Defines every delete operation in CRUD.
mixin DeleteOperation<Model extends ObjectModel, In> on AsyncUseCase<In, void> {}

View File

@ -17,13 +17,17 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart'; import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/core/extensions/num_extension.dart'; import 'package:wyatt_crud_bloc/src/domain/data_sources/data_sources.dart';
import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
/// {@template crud_in_memory_data_source_impl}
/// A [CrudDataSource] that stores data in memory.
/// {@endtemplate}
class CrudInMemoryDataSourceImpl<Model extends ObjectModel> class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
extends CrudDataSource<Model> { extends CrudDataSource<Model> {
/// {@macro crud_in_memory_data_source_impl}
CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data}) CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data})
: _data = data ?? {}; : _data = data ?? {};
final Map<String, Model> _data; final Map<String, Model> _data;

View File

@ -20,15 +20,23 @@ import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_firestore_data_source_impl}
/// A concrete implementation of [CrudDataSource] that uses
/// [FirebaseFirestore] as the data source.
/// {@endtemplate}
class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity> class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity>
extends CrudDataSource<Model> { extends CrudDataSource<Model> {
/// {@macro crud_firestore_data_source_impl}
CrudFirestoreDataSourceImpl( CrudFirestoreDataSourceImpl(
String collection, { String collection, {
/// The function that converts a [DocumentSnapshot] to a [Model].
required Model Function( required Model Function(
DocumentSnapshot<Map<String, dynamic>>, DocumentSnapshot<Map<String, dynamic>>,
SnapshotOptions?, SnapshotOptions?,
) )
fromFirestore, fromFirestore,
/// The function that converts a [Model] to a [Map<String, Object?>].
required Map<String, Object?> Function(Model, SetOptions?) toFirestore, required Map<String, Object?> Function(Model, SetOptions?) toFirestore,
FirebaseFirestore? firestore, FirebaseFirestore? firestore,
}) : _firestore = firestore ?? FirebaseFirestore.instance, }) : _firestore = firestore ?? FirebaseFirestore.instance,

View File

@ -21,9 +21,13 @@ import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
/// {@template crud_repository_impl}
/// A repository that implements the [CrudRepository] interface.
/// {@endtemplate}
class CrudRepositoryImpl<Model extends ObjectModel> class CrudRepositoryImpl<Model extends ObjectModel>
extends CrudRepository<Model> { extends CrudRepository<Model> {
CrudRepositoryImpl({ /// {@macro crud_repository_impl}
const CrudRepositoryImpl({
required CrudDataSource<Model> crudDataSource, required CrudDataSource<Model> crudDataSource,
}) : _crudDataSource = crudDataSource; }) : _crudDataSource = crudDataSource;
final CrudDataSource<Model> _crudDataSource; final CrudDataSource<Model> _crudDataSource;
@ -99,6 +103,6 @@ class CrudRepositoryImpl<Model extends ObjectModel>
if (lst.isNotNull) { if (lst.isNotNull) {
return Ok<List<Model?>, AppException>(lst); return Ok<List<Model?>, AppException>(lst);
} }
return Err<List<Model?>, AppException>(ServerException()); return Err<List<Model?>, AppException>(const ServerException());
}); });
} }

View File

@ -17,27 +17,42 @@
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_data_source}
/// A [BaseDataSource] that provides SCRUD operations.
/// {@endtemplate}
abstract class CrudDataSource<Model> extends BaseDataSource { abstract class CrudDataSource<Model> extends BaseDataSource {
/// {@macro crud_data_source}
const CrudDataSource();
/// Creates a new [Model] object.
Future<void> create(Model object, {String? id}); Future<void> create(Model object, {String? id});
/// Gets a [Model] object by its [id].
Future<Model?> get(String id); Future<Model?> get(String id);
/// Gets all [Model] objects.
Future<List<Model?>> getAll(); Future<List<Model?>> getAll();
/// Updates a [Model] object by its [id].
Future<void> update( Future<void> update(
String id, { String id, {
Model? object, Model? object,
Map<String, dynamic>? raw, Map<String, dynamic>? raw,
}); });
/// Updates all [Model] objects.
Future<void> updateAll(Map<String, Object?>? data); Future<void> updateAll(Map<String, Object?>? data);
/// Deletes a [Model] object by its [id].
Future<void> delete(String id); Future<void> delete(String id);
/// Deletes all [Model] objects.
Future<void> deleteAll(); Future<void> deleteAll();
/// Queries [Model] objects by [conditions].
Future<List<Model?>> query(List<QueryInterface> conditions); Future<List<Model?>> query(List<QueryInterface> conditions);
/// Streams [Model] objects by [conditions].
Stream<List<Model?>> stream({ Stream<List<Model?>> stream({
String? id, String? id,
List<QueryInterface>? conditions, List<QueryInterface>? conditions,

View File

@ -16,6 +16,13 @@
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
/// {@template object_model}
/// An abstract class that represents an object model.
/// {@endtemplate}
abstract class ObjectModel extends Entity { abstract class ObjectModel extends Entity {
/// {@macro object_model}
const ObjectModel();
/// The id of the object model.
String? get id; String? get id;
} }

View File

@ -17,27 +17,59 @@
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart'; import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
// ignore: one_member_abstracts // // ignore: one_member_abstracts
abstract class QueryParser<Q> { // abstract class QueryParser<Q> {
Q parser(QueryInterface condition, Q query); // Q parser(QueryInterface condition, Q query);
// }
typedef QueryParser<Q> = Q Function(QueryInterface condition, Q query);
/// {@template query}
/// An abstract class that represents a query.
/// {@endtemplate}
abstract class QueryInterface extends Entity {
/// {@macro query}
const QueryInterface();
} }
abstract class QueryInterface extends Entity {} /// {@template where_query}
/// Represents a where query.
/// {@endtemplate}
class WhereQuery<Value> extends QueryInterface { class WhereQuery<Value> extends QueryInterface {
WhereQuery(this.type, this.field, this.value); /// {@macro where_query}
const WhereQuery(this.type, this.field, this.value);
/// The type of the where query.
final WhereQueryType type; final WhereQueryType type;
/// The field of the where query.
final String field; final String field;
/// The value of the where query.
final Value value; final Value value;
} }
/// {@template limit_query}
/// Represents a limit query.
/// {@endtemplate}
class LimitQuery extends QueryInterface { class LimitQuery extends QueryInterface {
LimitQuery(this.limit); /// {@macro limit_query}
const LimitQuery(this.limit);
/// The limit of the limit query.
final int limit; final int limit;
} }
/// {@template offset_query}
/// Represents an offset query.
/// {@endtemplate}
class OrderByQuery extends QueryInterface { class OrderByQuery extends QueryInterface {
OrderByQuery(this.field, {this.ascending = true}); /// {@macro offset_query}
const OrderByQuery(this.field, {this.ascending = true});
/// The field of the order by query.
final String field; final String field;
/// The ascending of the order by query.
final bool ascending; final bool ascending;
} }

View File

@ -18,20 +18,43 @@ import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_repository}
/// An abstract class that represents a SCRUD repository.
/// {@endtemplate}
abstract class CrudRepository<Model extends ObjectModel> abstract class CrudRepository<Model extends ObjectModel>
extends BaseRepository { extends BaseRepository {
/// {@macro crud_repository}
const CrudRepository();
/// Creates a new object.
FutureOrResult<void> create(Model object, {String? id}); FutureOrResult<void> create(Model object, {String? id});
/// Gets an object by its [id].
FutureOrResult<Model?> get(String id); FutureOrResult<Model?> get(String id);
/// Gets all objects.
FutureOrResult<List<Model?>> getAll(); FutureOrResult<List<Model?>> getAll();
/// Updates an object by its [id].
FutureOrResult<void> update( FutureOrResult<void> update(
String id, { String id, {
Model? object, Model? object,
Map<String, dynamic>? raw, Map<String, dynamic>? raw,
}); });
/// Updates all objects.
FutureOrResult<void> updateAll(Map<String, Object?> raw); FutureOrResult<void> updateAll(Map<String, Object?> raw);
/// Deletes an object by its [id].
FutureOrResult<void> delete(String id); FutureOrResult<void> delete(String id);
/// Deletes all objects.
FutureOrResult<void> deleteAll(); FutureOrResult<void> deleteAll();
/// Queries objects by [conditions].
FutureOrResult<List<Model?>> query(List<QueryInterface> conditions); FutureOrResult<List<Model?>> query(List<QueryInterface> conditions);
/// Streams objects by [conditions].
StreamResult<List<Model?>> stream({ StreamResult<List<Model?>> stream({
String? id, String? id,
List<QueryInterface>? conditions, List<QueryInterface>? conditions,

View File

@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP // Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details. // Please see the AUTHORS file for details.
// //
@ -18,13 +17,19 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Create<Model extends ObjectModel> extends AsyncUseCase<Model, void> { /// {@template create}
final CrudRepository<Model> _crudRepository; /// A use case that creates an object model.
/// {@endtemplate}
class Create<Model extends ObjectModel> extends AsyncUseCase<Model, void>
with CreateOperation<Model, void> {
/// {@macro create}
const Create(this._crudRepository);
Create(this._crudRepository); final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(Model? params) { FutureOr<void> onStart(Model? params) {

View File

@ -17,17 +17,24 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void> { /// {@template delete}
Delete(this._crudRepository); /// A use case that deletes an object model.
/// {@endtemplate}
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void>
with DeleteOperation<Model, String> {
/// {@macro delete}
const Delete(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(String? params) { FutureOr<void> onStart(String? params) {
if (params == null) { if (params == null) {
throw ClientException('Id cannot be null.'); throw const ClientException('Id cannot be null.');
} }
} }

View File

@ -15,11 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void> { /// {@template delete_all}
DeleteAll(this._crudRepository); /// A use case that deletes all the object models.
/// {@endtemplate}
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void>
with DeleteOperation<Model, void> {
/// {@macro delete_all}
const DeleteAll(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override

View File

@ -17,17 +17,24 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?> { /// {@template get}
/// A use case that gets an object model.
/// {@endtemplate}
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?>
with ReadOperation<Model, String, Model?> {
/// {@macro get}
Get(this._crudRepository); Get(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(String? params) { FutureOr<void> onStart(String? params) {
if (params == null) { if (params == null) {
throw ClientException('Id cannot be null.'); throw const ClientException('Id cannot be null.');
} }
} }

View File

@ -15,12 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class GetAll<Model extends ObjectModel> /// {@template get_all}
extends AsyncUseCase<void, List<Model?>> { /// A use case that gets all the object models.
GetAll(this._crudRepository); /// {@endtemplate}
class GetAll<Model extends ObjectModel> extends AsyncUseCase<void, List<Model?>>
with ReadOperation<Model, void, List<Model?>> {
/// {@macro get_all}
const GetAll(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override

View File

@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP // Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details. // Please see the AUTHORS file for details.
// //
@ -17,12 +16,19 @@
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template stream_parameters}
/// Represents the parameters for a query stream
/// {@endtemplate}
class StreamParameters { class StreamParameters {
final String? id; /// {@macro stream_parameters}
final List<QueryInterface>? conditions; const StreamParameters({
StreamParameters({
this.id, this.id,
this.conditions, this.conditions,
}); });
/// The id of the object model.
final String? id;
/// The conditions of the query.
final List<QueryInterface>? conditions;
} }

View File

@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP // Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details. // Please see the AUTHORS file for details.
// //
@ -15,14 +14,23 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
/// {@template update_parameters}
/// Represents the parameters for an update use case
/// {@endtemplate}
class UpdateParameters<Model> { class UpdateParameters<Model> {
final String id; /// {@macro update_parameters}
final Model? object; const UpdateParameters({
final Map<String, dynamic>? raw;
UpdateParameters({
required this.id, required this.id,
this.object, this.object,
this.raw, this.raw,
}); });
/// The id of the object model.
final String id;
/// The object model.
final Model? object;
/// The raw data.
final Map<String, dynamic>? raw;
} }

View File

@ -17,19 +17,26 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
/// {@template query}
/// A use case that queries the object models.
/// {@endtemplate}
class Query<Model extends ObjectModel> class Query<Model extends ObjectModel>
extends AsyncUseCase<List<QueryInterface>, List<Model?>> { extends AsyncUseCase<List<QueryInterface>, List<Model?>>
Query(this._crudRepository); with ReadOperation<Model, List<QueryInterface>, List<Model?>> {
/// {@macro query}
const Query(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(List<QueryInterface>? params) { FutureOr<void> onStart(List<QueryInterface>? params) {
if (params == null) { if (params == null) {
throw ClientException('List of conditions cannot be null.'); throw const ClientException('List of conditions cannot be null.');
} }
} }

View File

@ -1,44 +0,0 @@
// 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 Stream<Model extends ObjectModel> extends UseCase<StreamParameters,
//List<Model?>> {
// final CrudRepository<Model> _crudRepository;
// Stream(this._crudRepository);
// @override
// StreamResult<List<Model?>> call(StreamParameters params) =>
// _crudRepository.stream(id: params.id, conditions: params.conditions);
// }
// class StreamQuery<Model extends ObjectModel>
// extends StreamUseCase<StreamParameters, List<Model?>> {
// final CrudRepository<Model> _crudRepository;
// StreamQuery(this._crudRepository);
// @override
// FutureOr<void> onStart(StreamParameters? params) {
// if(params == null){
// throw ClientException('Stream parameters cannot be null.');
// }
// }
// @override
// FutureOrResult<Stream<List<Model?>>> call(StreamParameters? params) =>
// _crudRepository.stream();
// }

View File

@ -17,19 +17,26 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart'; import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
/// {@template update}
/// A use case that updates an object model.
/// {@endtemplate}
class Update<Model extends ObjectModel> class Update<Model extends ObjectModel>
extends AsyncUseCase<UpdateParameters<Model>, void> { extends AsyncUseCase<UpdateParameters<Model>, void>
Update(this._crudRepository); with UpdateOperation<Model, UpdateParameters<Model>> {
/// {@macro update}
const Update(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(UpdateParameters<Model>? params) { FutureOr<void> onStart(UpdateParameters<Model>? params) {
if (params == null) { if (params == null) {
throw ClientException('Update parameters cannot be null.'); throw const ClientException('Update parameters cannot be null.');
} }
} }

View File

@ -17,18 +17,25 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart'; import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
/// {@template update_all}
/// A use case that updates all the object models.
/// {@endtemplate}
class UpdateAll<Model extends ObjectModel> class UpdateAll<Model extends ObjectModel>
extends AsyncUseCase<Map<String, Object?>, void> { extends AsyncUseCase<Map<String, Object?>, void>
UpdateAll(this._crudRepository); with UpdateOperation<Model, Map<String, Object?>> {
/// {@macro update_all}
const UpdateAll(this._crudRepository);
final CrudRepository<Model> _crudRepository; final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(Map<String, Object?>? params) { FutureOr<void> onStart(Map<String, Object?>? params) {
if (params == null) { if (params == null) {
throw ClientException('Data cannot be null.'); throw const ClientException('Data cannot be null.');
} }
} }

View File

@ -21,6 +21,5 @@ export 'get.dart';
export 'get_all.dart'; export 'get_all.dart';
export 'params/params.dart'; export 'params/params.dart';
export 'query.dart'; export 'query.dart';
export 'stream_query.dart';
export 'update.dart'; export 'update.dart';
export 'update_all.dart'; export 'update_all.dart';

View File

@ -0,0 +1,305 @@
// Copyright (C) 2023 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:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_cubit_advanced}
/// Cubit that handles CRUD operations with more granularity.
/// {@endtemplate}
abstract class CrudAdvancedCubit<Model extends ObjectModel>
extends CrudBaseCubit {
/// {@macro crud_cubit}
CrudAdvancedCubit() : super();
Create<Model>? get crudCreate;
DeleteAll<Model>? get crudDeleteAll;
Delete<Model>? get crudDelete;
GetAll<Model>? get crudGetAll;
Get<Model>? get crudGet;
Query<Model>? get crudQuery;
UpdateAll<Model>? get crudUpdateAll;
Update<Model>? get crudUpdate;
FutureOr<void> create(Model model) async {
final crud = crudCreate;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data == null) {
return CrudLoaded<Model?>(model);
}
if (stateCopy.data!.id == model.id) {
return CrudLoaded<Model?>(model);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> delete(String id) async {
final crud = crudDelete;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == id) {
return CrudLoaded<Model?>(null);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> deleteAll() async {
final crud = crudDeleteAll;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> get(String id) async {
final crud = crudGet;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> getAll() async {
final crud = crudGetAll;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> query(List<QueryInterface> conditions) async {
final crud = crudQuery;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> update(UpdateParameters<Model> param) async {
final crud = crudUpdate;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
FutureOr<void> updateAll(Map<String, Object?> param) async {
final crud = crudUpdateAll;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final crudQuery = this.crudQuery;
if (crudQuery == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}

View File

@ -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:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'crud_state.dart';
/// {@template crud_base_cubit}
/// A base [Cubit] that handles SCRUD operations.
/// {@endtemplate}
abstract class CrudBaseCubit extends Cubit<CrudState> {
/// {@macro crud_base_cubit}
CrudBaseCubit() : super(const CrudInitial());
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2022 WYATT GROUP // Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details. // Please see the AUTHORS file for details.
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'crud_cubit.dart'; part of 'crud_base_cubit.dart';
abstract class CrudState extends Equatable { abstract class CrudState extends Equatable {
const CrudState(); const CrudState();
@ -23,18 +23,17 @@ abstract class CrudState extends Equatable {
List<Object?> get props => []; List<Object?> get props => [];
} }
class CrudInitial extends CrudState {} /// Initial state of the CrudBaseCubit.
class CrudInitial extends CrudState {
class CrudLoading extends CrudState {} const CrudInitial();
abstract class CrudSuccess extends CrudState {
const CrudSuccess();
} }
class CrudOkReturn extends CrudState { /// Loading state of the CrudBaseCubit.
const CrudOkReturn(); class CrudLoading extends CrudState {
const CrudLoading();
} }
/// Error state of the CrudBaseCubit.
class CrudError extends CrudState { class CrudError extends CrudState {
const CrudError(this.message); const CrudError(this.message);
final String? message; final String? message;
@ -43,6 +42,24 @@ class CrudError extends CrudState {
List<Object?> get props => [message]; List<Object?> get props => [message];
} }
/// Success state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Can be one or list of objects.
abstract class CrudSuccess extends CrudState {
const CrudSuccess();
}
/// Success state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains no objects.
/// Used for create, update, delete operations.
class CrudOkReturn extends CrudSuccess {
const CrudOkReturn();
}
/// Loaded state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains one object.
class CrudLoaded<T> extends CrudSuccess { class CrudLoaded<T> extends CrudSuccess {
const CrudLoaded(this.data); const CrudLoaded(this.data);
final T? data; final T? data;
@ -51,6 +68,9 @@ class CrudLoaded<T> extends CrudSuccess {
List<Object?> get props => [data]; List<Object?> get props => [data];
} }
/// Loaded state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains list of objects.
class CrudListLoaded<T> extends CrudSuccess { class CrudListLoaded<T> extends CrudSuccess {
const CrudListLoaded(this.data); const CrudListLoaded(this.data);
final List<T?> data; final List<T?> data;

View File

@ -0,0 +1,369 @@
// Copyright (C) 2023 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:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_cubit}
/// Cubit that handles CRUD operations.
/// {@endtemplate}
abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
/// {@macro crud_cubit}
CrudCubit() : super();
/// Create operation.
/// Can be create.
CreateOperation<Model, dynamic>? get createOperation;
/// Read operation.
/// Can be get, getAll, query.
ReadOperation<Model, dynamic, dynamic>? get readOperation;
/// Update operation.
/// Can be update, updateAll.
UpdateOperation<Model, dynamic>? get updateOperation;
/// Delete operation.
/// Can be delete or deleteAll.
DeleteOperation<Model, dynamic>? get deleteOperation;
Expected? _checkOperation<Expected>(dynamic operation) {
if (operation == null) {
return null;
}
if (operation is! Expected) {
return null;
}
return operation;
}
FutureOr<void> create(Model model) async {
if (_checkOperation<Create<Model>>(createOperation) != null) {
return _create(model);
}
}
FutureOr<void> read({String? id, List<QueryInterface>? conditions}) async {
if (_checkOperation<Get<Model>>(readOperation) != null && id != null) {
return _get(id);
}
if (_checkOperation<GetAll<Model>>(readOperation) != null) {
return _getAll();
}
if (_checkOperation<Query<Model>>(readOperation) != null &&
conditions != null) {
return _query(conditions);
}
if (_checkOperation<Query<Model>>(readOperation) != null &&
conditions == null) {
return _getAll();
}
}
FutureOr<void> update(
UpdateParameters<Model>? single,
Map<String, dynamic>? all,
) async {
if (_checkOperation<Update<Model>>(updateOperation) != null &&
single != null) {
return _update(single);
}
if (_checkOperation<UpdateAll<Model>>(updateOperation) != null &&
all != null) {
return _updateAll(all);
}
}
FutureOr<void> delete({String? id}) async {
if (_checkOperation<Delete<Model>>(deleteOperation) != null && id != null) {
return _delete(id);
}
if (_checkOperation<DeleteAll<Model>>(deleteOperation) != null) {
return _deleteAll();
}
}
FutureOr<void> _create(Model model) async {
final crud = _checkOperation<Create<Model>>(createOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data == null) {
return CrudLoaded<Model?>(model);
}
if (stateCopy.data!.id == model.id) {
return CrudLoaded<Model?>(model);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _delete(String id) async {
final crud = _checkOperation<Delete<Model>>(deleteOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == id) {
return CrudLoaded<Model?>(null);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _deleteAll() async {
final crud = _checkOperation<DeleteAll<Model>>(deleteOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _get(String id) async {
final crud = _checkOperation<Get<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _getAll() async {
final crud = _checkOperation<GetAll<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _query(List<QueryInterface> conditions) async {
final crud = _checkOperation<Query<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _update(UpdateParameters<Model> param) async {
final crud = _checkOperation<Update<Model>>(updateOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
FutureOr<void> _updateAll(Map<String, Object?> param) async {
final crud = _checkOperation<UpdateAll<Model>>(updateOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final crudQuery = _checkOperation<Query<Model>>(readOperation);
if (crudQuery == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}

View File

@ -15,15 +15,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wyatt_crud_bloc/src/features/crud/cubit/crud_cubit.dart'; import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_builder}
/// A widget that builds itself based on the latest snapshot of interaction
/// with a [CrudBaseCubit].
///
/// * I = Initial State
/// * L = Loading State
/// * S = Success State
/// * E = Error State
/// {@endtemplate}
class CrudBuilder<I, L, S, E> extends StatelessWidget { class CrudBuilder<I, L, S, E> extends StatelessWidget {
/// `<I, L, S, E>` /// {@macro crud_builder}
///
/// - I: the Initial State
/// - L: the Loading State
/// - S: the Success State
/// - E: the Error State
const CrudBuilder({ const CrudBuilder({
required this.state, required this.state,
required this.builder, required this.builder,
@ -34,11 +38,11 @@ class CrudBuilder<I, L, S, E> extends StatelessWidget {
super.key, super.key,
}); });
/// `<CrudInitial, CrudLoading, S extends CrudSuccess, CrudError>` /// {@macro crud_builder}
/// ///
/// - S: the Success State /// This factory constructor is used to create a [CrudBuilder] with
/// /// [CrudState]s. `S` is the Success State, and it must be a subtype of
/// For CrudStates only. /// [CrudSuccess]. It the only type that you have to specify.
static CrudBuilder<CrudInitial, CrudLoading, CrudSuccess, CrudError> static CrudBuilder<CrudInitial, CrudLoading, CrudSuccess, CrudError>
typed<S extends CrudSuccess>({ typed<S extends CrudSuccess>({
required CrudState state, required CrudState state,

View File

@ -14,5 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'blocs/crud_advanced_cubit.dart';
export 'blocs/crud_base_cubit/crud_base_cubit.dart';
export 'blocs/crud_cubit.dart';
export 'builder/builder.dart'; export 'builder/builder.dart';
export 'cubit/crud_cubit.dart';

View File

@ -1,266 +0,0 @@
// 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:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
part 'crud_state.dart';
abstract class CrudCubit<Model extends ObjectModel> extends Cubit<CrudState> {
CrudCubit() : super(CrudInitial());
Create<Model>? get crudCreate;
DeleteAll<Model>? get crudDeleteAll;
Delete<Model>? get crudDelete;
GetAll<Model>? get crudGetAll;
Get<Model>? get crudGet;
Query<Model>? get crudQuery;
UpdateAll<Model>? get crudUpdateAll;
Update<Model>? get crudUpdate;
FutureOr<void> create(Model model) async {
if (crudCreate != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudCreate!.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> delete(String id) async {
if (crudDelete != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudDelete!.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> deleteAll() async {
if (crudDeleteAll != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudDeleteAll!.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> get(String id) async {
if (crudGet != null) {
emit(CrudLoading());
final result = await crudGet!.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> getAll() async {
if (crudGetAll != null) {
emit(CrudLoading());
final result = await crudGetAll!.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> query(List<QueryInterface> conditions) async {
if (crudQuery != null) {
emit(CrudLoading());
final result = await crudQuery!.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> update(UpdateParameters<Model> param) async {
if (crudUpdate != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudUpdate!.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use update.',
);
}
final newVersion = await crudGet!.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use update.',
);
}
final newVersion = await crudGet!.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
FutureOr<void> updateAll(Map<String, Object?> param) async {
if (crudUpdateAll != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudUpdateAll!.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use updateAll.',
);
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet!.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (crudQuery == null) {
throw ClientException(
'Need to init Query usecase to use updateAll.',
);
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery!.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
}

View File

@ -10,8 +10,7 @@ environment:
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter: { sdk: flutter }
sdk: flutter
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
equatable: ^2.0.5 equatable: ^2.0.5
@ -26,9 +25,8 @@ dependencies:
version: ^0.0.4 version: ^0.0.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: { sdk: flutter }
sdk: flutter
bloc_test: ^9.1.0 bloc_test: ^9.1.0
wyatt_analysis: wyatt_analysis:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
version: 2.4.1 version: ^2.4.1

View File

@ -1,2 +0,0 @@
firebase_options.dart
.vscode

View File

@ -0,0 +1 @@
../../.pubignore

View File

@ -16,12 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Dart - HTTP Client # HTTP Client
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
</a>
<img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" />
</p> </p>
@ -52,13 +50,13 @@ For example, if you want to log every request, and simplify an url you can use p
```dart ```dart
// Create the Pipeline // Create the Pipeline
final Pipeline pipeline = Pipeline() final Pipeline pipeline = Pipeline()
.addMiddleware( ..addMiddleware(
UriPrefixMiddleware( const UriPrefixMiddleware(
protocol: Protocols.http, protocol: Protocols.http,
authority: 'localhost:80', authority: 'localhost:80',
), ),
) )
.addMiddleware(SimpleLoggerMiddleware()); ..addMiddleware(const SimpleLoggerMiddleware());
``` ```
Then if you print the pipeline, Then if you print the pipeline,
@ -94,20 +92,20 @@ Let's start by creating the Pipeline:
```dart ```dart
final Pipeline pipeline = Pipeline() final Pipeline pipeline = Pipeline()
.addMiddleware( ..addMiddleware(
UriPrefixMiddleware( const UriPrefixMiddleware(
protocol: Protocols.http, protocol: Protocols.http,
authority: 'localhost:80', authority: 'localhost:80',
), ),
) )
.addMiddleware(BodyToJsonMiddleware()) ..addMiddleware(const BodyToJsonMiddleware())
.addMiddleware( ..addMiddleware(
UnsafeAuthMiddleware( const UnsafeAuthMiddleware(
username: 'wyatt', username: 'wyatt',
password: 'motdepasse', password: 'motdepasse',
), ),
) )
.addMiddleware(SimpleLoggerMiddleware()); ..addMiddleware(SimpleLoggerMiddleware());
``` ```
Then simply create a client and make a call. Then simply create a client and make a call.
@ -128,14 +126,14 @@ So now we want a real authentication.
```dart ```dart
final Pipeline pipeline = Pipeline() final Pipeline pipeline = Pipeline()
.addMiddleware( ..addMiddleware(
UriPrefixMiddleware( const UriPrefixMiddleware(
protocol: Protocols.http, protocol: Protocols.http,
authority: 'localhost:80', authority: 'localhost:80',
), ),
) )
.addMiddleware(BodyToJsonMiddleware()) ..addMiddleware(const BodyToJsonMiddleware())
.addMiddleware( ..addMiddleware(
RefreshTokenAuthMiddleware( RefreshTokenAuthMiddleware(
authorizationEndpoint: '/auth/sign-in', authorizationEndpoint: '/auth/sign-in',
tokenEndpoint: '/auth/refresh', tokenEndpoint: '/auth/refresh',
@ -144,7 +142,7 @@ final Pipeline pipeline = Pipeline()
unauthorized: HttpStatus.forbidden, unauthorized: HttpStatus.forbidden,
), ),
) )
.addMiddleware(SimpleLoggerMiddleware()); ..addMiddleware(const SimpleLoggerMiddleware());
``` ```
> Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system. > Here we just change `UnsafeAuthMiddleware` by `RefreshTokenAuthMiddleware` and the whole app while adapt to a new authentication system.
@ -158,6 +156,8 @@ class SimpleLoggerMiddleware
with OnRequestMiddleware, OnResponseMiddleware with OnRequestMiddleware, OnResponseMiddleware
implements Middleware { implements Middleware {
const SimpleLoggerMiddleware();
@override @override
String getName() => 'SimpleLogger'; String getName() => 'SimpleLogger';

View File

@ -0,0 +1,78 @@
// Copyright (C) 2023 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:wyatt_http_client/wyatt_http_client.dart';
Future<void> testSimpleGet() async {
print('testSimpleGet');
final pipeline = Pipeline()
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response = await client
.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
print(response.body);
}
Future<void> testUriPrefix() async {
print('testUriPrefix');
final pipeline = Pipeline()
..addMiddleware(
const UriPrefixMiddleware(
protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response = await client.get(Uri.parse('/todos/1'));
print(response.body);
}
Future<void> testBasicAuth() async {
print('testBasicAuth');
final pipeline = Pipeline()
..addMiddleware(
const BasicAuthMiddleware(
username: 'guest',
password: 'guest',
),
)
..addMiddleware(const BodyToJsonMiddleware())
..addMiddleware(const SimpleLoggerMiddleware());
final client = MiddlewareClient(pipeline: pipeline);
final response =
await client.get(Uri.parse('https://jigsaw.w3.org/HTTP/Basic/'));
if (HttpStatus.from(response.statusCode).isSuccess()) {
print("🎉 You're in!");
} else {
print("⭕️ Nope, you're not in.");
}
}
void main(List<String> args) {
testSimpleGet();
testUriPrefix();
testBasicAuth();
}

View File

@ -1,294 +0,0 @@
// 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 'dart:io';
import 'package:wyatt_http_client/wyatt_http_client.dart';
String lastToken = '';
int token = 0;
void printAuth(HttpRequest req) {
print(
'Authorization => '
"${req.headers.value('Authorization') ?? 'no authorization header'}",
);
}
Future<void> handleBasic(HttpRequest req) async {
printAuth(req);
}
Future<void> handleBasicNegotiate(HttpRequest req) async {
if (req.headers.value('Authorization') == null) {
req.response.statusCode = HttpStatus.unauthorized.statusCode;
req.response.headers.set(HeaderKeys.wwwAuthenticate, 'Basic realm="Wyatt"');
print(req.response.headers.value('WWW-Authenticate'));
return req.response.close();
}
printAuth(req);
}
Future<void> handleBearer(HttpRequest req) async {
printAuth(req);
}
Future<void> handleDigest(HttpRequest req) async {
if (req.headers.value('Authorization') == null) {
req.response.statusCode = HttpStatus.unauthorized.statusCode;
req.response.headers.set(
'WWW-Authenticate',
'Digest realm="Wyatt", '
'qop="auth,auth-int", '
'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", '
'opaque="5ccc069c403ebaf9f0171e9517f40e41"',
);
print(req.response.headers.value('WWW-Authenticate'));
return req.response.close();
}
printAuth(req);
}
Future<void> handleUnsafe(HttpRequest req) async {
print(
'Query parameters => '
'${req.uri.queryParameters}',
);
}
Future<void> handleOauth2RefreshToken(HttpRequest req) async {
final action = req.uri.queryParameters['action'];
if (action == null) {
printAuth(req);
}
switch (action) {
case 'login':
if (req.method == 'POST') {
token++;
req.response.write(
'{"accessToken": "access-token-awesome$token", '
'"refreshToken": "refresh-token-awesome$token"}',
);
}
break;
case 'refresh':
printAuth(req);
if (req.method == 'GET') {
token++;
req.response.write('{"accessToken": "access-token-refreshed$token"}');
}
break;
case 'access-denied':
final String receivedToken = req.headers.value('Authorization') ?? '';
if (receivedToken != '' &&
lastToken != '' &&
receivedToken != lastToken) {
lastToken = receivedToken;
printAuth(req);
return req.response.close();
} else {
lastToken = receivedToken;
req.response.statusCode = HttpStatus.unauthorized.statusCode;
return req.response.close();
}
default:
break;
}
}
Future<void> server() async {
final server = await HttpServer.bind(InternetAddress.anyIPv6, 8080);
var error = 0;
var token = 0;
await server.forEach((request) {
print('[${request.method}] ${request.uri}');
switch (request.uri.path) {
case '/test/basic-test':
handleBasic(request);
break;
case '/test/basic-test-with-negotiate':
handleBasicNegotiate(request);
break;
case '/test/digest-test':
handleDigest(request);
break;
case '/test/apikey-test':
handleBearer(request);
break;
case '/test/bearer-test':
handleBearer(request);
break;
case '/test/unsafe-test':
handleUnsafe(request);
break;
case '/test/oauth2-test':
handleOauth2RefreshToken(request);
break;
case '/test/bearer-login':
if (request.method == 'POST') {
request.response.write('{"token": "access-token-test"}');
}
break;
case '/test/oauth2-test-error':
error++;
print('Error $error');
if (error >= 3) {
print('Authorized');
error = 0;
} else {
request.response.statusCode = HttpStatus.unauthorized.statusCode;
}
break;
case '/test/oauth2-test-timeout':
error++;
print('Error $error');
request.response.statusCode = HttpStatus.unauthorized.statusCode;
break;
case '/test/oauth2-login':
if (request.method == 'POST') {
token++;
request.response.write(
'{"accessToken": "access-token-awesome$token", '
'"refreshToken": "refresh-token-awesome$token"}',
);
}
break;
case '/test/oauth2-refresh':
print(
'Authorization => '
"${request.headers.value('Authorization') ?? 'no refresh token'}",
);
if (request.method == 'GET') {
token++;
request.response
.write('{"accessToken": "access-token-refreshed$token"}');
}
break;
case '/test/oauth2-refresh-error':
request.response.statusCode = HttpStatus.unauthorized.statusCode;
break;
default:
print(' => Unknown path or method');
request.response.statusCode = HttpStatus.notFound.statusCode;
}
request.response.close();
print('====================');
});
}
Future<void> main() async {
unawaited(server());
const base = 'localhost:8080';
final uriPrefix = UriPrefixMiddleware(
protocol: Protocols.http,
authority: base,
);
final jsonEncoder = BodyToJsonMiddleware();
final logger = SimpleLoggerMiddleware();
// Basic
final basicAuth = BasicAuthMiddleware(
username: 'username',
password: 'password',
);
final basic = MiddlewareClient(
pipeline: Pipeline.fromIterable([
uriPrefix,
basicAuth,
logger,
]),
);
await basic.get(Uri.parse('/test/basic-test'));
// Digest
final digestAuth = DigestAuthMiddleware(
username: 'Mufasa',
password: 'Circle Of Life',
);
final digest = MiddlewareClient(
pipeline: Pipeline.fromIterable([
uriPrefix,
digestAuth,
logger,
]),
);
await digest.get(Uri.parse('/test/digest-test'));
// // Bearer
// final bearer = BearerAuthenticationClient(
// token: 'access-token-test',
// inner: restClient,
// );
// await bearer.get(Uri.parse('/test/bearer-test'));
// // API Key
// final apiKey = BearerAuthenticationClient(
// token: 'awesome-api-key',
// authenticationMethod: 'ApiKey',
// inner: restClient,
// );
// await apiKey.get(Uri.parse('/test/apikey-test'));
// Unsafe URL
final unsafeAuth = UnsafeAuthMiddleware(
username: 'Mufasa',
password: 'Circle Of Life',
);
final unsafe = MiddlewareClient(
pipeline: Pipeline.fromIterable([
uriPrefix,
unsafeAuth,
logger,
]),
);
await unsafe.get(Uri.parse('/test/unsafe-test'));
// OAuth2
final refreshTokenAuth = RefreshTokenAuthMiddleware(
authorizationEndpoint: '/test/oauth2-test?action=login',
tokenEndpoint: '/test/oauth2-test?action=refresh',
accessTokenParser: (body) => body['accessToken']! as String,
refreshTokenParser: (body) => body['refreshToken']! as String,
);
final refreshToken = MiddlewareClient(
pipeline: Pipeline.fromIterable([
uriPrefix,
jsonEncoder,
refreshTokenAuth,
logger,
]),
);
await refreshToken.get(Uri.parse('/test/oauth2-test'));
// Login
await refreshToken.post(
Uri.parse('/test/oauth2-test'),
body: <String, String>{
'username': 'username',
'password': 'password',
},
);
await refreshToken.get(Uri.parse('/test/oauth2-test'));
// await refreshToken.refresh();
// await refreshToken.get(Uri.parse('/test/oauth2-test'));
// await refreshToken.get(Uri.parse('/test/oauth2-test?action=access-denied'));
exit(0);
}

View File

@ -1,371 +0,0 @@
// 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/>.
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'package:wyatt_http_client/src/middleware_client.dart';
import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/refresh_token_auth_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart';
import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/http_status.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart';
enum EmailVerificationAction {
signUp,
resetPassword,
changeEmail;
String toSnakeCase() => name.splitMapJoin(
RegExp('[A-Z]'),
onMatch: (m) => '_${m[0]?.toLowerCase()}',
onNonMatch: (n) => n,
);
factory EmailVerificationAction.fromString(String str) =>
EmailVerificationAction.values.firstWhere(
(element) => element.toSnakeCase() == str,
);
}
class VerifyCode {
final String email;
final String verificationCode;
final EmailVerificationAction action;
VerifyCode({
required this.email,
required this.verificationCode,
required this.action,
});
VerifyCode copyWith({
String? email,
String? verificationCode,
EmailVerificationAction? action,
}) =>
VerifyCode(
email: email ?? this.email,
verificationCode: verificationCode ?? this.verificationCode,
action: action ?? this.action,
);
Map<String, dynamic> toMap() => <String, dynamic>{
'email': email,
'verification_code': verificationCode,
'action': action.toSnakeCase(),
};
factory VerifyCode.fromMap(Map<String, dynamic> map) => VerifyCode(
email: map['email'] as String,
verificationCode: map['verification_code'] as String,
action: EmailVerificationAction.fromString(map['action'] as String),
);
String toJson() => json.encode(toMap());
factory VerifyCode.fromJson(String source) =>
VerifyCode.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'VerifyCode(email: $email, verificationCode: '
'$verificationCode, action: $action)';
}
class Account {
final String email;
final String? sessionId;
Account({
required this.email,
this.sessionId,
});
Account copyWith({
String? email,
String? sessionId,
}) =>
Account(
email: email ?? this.email,
sessionId: sessionId ?? this.sessionId,
);
Map<String, dynamic> toMap() => <String, dynamic>{
'email': email,
'session_id': sessionId,
};
factory Account.fromMap(Map<String, dynamic> map) => Account(
email: map['email'] as String,
sessionId:
map['session_id'] != null ? map['session_id'] as String : null,
);
String toJson() => json.encode(toMap());
factory Account.fromJson(String source) =>
Account.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Account(email: $email, sessionId: $sessionId)';
}
class SignUp {
final String sessionId;
final String password;
SignUp({
required this.sessionId,
required this.password,
});
SignUp copyWith({
String? sessionId,
String? password,
}) =>
SignUp(
sessionId: sessionId ?? this.sessionId,
password: password ?? this.password,
);
Map<String, dynamic> toMap() => <String, dynamic>{
'session_id': sessionId,
'password': password,
};
factory SignUp.fromMap(Map<String, dynamic> map) => SignUp(
sessionId: map['session_id'] as String,
password: map['password'] as String,
);
String toJson() => json.encode(toMap());
factory SignUp.fromJson(String source) =>
SignUp.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'SignUp(sessionId: $sessionId, password: $password)';
}
class TokenSuccess {
final String accessToken;
final String refreshToken;
final Account account;
TokenSuccess({
required this.accessToken,
required this.refreshToken,
required this.account,
});
TokenSuccess copyWith({
String? accessToken,
String? refreshToken,
Account? account,
}) =>
TokenSuccess(
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
account: account ?? this.account,
);
Map<String, dynamic> toMap() => <String, dynamic>{
'access_token': accessToken,
'refresh_token': refreshToken,
'account': account.toMap(),
};
factory TokenSuccess.fromMap(Map<String, dynamic> map) => TokenSuccess(
accessToken: map['access_token'] as String,
refreshToken: map['refresh_token'] as String,
account: Account.fromMap(map['account'] as Map<String, dynamic>),
);
String toJson() => json.encode(toMap());
factory TokenSuccess.fromJson(String source) =>
TokenSuccess.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'TokenSuccess(accessToken: $accessToken, refreshToken: '
'$refreshToken, account: $account)';
}
class Login {
final String email;
final String password;
Login({
required this.email,
required this.password,
});
Login copyWith({
String? email,
String? password,
}) =>
Login(
email: email ?? this.email,
password: password ?? this.password,
);
Map<String, dynamic> toMap() => <String, dynamic>{
'email': email,
'password': password,
};
factory Login.fromMap(Map<String, dynamic> map) => Login(
email: map['email'] as String,
password: map['password'] as String,
);
String toJson() => json.encode(toMap());
factory Login.fromJson(String source) =>
Login.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Login(email: $email, password: $password)';
}
class FastAPI {
final String baseUrl;
final MiddlewareClient client;
final int apiVersion;
FastAPI({
this.baseUrl = 'localhost:80',
MiddlewareClient? client,
this.apiVersion = 1,
}) : client = client ?? MiddlewareClient();
String get apiPath => '/api/v$apiVersion';
Future<void> sendSignUpCode(String email) async {
final r = await client.post(
Uri.parse('$apiPath/auth/send-sign-up-code'),
body: <String, String>{
'email': email,
},
);
if (r.statusCode != 201) {
throw Exception('Invalid reponse: ${r.statusCode}');
}
}
Future<Account> verifyCode(VerifyCode verifyCode) async {
final r = await client.post(
Uri.parse('$apiPath/auth/verify-code'),
body: verifyCode.toMap(),
);
if (r.statusCode != 202) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
return Account.fromMap(
(jsonDecode(r.body) as Map<String, dynamic>)['account']
as Map<String, dynamic>,
);
}
}
Future<Account> signUp(SignUp signUp) async {
final r = await client.post(
Uri.parse('$apiPath/auth/sign-up'),
body: signUp.toMap(),
);
if (r.statusCode != 201) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
return Account.fromJson(r.body);
}
}
Future<TokenSuccess> signInWithPassword(Login login) async {
final r = await client.post(
Uri.parse('$apiPath/auth/sign-in-with-password'),
body: login.toMap(),
);
if (r.statusCode != 200) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
return TokenSuccess.fromJson(r.body);
}
}
// Future<TokenSuccess> refresh() async {
// final r = await client.refresh();
// return TokenSuccess.fromJson(r?.body ?? '');
// }
Future<List<Account>> getAccountList() async {
final r = await client.get(
Uri.parse('$apiPath/account'),
);
if (r.statusCode != 200) {
throw Exception('Invalid reponse: ${r.statusCode}');
} else {
final list = (jsonDecode(r.body) as Map<String, dynamic>)['founds']
as List<Map<String, dynamic>>;
final result = <Account>[];
for (final element in list) {
result.add(Account.fromMap(element));
}
return result;
}
}
}
void main(List<String> args) async {
final Pipeline pipeline = Pipeline()
.addMiddleware(
UriPrefixMiddleware(
protocol: Protocols.http,
authority: 'localhost:80',
),
)
.addMiddleware(BodyToJsonMiddleware())
.addMiddleware(
RefreshTokenAuthMiddleware(
authorizationEndpoint: '/api/v1/auth/sign-in-with-password',
tokenEndpoint: '/api/v1/auth/refresh',
accessTokenParser: (body) => body['access_token']! as String,
refreshTokenParser: (body) => body['refresh_token']! as String,
unauthorized: HttpStatus.forbidden,
),
)
.addMiddleware(SimpleLoggerMiddleware());
print(pipeline);
final client = MiddlewareClient(pipeline: pipeline);
final api = FastAPI(
client: client,
);
await api.sendSignUpCode('git@pcl.ovh');
// final verifiedAccount = await api.verifyCode(
// VerifyCode(
// email: 'git@pcl.ovh',
// verificationCode: '000000000',
// action: EmailVerificationAction.signUp,
// ),
// );
// final registeredAccount = await api.signUp(
// SignUp(sessionId: verifiedAccount.sessionId ?? '', password: 'password'),
// );
// final signedInAccount = await api.signInWithPassword(
// Login(email: 'git@pcl.ovh', password: 'password'),
// );
final accountList = await api.getAccountList();
print(accountList);
}

View File

@ -1,164 +0,0 @@
// 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:wyatt_http_client/src/middleware_client.dart';
import 'package:wyatt_http_client/src/middlewares/body_to_json_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/simple_logger_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/unsafe_auth_middleware.dart';
import 'package:wyatt_http_client/src/middlewares/uri_prefix_middleware.dart';
import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart';
// class RequestMutatorMiddleware implements Middleware {
// @override
// Middleware? parent;
// @override
// Middleware? child;
// RequestMutatorMiddleware({
// this.parent,
// this.child,
// });
// @override
// BaseRequest onRequest(BaseRequest request) {
// print('RequestMutator::OnRequest: ${request.method} -> ${request.url}');
// return child?.onRequest(request) ?? request;
// }
// @override
// BaseResponse onResponse(BaseResponse response) {
// final res = child?.onResponse(response) ?? response;
// print(
// 'RequestMutator::OnResponse: ${res.statusCode} -> ${res.contentLength}
// bytes',
// );
// return res;
// }
// }
// typedef Middleware = Handler Function(Handler innerHandler);
// Middleware createMiddleware({
// FutureOr<Response?> Function(Request)? requestHandler,
// FutureOr<Response> Function(Response)? responseHandler,
// FutureOr<Response> Function(Object error, StackTrace)? errorHandler,
// }) {
// requestHandler ??= (request) => null;
// responseHandler ??= (response) => response;
// FutureOr<Response> Function(Object, StackTrace)? onError;
// if (errorHandler != null) {
// onError = (error, stackTrace) {
// if (error is Exception) throw error;
// return errorHandler(error, stackTrace);
// };
// }
// return (Handler innerHandler) {
// return (request) {
// return Future.sync(() => requestHandler!(request)).then((response) {
// if (response != null) return response;
// return Future.sync(() => innerHandler(request))
// .then((response) => responseHandler!(response), onError:
// onError);
// });
// };
// };
// }
// extension MiddlewareX on Middleware {
// Middleware addMiddleware(Middleware other) =>
// (Handler handler) => this(other(handler));
// Handler addHandler(Handler handler) => this(handler);
// }
// typedef Handler = FutureOr<Response> Function(Request request);
// final headerMutator = createMiddleware(
// responseHandler: (response) {
// print(response.headers);
// return response;
// },);
// class Pipeline {
// const Pipeline();
// Pipeline addMiddleware(Middleware middleware) =>
// _Pipeline(middleware, addHandler);
// Handler addHandler(Handler handler) => handler;
// Middleware get middleware => addHandler;
// }
// class _Pipeline extends Pipeline {
// final Middleware _middleware;
// final Middleware _parent;
// _Pipeline(this._middleware, this._parent);
// @override
// Handler addHandler(Handler handler) => _parent(_middleware(handler));
// }
Future<void> main(List<String> args) async {
final UnsafeAuthMiddleware auth = UnsafeAuthMiddleware();
final Pipeline pipeline = Pipeline()
.addMiddleware(
UriPrefixMiddleware(
protocol: Protocols.http,
authority: 'localhost:80',
),
)
.addMiddleware(BodyToJsonMiddleware())
.addMiddleware(
UnsafeAuthMiddleware(
username: 'wyatt',
password: 'motdepasse',
),
)
.addMiddleware(SimpleLoggerMiddleware());
// .addMiddleware(
// RefreshTokenMiddleware(
// authorizationEndpoint: '/api/v1/account/test?action=authorize',
// tokenEndpoint: '/api/v1/account/test?action=refresh',
// accessTokenParser: (body) => body['access_token']! as String,
// refreshTokenParser: (body) => body['refresh_token']! as String,
// ),
// );
print(pipeline);
final client = MiddlewareClient(pipeline: pipeline);
await client.post(
Uri.parse('/api/v1/account/test'),
body: <String, String>{
'email': 'test@test.fr',
},
);
auth
..username = 'username'
..password = 'password';
await client.post(
Uri.parse('/api/v1/account/test'),
body: <String, String>{
'email': 'test@test.fr',
},
);
}

View File

@ -18,12 +18,21 @@ import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart'; import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart'; import 'package:wyatt_http_client/src/models/middleware_response.dart';
/// {@template middleware}
/// A middleware is a class that can intercept requests and responses
/// and modify them before they are sent to the server or before they
/// are returned to the client.
/// {@endtemplate}
abstract class Middleware { abstract class Middleware {
Middleware(); /// {@macro middleware}
const Middleware();
/// The name of the middleware.
String getName(); String getName();
} }
mixin OnRequestMiddleware { mixin OnRequestMiddleware {
/// Performs an action before the request is sent to the server.
Future<MiddlewareRequest> onRequest( Future<MiddlewareRequest> onRequest(
MiddlewareContext context, MiddlewareContext context,
MiddlewareRequest request, MiddlewareRequest request,
@ -31,6 +40,7 @@ mixin OnRequestMiddleware {
} }
mixin OnResponseMiddleware { mixin OnResponseMiddleware {
/// Performs an action before the response is returned to the client.
Future<MiddlewareResponse> onResponse( Future<MiddlewareResponse> onResponse(
MiddlewareContext context, MiddlewareContext context,
MiddlewareResponse response, MiddlewareResponse response,

View File

@ -24,15 +24,23 @@ import 'package:wyatt_http_client/src/models/unfreezed_request.dart';
import 'package:wyatt_http_client/src/pipeline.dart'; import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/http_methods.dart'; import 'package:wyatt_http_client/src/utils/http_methods.dart';
/// {@template middleware_client}
/// A custom [Client] implementation that allows you to intercept requests
/// and responses and modify them before they are sent to the server or
/// before they are returned to the client.
/// {@endtemplate}
class MiddlewareClient extends BaseClient { class MiddlewareClient extends BaseClient {
/// {@macro middleware_client}
MiddlewareClient({ MiddlewareClient({
Pipeline? pipeline, Pipeline? pipeline,
Client? inner, Client? inner,
}) : pipeline = pipeline ?? Pipeline(), }) : pipeline = pipeline ?? Pipeline(),
inner = inner ?? Client() { inner = inner ?? Client();
print('Using Pipeline:\n$pipeline');
} /// The [Client] that will be used to send requests.
final Client inner; final Client inner;
/// The [Pipeline] that will be used to intercept requests and responses.
final Pipeline pipeline; final Pipeline pipeline;
@override @override

View File

@ -22,14 +22,24 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/authentication_methods.dart'; import 'package:wyatt_http_client/src/utils/authentication_methods.dart';
import 'package:wyatt_http_client/src/utils/header_keys.dart'; import 'package:wyatt_http_client/src/utils/header_keys.dart';
/// {@template basic_auth_middleware}
/// A middleware that adds basic authentication to the request.
/// {@endtemplate}
class BasicAuthMiddleware with OnRequestMiddleware implements Middleware { class BasicAuthMiddleware with OnRequestMiddleware implements Middleware {
BasicAuthMiddleware({ /// {@macro basic_auth_middleware}
const BasicAuthMiddleware({
this.username, this.username,
this.password, this.password,
this.authenticationHeader = HeaderKeys.authorization, this.authenticationHeader = HeaderKeys.authorization,
}); });
String? username;
String? password; /// The username to use for authentication.
final String? username;
/// The password to use for authentication.
final String? password;
/// The header to use for authentication.
final String authenticationHeader; final String authenticationHeader;
@override @override
@ -43,10 +53,7 @@ class BasicAuthMiddleware with OnRequestMiddleware implements Middleware {
if (username == null || password == null) { if (username == null || password == null) {
return request; return request;
} }
print(
'${getName()}::OnRequest\n'
'>> Basic: ${base64Encode(utf8.encode('$username:$password'))}',
);
final mutation = { final mutation = {
authenticationHeader: '${AuthenticationMethods.basic} ' authenticationHeader: '${AuthenticationMethods.basic} '
'${base64Encode(utf8.encode('$username:$password'))}', '${base64Encode(utf8.encode('$username:$password'))}',

Some files were not shown because too many files have changed in this diff Show More