import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart'; import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_state.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; import 'package:eatvio_app/core/widgets/cpfc_container.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class AboutDietScreen extends StatefulWidget { final Diet diet; final int? index; final bool navigateToComments; late final List selectedIndexes; AboutDietScreen({super.key, required this.diet, required this.index, required this.navigateToComments}) { selectedIndexes = List.generate(diet.dailySchedule.length, (_) => 0); } @override State createState() => _AboutDietScreenState(); } class _AboutDietScreenState extends State { final CommentsCubit commentsCubit = sl(); final AboutDietCubit dietsCubit = sl(); int? replyId; final ScrollController scrollController = ScrollController(); final textEditingController = TextEditingController(); bool isMoreOptionsVisible = false; bool _jumped = false; @override void initState() { super.initState(); scrollController.addListener(() { if (isMoreOptionsVisible) { setState(() { isMoreOptionsVisible = false; }); } }); } void _scrollToBottom() { _jumped = true; scrollController.jumpTo( scrollController.position.maxScrollExtent, ); } @override Widget build(BuildContext context) { final s = S.of(context); return BlocProvider( create: (context) => dietsCubit..initDietPage(widget.diet.id), child: BlocProvider( create: (BuildContext context) => commentsCubit ..getComments( widget.diet.id.toString(), CommentSourceType.diet, true, ), child: BlocConsumer( listener: (BuildContext context, CommentsState commentsState) { if (commentsState is CommentsStateLoaded) { setState(() { replyId = commentsState.replyId; }); } }, builder: (BuildContext context, CommentsState state) { return BlocBuilder( builder: (context, dietState) { if (dietState is DietStateLoaded) { Future.delayed(Duration(milliseconds: 500), () { if (widget.navigateToComments && !_jumped) { _scrollToBottom(); } }); return Scaffold( backgroundColor: appTheme.gray100, resizeToAvoidBottomInset: true, bottomSheet: Container( padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), decoration: BoxDecoration( color: Colors.white, border: Border( top: BorderSide( color: Colors.black.withValues(alpha: 0.1), ), ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Expanded( child: SizedBox( child: FloatingActionButton.extended( heroTag: 'nextButtonRegistration', highlightElevation: 0, elevation: 0, focusElevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), backgroundColor: dietState.isStarted ? appTheme.transparentRed : appTheme.greenA700, onPressed: () { final cubit = context.read(); if (dietState.isStarted) { showModalBottomSheet( context: context, builder: (context) => CancelDietBottomSheet( aboutDietCubit: cubit, dietName: dietState.diet.name, ), ); } else if (dietState.hasStartedDiet) { showModalBottomSheet( context: context, builder: (context) => CancelDietBottomSheet( aboutDietCubit: cubit, title: 'Вы собираетесь начать диету ${dietState.diet.name}, завершить диету ${cubit.userStartedDietName}?', ), ); } else { showModalBottomSheet( context: context, builder: (context) => StartDietBottomSheet( aboutDietCubit: cubit, ), ); } }, label: Text( dietState.isStarted ? 'Отменить диету' : s.startDiet, style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), ), ), ), ), SizedBox(width: 8), BookmarkButton( isFavorite: dietState.diet.isFavorite, onTap: () { if (dietState.diet.isFavorite) { dietsCubit.removeFromFavorite(widget.diet.id, widget.index); } else { dietsCubit.addToFavorite(widget.diet.id, widget.index); } }, width: 52, height: 52, size: 24, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(20), color: appTheme.gray50, ), ], ), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (replyId != null) GestureDetector( onTap: () { context.read().setReplyId(null); setState(() { replyId = null; textEditingController.clear(); }); FocusManager.instance.primaryFocus?.unfocus(); }, child: Container( decoration: BoxDecoration( color: appTheme.blueA400, shape: BoxShape.circle, ), height: 32, width: 32, child: Icon( Icons.close_rounded, size: 18, color: Colors.white, ), ), ), if (replyId != null) SizedBox(width: 8), Expanded( child: SizedBox( child: TextFormField( onChanged: (_) { setState(() {}); }, controller: textEditingController, maxLines: 5, minLines: 1, keyboardType: TextInputType.multiline, style: TextStyles.textMMedium.copyWith(color: appTheme.black), decoration: InputDecoration( isDense: true, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), hintText: s.comment.capitalize(), hintStyle: TextStyles.textMMedium.copyWith(color: appTheme.lightGray600), filled: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: Color(0xFFD1D1D6), width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: Color(0xFFD1D1D6), width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: Color(0xFFD1D1D6), width: 1, ), ), ), ), ), ), SizedBox(width: 8), GestureDetector( onTap: () { if (textEditingController.text.length > 2) { commentsCubit.sendComment( id: widget.diet.id.toString(), commentText: textEditingController.text, parentCommentId: replyId, commentSourceType: CommentSourceType.diet, ); context.read().setReplyId(null); setState(() { textEditingController.clear(); replyId = null; }); Future.delayed(Duration(milliseconds: 500), () { dietsCubit.getDiet(widget.diet.id, index: widget.index); }); FocusManager.instance.primaryFocus?.unfocus(); } }, child: Container( decoration: BoxDecoration( color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, shape: BoxShape.circle, ), height: 32, width: 32, child: Icon( Icons.send_rounded, size: 18, color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.black, ), ), ), ], ), ], ), ), ), appBar: AppBar( centerTitle: true, title: Text( widget.diet.name, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, softWrap: true, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ PortalTarget( visible: isMoreOptionsVisible, portalFollower: MoreOptions( path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, ), anchor: Aligned( shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight, ), child: IconButton( onPressed: () { setState(() { isMoreOptionsVisible = !isMoreOptionsVisible; }); }, icon: SvgPicture.asset( 'assets/icons/more_button.svg', width: 20, height: 20, ), ), ), ], ), body: SingleChildScrollView( controller: scrollController, padding: EdgeInsets.only(bottom: 160), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 8), MainBackgroundBouble( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ CachedNetworkImage( imageUrl: baseUrl + '/storage/' + widget.diet.image, height: 300, width: MediaQuery.sizeOf(context).width, fit: BoxFit.fill, ), Positioned( bottom: 16, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ InfoBouble( children: [ SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), SizedBox(width: 8), Text( dietState.diet.totalViews.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], backgroundColor: appTheme.whiteA700, ), SizedBox(width: 12), GestureDetector( onTap: () { dietsCubit.likeDiet(widget.diet.id, widget.index); }, child: InfoBouble( children: [ SvgPicture.asset( 'assets/icons/heart.svg', width: 14, height: 12, colorFilter: dietState.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, ), SizedBox(width: 8), Text( dietState.diet.totalLikes.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], backgroundColor: appTheme.whiteA700, ), ), SizedBox(width: 12), InfoBouble( children: [ SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), SizedBox(width: 8), Text( dietState.diet.totalComments.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], backgroundColor: appTheme.whiteA700, ), ], ), ), ], ), SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( s.about_diet, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), ), SizedBox(height: 12), InfoRow( diet: widget.diet, type: ProductType.diet, ), ExpandableText( text: widget.diet.details, ), SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: AuthorCard( author: widget.diet.author, title: s.diet_author, ), ), SizedBox(height: 16), ], ), ), SizedBox(height: 16), MainBackgroundBouble( child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Center( child: Text( s.diet_plan + '(${widget.diet.duration.toString()} ' + getDayForm(widget.diet.duration, s) + ')', style: TextStyles.headingXLSemibold.copyWith( color: appTheme.black, ), ), ), ), ), SizedBox(height: 8), ListView.builder( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { return _dayOfPlan(widget.diet.dailySchedule[index + 1] ?? {}, index, s); }, itemCount: widget.diet.dailySchedule.length, ), MainBackgroundBouble( padding: EdgeInsets.symmetric(vertical: 16), child: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 16), alignment: Alignment.centerLeft, child: Text( dietState.diet.totalComments.toString() + ' ' + getCommentForm(dietState.diet.totalComments, s), style: TextStyles.textMMedium.copyWith( color: appTheme.gray500, ), ), ), SizedBox(height: 16), if (state is CommentsStateLoading) const Loader(), if (state is CommentsStateLoaded) CommentsWidget( id: widget.diet.id.toString(), commentSourceType: CommentSourceType.diet, comment: state.comment, replyId: replyId, ), ], ), ), ], ), ), ); } if (dietState is DietStateError) { return Scaffold( appBar: AppBar( centerTitle: true, title: Text( widget.diet.name, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, softWrap: true, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ PortalTarget( visible: isMoreOptionsVisible, portalFollower: MoreOptions( path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, ), anchor: Aligned( shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight, ), child: IconButton( onPressed: () { setState(() { isMoreOptionsVisible = !isMoreOptionsVisible; }); }, icon: SvgPicture.asset( 'assets/icons/more_button.svg', width: 20, height: 20, ), ), ), ], ), body: DataErrorWidget( onReload: () => context.read().getDiet(widget.diet.id, index: widget.index), ), ); } return Scaffold( appBar: AppBar( centerTitle: true, title: Text( widget.diet.name, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, softWrap: true, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ PortalTarget( visible: isMoreOptionsVisible, portalFollower: MoreOptions( path: 'diet/${widget.diet.id}', complainedContentType: ComplainedContent.diet, id: widget.diet.id, ), anchor: Aligned( shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight, ), child: IconButton( onPressed: () { setState(() { isMoreOptionsVisible = !isMoreOptionsVisible; }); }, icon: SvgPicture.asset( 'assets/icons/more_button.svg', width: 20, height: 20, ), ), ), ], ), body: const Loader(), ); }, ); }, ), ), ); } _dayOfPlan(Map> day, int dayNumber, S s) { _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: 16, vertical: 8), label: Text((index + 1).toString()), labelStyle: TextStyles.textXSSemibold.copyWith( color: index == widget.selectedIndexes[dayNumber] ? Colors.white : appTheme.gray600, ), selected: widget.selectedIndexes[dayNumber] == index, backgroundColor: appTheme.gray100, onSelected: (bool selected) { setState(() { widget.selectedIndexes[dayNumber] = index; }); }, ); } return Padding( padding: const EdgeInsets.only(bottom: 8), child: MainBackgroundBouble( padding: EdgeInsets.only( top: 16, ), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( s.day + ' ' + (dayNumber + 1).toString(), style: TextStyles.headingXLSemibold.copyWith( color: appTheme.black, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: CustomDivider(), ), Text( s.meal, style: TextStyles.textMMedium.copyWith( color: appTheme.black, ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _choiceChipWidget(0), _choiceChipWidget(1), _choiceChipWidget(2), _choiceChipWidget(3), _choiceChipWidget(4), ], ), SizedBox(height: 8), ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber] + 1]?[index + 1], s), itemCount: day[widget.selectedIndexes[dayNumber] + 1]?.length ?? 0, ), SizedBox(height: 8), ], ), ), CpfcContainer( calories: widget.diet.dailyNutrition[dayNumber].energy, proteins: widget.diet.dailyNutrition[dayNumber].proteins, fats: widget.diet.dailyNutrition[dayNumber].fats, carbohydrates: widget.diet.dailyNutrition[dayNumber].carbohydrates, ), ], ), ), ); } _daily(DailySchedule? daily, S s) { if (daily == null) return const SizedBox(); return Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ SizedBox( width: MediaQuery.sizeOf(context).width * 0.6, child: Text( daily.name, style: TextStyles.textMMedium.copyWith( color: appTheme.black, ), ), ), Spacer(), Text( daily.quantity.toString() + ' ' + getLocalizedMeasurementUnits(s, daily.portion), style: TextStyles.textMSemibold.copyWith( color: appTheme.black, ), ), ], ), ); } }