feat(recipe): Recipe edit screen and API

This commit is contained in:
2024-08-16 13:38:23 +03:00
parent aced7e8bcb
commit 1c63127eb5
20 changed files with 803 additions and 500 deletions

View File

@@ -16,6 +16,7 @@ import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart';
import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/about_recipes_screen.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/recipe_edit_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';
@@ -102,6 +103,13 @@ class AppRouter {
name: 'recipeCreateScreen',
builder: (context, state) => RecipeCreateScreen(),
),
GoRoute(
path: '/recipe-edit-screen',
name: 'recipeEditScreen',
builder: (context, state) => RecipeEditScreen(
recipeID: state.extra as String,
),
),
GoRoute(
path: '/profile-screen',
name: 'profileScreen',

View File

@@ -1,14 +0,0 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'image.freezed.dart';
part 'image.g.dart';
@freezed
class Image with _$Image {
const factory Image({
String? path,
}) = _Image;
factory Image.fromJson(Map<String, Object?> json) => _$ImageFromJson(json);
}

View File

@@ -1,147 +0,0 @@
// 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 'image.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
Image _$ImageFromJson(Map<String, dynamic> json) {
return _Image.fromJson(json);
}
/// @nodoc
mixin _$Image {
String? get path => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ImageCopyWith<Image> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ImageCopyWith<$Res> {
factory $ImageCopyWith(Image value, $Res Function(Image) then) =
_$ImageCopyWithImpl<$Res, Image>;
@useResult
$Res call({String? path});
}
/// @nodoc
class _$ImageCopyWithImpl<$Res, $Val extends Image>
implements $ImageCopyWith<$Res> {
_$ImageCopyWithImpl(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? path = freezed,
}) {
return _then(_value.copyWith(
path: freezed == path
? _value.path
: path // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$ImageImplCopyWith<$Res> implements $ImageCopyWith<$Res> {
factory _$$ImageImplCopyWith(
_$ImageImpl value, $Res Function(_$ImageImpl) then) =
__$$ImageImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String? path});
}
/// @nodoc
class __$$ImageImplCopyWithImpl<$Res>
extends _$ImageCopyWithImpl<$Res, _$ImageImpl>
implements _$$ImageImplCopyWith<$Res> {
__$$ImageImplCopyWithImpl(
_$ImageImpl _value, $Res Function(_$ImageImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? path = freezed,
}) {
return _then(_$ImageImpl(
path: freezed == path
? _value.path
: path // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ImageImpl implements _Image {
const _$ImageImpl({this.path});
factory _$ImageImpl.fromJson(Map<String, dynamic> json) =>
_$$ImageImplFromJson(json);
@override
final String? path;
@override
String toString() {
return 'Image(path: $path)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ImageImpl &&
(identical(other.path, path) || other.path == path));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, path);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ImageImplCopyWith<_$ImageImpl> get copyWith =>
__$$ImageImplCopyWithImpl<_$ImageImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ImageImplToJson(
this,
);
}
}
abstract class _Image implements Image {
const factory _Image({final String? path}) = _$ImageImpl;
factory _Image.fromJson(Map<String, dynamic> json) = _$ImageImpl.fromJson;
@override
String? get path;
@override
@JsonKey(ignore: true)
_$$ImageImplCopyWith<_$ImageImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -1,16 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'image.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ImageImpl _$$ImageImplFromJson(Map<String, dynamic> json) => _$ImageImpl(
path: json['path'] as String?,
);
Map<String, dynamic> _$$ImageImplToJson(_$ImageImpl instance) =>
<String, dynamic>{
'path': instance.path,
};

View File

@@ -7,12 +7,10 @@ part 'ingredient.g.dart';
@freezed
class Ingredient with _$Ingredient {
const factory Ingredient({
int? id,
required String product_name,
required String name,
required String unit,
required num portion,
required String portion,
}) = _Ingredient;
factory Ingredient.fromJson(Map<String, Object?> json) =>
_$IngredientFromJson(json);
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
}

View File

@@ -20,10 +20,9 @@ Ingredient _$IngredientFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$Ingredient {
int? get id => throw _privateConstructorUsedError;
String get product_name => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get unit => throw _privateConstructorUsedError;
num get portion => throw _privateConstructorUsedError;
String get portion => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -37,7 +36,7 @@ abstract class $IngredientCopyWith<$Res> {
Ingredient value, $Res Function(Ingredient) then) =
_$IngredientCopyWithImpl<$Res, Ingredient>;
@useResult
$Res call({int? id, String product_name, String unit, num portion});
$Res call({String name, String unit, String portion});
}
/// @nodoc
@@ -53,19 +52,14 @@ class _$IngredientCopyWithImpl<$Res, $Val extends Ingredient>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? product_name = null,
Object? name = null,
Object? unit = null,
Object? portion = null,
}) {
return _then(_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
product_name: null == product_name
? _value.product_name
: product_name // ignore: cast_nullable_to_non_nullable
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
unit: null == unit
? _value.unit
@@ -74,7 +68,7 @@ class _$IngredientCopyWithImpl<$Res, $Val extends Ingredient>
portion: null == portion
? _value.portion
: portion // ignore: cast_nullable_to_non_nullable
as num,
as String,
) as $Val);
}
}
@@ -87,7 +81,7 @@ abstract class _$$IngredientImplCopyWith<$Res>
__$$IngredientImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int? id, String product_name, String unit, num portion});
$Res call({String name, String unit, String portion});
}
/// @nodoc
@@ -101,19 +95,14 @@ class __$$IngredientImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? product_name = null,
Object? name = null,
Object? unit = null,
Object? portion = null,
}) {
return _then(_$IngredientImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
product_name: null == product_name
? _value.product_name
: product_name // ignore: cast_nullable_to_non_nullable
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
unit: null == unit
? _value.unit
@@ -122,7 +111,7 @@ class __$$IngredientImplCopyWithImpl<$Res>
portion: null == portion
? _value.portion
: portion // ignore: cast_nullable_to_non_nullable
as num,
as String,
));
}
}
@@ -131,26 +120,21 @@ class __$$IngredientImplCopyWithImpl<$Res>
@JsonSerializable()
class _$IngredientImpl implements _Ingredient {
const _$IngredientImpl(
{this.id,
required this.product_name,
required this.unit,
required this.portion});
{required this.name, required this.unit, required this.portion});
factory _$IngredientImpl.fromJson(Map<String, dynamic> json) =>
_$$IngredientImplFromJson(json);
@override
final int? id;
@override
final String product_name;
final String name;
@override
final String unit;
@override
final num portion;
final String portion;
@override
String toString() {
return 'Ingredient(id: $id, product_name: $product_name, unit: $unit, portion: $portion)';
return 'Ingredient(name: $name, unit: $unit, portion: $portion)';
}
@override
@@ -158,16 +142,14 @@ class _$IngredientImpl implements _Ingredient {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$IngredientImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.product_name, product_name) ||
other.product_name == product_name) &&
(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, id, product_name, unit, portion);
int get hashCode => Object.hash(runtimeType, name, unit, portion);
@JsonKey(ignore: true)
@override
@@ -185,22 +167,19 @@ class _$IngredientImpl implements _Ingredient {
abstract class _Ingredient implements Ingredient {
const factory _Ingredient(
{final int? id,
required final String product_name,
{required final String name,
required final String unit,
required final num portion}) = _$IngredientImpl;
required final String portion}) = _$IngredientImpl;
factory _Ingredient.fromJson(Map<String, dynamic> json) =
_$IngredientImpl.fromJson;
@override
int? get id;
@override
String get product_name;
String get name;
@override
String get unit;
@override
num get portion;
String get portion;
@override
@JsonKey(ignore: true)
_$$IngredientImplCopyWith<_$IngredientImpl> get copyWith =>

View File

@@ -8,16 +8,14 @@ part of 'ingredient.dart';
_$IngredientImpl _$$IngredientImplFromJson(Map<String, dynamic> json) =>
_$IngredientImpl(
id: (json['id'] as num?)?.toInt(),
product_name: json['product_name'] as String,
name: json['name'] as String,
unit: json['unit'] as String,
portion: json['portion'] as num,
portion: json['portion'] as String,
);
Map<String, dynamic> _$$IngredientImplToJson(_$IngredientImpl instance) =>
<String, dynamic>{
'id': instance.id,
'product_name': instance.product_name,
'name': instance.name,
'unit': instance.unit,
'portion': instance.portion,
};

View File

@@ -1,4 +1,3 @@
import 'package:eatvio_app/features/recipes/models/image.dart';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/models/step.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -9,16 +8,15 @@ part 'recipe_model.g.dart';
@freezed
class RecipeModel with _$RecipeModel {
const factory RecipeModel({
int? id,
required List<Ingredient> ingredients,
required List<Step> steps,
required String title,
required String state,
required String description,
required String type,
required Image image,
List<Ingredient?>? ingredients,
List<Step?>? steps,
String? type,
String? state,
String? title,
String? description,
String? video_url,
num? cooking_time,
String? image,
int? cooking_time,
}) = _RecipeModel;
factory RecipeModel.fromJson(Map<String, Object?> json) => _$RecipeModelFromJson(json);

View File

@@ -20,16 +20,15 @@ RecipeModel _$RecipeModelFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$RecipeModel {
int? get id => throw _privateConstructorUsedError;
List<Ingredient> get ingredients => throw _privateConstructorUsedError;
List<Step> get steps => throw _privateConstructorUsedError;
String get title => throw _privateConstructorUsedError;
String get state => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
String get type => throw _privateConstructorUsedError;
Image get image => throw _privateConstructorUsedError;
List<Ingredient?>? get ingredients => throw _privateConstructorUsedError;
List<Step?>? get steps => throw _privateConstructorUsedError;
String? get type => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
String? get title => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError;
String? get video_url => throw _privateConstructorUsedError;
num? get cooking_time => throw _privateConstructorUsedError;
String? get image => throw _privateConstructorUsedError;
int? get cooking_time => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -44,18 +43,15 @@ abstract class $RecipeModelCopyWith<$Res> {
_$RecipeModelCopyWithImpl<$Res, RecipeModel>;
@useResult
$Res call(
{int? id,
List<Ingredient> ingredients,
List<Step> steps,
String title,
String state,
String description,
String type,
Image image,
{List<Ingredient?>? ingredients,
List<Step?>? steps,
String? type,
String? state,
String? title,
String? description,
String? video_url,
num? cooking_time});
$ImageCopyWith<$Res> get image;
String? image,
int? cooking_time});
}
/// @nodoc
@@ -71,68 +67,55 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? ingredients = null,
Object? steps = null,
Object? title = null,
Object? state = null,
Object? description = null,
Object? type = null,
Object? image = null,
Object? ingredients = freezed,
Object? steps = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
Object? cooking_time = freezed,
}) {
return _then(_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
ingredients: null == ingredients
ingredients: freezed == ingredients
? _value.ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
as List<Ingredient>,
steps: null == steps
as List<Ingredient?>?,
steps: freezed == steps
? _value.steps
: steps // ignore: cast_nullable_to_non_nullable
as List<Step>,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
type: null == type
as List<Step?>?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String,
image: null == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as Image,
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
title: freezed == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
description: freezed == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String?,
video_url: freezed == video_url
? _value.video_url
: video_url // ignore: cast_nullable_to_non_nullable
as String?,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
cooking_time: freezed == cooking_time
? _value.cooking_time
: cooking_time // ignore: cast_nullable_to_non_nullable
as num?,
as int?,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$ImageCopyWith<$Res> get image {
return $ImageCopyWith<$Res>(_value.image, (value) {
return _then(_value.copyWith(image: value) as $Val);
});
}
}
/// @nodoc
@@ -144,19 +127,15 @@ abstract class _$$RecipeModelImplCopyWith<$Res>
@override
@useResult
$Res call(
{int? id,
List<Ingredient> ingredients,
List<Step> steps,
String title,
String state,
String description,
String type,
Image image,
{List<Ingredient?>? ingredients,
List<Step?>? steps,
String? type,
String? state,
String? title,
String? description,
String? video_url,
num? cooking_time});
@override
$ImageCopyWith<$Res> get image;
String? image,
int? cooking_time});
}
/// @nodoc
@@ -170,58 +149,53 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? ingredients = null,
Object? steps = null,
Object? title = null,
Object? state = null,
Object? description = null,
Object? type = null,
Object? image = null,
Object? ingredients = freezed,
Object? steps = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
Object? cooking_time = freezed,
}) {
return _then(_$RecipeModelImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
ingredients: null == ingredients
ingredients: freezed == ingredients
? _value._ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
as List<Ingredient>,
steps: null == steps
as List<Ingredient?>?,
steps: freezed == steps
? _value._steps
: steps // ignore: cast_nullable_to_non_nullable
as List<Step>,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
type: null == type
as List<Step?>?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String,
image: null == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as Image,
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
title: freezed == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
description: freezed == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String?,
video_url: freezed == video_url
? _value.video_url
: video_url // ignore: cast_nullable_to_non_nullable
as String?,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
cooking_time: freezed == cooking_time
? _value.cooking_time
: cooking_time // ignore: cast_nullable_to_non_nullable
as num?,
as int?,
));
}
}
@@ -230,15 +204,14 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
@JsonSerializable()
class _$RecipeModelImpl implements _RecipeModel {
const _$RecipeModelImpl(
{this.id,
required final List<Ingredient> ingredients,
required final List<Step> steps,
required this.title,
required this.state,
required this.description,
required this.type,
required this.image,
{final List<Ingredient?>? ingredients,
final List<Step?>? steps,
this.type,
this.state,
this.title,
this.description,
this.video_url,
this.image,
this.cooking_time})
: _ingredients = ingredients,
_steps = steps;
@@ -246,42 +219,44 @@ class _$RecipeModelImpl implements _RecipeModel {
factory _$RecipeModelImpl.fromJson(Map<String, dynamic> json) =>
_$$RecipeModelImplFromJson(json);
final List<Ingredient?>? _ingredients;
@override
final int? id;
final List<Ingredient> _ingredients;
@override
List<Ingredient> get ingredients {
List<Ingredient?>? get ingredients {
final value = _ingredients;
if (value == null) return null;
if (_ingredients is EqualUnmodifiableListView) return _ingredients;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ingredients);
return EqualUnmodifiableListView(value);
}
final List<Step> _steps;
final List<Step?>? _steps;
@override
List<Step> get steps {
List<Step?>? get steps {
final value = _steps;
if (value == null) return null;
if (_steps is EqualUnmodifiableListView) return _steps;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_steps);
return EqualUnmodifiableListView(value);
}
@override
final String title;
final String? type;
@override
final String state;
final String? state;
@override
final String description;
final String? title;
@override
final String type;
@override
final Image image;
final String? description;
@override
final String? video_url;
@override
final num? cooking_time;
final String? image;
@override
final int? cooking_time;
@override
String toString() {
return 'RecipeModel(id: $id, ingredients: $ingredients, steps: $steps, title: $title, state: $state, description: $description, type: $type, image: $image, video_url: $video_url, cooking_time: $cooking_time)';
return 'RecipeModel(ingredients: $ingredients, steps: $steps, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, cooking_time: $cooking_time)';
}
@override
@@ -289,18 +264,17 @@ class _$RecipeModelImpl implements _RecipeModel {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RecipeModelImpl &&
(identical(other.id, id) || other.id == id) &&
const DeepCollectionEquality()
.equals(other._ingredients, _ingredients) &&
const DeepCollectionEquality().equals(other._steps, _steps) &&
(identical(other.title, title) || other.title == title) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.title, title) || other.title == title) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.image, image) || other.image == image) &&
(identical(other.video_url, video_url) ||
other.video_url == video_url) &&
(identical(other.image, image) || other.image == image) &&
(identical(other.cooking_time, cooking_time) ||
other.cooking_time == cooking_time));
}
@@ -309,15 +283,14 @@ class _$RecipeModelImpl implements _RecipeModel {
@override
int get hashCode => Object.hash(
runtimeType,
id,
const DeepCollectionEquality().hash(_ingredients),
const DeepCollectionEquality().hash(_steps),
title,
state,
description,
type,
image,
state,
title,
description,
video_url,
image,
cooking_time);
@JsonKey(ignore: true)
@@ -336,40 +309,37 @@ class _$RecipeModelImpl implements _RecipeModel {
abstract class _RecipeModel implements RecipeModel {
const factory _RecipeModel(
{final int? id,
required final List<Ingredient> ingredients,
required final List<Step> steps,
required final String title,
required final String state,
required final String description,
required final String type,
required final Image image,
{final List<Ingredient?>? ingredients,
final List<Step?>? steps,
final String? type,
final String? state,
final String? title,
final String? description,
final String? video_url,
final num? cooking_time}) = _$RecipeModelImpl;
final String? image,
final int? cooking_time}) = _$RecipeModelImpl;
factory _RecipeModel.fromJson(Map<String, dynamic> json) =
_$RecipeModelImpl.fromJson;
@override
int? get id;
List<Ingredient?>? get ingredients;
@override
List<Ingredient> get ingredients;
List<Step?>? get steps;
@override
List<Step> get steps;
String? get type;
@override
String get title;
String? get state;
@override
String get state;
String? get title;
@override
String get description;
@override
String get type;
@override
Image get image;
String? get description;
@override
String? get video_url;
@override
num? get cooking_time;
String? get image;
@override
int? get cooking_time;
@override
@JsonKey(ignore: true)
_$$RecipeModelImplCopyWith<_$RecipeModelImpl> get copyWith =>

View File

@@ -8,32 +8,32 @@ part of 'recipe_model.dart';
_$RecipeModelImpl _$$RecipeModelImplFromJson(Map<String, dynamic> json) =>
_$RecipeModelImpl(
id: (json['id'] as num?)?.toInt(),
ingredients: (json['ingredients'] as List<dynamic>)
.map((e) => Ingredient.fromJson(e as Map<String, dynamic>))
ingredients: (json['ingredients'] as List<dynamic>?)
?.map((e) =>
e == null ? null : Ingredient.fromJson(e as Map<String, dynamic>))
.toList(),
steps: (json['steps'] as List<dynamic>)
.map((e) => Step.fromJson(e as Map<String, dynamic>))
steps: (json['steps'] as List<dynamic>?)
?.map((e) =>
e == null ? null : Step.fromJson(e as Map<String, dynamic>))
.toList(),
title: json['title'] as String,
state: json['state'] as String,
description: json['description'] as String,
type: json['type'] as String,
image: Image.fromJson(json['image'] as Map<String, dynamic>),
type: json['type'] as String?,
state: json['state'] as String?,
title: json['title'] as String?,
description: json['description'] as String?,
video_url: json['video_url'] as String?,
cooking_time: json['cooking_time'] as num?,
image: json['image'] as String?,
cooking_time: (json['cooking_time'] as num?)?.toInt(),
);
Map<String, dynamic> _$$RecipeModelImplToJson(_$RecipeModelImpl instance) =>
<String, dynamic>{
'id': instance.id,
'ingredients': instance.ingredients,
'steps': instance.steps,
'title': instance.title,
'state': instance.state,
'description': instance.description,
'type': instance.type,
'image': instance.image,
'state': instance.state,
'title': instance.title,
'description': instance.description,
'video_url': instance.video_url,
'image': instance.image,
'cooking_time': instance.cooking_time,
};

View File

@@ -1,4 +1,3 @@
import 'package:eatvio_app/features/recipes/models/image.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'step.freezed.dart';
@@ -8,8 +7,7 @@ part 'step.g.dart';
@freezed
class Step with _$Step {
const factory Step({
int? id,
Image? image,
String? image,
String? description,
}) = _Step;

View File

@@ -20,8 +20,7 @@ Step _$StepFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$Step {
int? get id => throw _privateConstructorUsedError;
Image? get image => throw _privateConstructorUsedError;
String? get image => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -34,9 +33,7 @@ abstract class $StepCopyWith<$Res> {
factory $StepCopyWith(Step value, $Res Function(Step) then) =
_$StepCopyWithImpl<$Res, Step>;
@useResult
$Res call({int? id, Image? image, String? description});
$ImageCopyWith<$Res>? get image;
$Res call({String? image, String? description});
}
/// @nodoc
@@ -52,37 +49,20 @@ class _$StepCopyWithImpl<$Res, $Val extends Step>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? image = freezed,
Object? description = freezed,
}) {
return _then(_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as Image?,
as String?,
description: freezed == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$ImageCopyWith<$Res>? get image {
if (_value.image == null) {
return null;
}
return $ImageCopyWith<$Res>(_value.image!, (value) {
return _then(_value.copyWith(image: value) as $Val);
});
}
}
/// @nodoc
@@ -92,10 +72,7 @@ abstract class _$$StepImplCopyWith<$Res> implements $StepCopyWith<$Res> {
__$$StepImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int? id, Image? image, String? description});
@override
$ImageCopyWith<$Res>? get image;
$Res call({String? image, String? description});
}
/// @nodoc
@@ -108,19 +85,14 @@ class __$$StepImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? image = freezed,
Object? description = freezed,
}) {
return _then(_$StepImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as Image?,
as String?,
description: freezed == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
@@ -132,21 +104,19 @@ class __$$StepImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$StepImpl implements _Step {
const _$StepImpl({this.id, this.image, this.description});
const _$StepImpl({this.image, this.description});
factory _$StepImpl.fromJson(Map<String, dynamic> json) =>
_$$StepImplFromJson(json);
@override
final int? id;
@override
final Image? image;
final String? image;
@override
final String? description;
@override
String toString() {
return 'Step(id: $id, image: $image, description: $description)';
return 'Step(image: $image, description: $description)';
}
@override
@@ -154,7 +124,6 @@ class _$StepImpl implements _Step {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$StepImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.image, image) || other.image == image) &&
(identical(other.description, description) ||
other.description == description));
@@ -162,7 +131,7 @@ class _$StepImpl implements _Step {
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, id, image, description);
int get hashCode => Object.hash(runtimeType, image, description);
@JsonKey(ignore: true)
@override
@@ -179,17 +148,13 @@ class _$StepImpl implements _Step {
}
abstract class _Step implements Step {
const factory _Step(
{final int? id,
final Image? image,
final String? description}) = _$StepImpl;
const factory _Step({final String? image, final String? description}) =
_$StepImpl;
factory _Step.fromJson(Map<String, dynamic> json) = _$StepImpl.fromJson;
@override
int? get id;
@override
Image? get image;
String? get image;
@override
String? get description;
@override

View File

@@ -7,16 +7,12 @@ part of 'step.dart';
// **************************************************************************
_$StepImpl _$$StepImplFromJson(Map<String, dynamic> json) => _$StepImpl(
id: (json['id'] as num?)?.toInt(),
image: json['image'] == null
? null
: Image.fromJson(json['image'] as Map<String, dynamic>),
image: json['image'] as String?,
description: json['description'] as String?,
);
Map<String, dynamic> _$$StepImplToJson(_$StepImpl instance) =>
<String, dynamic>{
'id': instance.id,
'image': instance.image,
'description': instance.description,
};

View File

@@ -12,6 +12,87 @@ class RecipeCreateCubit extends Cubit<RecipeCreateState> {
RecipeCreateCubit({required this.recipeRepository}) : super(const RecipeCreateState.loading());
loadRecipeById(String recipeID) async {
emit(const RecipeCreateStateLoading());
try {
final RecipeModel recipe = await recipeRepository.getRecipe(recipeID);
final List<SelectionPopupModel> types = [
SelectionPopupModel(title: 'Первые блюда', value: 'first_meals'),
SelectionPopupModel(title: 'Вторые блюда', value: 'second_meals'),
SelectionPopupModel(title: 'Cалаты', value: 'salads'),
SelectionPopupModel(title: 'Перекусы/закуски', value: 'snacks'),
SelectionPopupModel(title: 'Сладости', value: 'sweets'),
SelectionPopupModel(title: 'Напитки', value: 'drinks'),
SelectionPopupModel(title: 'Прочее', value: 'other'),
];
final List<SelectionPopupModel> cookingTimes = [
SelectionPopupModel(title: '10 минут', value: 10),
SelectionPopupModel(title: '20 минут', value: 20),
SelectionPopupModel(title: '30 минут', value: 30),
SelectionPopupModel(title: '1 час', value: 60),
SelectionPopupModel(title: '2 часа', value: 120),
SelectionPopupModel(title: '10 дней', value: 14400),
];
final List<SelectionPopupModel> statuses = [
SelectionPopupModel(title: 'Активна', value: 'active'),
SelectionPopupModel(title: 'Неактивна', value: 'inactive'),
];
final selectedType = types.firstWhere(
(type) => type.value == recipe.type,
orElse: () => types[0],
);
final selectedCookingTime = cookingTimes.firstWhere(
(time) => time.value == recipe.cooking_time,
orElse: () => cookingTimes[0],
);
final selectedStatus = statuses.firstWhere(
(status) => status.value == recipe.state,
orElse: () => statuses[0],
);
final stepControllers = recipe.steps?.map((step) {
return TextEditingController(text: step?.description);
}).toList();
final stepImages = recipe.steps?.map((step) {
return step?.image != null ? XFile(step!.image!) : null;
}).toList();
final ingredients = recipe.ingredients?.map((ingredient) {
return Ingredient(
name: ingredient!.name,
portion: ingredient.portion,
unit: ingredient.unit,
);
}).toList();
emit(RecipeCreateState.loaded(
picker: ImagePicker(),
recipeImage: XFile(recipe.image!),
titleController: TextEditingController(text: recipe.title),
productNameController: TextEditingController(),
ingredientController: TextEditingController(),
stepDescriptionController: TextEditingController(text: recipe.description),
youtubeLinkController: TextEditingController(text: recipe.video_url),
stepControllers: stepControllers ?? [],
stepImages: stepImages ?? [],
autocompleteList: [],
ingredients: ingredients ?? [],
numberOfSteps: 1,
ingredientAsItems: false,
types: types,
cookingTimes: cookingTimes,
statuses: statuses,
selectedType: selectedType,
selectedCookingTime: selectedCookingTime,
selectedStatus: selectedStatus));
} catch (e) {
emit(RecipeCreateState.error(errorMessage: e.toString()));
}
}
loadRecipeCreate() {
emit(const RecipeCreateStateLoading());
try {
@@ -117,7 +198,7 @@ class RecipeCreateCubit extends Cubit<RecipeCreateState> {
if (state is RecipeCreateStateLoaded) {
var currentState = state as RecipeCreateStateLoaded;
bool ingredientExists = currentState.ingredients.any((existingIngredient) => existingIngredient.product_name == ingredient.product_name);
bool ingredientExists = currentState.ingredients.any((existingIngredient) => existingIngredient.name == ingredient.name);
if (ingredientExists) {
emit(RecipeCreateState.error(errorMessage: 'Ингредиент уже существует.'));

View File

@@ -3,7 +3,6 @@ import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/features/diary/models/product.dart';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
import 'package:eatvio_app/features/recipes/models/image.dart' as image;
import 'package:eatvio_app/features/recipes/models/step.dart' as step;
import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_cubit.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_state.dart';
@@ -223,8 +222,8 @@ class RecipeCreateScreen extends StatelessWidget {
style: theme.textTheme.titleSmall,
),
onPressed: () => context.read<RecipeCreateCubit>().addIngredient(new Ingredient(
product_name: state.productNameController.text,
portion: int.parse(state.ingredientController.text),
name: state.productNameController.text,
portion: state.ingredientController.text,
unit: state.ingredientAsItems ? 'pcs' : 'gr'))),
),
SizedBox(height: 24),
@@ -378,7 +377,7 @@ class RecipeCreateScreen extends StatelessWidget {
top: 5.v,
),
child: Text(
ingredient.product_name.length > 8 ? ingredient.product_name.substring(0, 8) : ingredient.product_name,
ingredient.name.length > 8 ? ingredient.name.substring(0, 8) : ingredient.name,
style: CustomTextStyles.titleMedium16,
),
),
@@ -454,18 +453,15 @@ class RecipeCreateScreen extends StatelessWidget {
List<step.Step> steps = [];
for (int i = 0; i < state.stepControllers.length; i++) {
image.Image picStep = image.Image(path: state.stepImages[i]?.path ?? "");
if (state.stepControllers[i].text.isNotEmpty) {
steps.add(step.Step(
description: state.stepControllers[i].text,
image: picStep,
image: state.stepImages[i]?.path,
));
}
}
image.Image pic = image.Image(path: state.recipeImage?.path ?? '');
final RecipeModel recipe = RecipeModel(
id: null,
ingredients: ingredients,
steps: steps,
state: state.selectedStatus.value,
@@ -473,7 +469,7 @@ class RecipeCreateScreen extends StatelessWidget {
cooking_time: state.selectedCookingTime.value,
description: state.stepDescriptionController.text,
title: state.titleController.text,
image: pic,
image: state.recipeImage?.path,
video_url: state.youtubeLinkController.text.isNotEmpty ? state.youtubeLinkController.text : null,
);
print(recipe);

View File

@@ -0,0 +1,493 @@
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/features/diary/models/product.dart';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
import 'package:eatvio_app/features/recipes/models/step.dart' as step;
import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_cubit.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_state.dart';
import 'package:eatvio_app/widgets/custom_drop_down.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
// ignore: must_be_immutable
class RecipeEditScreen extends StatelessWidget {
final String recipeID;
const RecipeEditScreen({
required this.recipeID,
super.key,
});
Widget build(BuildContext context) {
if (recipeID != "0") {
context.read<RecipeCreateCubit>().loadRecipeById(recipeID);
}
return BlocBuilder<RecipeCreateCubit, RecipeCreateState>(
builder: (context, state) {
if (state is RecipeCreateStateLoaded) {
return _buildRecipeCreate(context, state);
} else if (state is RecipeCreateStateLoading) {
return Scaffold(
body: Center(
child: CupertinoActivityIndicator(),
),
);
} else if (state is RecipeCreateStateError) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage),
),
),
);
} else
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text("Неопределенная ошибка"),
),
),
);
},
);
}
Widget _buildRecipeCreate(BuildContext context, RecipeCreateStateLoaded state) {
List<String> _options = state.autocompleteList.map((Product item) => item.name).toList();
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
automaticallyImplyLeading: false,
),
backgroundColor: Color.fromARGB(255, 240, 242, 245),
resizeToAvoidBottomInset: true,
body: SizedBox(
width: double.maxFinite,
child: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Заголовок'),
SizedBox(height: 8),
CustomTextFormField(
fillColor: appTheme.gray50,
controller: state.titleController,
hintText: 'Заголовок',
),
SizedBox(height: 16),
Text('Тип блюда'),
SizedBox(height: 8),
CustomDropDown(
value: state.selectedType,
items: state.types,
onChanged: changeType(context),
),
SizedBox(height: 16),
Text('Изображение'),
GestureDetector(
onTap: () async {
final pickedFile = await state.picker.pickImage(source: ImageSource.gallery);
context.read<RecipeCreateCubit>().uploadRecipeImage(pickedFile);
},
child: state.recipeImage == null
? Container(
height: 128,
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.photo_library,
color: appTheme.blueGray300,
),
SizedBox(width: 8),
Text(
"Добавить фото",
selectionColor: appTheme.blueA400,
),
],
),
)
: Image.network(
baseUrl + '/storage/' + state.recipeImage!.path,
height: 150,
fit: BoxFit.cover,
),
),
SizedBox(height: 16),
Text('Время приготовления'),
SizedBox(height: 8),
CustomDropDown(
items: state.cookingTimes,
value: state.selectedCookingTime,
onChanged: changeCookingTime(context),
),
SizedBox(height: 16),
Text('Статус'),
SizedBox(height: 8),
CustomDropDown(
items: state.statuses,
value: state.selectedStatus,
onChanged: changeStatus(context),
),
SizedBox(height: 24),
if (!state.ingredients.isEmpty)
for (int j = 0; j < state.ingredients.length; j++) _buildIngredient(state.ingredients[j]),
SizedBox(height: 24),
RawAutocomplete<String>(
optionsBuilder: _buildOptions(_options),
fieldViewBuilder: (
BuildContext context,
productEditingController,
FocusNode focusNodeName,
VoidCallback onFieldSubmitted,
) {
return CustomTextFormField(
hintText: 'Введите название продукта',
controller: TextEditingController(text: state.productNameController.text),
focusNode: focusNodeName,
onChanged: getAutocomplete(context, focusNodeName),
);
},
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<String> onSelected,
Iterable<String> options,
) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
child: SizedBox(
height: 200.0,
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final String option = options.elementAt(index);
return GestureDetector(
onTap: selectProduct(onSelected, option, state),
child: ListTile(
title: Text(option),
),
);
},
),
),
),
);
},
),
SizedBox(height: 24),
CustomTextFormField(
hintText: 'Порция',
fillColor: appTheme.gray50,
controller: state.ingredientController,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
),
SizedBox(height: 8),
Row(children: [
Text('гр'),
Switch(
value: state.ingredientAsItems,
onChanged: (bool value) => context.read<RecipeCreateCubit>().switchUnits(value),
activeColor: appTheme.blueA400,
inactiveThumbColor: Colors.grey,
inactiveTrackColor: Colors.grey.shade300,
),
Text('шт'),
]),
SizedBox(height: 8),
Container(
height: 48,
width: MediaQuery.of(context).size.width,
child: FloatingActionButton.extended(
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: appTheme.blueA400,
label: Text(
'+',
style: theme.textTheme.titleSmall,
),
onPressed: () => context.read<RecipeCreateCubit>().addIngredient(new Ingredient(
name: state.productNameController.text,
portion: state.ingredientController.text,
unit: state.ingredientAsItems ? 'pcs' : 'gr'))),
),
SizedBox(height: 24),
Text('Описание рецепта'),
SizedBox(height: 8),
CustomTextFormField(
maxLines: 8,
minLines: 8,
controller: state.stepDescriptionController,
),
SizedBox(height: 24),
for (int i = 0; i < state.numberOfSteps; i++) _buildStep(context, i, state),
SizedBox(height: 24),
Container(
height: 48,
width: MediaQuery.of(context).size.width,
child: FloatingActionButton.extended(
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: appTheme.blueA400,
onPressed: () => context.read<RecipeCreateCubit>().addStep(),
label: Text(
'+',
style: theme.textTheme.titleSmall,
),
),
),
SizedBox(height: 8),
Container(
height: 48,
width: MediaQuery.of(context).size.width,
child: FloatingActionButton.extended(
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: Colors.grey,
onPressed: () => context.read<RecipeCreateCubit>().removeStep(),
label: Text(
'-',
style: theme.textTheme.titleSmall,
),
),
),
SizedBox(height: 24),
Text('Ссылка на видео (YouTube)'),
SizedBox(height: 8),
CustomTextFormField(
hintText: 'Введите описание шага',
fillColor: appTheme.gray50,
controller: state.youtubeLinkController,
),
SizedBox(height: 24),
Container(
height: 48,
width: MediaQuery.of(context).size.width,
child: FloatingActionButton.extended(
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: appTheme.blueA400,
onPressed: () => context.read<RecipeCreateCubit>().publishRecipe(createRecipeFromState(state)),
label: Text(
'Опубликовать',
style: theme.textTheme.titleSmall,
),
),
),
],
)),
],
),
),
),
);
}
_buildStep(BuildContext context, int stepNumber, RecipeCreateStateLoaded state) {
print(baseUrl + '/storage/' + state.stepImages[stepNumber]!.path);
return Column(
children: [
Row(children: [Text('Шаг ${stepNumber + 1}'), Spacer(), Text('Всего шагов: ${state.numberOfSteps}')]),
SizedBox(height: 8),
GestureDetector(
onTap: () async {
final pickedFile = await state.picker.pickImage(source: ImageSource.gallery);
context.read<RecipeCreateCubit>().uploadStepImage(pickedFile, stepNumber);
},
child: state.stepImages[stepNumber] == null
? Container(
height: 128,
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.photo_library,
color: appTheme.blueGray300,
),
SizedBox(width: 8),
Text(
"Добавить фото",
selectionColor: appTheme.blueA400,
),
SizedBox(height: 8),
],
),
)
: Image.network(
baseUrl + '/storage/' + state.stepImages[stepNumber]!.path,
height: 150,
fit: BoxFit.cover,
)),
SizedBox(height: 16),
CustomTextFormField(
hintText: 'Введите описание шага',
maxLines: 8,
minLines: 8,
fillColor: appTheme.gray50,
controller: state.stepControllers[stepNumber],
),
SizedBox(height: 24),
],
);
}
_buildIngredient(Ingredient ingredient) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 8.h,
vertical: 6.v,
),
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(
left: 7.h,
top: 5.v,
),
child: Text(
ingredient.name.length > 8 ? ingredient.name.substring(0, 8) : ingredient.name,
style: CustomTextStyles.titleMedium16,
),
),
Padding(
padding: EdgeInsets.only(
left: 20.h,
top: 3.v,
bottom: 3.v,
),
child: Text(
ingredient.portion.toString() + ' ' + ingredient.unit.toString().tr,
),
),
Spacer(),
GestureDetector(
// onTap: removeIngredient(context, ingredient),
child: Padding(
padding: const EdgeInsets.only(right: 10),
child: Icon(
Icons.close,
color: appTheme.blueGray300,
),
),
),
],
),
);
}
_buildOptions(List<String> _options) {
return (TextEditingValue textEditingValue) {
return _options.where((String option) {
return true;
});
};
}
selectProduct(AutocompleteOnSelected<String> onSelected, String option, RecipeCreateStateLoaded state) {
state.productNameController.text = option;
return () {
onSelected(option);
};
}
getAutocomplete(BuildContext context, FocusNode focusNode) {
return (value) {
if (value.length > 2) {
context.read<RecipeCreateCubit>().inputProduct(value);
}
};
}
changeType(BuildContext context) {
return (value) {
context.read<RecipeCreateCubit>().changeType(value);
};
}
changeStatus(BuildContext context) {
return (value) {
context.read<RecipeCreateCubit>().changeStatus(value);
};
}
changeCookingTime(BuildContext context) {
return (value) {
context.read<RecipeCreateCubit>().changeCookingTime(value);
};
}
RecipeModel createRecipeFromState(RecipeCreateStateLoaded state) {
List<Ingredient> ingredients = state.ingredients;
List<step.Step> steps = [];
for (int i = 0; i < state.stepControllers.length; i++) {
if (state.stepControllers[i].text.isNotEmpty) {
steps.add(step.Step(
description: state.stepControllers[i].text,
image: state.stepImages[i]?.path,
));
}
}
final RecipeModel recipe = RecipeModel(
ingredients: ingredients,
steps: steps,
state: state.selectedStatus.value,
type: state.selectedType.value,
cooking_time: state.selectedCookingTime.value,
description: state.stepDescriptionController.text,
title: state.titleController.text,
image: state.recipeImage?.path,
video_url: state.youtubeLinkController.text.isNotEmpty ? state.youtubeLinkController.text : null,
);
print(recipe.toJson());
return recipe;
}
}

