feat(recipe): edit recipe

This commit is contained in:
mso7nueh
2025-05-19 15:40:00 +03:00
parent 2a1c1dc56d
commit b2b2253f06
18 changed files with 328 additions and 147 deletions

View File

@@ -14,7 +14,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class InfoAboutRecipeWidget extends StatefulWidget {
const InfoAboutRecipeWidget({super.key});
final CreateRecipeCubit createRecipeCubit;
const InfoAboutRecipeWidget({super.key, required this.createRecipeCubit});
@override
State<InfoAboutRecipeWidget> createState() => _InfoAboutRecipeWidgetState();
@@ -23,13 +25,19 @@ class InfoAboutRecipeWidget extends StatefulWidget {
class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
final List<SelectionPopupModel> items = TypeOfRecipe.values.map((e) => SelectionPopupModel(title: e.toString())).toList();
late SelectionPopupModel selectedType;
late final CreateRecipeCubit createRecipeCubit;
@override
void initState() {
super.initState();
selectedType = items.first;
createRecipeCubit = context.read<CreateRecipeCubit>();
if (widget.createRecipeCubit.state.entity.typeOfRecipe != null) {
for (int i = 0; i < items.length; i++) {
if (items[i].title == widget.createRecipeCubit.state.entity.typeOfRecipe!.toString()) {
selectedType = items[i];
}
}
} else {
selectedType = items.first;
}
}
@override
@@ -44,6 +52,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
const Text('Название'),
const SizedBox(height: 4),
BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
bloc: widget.createRecipeCubit,
buildWhen: (previous, current) => previous.titleValidationError != current.titleValidationError,
builder: (context, state) {
return Column(
@@ -51,10 +60,11 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
CustomTextFormField(
hintText: 'Например: тар-тар из говядины',
hasError: state.titleValidationError != null,
initialValue: state.entity.title,
onChanged: (value) {
createRecipeCubit.replaceTitle(value);
widget.createRecipeCubit.replaceTitle(value);
if (state.titleValidationError != null) {
createRecipeCubit.validateTitle();
widget.createRecipeCubit.validateTitle();
}
},
),
@@ -78,7 +88,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
setState(() {
selectedType = newSelectedType;
});
createRecipeCubit.changeType(
widget.createRecipeCubit.changeType(
TypeOfRecipe.fromString(selectedType.title) ?? TypeOfRecipe.other,
);
},
@@ -100,12 +110,13 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
CustomTextFormField(
hintText: 'Расскажите, почему вы выбрали именно этот рецепт? Опишите, каким будет готовое блюдо и в чём его достоинства.',
minLines: 5,
initialValue: state.entity.description,
maxLines: 5,
hasError: state.descriptionValidationError != null,
onChanged: (value) {
createRecipeCubit.replaceDescription(value);
widget.createRecipeCubit.replaceDescription(value);
if (state.descriptionValidationError != null) {
createRecipeCubit.validateDescription();
widget.createRecipeCubit.validateDescription();
}
},
),
@@ -148,8 +159,9 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
const SizedBox(height: 4),
CustomTextFormField(
hintText: 'Прикрепите ссылку на видео',
initialValue: widget.createRecipeCubit.state.entity.videoUrl,
onChanged: (value) {
createRecipeCubit.replaceVideoUrl(value);
widget.createRecipeCubit.replaceVideoUrl(value);
},
),
],

View File

@@ -14,6 +14,7 @@ class PortionWidget extends StatefulWidget {
class _PortionWidgetState extends State<PortionWidget> {
late final CreateRecipeCubit createRecipeCubit;
@override
void initState() {
super.initState();
@@ -45,6 +46,7 @@ class _PortionWidgetState extends State<PortionWidget> {
CustomTextFormField(
hintText: '1',
hasError: state.servingsValidationError != null,
initialValue: state.entity.countOfPortions?.toString(),
textInputType: TextInputType.number,
onChanged: (value) {
createRecipeCubit.replacePortionsCount(int.parse(value));
@@ -77,6 +79,7 @@ class _PortionWidgetState extends State<PortionWidget> {
CustomTextFormField(
hintText: '100',
hasError: state.energyPerServingValidationError != null,
initialValue: state.entity.energyInOnePortion?.toString(),
suffixText: 'Ккал',
textInputType: const TextInputType.numberWithOptions(decimal: true),
onChanged: (value) {

View File

@@ -3,16 +3,16 @@ import 'dart:io';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart';
class StepImageWidget extends StatefulWidget {
final CreateRecipeCubit createRecipeCubit;
final int index;
const StepImageWidget({
super.key,
required this.index,
required this.index, required this.createRecipeCubit,
});
@override
@@ -21,12 +21,15 @@ class StepImageWidget extends StatefulWidget {
class _StepImageWidgetState extends State<StepImageWidget> {
XFile? image;
late final CreateRecipeCubit createRecipeCubit;
@override
void initState() {
super.initState();
createRecipeCubit = context.read<CreateRecipeCubit>();
if (widget.createRecipeCubit.state.entity.steps.length > widget.index) {
if (widget.createRecipeCubit.state.entity.steps[widget.index].image != null) {
image = XFile(widget.createRecipeCubit.state.entity.steps[widget.index].image!.path);
}
}
}
@override
@@ -56,7 +59,7 @@ class _StepImageWidgetState extends State<StepImageWidget> {
setState(() {
image = null;
});
createRecipeCubit.replaceImageInStep(null, widget.index);
widget.createRecipeCubit.replaceImageInStep(null, widget.index);
},
child: Container(
padding: const EdgeInsets.all(4),
@@ -84,7 +87,7 @@ class _StepImageWidgetState extends State<StepImageWidget> {
image = await picker.pickImage(source: ImageSource.camera);
setState(() {});
if (image != null) {
createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
}
if (!context.mounted) return;
Navigator.of(context).pop();
@@ -103,7 +106,7 @@ class _StepImageWidgetState extends State<StepImageWidget> {
image = await picker.pickImage(source: ImageSource.gallery);
setState(() {});
if (image != null) {
createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
}
if (!context.mounted) return;
Navigator.of(context).pop();

View File

@@ -7,24 +7,19 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class StepsWidget extends StatefulWidget {
const StepsWidget({super.key});
final CreateRecipeCubit createRecipeCubit;
const StepsWidget({super.key, required this.createRecipeCubit});
@override
State<StepsWidget> createState() => _StepsWidgetState();
}
class _StepsWidgetState extends State<StepsWidget> {
late final CreateRecipeCubit createRecipeCubit;
@override
void initState() {
super.initState();
createRecipeCubit = context.read<CreateRecipeCubit>();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
bloc: widget.createRecipeCubit,
builder: (BuildContext context, CreateRecipeState state) {
return Material(
shape: RoundedRectangleBorder(
@@ -83,10 +78,11 @@ class _StepsWidgetState extends State<StepsWidget> {
const SizedBox(height: 4),
CustomTextFormField(
hintText: 'Например: подготовьте ингредиенты',
initialValue: widget.createRecipeCubit.state.entity.steps[index].description,
maxLines: 5,
minLines: 3,
onChanged: (value) {
createRecipeCubit.replaceDescriptionInStep(value, index);
widget.createRecipeCubit.replaceDescriptionInStep(value, index);
},
),
const SizedBox(height: 12),
@@ -94,6 +90,7 @@ class _StepsWidgetState extends State<StepsWidget> {
const SizedBox(height: 4),
StepImageWidget(
index: index,
createRecipeCubit: widget.createRecipeCubit,
),
],
),

View File

@@ -1,6 +1,8 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class TagsWidget extends StatefulWidget {
const TagsWidget({super.key});
@@ -10,8 +12,16 @@ class TagsWidget extends StatefulWidget {
}
class _TagsWidgetState extends State<TagsWidget> {
late final CreateRecipeCubit createRecipeCubit;
final tagController = TextEditingController();
final List<String> tags = [];
late final List<String> tags;
@override
void initState() {
super.initState();
createRecipeCubit = context.read<CreateRecipeCubit>();
tags = createRecipeCubit.state.entity.tags;
}
@override
Widget build(BuildContext context) {

View File

@@ -71,6 +71,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
hintText: 'Часов',
hasError: state.cookingTimeValidationError != null,
textInputType: TextInputType.number,
initialValue: state.entity.totalCookHours?.toString(),
onChanged: (value) {
final hours = int.tryParse(value);
if (hours == null) return;
@@ -97,6 +98,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
hintText: 'Минут',
hasError: state.cookingTimeValidationError != null,
textInputType: TextInputType.number,
initialValue: state.entity.totalCookMinutes?.toString(),
onChanged: (value) {
final minutes = int.tryParse(value);
if (minutes == null) return;
@@ -156,6 +158,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
CustomTextFormField(
hintText: 'Часов',
hasError: state.activeTimeValidationError != null,
initialValue: state.entity.activeCookHours?.toString(),
textInputType: TextInputType.number,
onChanged: (value) {
final hours = int.tryParse(value);
@@ -183,6 +186,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
CustomTextFormField(
hintText: 'Минут',
hasError: state.activeTimeValidationError != null,
initialValue: state.entity.activeCookMinutes?.toString(),
textInputType: TextInputType.number,
onChanged: (value) {
final minutes = int.tryParse(value);