forked from eatvio/eatvio_app
339 lines
14 KiB
Dart
339 lines
14 KiB
Dart
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
|
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
|
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
|
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
|
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
|
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
|
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
|
import 'package:eatvio_app/features/create_plan/presentation/widgets/add_item_widget.dart';
|
|
import 'package:eatvio_app/generated/l10n.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_portal/flutter_portal.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
class OptionWidget extends StatefulWidget {
|
|
const OptionWidget({super.key});
|
|
|
|
@override
|
|
State<OptionWidget> createState() => _OptionWidgetState();
|
|
}
|
|
|
|
class _OptionWidgetState extends State<OptionWidget> {
|
|
final firstOptionCustomerPriceController = TextEditingController()..text = '1200₽';
|
|
final secondOptionCustomerPriceController = TextEditingController()..text = '1200₽';
|
|
final thirdOptionCustomerPriceController = TextEditingController()..text = '1200₽';
|
|
|
|
final creatorPriceController = TextEditingController()..text = '1000₽';
|
|
|
|
final firstOptionTitleController = TextEditingController();
|
|
final secondOptionTitleController = TextEditingController();
|
|
final thirdOptionTitleController = TextEditingController();
|
|
|
|
final firstOptionDescriptionController = TextEditingController();
|
|
final secondOptionDescriptionController = TextEditingController();
|
|
final thirdOptionDescriptionController = TextEditingController();
|
|
|
|
bool firstExplanationVisible = false;
|
|
bool isChecked = false;
|
|
bool secondExplanationVisible = false;
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
firstOptionCustomerPriceController.addListener(() {
|
|
if (!firstOptionCustomerPriceController.text.endsWith('₽')) {
|
|
final text = firstOptionCustomerPriceController.text.replaceAll('₽', '');
|
|
firstOptionCustomerPriceController.text = '$text₽';
|
|
firstOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length);
|
|
}
|
|
});
|
|
secondOptionCustomerPriceController.addListener(() {
|
|
if (!secondOptionCustomerPriceController.text.endsWith('₽')) {
|
|
final text = secondOptionCustomerPriceController.text.replaceAll('₽', '');
|
|
secondOptionCustomerPriceController.text = '$text₽';
|
|
secondOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length);
|
|
}
|
|
});
|
|
thirdOptionCustomerPriceController.addListener(() {
|
|
if (!thirdOptionCustomerPriceController.text.endsWith('₽')) {
|
|
final text = thirdOptionCustomerPriceController.text.replaceAll('₽', '');
|
|
thirdOptionCustomerPriceController.text = '$text₽';
|
|
thirdOptionCustomerPriceController.selection = TextSelection.collapsed(offset: text.length);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final s = S.of(context);
|
|
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
|
builder: (context, state) {
|
|
final selectedOptionIndex = state.selectedOptionIndex ?? 0;
|
|
final optionNameHintText = switch (selectedOptionIndex) {
|
|
0 => s.economy,
|
|
2 => s.comfort,
|
|
_ => s.standard,
|
|
};
|
|
final optionTitleController = switch (selectedOptionIndex) {
|
|
0 => firstOptionTitleController,
|
|
2 => thirdOptionTitleController,
|
|
_ => secondOptionTitleController,
|
|
};
|
|
final optionDescriptionController = switch (selectedOptionIndex) {
|
|
0 => firstOptionDescriptionController,
|
|
2 => thirdOptionDescriptionController,
|
|
_ => secondOptionDescriptionController,
|
|
};
|
|
final customerPriceController = switch (selectedOptionIndex) {
|
|
0 => firstOptionCustomerPriceController,
|
|
2 => thirdOptionCustomerPriceController,
|
|
_ => secondOptionCustomerPriceController,
|
|
};
|
|
|
|
final price = double.tryParse(customerPriceController.text.replaceAll('₽', ''));
|
|
creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽';
|
|
firstOptionTitleController.text = state.planData.options[selectedOptionIndex].short;
|
|
firstOptionDescriptionController.text = state.planData.options[selectedOptionIndex].description ?? '';
|
|
return MainBackgroundBouble(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
s.option_title,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(height: 4),
|
|
CustomTextFormField(
|
|
hintText: optionNameHintText,
|
|
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError,
|
|
controller: optionTitleController,
|
|
onChanged: (value) {
|
|
context.read<CreatePlanCubit>().setOptionTitle(value);
|
|
},
|
|
maxLength: 150,
|
|
),
|
|
if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError) ...[
|
|
const SizedBox(height: 4),
|
|
ValidationErrorWidget(error: s.atLeast3Symbols),
|
|
],
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
s.option_desc,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(height: 4),
|
|
SizedBox(
|
|
height: 120,
|
|
child: CustomTextFormField(
|
|
controller: optionDescriptionController,
|
|
textAlignVertical: TextAlignVertical.top,
|
|
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError,
|
|
maxLength: 1000,
|
|
minLines: null,
|
|
maxLines: null,
|
|
expands: true,
|
|
hintText: s.option_desc_hint_text,
|
|
onChanged: (value) {
|
|
context.read<CreatePlanCubit>().setOptionDescription(value);
|
|
},
|
|
),
|
|
),
|
|
if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError) ...[
|
|
const SizedBox(height: 4),
|
|
ValidationErrorWidget(error: s.atLeast3Symbols),
|
|
],
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
children: [
|
|
Text(
|
|
s.you_will_get,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(width: 4),
|
|
const _PriceExplanationWidget(),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
CustomTextFormField(
|
|
controller: creatorPriceController,
|
|
readOnly: true,
|
|
textStyle: TextStyle(color: appTheme.gray),
|
|
maxLength: 20,
|
|
),
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
children: [
|
|
Text(
|
|
s.customer_price,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(width: 4),
|
|
const _PriceExplanationWidget(),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
CustomTextFormField(
|
|
controller: customerPriceController,
|
|
textInputType: const TextInputType.numberWithOptions(decimal: true),
|
|
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].priceHasError,
|
|
onChanged: (value) {
|
|
final price = double.tryParse(value.replaceAll('₽', ''));
|
|
if (price != null) {
|
|
context.read<CreatePlanCubit>().setPrice(price);
|
|
}
|
|
},
|
|
maxLength: 20,
|
|
),
|
|
const SizedBox(height: 10),
|
|
Text(
|
|
s.minimal_customer_price,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(height: 16),
|
|
const CustomDivider(),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
s.option_content,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
softWrap: true,
|
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
),
|
|
const SizedBox(height: 4),
|
|
ListView.separated(
|
|
padding: EdgeInsets.zero,
|
|
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
|
reverse: false,
|
|
itemBuilder: (context, index) {
|
|
final items = state.planData.options[selectedOptionIndex].items;
|
|
|
|
if (items.isEmpty) {
|
|
return AddItemWidget(
|
|
index: index,
|
|
canBeDeleted: false,
|
|
enableHint: true,
|
|
items: items,
|
|
);
|
|
} else {
|
|
if (index < items.length) {
|
|
return AddItemWidget(
|
|
canBeDeleted: true,
|
|
enableHint: false,
|
|
index: index,
|
|
items: items,
|
|
);
|
|
} else {
|
|
return AddItemWidget(
|
|
canBeDeleted: false,
|
|
enableHint: false,
|
|
index: index,
|
|
items: items,
|
|
);
|
|
}
|
|
}
|
|
},
|
|
itemCount: state.planData.options[selectedOptionIndex].items.length + 1,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
),
|
|
const SizedBox(height: 16),
|
|
const CustomDivider(),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
children: [
|
|
Checkbox(
|
|
value: state.planData.options[selectedOptionIndex].hasPersonalDiet,
|
|
activeColor: appTheme.gray,
|
|
checkColor: appTheme.gray,
|
|
fillColor: const WidgetStatePropertyAll(Colors.transparent),
|
|
side: WidgetStateBorderSide.resolveWith(
|
|
(Set<WidgetState> states) {
|
|
return BorderSide(width: 1, color: appTheme.gray);
|
|
},
|
|
),
|
|
onChanged: (bool? value) {
|
|
context.read<CreatePlanCubit>().togglePersonalizedDietOptionState();
|
|
},
|
|
),
|
|
Expanded(
|
|
child: Text(
|
|
s.option_personal_diet_desc,
|
|
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _PriceExplanationWidget extends StatefulWidget {
|
|
const _PriceExplanationWidget();
|
|
|
|
@override
|
|
State<_PriceExplanationWidget> createState() => _PriceExplanationWidgetState();
|
|
}
|
|
|
|
class _PriceExplanationWidgetState extends State<_PriceExplanationWidget> {
|
|
bool visible = false;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final s = S.of(context);
|
|
return PortalTarget(
|
|
closeDuration: const Duration(milliseconds: 300),
|
|
portalFollower: AnimatedContainer(
|
|
padding: const EdgeInsets.all(10),
|
|
duration: const Duration(milliseconds: 300),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
color: visible ? appTheme.black : Colors.transparent,
|
|
),
|
|
child: AnimatedDefaultTextStyle(
|
|
style: TextStyles.textMMediumAuto.copyWith(
|
|
color: visible ? appTheme.whiteA700 : Colors.transparent,
|
|
),
|
|
duration: const Duration(milliseconds: 300),
|
|
child: Text(
|
|
s.price_explanation,
|
|
),
|
|
),
|
|
),
|
|
visible: visible,
|
|
anchor: const Aligned(
|
|
shiftToWithinBound: AxisFlag(x: true, y: true),
|
|
follower: Alignment.bottomLeft,
|
|
target: Alignment.topRight,
|
|
),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
visible = !visible;
|
|
});
|
|
},
|
|
child: SvgPicture.asset('assets/icons/question_mark.svg', height: 12, width: 12),
|
|
),
|
|
);
|
|
}
|
|
}
|