From 264b10bd0da3e17eb65d6b640213fa8ecf7c1bd5 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 31 Jul 2024 17:41:38 +0300 Subject: [PATCH] feat(blogPost): add recipes --- lib/app_router.dart | 4 +- lib/features/feed/models/recipes_model.dart | 47 ++ .../feed/models/recipes_model.freezed.dart | 785 ++++++++++++++++++ lib/features/feed/models/recipes_model.g.dart | 72 ++ .../feed/models/recipes_pagination_model.dart | 27 + .../recipes_pagination_model.freezed.dart | 427 ++++++++++ .../models/recipes_pagination_model.g.dart | 43 + .../bloc/recipes_cubit/recipes_cubit.dart | 18 + .../bloc/recipes_cubit/recipes_state.dart | 19 + .../recipes_cubit/recipes_state.freezed.dart | 619 ++++++++++++++ .../feed/presentation/pages/feed_screen.dart | 521 +++++------- .../presentation/pages/feed_screen_new.dart | 214 ----- .../widgets/blog_post_widget.dart | 16 +- .../presentation/widgets/recipes_widget.dart | 187 +++++ .../feed/repositories/feed_repository.dart | 9 + lib/features/feed/services/feed_service.dart | 4 + .../feed/services/feed_service.g.dart | 27 + lib/locator_service.dart | 2 + lib/main.dart | 2 + 19 files changed, 2531 insertions(+), 512 deletions(-) create mode 100644 lib/features/feed/models/recipes_model.dart create mode 100644 lib/features/feed/models/recipes_model.freezed.dart create mode 100644 lib/features/feed/models/recipes_model.g.dart create mode 100644 lib/features/feed/models/recipes_pagination_model.dart create mode 100644 lib/features/feed/models/recipes_pagination_model.freezed.dart create mode 100644 lib/features/feed/models/recipes_pagination_model.g.dart create mode 100644 lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart create mode 100644 lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart create mode 100644 lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart delete mode 100644 lib/features/feed/presentation/pages/feed_screen_new.dart create mode 100644 lib/features/feed/presentation/widgets/recipes_widget.dart diff --git a/lib/app_router.dart b/lib/app_router.dart index 5eb1d0cc..61fac3e7 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -8,7 +8,7 @@ import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/chat_select_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart'; -import 'package:eatvio_app/features/feed/presentation/pages/feed_screen_new.dart'; +import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart'; import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart'; import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart'; import 'package:eatvio_app/presentation/diets/diet_create_screen/diet_create_screen.dart'; @@ -65,7 +65,7 @@ class AppRouter { GoRoute( path: '/feed-screen', name: 'feedScreen', - builder: (context, state) => FeedScreenNew(), + builder: (context, state) => FeedScreen(), ), GoRoute( path: '/diary-screen', diff --git a/lib/features/feed/models/recipes_model.dart b/lib/features/feed/models/recipes_model.dart new file mode 100644 index 00000000..d654fafc --- /dev/null +++ b/lib/features/feed/models/recipes_model.dart @@ -0,0 +1,47 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'recipes_model.freezed.dart'; +part 'recipes_model.g.dart'; + +@freezed +class Recipes with _$Recipes { + const factory Recipes({ + required int id, + required String image, + required String state, + @JsonKey(name: "cooking_time") required int cookingTime, + required List ingredients, + required String description, + required List steps, + @JsonKey(name: "video_url") required String videoUrl, + @JsonKey(name: "created_at") required DateTime createdAt, + @JsonKey(name: "updated_at") required DateTime updatedAt, + required String title, + required String type, + required String slug, + @JsonKey(name: "comments_count") required int commentsCount, + }) = _Recipes; + + factory Recipes.fromJson(Map json) => _$RecipesFromJson(json); +} + +@freezed +class Ingredient with _$Ingredient { + const factory Ingredient({ + required String name, + String? unit, + required String portion, + }) = _Ingredient; + + factory Ingredient.fromJson(Map json) => _$IngredientFromJson(json); +} + +@freezed +class Step with _$Step { + const factory Step({ + required String image, + required String description, + }) = _Step; + + factory Step.fromJson(Map json) => _$StepFromJson(json); +} diff --git a/lib/features/feed/models/recipes_model.freezed.dart b/lib/features/feed/models/recipes_model.freezed.dart new file mode 100644 index 00000000..cb55e508 --- /dev/null +++ b/lib/features/feed/models/recipes_model.freezed.dart @@ -0,0 +1,785 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recipes_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +Recipes _$RecipesFromJson(Map json) { + return _Recipes.fromJson(json); +} + +/// @nodoc +mixin _$Recipes { + int get id => throw _privateConstructorUsedError; + String get image => throw _privateConstructorUsedError; + String get state => throw _privateConstructorUsedError; + @JsonKey(name: "cooking_time") + int get cookingTime => throw _privateConstructorUsedError; + List get ingredients => throw _privateConstructorUsedError; + String get description => throw _privateConstructorUsedError; + List get steps => throw _privateConstructorUsedError; + @JsonKey(name: "video_url") + String get videoUrl => throw _privateConstructorUsedError; + @JsonKey(name: "created_at") + DateTime get createdAt => throw _privateConstructorUsedError; + @JsonKey(name: "updated_at") + DateTime get updatedAt => throw _privateConstructorUsedError; + String get title => throw _privateConstructorUsedError; + String get type => throw _privateConstructorUsedError; + String get slug => throw _privateConstructorUsedError; + @JsonKey(name: "comments_count") + int get commentsCount => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $RecipesCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RecipesCopyWith<$Res> { + factory $RecipesCopyWith(Recipes value, $Res Function(Recipes) then) = + _$RecipesCopyWithImpl<$Res, Recipes>; + @useResult + $Res call( + {int id, + String image, + String state, + @JsonKey(name: "cooking_time") int cookingTime, + List ingredients, + String description, + List steps, + @JsonKey(name: "video_url") String videoUrl, + @JsonKey(name: "created_at") DateTime createdAt, + @JsonKey(name: "updated_at") DateTime updatedAt, + String title, + String type, + String slug, + @JsonKey(name: "comments_count") int commentsCount}); +} + +/// @nodoc +class _$RecipesCopyWithImpl<$Res, $Val extends Recipes> + implements $RecipesCopyWith<$Res> { + _$RecipesCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? image = null, + Object? state = null, + Object? cookingTime = null, + Object? ingredients = null, + Object? description = null, + Object? steps = null, + Object? videoUrl = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? title = null, + Object? type = null, + Object? slug = null, + Object? commentsCount = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + image: null == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String, + state: null == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String, + cookingTime: null == cookingTime + ? _value.cookingTime + : cookingTime // ignore: cast_nullable_to_non_nullable + as int, + ingredients: null == ingredients + ? _value.ingredients + : ingredients // ignore: cast_nullable_to_non_nullable + as List, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + steps: null == steps + ? _value.steps + : steps // ignore: cast_nullable_to_non_nullable + as List, + videoUrl: null == videoUrl + ? _value.videoUrl + : videoUrl // ignore: cast_nullable_to_non_nullable + as String, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + commentsCount: null == commentsCount + ? _value.commentsCount + : commentsCount // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> { + factory _$$RecipesImplCopyWith( + _$RecipesImpl value, $Res Function(_$RecipesImpl) then) = + __$$RecipesImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + String image, + String state, + @JsonKey(name: "cooking_time") int cookingTime, + List ingredients, + String description, + List steps, + @JsonKey(name: "video_url") String videoUrl, + @JsonKey(name: "created_at") DateTime createdAt, + @JsonKey(name: "updated_at") DateTime updatedAt, + String title, + String type, + String slug, + @JsonKey(name: "comments_count") int commentsCount}); +} + +/// @nodoc +class __$$RecipesImplCopyWithImpl<$Res> + extends _$RecipesCopyWithImpl<$Res, _$RecipesImpl> + implements _$$RecipesImplCopyWith<$Res> { + __$$RecipesImplCopyWithImpl( + _$RecipesImpl _value, $Res Function(_$RecipesImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? image = null, + Object? state = null, + Object? cookingTime = null, + Object? ingredients = null, + Object? description = null, + Object? steps = null, + Object? videoUrl = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? title = null, + Object? type = null, + Object? slug = null, + Object? commentsCount = null, + }) { + return _then(_$RecipesImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + image: null == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String, + state: null == state + ? _value.state + : state // ignore: cast_nullable_to_non_nullable + as String, + cookingTime: null == cookingTime + ? _value.cookingTime + : cookingTime // ignore: cast_nullable_to_non_nullable + as int, + ingredients: null == ingredients + ? _value._ingredients + : ingredients // ignore: cast_nullable_to_non_nullable + as List, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + steps: null == steps + ? _value._steps + : steps // ignore: cast_nullable_to_non_nullable + as List, + videoUrl: null == videoUrl + ? _value.videoUrl + : videoUrl // ignore: cast_nullable_to_non_nullable + as String, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + commentsCount: null == commentsCount + ? _value.commentsCount + : commentsCount // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$RecipesImpl implements _Recipes { + const _$RecipesImpl( + {required this.id, + required this.image, + required this.state, + @JsonKey(name: "cooking_time") required this.cookingTime, + required final List ingredients, + required this.description, + required final List steps, + @JsonKey(name: "video_url") required this.videoUrl, + @JsonKey(name: "created_at") required this.createdAt, + @JsonKey(name: "updated_at") required this.updatedAt, + required this.title, + required this.type, + required this.slug, + @JsonKey(name: "comments_count") required this.commentsCount}) + : _ingredients = ingredients, + _steps = steps; + + factory _$RecipesImpl.fromJson(Map json) => + _$$RecipesImplFromJson(json); + + @override + final int id; + @override + final String image; + @override + final String state; + @override + @JsonKey(name: "cooking_time") + final int cookingTime; + final List _ingredients; + @override + List get ingredients { + if (_ingredients is EqualUnmodifiableListView) return _ingredients; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_ingredients); + } + + @override + final String description; + final List _steps; + @override + List get steps { + if (_steps is EqualUnmodifiableListView) return _steps; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_steps); + } + + @override + @JsonKey(name: "video_url") + final String videoUrl; + @override + @JsonKey(name: "created_at") + final DateTime createdAt; + @override + @JsonKey(name: "updated_at") + final DateTime updatedAt; + @override + final String title; + @override + final String type; + @override + final String slug; + @override + @JsonKey(name: "comments_count") + final int commentsCount; + + @override + String toString() { + return 'Recipes(id: $id, image: $image, state: $state, cookingTime: $cookingTime, ingredients: $ingredients, description: $description, steps: $steps, videoUrl: $videoUrl, createdAt: $createdAt, updatedAt: $updatedAt, title: $title, type: $type, slug: $slug, commentsCount: $commentsCount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipesImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.image, image) || other.image == image) && + (identical(other.state, state) || other.state == state) && + (identical(other.cookingTime, cookingTime) || + other.cookingTime == cookingTime) && + const DeepCollectionEquality() + .equals(other._ingredients, _ingredients) && + (identical(other.description, description) || + other.description == description) && + const DeepCollectionEquality().equals(other._steps, _steps) && + (identical(other.videoUrl, videoUrl) || + other.videoUrl == videoUrl) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt) && + (identical(other.title, title) || other.title == title) && + (identical(other.type, type) || other.type == type) && + (identical(other.slug, slug) || other.slug == slug) && + (identical(other.commentsCount, commentsCount) || + other.commentsCount == commentsCount)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + id, + image, + state, + cookingTime, + const DeepCollectionEquality().hash(_ingredients), + description, + const DeepCollectionEquality().hash(_steps), + videoUrl, + createdAt, + updatedAt, + title, + type, + slug, + commentsCount); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$RecipesImplCopyWith<_$RecipesImpl> get copyWith => + __$$RecipesImplCopyWithImpl<_$RecipesImpl>(this, _$identity); + + @override + Map toJson() { + return _$$RecipesImplToJson( + this, + ); + } +} + +abstract class _Recipes implements Recipes { + const factory _Recipes( + {required final int id, + required final String image, + required final String state, + @JsonKey(name: "cooking_time") required final int cookingTime, + required final List ingredients, + required final String description, + required final List steps, + @JsonKey(name: "video_url") required final String videoUrl, + @JsonKey(name: "created_at") required final DateTime createdAt, + @JsonKey(name: "updated_at") required final DateTime updatedAt, + required final String title, + required final String type, + required final String slug, + @JsonKey(name: "comments_count") required final int commentsCount}) = + _$RecipesImpl; + + factory _Recipes.fromJson(Map json) = _$RecipesImpl.fromJson; + + @override + int get id; + @override + String get image; + @override + String get state; + @override + @JsonKey(name: "cooking_time") + int get cookingTime; + @override + List get ingredients; + @override + String get description; + @override + List get steps; + @override + @JsonKey(name: "video_url") + String get videoUrl; + @override + @JsonKey(name: "created_at") + DateTime get createdAt; + @override + @JsonKey(name: "updated_at") + DateTime get updatedAt; + @override + String get title; + @override + String get type; + @override + String get slug; + @override + @JsonKey(name: "comments_count") + int get commentsCount; + @override + @JsonKey(ignore: true) + _$$RecipesImplCopyWith<_$RecipesImpl> get copyWith => + throw _privateConstructorUsedError; +} + +Ingredient _$IngredientFromJson(Map json) { + return _Ingredient.fromJson(json); +} + +/// @nodoc +mixin _$Ingredient { + String get name => throw _privateConstructorUsedError; + String? get unit => throw _privateConstructorUsedError; + String get portion => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $IngredientCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IngredientCopyWith<$Res> { + factory $IngredientCopyWith( + Ingredient value, $Res Function(Ingredient) then) = + _$IngredientCopyWithImpl<$Res, Ingredient>; + @useResult + $Res call({String name, String? unit, String portion}); +} + +/// @nodoc +class _$IngredientCopyWithImpl<$Res, $Val extends Ingredient> + implements $IngredientCopyWith<$Res> { + _$IngredientCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? unit = freezed, + Object? portion = null, + }) { + return _then(_value.copyWith( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + unit: freezed == unit + ? _value.unit + : unit // ignore: cast_nullable_to_non_nullable + as String?, + portion: null == portion + ? _value.portion + : portion // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IngredientImplCopyWith<$Res> + implements $IngredientCopyWith<$Res> { + factory _$$IngredientImplCopyWith( + _$IngredientImpl value, $Res Function(_$IngredientImpl) then) = + __$$IngredientImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String name, String? unit, String portion}); +} + +/// @nodoc +class __$$IngredientImplCopyWithImpl<$Res> + extends _$IngredientCopyWithImpl<$Res, _$IngredientImpl> + implements _$$IngredientImplCopyWith<$Res> { + __$$IngredientImplCopyWithImpl( + _$IngredientImpl _value, $Res Function(_$IngredientImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? unit = freezed, + Object? portion = null, + }) { + return _then(_$IngredientImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + unit: freezed == unit + ? _value.unit + : unit // ignore: cast_nullable_to_non_nullable + as String?, + portion: null == portion + ? _value.portion + : portion // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$IngredientImpl implements _Ingredient { + const _$IngredientImpl( + {required this.name, this.unit, required this.portion}); + + factory _$IngredientImpl.fromJson(Map json) => + _$$IngredientImplFromJson(json); + + @override + final String name; + @override + final String? unit; + @override + final String portion; + + @override + String toString() { + return 'Ingredient(name: $name, unit: $unit, portion: $portion)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IngredientImpl && + (identical(other.name, name) || other.name == name) && + (identical(other.unit, unit) || other.unit == unit) && + (identical(other.portion, portion) || other.portion == portion)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, name, unit, portion); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$IngredientImplCopyWith<_$IngredientImpl> get copyWith => + __$$IngredientImplCopyWithImpl<_$IngredientImpl>(this, _$identity); + + @override + Map toJson() { + return _$$IngredientImplToJson( + this, + ); + } +} + +abstract class _Ingredient implements Ingredient { + const factory _Ingredient( + {required final String name, + final String? unit, + required final String portion}) = _$IngredientImpl; + + factory _Ingredient.fromJson(Map json) = + _$IngredientImpl.fromJson; + + @override + String get name; + @override + String? get unit; + @override + String get portion; + @override + @JsonKey(ignore: true) + _$$IngredientImplCopyWith<_$IngredientImpl> get copyWith => + throw _privateConstructorUsedError; +} + +Step _$StepFromJson(Map json) { + return _Step.fromJson(json); +} + +/// @nodoc +mixin _$Step { + String get image => throw _privateConstructorUsedError; + String get description => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $StepCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StepCopyWith<$Res> { + factory $StepCopyWith(Step value, $Res Function(Step) then) = + _$StepCopyWithImpl<$Res, Step>; + @useResult + $Res call({String image, String description}); +} + +/// @nodoc +class _$StepCopyWithImpl<$Res, $Val extends Step> + implements $StepCopyWith<$Res> { + _$StepCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? image = null, + Object? description = null, + }) { + return _then(_value.copyWith( + image: null == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StepImplCopyWith<$Res> implements $StepCopyWith<$Res> { + factory _$$StepImplCopyWith( + _$StepImpl value, $Res Function(_$StepImpl) then) = + __$$StepImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String image, String description}); +} + +/// @nodoc +class __$$StepImplCopyWithImpl<$Res> + extends _$StepCopyWithImpl<$Res, _$StepImpl> + implements _$$StepImplCopyWith<$Res> { + __$$StepImplCopyWithImpl(_$StepImpl _value, $Res Function(_$StepImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? image = null, + Object? description = null, + }) { + return _then(_$StepImpl( + image: null == image + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StepImpl implements _Step { + const _$StepImpl({required this.image, required this.description}); + + factory _$StepImpl.fromJson(Map json) => + _$$StepImplFromJson(json); + + @override + final String image; + @override + final String description; + + @override + String toString() { + return 'Step(image: $image, description: $description)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StepImpl && + (identical(other.image, image) || other.image == image) && + (identical(other.description, description) || + other.description == description)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, image, description); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$StepImplCopyWith<_$StepImpl> get copyWith => + __$$StepImplCopyWithImpl<_$StepImpl>(this, _$identity); + + @override + Map toJson() { + return _$$StepImplToJson( + this, + ); + } +} + +abstract class _Step implements Step { + const factory _Step( + {required final String image, + required final String description}) = _$StepImpl; + + factory _Step.fromJson(Map json) = _$StepImpl.fromJson; + + @override + String get image; + @override + String get description; + @override + @JsonKey(ignore: true) + _$$StepImplCopyWith<_$StepImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/feed/models/recipes_model.g.dart b/lib/features/feed/models/recipes_model.g.dart new file mode 100644 index 00000000..74ecedfb --- /dev/null +++ b/lib/features/feed/models/recipes_model.g.dart @@ -0,0 +1,72 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recipes_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$RecipesImpl _$$RecipesImplFromJson(Map json) => + _$RecipesImpl( + id: (json['id'] as num).toInt(), + image: json['image'] as String, + state: json['state'] as String, + cookingTime: (json['cooking_time'] as num).toInt(), + ingredients: (json['ingredients'] as List) + .map((e) => Ingredient.fromJson(e as Map)) + .toList(), + description: json['description'] as String, + steps: (json['steps'] as List) + .map((e) => Step.fromJson(e as Map)) + .toList(), + videoUrl: json['video_url'] as String, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + title: json['title'] as String, + type: json['type'] as String, + slug: json['slug'] as String, + commentsCount: (json['comments_count'] as num).toInt(), + ); + +Map _$$RecipesImplToJson(_$RecipesImpl instance) => + { + 'id': instance.id, + 'image': instance.image, + 'state': instance.state, + 'cooking_time': instance.cookingTime, + 'ingredients': instance.ingredients, + 'description': instance.description, + 'steps': instance.steps, + 'video_url': instance.videoUrl, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'title': instance.title, + 'type': instance.type, + 'slug': instance.slug, + 'comments_count': instance.commentsCount, + }; + +_$IngredientImpl _$$IngredientImplFromJson(Map json) => + _$IngredientImpl( + name: json['name'] as String, + unit: json['unit'] as String?, + portion: json['portion'] as String, + ); + +Map _$$IngredientImplToJson(_$IngredientImpl instance) => + { + 'name': instance.name, + 'unit': instance.unit, + 'portion': instance.portion, + }; + +_$StepImpl _$$StepImplFromJson(Map json) => _$StepImpl( + image: json['image'] as String, + description: json['description'] as String, + ); + +Map _$$StepImplToJson(_$StepImpl instance) => + { + 'image': instance.image, + 'description': instance.description, + }; diff --git a/lib/features/feed/models/recipes_pagination_model.dart b/lib/features/feed/models/recipes_pagination_model.dart new file mode 100644 index 00000000..fb27b882 --- /dev/null +++ b/lib/features/feed/models/recipes_pagination_model.dart @@ -0,0 +1,27 @@ +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'recipes_pagination_model.freezed.dart'; + +part 'recipes_pagination_model.g.dart'; + +@freezed +class RecipesPagination with _$RecipesPagination { + const factory RecipesPagination({ + @JsonKey(name: 'data') + required List recipesList, + @JsonKey(name: "current_page") required int currentPage, + @JsonKey(name: "first_page_url") required String firstPageUrl, + required int from, + @JsonKey(name: "last_page") required int lastPage, + @JsonKey(name: "last_page_url") required String lastPageUrl, + @JsonKey(name: "next_page_url") required String nextPageUrl, + required String path, + @JsonKey(name: "per_page") required int perPage, + @JsonKey(name: "prev_page_url") required dynamic prevPageUrl, + required int to, + required int total, + }) = _RecipesPagination; + + factory RecipesPagination.fromJson(Map json) => _$RecipesPaginationFromJson(json); +} diff --git a/lib/features/feed/models/recipes_pagination_model.freezed.dart b/lib/features/feed/models/recipes_pagination_model.freezed.dart new file mode 100644 index 00000000..272422c3 --- /dev/null +++ b/lib/features/feed/models/recipes_pagination_model.freezed.dart @@ -0,0 +1,427 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recipes_pagination_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +RecipesPagination _$RecipesPaginationFromJson(Map json) { + return _RecipesPagination.fromJson(json); +} + +/// @nodoc +mixin _$RecipesPagination { + @JsonKey(name: 'data') + List get recipesList => throw _privateConstructorUsedError; + @JsonKey(name: "current_page") + int get currentPage => throw _privateConstructorUsedError; + @JsonKey(name: "first_page_url") + String get firstPageUrl => throw _privateConstructorUsedError; + int get from => throw _privateConstructorUsedError; + @JsonKey(name: "last_page") + int get lastPage => throw _privateConstructorUsedError; + @JsonKey(name: "last_page_url") + String get lastPageUrl => throw _privateConstructorUsedError; + @JsonKey(name: "next_page_url") + String get nextPageUrl => throw _privateConstructorUsedError; + String get path => throw _privateConstructorUsedError; + @JsonKey(name: "per_page") + int get perPage => throw _privateConstructorUsedError; + @JsonKey(name: "prev_page_url") + dynamic get prevPageUrl => throw _privateConstructorUsedError; + int get to => throw _privateConstructorUsedError; + int get total => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $RecipesPaginationCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RecipesPaginationCopyWith<$Res> { + factory $RecipesPaginationCopyWith( + RecipesPagination value, $Res Function(RecipesPagination) then) = + _$RecipesPaginationCopyWithImpl<$Res, RecipesPagination>; + @useResult + $Res call( + {@JsonKey(name: 'data') List recipesList, + @JsonKey(name: "current_page") int currentPage, + @JsonKey(name: "first_page_url") String firstPageUrl, + int from, + @JsonKey(name: "last_page") int lastPage, + @JsonKey(name: "last_page_url") String lastPageUrl, + @JsonKey(name: "next_page_url") String nextPageUrl, + String path, + @JsonKey(name: "per_page") int perPage, + @JsonKey(name: "prev_page_url") dynamic prevPageUrl, + int to, + int total}); +} + +/// @nodoc +class _$RecipesPaginationCopyWithImpl<$Res, $Val extends RecipesPagination> + implements $RecipesPaginationCopyWith<$Res> { + _$RecipesPaginationCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recipesList = null, + Object? currentPage = null, + Object? firstPageUrl = null, + Object? from = null, + Object? lastPage = null, + Object? lastPageUrl = null, + Object? nextPageUrl = null, + Object? path = null, + Object? perPage = null, + Object? prevPageUrl = freezed, + Object? to = null, + Object? total = null, + }) { + return _then(_value.copyWith( + recipesList: null == recipesList + ? _value.recipesList + : recipesList // ignore: cast_nullable_to_non_nullable + as List, + currentPage: null == currentPage + ? _value.currentPage + : currentPage // ignore: cast_nullable_to_non_nullable + as int, + firstPageUrl: null == firstPageUrl + ? _value.firstPageUrl + : firstPageUrl // ignore: cast_nullable_to_non_nullable + as String, + from: null == from + ? _value.from + : from // ignore: cast_nullable_to_non_nullable + as int, + lastPage: null == lastPage + ? _value.lastPage + : lastPage // ignore: cast_nullable_to_non_nullable + as int, + lastPageUrl: null == lastPageUrl + ? _value.lastPageUrl + : lastPageUrl // ignore: cast_nullable_to_non_nullable + as String, + nextPageUrl: null == nextPageUrl + ? _value.nextPageUrl + : nextPageUrl // ignore: cast_nullable_to_non_nullable + as String, + path: null == path + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as String, + perPage: null == perPage + ? _value.perPage + : perPage // ignore: cast_nullable_to_non_nullable + as int, + prevPageUrl: freezed == prevPageUrl + ? _value.prevPageUrl + : prevPageUrl // ignore: cast_nullable_to_non_nullable + as dynamic, + to: null == to + ? _value.to + : to // ignore: cast_nullable_to_non_nullable + as int, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$RecipesPaginationImplCopyWith<$Res> + implements $RecipesPaginationCopyWith<$Res> { + factory _$$RecipesPaginationImplCopyWith(_$RecipesPaginationImpl value, + $Res Function(_$RecipesPaginationImpl) then) = + __$$RecipesPaginationImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@JsonKey(name: 'data') List recipesList, + @JsonKey(name: "current_page") int currentPage, + @JsonKey(name: "first_page_url") String firstPageUrl, + int from, + @JsonKey(name: "last_page") int lastPage, + @JsonKey(name: "last_page_url") String lastPageUrl, + @JsonKey(name: "next_page_url") String nextPageUrl, + String path, + @JsonKey(name: "per_page") int perPage, + @JsonKey(name: "prev_page_url") dynamic prevPageUrl, + int to, + int total}); +} + +/// @nodoc +class __$$RecipesPaginationImplCopyWithImpl<$Res> + extends _$RecipesPaginationCopyWithImpl<$Res, _$RecipesPaginationImpl> + implements _$$RecipesPaginationImplCopyWith<$Res> { + __$$RecipesPaginationImplCopyWithImpl(_$RecipesPaginationImpl _value, + $Res Function(_$RecipesPaginationImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recipesList = null, + Object? currentPage = null, + Object? firstPageUrl = null, + Object? from = null, + Object? lastPage = null, + Object? lastPageUrl = null, + Object? nextPageUrl = null, + Object? path = null, + Object? perPage = null, + Object? prevPageUrl = freezed, + Object? to = null, + Object? total = null, + }) { + return _then(_$RecipesPaginationImpl( + recipesList: null == recipesList + ? _value._recipesList + : recipesList // ignore: cast_nullable_to_non_nullable + as List, + currentPage: null == currentPage + ? _value.currentPage + : currentPage // ignore: cast_nullable_to_non_nullable + as int, + firstPageUrl: null == firstPageUrl + ? _value.firstPageUrl + : firstPageUrl // ignore: cast_nullable_to_non_nullable + as String, + from: null == from + ? _value.from + : from // ignore: cast_nullable_to_non_nullable + as int, + lastPage: null == lastPage + ? _value.lastPage + : lastPage // ignore: cast_nullable_to_non_nullable + as int, + lastPageUrl: null == lastPageUrl + ? _value.lastPageUrl + : lastPageUrl // ignore: cast_nullable_to_non_nullable + as String, + nextPageUrl: null == nextPageUrl + ? _value.nextPageUrl + : nextPageUrl // ignore: cast_nullable_to_non_nullable + as String, + path: null == path + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as String, + perPage: null == perPage + ? _value.perPage + : perPage // ignore: cast_nullable_to_non_nullable + as int, + prevPageUrl: freezed == prevPageUrl + ? _value.prevPageUrl + : prevPageUrl // ignore: cast_nullable_to_non_nullable + as dynamic, + to: null == to + ? _value.to + : to // ignore: cast_nullable_to_non_nullable + as int, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$RecipesPaginationImpl implements _RecipesPagination { + const _$RecipesPaginationImpl( + {@JsonKey(name: 'data') required final List recipesList, + @JsonKey(name: "current_page") required this.currentPage, + @JsonKey(name: "first_page_url") required this.firstPageUrl, + required this.from, + @JsonKey(name: "last_page") required this.lastPage, + @JsonKey(name: "last_page_url") required this.lastPageUrl, + @JsonKey(name: "next_page_url") required this.nextPageUrl, + required this.path, + @JsonKey(name: "per_page") required this.perPage, + @JsonKey(name: "prev_page_url") required this.prevPageUrl, + required this.to, + required this.total}) + : _recipesList = recipesList; + + factory _$RecipesPaginationImpl.fromJson(Map json) => + _$$RecipesPaginationImplFromJson(json); + + final List _recipesList; + @override + @JsonKey(name: 'data') + List get recipesList { + if (_recipesList is EqualUnmodifiableListView) return _recipesList; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_recipesList); + } + + @override + @JsonKey(name: "current_page") + final int currentPage; + @override + @JsonKey(name: "first_page_url") + final String firstPageUrl; + @override + final int from; + @override + @JsonKey(name: "last_page") + final int lastPage; + @override + @JsonKey(name: "last_page_url") + final String lastPageUrl; + @override + @JsonKey(name: "next_page_url") + final String nextPageUrl; + @override + final String path; + @override + @JsonKey(name: "per_page") + final int perPage; + @override + @JsonKey(name: "prev_page_url") + final dynamic prevPageUrl; + @override + final int to; + @override + final int total; + + @override + String toString() { + return 'RecipesPagination(recipesList: $recipesList, currentPage: $currentPage, firstPageUrl: $firstPageUrl, from: $from, lastPage: $lastPage, lastPageUrl: $lastPageUrl, nextPageUrl: $nextPageUrl, path: $path, perPage: $perPage, prevPageUrl: $prevPageUrl, to: $to, total: $total)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipesPaginationImpl && + const DeepCollectionEquality() + .equals(other._recipesList, _recipesList) && + (identical(other.currentPage, currentPage) || + other.currentPage == currentPage) && + (identical(other.firstPageUrl, firstPageUrl) || + other.firstPageUrl == firstPageUrl) && + (identical(other.from, from) || other.from == from) && + (identical(other.lastPage, lastPage) || + other.lastPage == lastPage) && + (identical(other.lastPageUrl, lastPageUrl) || + other.lastPageUrl == lastPageUrl) && + (identical(other.nextPageUrl, nextPageUrl) || + other.nextPageUrl == nextPageUrl) && + (identical(other.path, path) || other.path == path) && + (identical(other.perPage, perPage) || other.perPage == perPage) && + const DeepCollectionEquality() + .equals(other.prevPageUrl, prevPageUrl) && + (identical(other.to, to) || other.to == to) && + (identical(other.total, total) || other.total == total)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_recipesList), + currentPage, + firstPageUrl, + from, + lastPage, + lastPageUrl, + nextPageUrl, + path, + perPage, + const DeepCollectionEquality().hash(prevPageUrl), + to, + total); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$RecipesPaginationImplCopyWith<_$RecipesPaginationImpl> get copyWith => + __$$RecipesPaginationImplCopyWithImpl<_$RecipesPaginationImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$RecipesPaginationImplToJson( + this, + ); + } +} + +abstract class _RecipesPagination implements RecipesPagination { + const factory _RecipesPagination( + {@JsonKey(name: 'data') required final List recipesList, + @JsonKey(name: "current_page") required final int currentPage, + @JsonKey(name: "first_page_url") required final String firstPageUrl, + required final int from, + @JsonKey(name: "last_page") required final int lastPage, + @JsonKey(name: "last_page_url") required final String lastPageUrl, + @JsonKey(name: "next_page_url") required final String nextPageUrl, + required final String path, + @JsonKey(name: "per_page") required final int perPage, + @JsonKey(name: "prev_page_url") required final dynamic prevPageUrl, + required final int to, + required final int total}) = _$RecipesPaginationImpl; + + factory _RecipesPagination.fromJson(Map json) = + _$RecipesPaginationImpl.fromJson; + + @override + @JsonKey(name: 'data') + List get recipesList; + @override + @JsonKey(name: "current_page") + int get currentPage; + @override + @JsonKey(name: "first_page_url") + String get firstPageUrl; + @override + int get from; + @override + @JsonKey(name: "last_page") + int get lastPage; + @override + @JsonKey(name: "last_page_url") + String get lastPageUrl; + @override + @JsonKey(name: "next_page_url") + String get nextPageUrl; + @override + String get path; + @override + @JsonKey(name: "per_page") + int get perPage; + @override + @JsonKey(name: "prev_page_url") + dynamic get prevPageUrl; + @override + int get to; + @override + int get total; + @override + @JsonKey(ignore: true) + _$$RecipesPaginationImplCopyWith<_$RecipesPaginationImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/feed/models/recipes_pagination_model.g.dart b/lib/features/feed/models/recipes_pagination_model.g.dart new file mode 100644 index 00000000..d1e483f1 --- /dev/null +++ b/lib/features/feed/models/recipes_pagination_model.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recipes_pagination_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$RecipesPaginationImpl _$$RecipesPaginationImplFromJson( + Map json) => + _$RecipesPaginationImpl( + recipesList: (json['data'] as List) + .map((e) => Recipes.fromJson(e as Map)) + .toList(), + currentPage: (json['current_page'] as num).toInt(), + firstPageUrl: json['first_page_url'] as String, + from: (json['from'] as num).toInt(), + lastPage: (json['last_page'] as num).toInt(), + lastPageUrl: json['last_page_url'] as String, + nextPageUrl: json['next_page_url'] as String, + path: json['path'] as String, + perPage: (json['per_page'] as num).toInt(), + prevPageUrl: json['prev_page_url'], + to: (json['to'] as num).toInt(), + total: (json['total'] as num).toInt(), + ); + +Map _$$RecipesPaginationImplToJson( + _$RecipesPaginationImpl instance) => + { + 'data': instance.recipesList, + 'current_page': instance.currentPage, + 'first_page_url': instance.firstPageUrl, + 'from': instance.from, + 'last_page': instance.lastPage, + 'last_page_url': instance.lastPageUrl, + 'next_page_url': instance.nextPageUrl, + 'path': instance.path, + 'per_page': instance.perPage, + 'prev_page_url': instance.prevPageUrl, + 'to': instance.to, + 'total': instance.total, + }; diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart new file mode 100644 index 00000000..8d8aeb2e --- /dev/null +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart @@ -0,0 +1,18 @@ +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; +import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class RecipesCubit extends Cubit { + final FeedRepository feedRepository; + + RecipesCubit({required this.feedRepository}) : super(const RecipesState.loading()); + + loadRecipesList() async { + try { + final recipesList = await feedRepository.getRecipes(); + emit(RecipesState.loaded(recipesList: recipesList)); + } catch (e) { + emit(RecipesState.error(errorMessage: e.toString())); + } + } +} \ No newline at end of file diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart new file mode 100644 index 00000000..ecdf17c2 --- /dev/null +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart @@ -0,0 +1,19 @@ +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'recipes_state.freezed.dart'; + +@freezed +abstract class RecipesState with _$RecipesState { + const factory RecipesState() = _RecipesState; + + const factory RecipesState.loading() = RecipesStateLoading; + + const factory RecipesState.loaded({ + required List recipesList, + }) = RecipesStateLoaded; + + const factory RecipesState.error({ + required String errorMessage, + }) = RecipesStateError; +} diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart new file mode 100644 index 00000000..e8aa88e0 --- /dev/null +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_state.freezed.dart @@ -0,0 +1,619 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recipes_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$RecipesState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List recipesList) loaded, + required TResult Function(String errorMessage) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List recipesList)? loaded, + TResult? Function(String errorMessage)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List recipesList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(_RecipesState value) $default, { + required TResult Function(RecipesStateLoading value) loading, + required TResult Function(RecipesStateLoaded value) loaded, + required TResult Function(RecipesStateError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_RecipesState value)? $default, { + TResult? Function(RecipesStateLoading value)? loading, + TResult? Function(RecipesStateLoaded value)? loaded, + TResult? Function(RecipesStateError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(_RecipesState value)? $default, { + TResult Function(RecipesStateLoading value)? loading, + TResult Function(RecipesStateLoaded value)? loaded, + TResult Function(RecipesStateError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RecipesStateCopyWith<$Res> { + factory $RecipesStateCopyWith( + RecipesState value, $Res Function(RecipesState) then) = + _$RecipesStateCopyWithImpl<$Res, RecipesState>; +} + +/// @nodoc +class _$RecipesStateCopyWithImpl<$Res, $Val extends RecipesState> + implements $RecipesStateCopyWith<$Res> { + _$RecipesStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$RecipesStateImplCopyWith<$Res> { + factory _$$RecipesStateImplCopyWith( + _$RecipesStateImpl value, $Res Function(_$RecipesStateImpl) then) = + __$$RecipesStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$RecipesStateImplCopyWithImpl<$Res> + extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateImpl> + implements _$$RecipesStateImplCopyWith<$Res> { + __$$RecipesStateImplCopyWithImpl( + _$RecipesStateImpl _value, $Res Function(_$RecipesStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$RecipesStateImpl implements _RecipesState { + const _$RecipesStateImpl(); + + @override + String toString() { + return 'RecipesState()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$RecipesStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List recipesList) loaded, + required TResult Function(String errorMessage) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List recipesList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List recipesList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_RecipesState value) $default, { + required TResult Function(RecipesStateLoading value) loading, + required TResult Function(RecipesStateLoaded value) loaded, + required TResult Function(RecipesStateError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_RecipesState value)? $default, { + TResult? Function(RecipesStateLoading value)? loading, + TResult? Function(RecipesStateLoaded value)? loaded, + TResult? Function(RecipesStateError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_RecipesState value)? $default, { + TResult Function(RecipesStateLoading value)? loading, + TResult Function(RecipesStateLoaded value)? loaded, + TResult Function(RecipesStateError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class _RecipesState implements RecipesState { + const factory _RecipesState() = _$RecipesStateImpl; +} + +/// @nodoc +abstract class _$$RecipesStateLoadingImplCopyWith<$Res> { + factory _$$RecipesStateLoadingImplCopyWith(_$RecipesStateLoadingImpl value, + $Res Function(_$RecipesStateLoadingImpl) then) = + __$$RecipesStateLoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$RecipesStateLoadingImplCopyWithImpl<$Res> + extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateLoadingImpl> + implements _$$RecipesStateLoadingImplCopyWith<$Res> { + __$$RecipesStateLoadingImplCopyWithImpl(_$RecipesStateLoadingImpl _value, + $Res Function(_$RecipesStateLoadingImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$RecipesStateLoadingImpl implements RecipesStateLoading { + const _$RecipesStateLoadingImpl(); + + @override + String toString() { + return 'RecipesState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipesStateLoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List recipesList) loaded, + required TResult Function(String errorMessage) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List recipesList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List recipesList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_RecipesState value) $default, { + required TResult Function(RecipesStateLoading value) loading, + required TResult Function(RecipesStateLoaded value) loaded, + required TResult Function(RecipesStateError value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_RecipesState value)? $default, { + TResult? Function(RecipesStateLoading value)? loading, + TResult? Function(RecipesStateLoaded value)? loaded, + TResult? Function(RecipesStateError value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_RecipesState value)? $default, { + TResult Function(RecipesStateLoading value)? loading, + TResult Function(RecipesStateLoaded value)? loaded, + TResult Function(RecipesStateError value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class RecipesStateLoading implements RecipesState { + const factory RecipesStateLoading() = _$RecipesStateLoadingImpl; +} + +/// @nodoc +abstract class _$$RecipesStateLoadedImplCopyWith<$Res> { + factory _$$RecipesStateLoadedImplCopyWith(_$RecipesStateLoadedImpl value, + $Res Function(_$RecipesStateLoadedImpl) then) = + __$$RecipesStateLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({List recipesList}); +} + +/// @nodoc +class __$$RecipesStateLoadedImplCopyWithImpl<$Res> + extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateLoadedImpl> + implements _$$RecipesStateLoadedImplCopyWith<$Res> { + __$$RecipesStateLoadedImplCopyWithImpl(_$RecipesStateLoadedImpl _value, + $Res Function(_$RecipesStateLoadedImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recipesList = null, + }) { + return _then(_$RecipesStateLoadedImpl( + recipesList: null == recipesList + ? _value._recipesList + : recipesList // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +class _$RecipesStateLoadedImpl implements RecipesStateLoaded { + const _$RecipesStateLoadedImpl({required final List recipesList}) + : _recipesList = recipesList; + + final List _recipesList; + @override + List get recipesList { + if (_recipesList is EqualUnmodifiableListView) return _recipesList; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_recipesList); + } + + @override + String toString() { + return 'RecipesState.loaded(recipesList: $recipesList)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipesStateLoadedImpl && + const DeepCollectionEquality() + .equals(other._recipesList, _recipesList)); + } + + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_recipesList)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$RecipesStateLoadedImplCopyWith<_$RecipesStateLoadedImpl> get copyWith => + __$$RecipesStateLoadedImplCopyWithImpl<_$RecipesStateLoadedImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List recipesList) loaded, + required TResult Function(String errorMessage) error, + }) { + return loaded(recipesList); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List recipesList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loaded?.call(recipesList); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List recipesList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(recipesList); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_RecipesState value) $default, { + required TResult Function(RecipesStateLoading value) loading, + required TResult Function(RecipesStateLoaded value) loaded, + required TResult Function(RecipesStateError value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_RecipesState value)? $default, { + TResult? Function(RecipesStateLoading value)? loading, + TResult? Function(RecipesStateLoaded value)? loaded, + TResult? Function(RecipesStateError value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_RecipesState value)? $default, { + TResult Function(RecipesStateLoading value)? loading, + TResult Function(RecipesStateLoaded value)? loaded, + TResult Function(RecipesStateError value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class RecipesStateLoaded implements RecipesState { + const factory RecipesStateLoaded({required final List recipesList}) = + _$RecipesStateLoadedImpl; + + List get recipesList; + @JsonKey(ignore: true) + _$$RecipesStateLoadedImplCopyWith<_$RecipesStateLoadedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$RecipesStateErrorImplCopyWith<$Res> { + factory _$$RecipesStateErrorImplCopyWith(_$RecipesStateErrorImpl value, + $Res Function(_$RecipesStateErrorImpl) then) = + __$$RecipesStateErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String errorMessage}); +} + +/// @nodoc +class __$$RecipesStateErrorImplCopyWithImpl<$Res> + extends _$RecipesStateCopyWithImpl<$Res, _$RecipesStateErrorImpl> + implements _$$RecipesStateErrorImplCopyWith<$Res> { + __$$RecipesStateErrorImplCopyWithImpl(_$RecipesStateErrorImpl _value, + $Res Function(_$RecipesStateErrorImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? errorMessage = null, + }) { + return _then(_$RecipesStateErrorImpl( + errorMessage: null == errorMessage + ? _value.errorMessage + : errorMessage // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$RecipesStateErrorImpl implements RecipesStateError { + const _$RecipesStateErrorImpl({required this.errorMessage}); + + @override + final String errorMessage; + + @override + String toString() { + return 'RecipesState.error(errorMessage: $errorMessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecipesStateErrorImpl && + (identical(other.errorMessage, errorMessage) || + other.errorMessage == errorMessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, errorMessage); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$RecipesStateErrorImplCopyWith<_$RecipesStateErrorImpl> get copyWith => + __$$RecipesStateErrorImplCopyWithImpl<_$RecipesStateErrorImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List recipesList) loaded, + required TResult Function(String errorMessage) error, + }) { + return error(errorMessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List recipesList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return error?.call(errorMessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List recipesList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(errorMessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_RecipesState value) $default, { + required TResult Function(RecipesStateLoading value) loading, + required TResult Function(RecipesStateLoaded value) loaded, + required TResult Function(RecipesStateError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_RecipesState value)? $default, { + TResult? Function(RecipesStateLoading value)? loading, + TResult? Function(RecipesStateLoaded value)? loaded, + TResult? Function(RecipesStateError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_RecipesState value)? $default, { + TResult Function(RecipesStateLoading value)? loading, + TResult Function(RecipesStateLoaded value)? loaded, + TResult Function(RecipesStateError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class RecipesStateError implements RecipesState { + const factory RecipesStateError({required final String errorMessage}) = + _$RecipesStateErrorImpl; + + String get errorMessage; + @JsonKey(ignore: true) + _$$RecipesStateErrorImplCopyWith<_$RecipesStateErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart index 93697ebc..cb6c714d 100644 --- a/lib/features/feed/presentation/pages/feed_screen.dart +++ b/lib/features/feed/presentation/pages/feed_screen.dart @@ -1,323 +1,256 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:eatvio_app/core/app_export.dart'; -import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart'; -import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_scroll_item.dart'; -import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_search_view.dart'; -import 'package:eatvio_app/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/recipes_widget.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; -class FeedScreen extends StatelessWidget { +class FeedScreen extends StatefulWidget { + const FeedScreen({super.key}); + + @override + State createState() => _FeedScreenState(); +} + +class _FeedScreenState extends State { + final List chipsLabelList = ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты']; + + int _selectedChipIndex = 0; + + @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (BuildContext context, FeedState state) { - if (state is FeedStateLoaded) { - return _buildFeedScreen(context, state); - } else if (state is FeedStateError) { - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(16), - child: Center( - child: Text(state.errorMessage), - ), - ), - ); - } else { - return Scaffold( - body: Center( - child: CupertinoActivityIndicator(), - ), - ); - } - }, - ); - } - - _buildFeedScreen(BuildContext context, FeedStateLoaded state) { - mediaQueryData = MediaQuery.of(context); - return RefreshIndicator( - onRefresh: () => context.read().loadFeedBlogPostList(), - child: Scaffold( - appBar: AppBar( - toolbarHeight: 0, + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + toolbarHeight: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: Colors.white, ), - backgroundColor: Colors.grey[200], - resizeToAvoidBottomInset: true, - body: SingleChildScrollView( - child: Container( - width: double.maxFinite, - decoration: BoxDecoration(color: Colors.grey[200]), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + ), + backgroundColor: Colors.grey.shade200, + body: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + ), + backgroundColor: Colors.white, + foregroundColor: Colors.white, + automaticallyImplyLeading: false, + stretchTriggerOffset: 1, + floating: true, + snap: true, + scrolledUnderElevation: 0, + bottom: _chipGroup(), + title: Row( children: [ - SizedBox(height: 20), - _buildSearch(context), - SizedBox(height: 20), - _buildMenu(context, state), - SizedBox(height: 20), - _buildBlogPostList(context, state), + _searchBar(), + SizedBox(width: 12), + _addBlogPostButton(), ], ), ), + ]; + }, + body: _selectedChipIndex == 0 ? _buildAllBlogPosts() : _buildAllRecipes(), + ), + ); + } + + _buildAllBlogPosts() { + return RefreshIndicator( + onRefresh: () => context.read().loadFeedBlogPostList(), + child: BlocBuilder( + builder: (BuildContext context, FeedState state) { + if (state is FeedStateLoaded) { + return ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + shrinkWrap: true, + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return BlogPostWidget(feedBlogPostModel: state.feedBlogPostList[index]); + }, + separatorBuilder: (context, index) { + return SizedBox(height: 8); + }, + itemCount: state.feedBlogPostList.length, + ); + } else if (state is FeedStateError) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: Text(state.errorMessage), + ), + ), + ); + } else { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } + }, + ), + ); + } + + _buildAllRecipes() { + return RefreshIndicator( + onRefresh: () => context.read().loadRecipesList(), + child: BlocBuilder( + builder: (BuildContext context, RecipesState state) { + if (state is RecipesStateLoaded) { + return ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + shrinkWrap: true, + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return RecipesWidget(recipes: state.recipesList[index]); + }, + separatorBuilder: (context, index) { + return SizedBox(height: 8); + }, + itemCount: state.recipesList.length, + ); + } else if (state is RecipesStateError) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: Text(state.errorMessage), + ), + ), + ); + } else { + return Scaffold( + body: Center( + child: CupertinoActivityIndicator(), + ), + ); + } + }, + ), + ); + } + + _chipGroup() { + return PreferredSize( + preferredSize: Size(MediaQuery.of(context).size.width, 32), + child: Align( + alignment: Alignment.centerLeft, + child: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 16), + scrollDirection: Axis.horizontal, + child: Row( + children: [ + _choiceChipWidget(0), + SizedBox(width: 8), + _choiceChipWidget(1), + SizedBox(width: 8), + _choiceChipWidget(2), + SizedBox(width: 8), + _choiceChipWidget(3), + SizedBox(width: 8), + _choiceChipWidget(4), + ], ), ), ), ); } - _buildSearch(BuildContext context) { - return Container( - margin: EdgeInsets.only(bottom: 10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], + _addBlogPostButton() { + return InkWell( + onTap: () => GoRouter.of(context).pushNamed('blogPostCreateScreen'), + borderRadius: BorderRadius.circular(64), + child: Ink( + decoration: BoxDecoration( + color: appTheme.blueA400, + shape: BoxShape.circle, + ), + child: Icon( + Icons.add_rounded, + color: appTheme.gray100, + ), + width: 40, + height: 40, ), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - children: [ - CustomSearchView( - hintText: "Поиск записей", - ), - SizedBox(height: 10), - Row( - children: [ - Flexible( - flex: 5, - child: CustomOutlinedButton( - text: "Найти запись", - buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - ), - ), - SizedBox(width: 8), // Add some spacing between the buttons - Flexible( - flex: 1, - child: CustomOutlinedButton( - text: "+", - onPressed: () => GoRouter.of(context).pushNamed('blogPostCreateScreen'), - buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - ), - ), - ], - ), - ], + ); + } + + _searchBar() { + return SizedBox( + height: 40, + width: MediaQuery.of(context).size.width - 84, + child: TextFormField( + style: TextStyle( + fontSize: 12, + ), + decoration: InputDecoration( + hintText: 'Поиск', + hintStyle: TextStyle( + fontSize: 12, + color: appTheme.gray600, + fontWeight: FontWeight.w400, + ), + prefixIcon: Icon( + Icons.search, + color: appTheme.gray600, + size: 16, + ), + isDense: false, + contentPadding: EdgeInsets.all(8), + fillColor: appTheme.gray100, + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), ), ), ); } - _buildBlogPostList(BuildContext context, FeedStateLoaded state) { - List postWidgets = []; - - for (var post in state.feedBlogPostList) { - postWidgets.add(_buildFeedEntry(post, context)); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: postWidgets, - ); - } - - _buildFeedEntry(FeedBlogPostModel post, BuildContext context) { - return Container( - margin: EdgeInsets.only(bottom: 10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], + _choiceChipWidget(int index) { + return ChoiceChip( + selectedColor: appTheme.blueA400, + pressElevation: 0, + showCheckmark: false, + side: BorderSide.none, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - child: Column( - children: [ - ListTile( - leading: CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + (''), - - /// todo profile photo path - imageBuilder: (context, imageProvider) => CircleAvatar( - backgroundImage: imageProvider, - radius: 24, - ), - placeholder: (context, url) => CircleAvatar( - child: Icon(Icons.person), - backgroundColor: Colors.white, - radius: 24, - ), - errorWidget: (context, url, error) => CircleAvatar( - child: Icon(Icons.person), - backgroundColor: Colors.white, - radius: 24, - ), - ), - title: Text( - 'Authorae', - - /// todo author name - style: CustomTextStyles.titleMediumSemiBold, - ), - subtitle: Text( - DateFormat('dd.MM.yyyy').format(post.updatedAt), - style: theme.textTheme.bodyMedium, - ), - onTap: () => GoRouter.of(context).goNamed( - 'blogPostScreen', - extra: post.id.toString(), - ), - trailing: PopupMenuButton( - onSelected: (value) => print("tap"), - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - value: 'edit', - child: Text('Редактировать'), - ), - PopupMenuItem( - value: 'delete', - child: Text('Удалить'), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Spacer(), - Align( - alignment: Alignment.center, - child: Text( - post.title, - style: CustomTextStyles.titleMediumBlack, - ), - ), - Spacer(), - ], - ), - SizedBox(height: 8.0), - Text( - post.description, - style: CustomTextStyles.bodyMediumBluegray800, - ), - SizedBox(height: 16.0), - CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + (post.image), - imageBuilder: (context, imageProvider) => Stack( - children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8.0)), - child: Image( - image: imageProvider, - ), - ), - ], - ), - placeholder: (context, url) => CircularProgressIndicator(), - ), - SizedBox(height: 16.0), - Row( - children: [ - Padding( - padding: EdgeInsets.only(left: 4), - child: Row( - children: [ - Padding( - padding: EdgeInsets.only(right: 4), - child: Icon(Icons.comment_outlined), - ), - Text( - "Комментировать", - ), - ], - ), - ), - Spacer(), - Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 4), - child: Icon(Icons.favorite_outline), - ), - Text( - "Нравится", - ), - ], - ), - Spacer(), - Padding( - padding: EdgeInsets.only(right: 4), - child: Icon(Icons.share_outlined), - ), - ], - ), - ], - ), - ), - ], - ), - ); - } - - Function onTapBack(BuildContext context) { - return () { - onTapArrowLeft(context); - }; - } - - onTapArrowLeft(BuildContext context) { - GoRouter.of(context).pop(); - } - - _buildMenu(BuildContext context, FeedStateLoaded state) { - return Container( - padding: EdgeInsets.all(12), - margin: EdgeInsets.only(bottom: 10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], - ), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - ScrollItem(title: 'Все'), - ScrollItem(title: 'Рецепты'), - ScrollItem(title: 'Диеты'), - ScrollItem(title: 'Планы'), - ScrollItem(title: 'Записи'), - ], - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + label: Text(chipsLabelList[index]), + labelStyle: TextStyle( + color: index == _selectedChipIndex ? Colors.white : appTheme.gray600, + fontWeight: FontWeight.w500, + fontSize: 12, ), + selected: _selectedChipIndex == index, + backgroundColor: appTheme.gray100, + onSelected: (bool selected) { + setState(() { + _selectedChipIndex = index; + }); + }, ); } } diff --git a/lib/features/feed/presentation/pages/feed_screen_new.dart b/lib/features/feed/presentation/pages/feed_screen_new.dart deleted file mode 100644 index 6b5e1835..00000000 --- a/lib/features/feed/presentation/pages/feed_screen_new.dart +++ /dev/null @@ -1,214 +0,0 @@ -import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; -import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart'; -import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart'; -import 'package:eatvio_app/theme/theme_helper.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; - -class FeedScreenNew extends StatefulWidget { - const FeedScreenNew({super.key}); - - @override - State createState() => _FeedScreenNewState(); -} - -class _FeedScreenNewState extends State { - final List chipsLabelList = ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты']; - - int _selectedChipIndex = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Colors.white, - toolbarHeight: 0, - systemOverlayStyle: SystemUiOverlayStyle( - statusBarColor: Colors.white, - ), - ), - backgroundColor: Colors.grey.shade200, - body: NestedScrollView( - headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), - ), - backgroundColor: Colors.white, - foregroundColor: Colors.white, - automaticallyImplyLeading: false, - stretchTriggerOffset: 1, - floating: true, - snap: true, - scrolledUnderElevation: 0, - bottom: _chipGroup(), - title: Row( - children: [ - _searchBar(), - SizedBox(width: 12), - _addBlogPostButton(), - ], - ), - ), - ]; - }, - body: _buildAllBlogPosts(), - ), - ); - } - - _buildAllBlogPosts() { - return RefreshIndicator( - onRefresh: () => context.read().loadFeedBlogPostList(), - child: BlocBuilder( - builder: (BuildContext context, FeedState state) { - if (state is FeedStateLoaded) { - return ListView.separated( - padding: EdgeInsets.symmetric(vertical: 8), - shrinkWrap: true, - physics: BouncingScrollPhysics(), - itemBuilder: (context, index) { - return BlogPostWidget(feedBlogPostModel: state.feedBlogPostList[index]); - }, - separatorBuilder: (context, index) { - return SizedBox(height: 8); - }, - itemCount: state.feedBlogPostList.length, - ); - } else if (state is FeedStateError) { - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(16), - child: Center( - child: Text(state.errorMessage), - ), - ), - ); - } else { - return Scaffold( - body: Center( - child: CupertinoActivityIndicator(), - ), - ); - } - }, - ), - ); - } - - _chipGroup() { - return PreferredSize( - preferredSize: Size(MediaQuery.of(context).size.width, 32), - child: Align( - alignment: Alignment.centerLeft, - child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: 16), - scrollDirection: Axis.horizontal, - child: Row( - children: [ - _choiceChipWidget(0), - SizedBox(width: 8), - _choiceChipWidget(1), - SizedBox(width: 8), - _choiceChipWidget(2), - SizedBox(width: 8), - _choiceChipWidget(3), - SizedBox(width: 8), - _choiceChipWidget(4), - ], - ), - ), - ), - ); - } - - _addBlogPostButton() { - return InkWell( - onTap: () => GoRouter.of(context).pushNamed('blogPostCreateScreen'), - borderRadius: BorderRadius.circular(64), - child: Ink( - decoration: BoxDecoration( - color: appTheme.blueA400, - shape: BoxShape.circle, - ), - child: Icon( - Icons.add_rounded, - color: appTheme.gray100, - ), - width: 40, - height: 40, - ), - ); - } - - _searchBar() { - return SizedBox( - height: 40, - width: MediaQuery.of(context).size.width - 84, - child: TextFormField( - style: TextStyle( - fontSize: 12, - ), - decoration: InputDecoration( - hintText: 'Поиск', - hintStyle: TextStyle( - fontSize: 12, - color: appTheme.gray600, - fontWeight: FontWeight.w400, - ), - prefixIcon: Icon( - Icons.search, - color: appTheme.gray600, - size: 16, - ), - isDense: false, - contentPadding: EdgeInsets.all(8), - fillColor: appTheme.gray100, - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide.none, - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide.none, - ), - ), - ), - ); - } - - _choiceChipWidget(int index) { - return ChoiceChip( - selectedColor: appTheme.blueA400, - pressElevation: 0, - showCheckmark: false, - side: BorderSide.none, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - label: Text(chipsLabelList[index]), - labelStyle: TextStyle( - color: index == _selectedChipIndex ? Colors.white : appTheme.gray600, - fontWeight: FontWeight.w500, - fontSize: 12, - ), - selected: _selectedChipIndex == index, - backgroundColor: appTheme.gray100, - onSelected: (bool selected) { - setState(() { - _selectedChipIndex = index; - }); - }, - ); - } -} diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart index abfba976..1139fe0f 100644 --- a/lib/features/feed/presentation/widgets/blog_post_widget.dart +++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart @@ -19,6 +19,7 @@ class _BlogPostWidgetState extends State { @override Widget build(BuildContext context) { return InkWell( + borderRadius: BorderRadius.circular(12), onTap: () => GoRouter.of(context).pushNamed( 'blogPostScreen', extra: widget.feedBlogPostModel.id.toString(), @@ -52,12 +53,23 @@ class _BlogPostWidgetState extends State { if (widget.feedBlogPostModel.title.isNotEmpty || widget.feedBlogPostModel.description.isNotEmpty) SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text(widget.feedBlogPostModel.title), + child: Text( + widget.feedBlogPostModel.title, + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), ), if (widget.feedBlogPostModel.title.isNotEmpty && widget.feedBlogPostModel.description.isNotEmpty) SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text(widget.feedBlogPostModel.description), + child: Text( + widget.feedBlogPostModel.description, + style: TextStyle( + color: Colors.black87, + ), + ), ), if (widget.feedBlogPostModel.title.isNotEmpty || widget.feedBlogPostModel.description.isNotEmpty) SizedBox(height: 8), GestureDetector( diff --git a/lib/features/feed/presentation/widgets/recipes_widget.dart b/lib/features/feed/presentation/widgets/recipes_widget.dart new file mode 100644 index 00000000..233e45bb --- /dev/null +++ b/lib/features/feed/presentation/widgets/recipes_widget.dart @@ -0,0 +1,187 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dismissible_page/dismissible_page.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; +import 'package:flutter/material.dart'; + +class RecipesWidget extends StatefulWidget { + final Recipes recipes; + + const RecipesWidget({super.key, required this.recipes}); + + @override + State createState() => _RecipesWidgetState(); +} + +class _RecipesWidgetState extends State { + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadius.circular(12), + onTap: () {}, + child: Ink( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.bottomLeft, + children: [ + ClipRRect( + borderRadius: BorderRadius.vertical(top: Radius.circular(12)), + child: GestureDetector( + onTap: () { + context.pushTransparentRoute( + rootNavigator: true, + PhotoViewScreen( + imageUrls: [baseUrl + '/storage/' + widget.recipes.image], + index: 0, + ), + ); + }, + child: Hero( + tag: baseUrl + '/storage/' + widget.recipes.image, + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + widget.recipes.image, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.white54, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Icon( + Icons.visibility_rounded, + size: 20, + color: Colors.black, + ), + SizedBox(width: 4), + Text( + '0', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + SizedBox(width: 8), + Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.white54, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Icon( + Icons.favorite_border_rounded, + size: 20, + color: Colors.black, + ), + SizedBox(width: 4), + Text( + '0', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Text( + widget.recipes.title, + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + ), + SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Text( + widget.recipes.description, + style: TextStyle( + color: Colors.black87, + ), + ), + ), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Wrap( + runSpacing: 8, + children: [ + _iconButton(Icons.fastfood, '139.9 ккал'), + SizedBox(width: 8), + _iconButton(Icons.timer_rounded, widget.recipes.cookingTime.toString() + ' минут'), + SizedBox(width: 8), + _iconButton(Icons.fast_forward_rounded, widget.recipes.steps.length.toString() + ' этапов'), + ], + ), + ), + SizedBox(height: 8), + ], + ), + ), + ); + } + + _iconButton(IconData icon, [String? text]) { + return Ink( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: appTheme.gray100, + borderRadius: BorderRadius.circular(16), + ), + height: 32, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + size: 20, + color: appTheme.gray600, + ), + if (text != null) SizedBox(width: 8), + if (text != null) + Text( + text, + style: TextStyle( + fontSize: 14, + color: appTheme.gray600, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/feed/repositories/feed_repository.dart b/lib/features/feed/repositories/feed_repository.dart index 319ac32e..60cd1df6 100644 --- a/lib/features/feed/repositories/feed_repository.dart +++ b/lib/features/feed/repositories/feed_repository.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/feed/services/feed_service.dart'; class FeedRepository { @@ -13,4 +14,12 @@ class FeedRepository { rethrow; } } + + Future> getRecipes() async { + try { + return (await feedService.getRecipes()).recipesList; + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/feed/services/feed_service.dart b/lib/features/feed/services/feed_service.dart index d96ba1cc..d9bf881b 100644 --- a/lib/features/feed/services/feed_service.dart +++ b/lib/features/feed/services/feed_service.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_settings_model.dart'; +import 'package:eatvio_app/features/feed/models/recipes_pagination_model.dart'; import 'package:retrofit/retrofit.dart'; part 'feed_service.g.dart'; @@ -10,4 +11,7 @@ abstract class FeedService { @GET('/api/blogPosts/index') Future getBlocPostList(); + + @GET('/api/recipes/index') + Future getRecipes(); } diff --git a/lib/features/feed/services/feed_service.g.dart b/lib/features/feed/services/feed_service.g.dart index 89003f6e..fbccc937 100644 --- a/lib/features/feed/services/feed_service.g.dart +++ b/lib/features/feed/services/feed_service.g.dart @@ -45,6 +45,33 @@ class _FeedService implements FeedService { return _value; } + @override + Future getRecipes() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _result = await _dio + .fetch>(_setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/recipes/index', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final _value = RecipesPagination.fromJson(_result.data!); + return _value; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/locator_service.dart b/lib/locator_service.dart index 592aff96..5947a11c 100644 --- a/lib/locator_service.dart +++ b/lib/locator_service.dart @@ -13,6 +13,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_ import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/chat/services/chat_service.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; import 'package:eatvio_app/features/feed/services/feed_service.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart'; @@ -34,6 +35,7 @@ Future init() async { sl.registerFactory(() => RegistrationCubit(registrationRepository: sl())); sl.registerFactory(() => ChatRoomCubit(chatRepository: sl(), initializationCubit: sl())); sl.registerFactory(() => FeedCubit(feedRepository: sl())); + sl.registerFactory(() => RecipesCubit(feedRepository: sl())); sl.registerFactory(() => BlogPostCubit(blogPostRepository: sl())); sl.registerFactory(() => DiaryBloc(diaryRepository: sl())); sl.registerFactory(() => CalendarCubit(calendarRepository: sl())); diff --git a/lib/main.dart b/lib/main.dart index d9bc52c6..03f769a7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.da import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart'; import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; import 'package:eatvio_app/locator_service.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart'; import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart'; @@ -48,6 +49,7 @@ class MyApp extends StatelessWidget { BlocProvider(create: (context) => sl()), BlocProvider(create: (context) => ThemeBloc(ThemeState(themeType: PrefUtils().getThemeData()))), BlocProvider(create: (context) => sl()..loadFeedBlogPostList()), + BlocProvider(create: (context) => sl()..loadRecipesList()), BlocProvider(create: (context) => MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))..add(MyProfileInitialEvent())), BlocProvider(create: (context) => sl()..add(LoadDiary())), BlocProvider(