View File

@@ -22,11 +22,12 @@ class RecipeRepository {
rethrow;
}
}
/*Future<RecipeModel> getRecipe(int recipeID) async {
Future<RecipeModel> getRecipe(String recipeID) async {
try {
return (await recipeService.getRecipe(recipeID));
return await recipeService.getRecipe(recipeID);
} catch (e) {
rethrow;
}
}*/
}
}

View File

@@ -9,8 +9,8 @@ part 'recipe_service.g.dart';
abstract class RecipeService {
factory RecipeService(Dio dio, {String baseUrl}) = _RecipeService;
@GET('/api/recipe/{id}/show/')
Future<Map<String, RecipeModel>> getRecipe(@Path() int id);
@GET('/api/recipes/show/{id}')
Future<RecipeModel> getRecipe(@Path() String id);
@GET('/api/diary/product_nutritional_values/filter')
Future<Map<String, List<Product>>> getInputProduct(@Query('name') String name);

View File

@@ -19,20 +19,20 @@ class _RecipeService implements RecipeService {
String? baseUrl;
@override
Future<Map<String, RecipeModel>> getRecipe(int id) async {
Future<RecipeModel> getRecipe(String id) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _result = await _dio.fetch<Map<String, dynamic>>(
_setStreamType<Map<String, RecipeModel>>(Options(
final _result = await _dio
.fetch<Map<String, dynamic>>(_setStreamType<RecipeModel>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/recipe/${id}/show/',
'/api/recipes/show/${id}',
queryParameters: queryParameters,
data: _data,
)
@@ -41,8 +41,7 @@ class _RecipeService implements RecipeService {
_dio.options.baseUrl,
baseUrl,
))));
var _value = _result.data!.map((k, dynamic v) =>
MapEntry(k, RecipeModel.fromJson(v as Map<String, dynamic>)));
final _value = RecipeModel.fromJson(_result.data!);
return _value;
}

View File

@@ -91,7 +91,7 @@ class RecipeSelectScreen extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: GestureDetector(
onTap: () => GoRouter.of(context).goNamed('recipeCreateScreen'),
onTap: () => GoRouter.of(context).pushNamed('recipeCreateScreen'),
child: Icon(Icons.create_outlined),
),
),
@@ -155,9 +155,9 @@ class RecipeSelectScreen extends StatelessWidget {
extra: post?['id'].toString(),
),
trailing: GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
onTap: () => GoRouter.of(context).pushNamed(
'recipeEditScreen',
extra: post?['id'].toString(),
extra: post?['slug'].toString(),
),
child: Icon(Icons.edit),
),