From aa78d385a2ac70ddc67f87906924327f841b0bac Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 24 Jun 2025 03:18:03 +0700 Subject: [PATCH] feat(deeplinks): added backwards novigation for contant that was oppened via deeplinks, added routing to plans --- lib/core/app_router.dart | 18 +- .../presentation/pages/blog_post_screen.dart | 33 +- .../presentation/widgets/plan_widget.dart | 8 +- .../presentation/widgets/my_plan_widget.dart | 2 +- .../presentation/pages/about_plan_screen.dart | 855 +++++++++--------- .../pages/about_recipe_screen.dart | 28 + 6 files changed, 511 insertions(+), 433 deletions(-) diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 9ff5ebd2..3dc4996a 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -42,7 +42,6 @@ import 'package:eatvio_app/features/finances_and_consultations_archive/presentat import 'package:eatvio_app/features/my_finance/presentation/pages/user_finance_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart'; -import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; @@ -88,9 +87,9 @@ class AppRouter { case 'recipe': return '/about-recipes-screen?slug=$contentIdentifier'; case 'blog_post': - return '/blog-post-screen?slug=$contentIdentifier'; + return '/blog-post-screen?id=$contentIdentifier'; case 'plan': - return '/plan?slug=$contentIdentifier'; + return '/about-plan-screen?slug=$contentIdentifier'; case 'diet': return '/diet?slug=$contentIdentifier'; } @@ -255,8 +254,17 @@ class AppRouter { path: '/about-plan-screen', name: 'aboutPlanScreen', builder: (context, state) { + final slug = state.uri.queryParameters['slug']; + if (slug != null) { + return AboutPlanScreen( + slug: slug, + navigateToComments: false, + index: null, + ); + } + final data = state.extra as Map; - return AboutPlanScreen(plan: data['plan'] as PlanModel, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool); + return AboutPlanScreen(slug: data['slug'] as String, index: data['index'] as int?, navigateToComments: data['navigateToComments'] as bool); }, ), GoRoute(path: '/glossary-screen', name: 'glossaryScreen', builder: (context, state) => const GlossaryScreen()), @@ -311,7 +319,7 @@ class AppRouter { path: '/blog-post-screen', name: 'blogPostScreen', builder: (context, state) { - final id = state.uri.queryParameters['slug']; + final id = state.uri.queryParameters['id']; final parsedId = int.tryParse(id ?? ''); if (parsedId != null) { return BlogPostScreen( diff --git a/lib/features/blog_post/presentation/pages/blog_post_screen.dart b/lib/features/blog_post/presentation/pages/blog_post_screen.dart index 96a01f2c..1dc5bd36 100644 --- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart +++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart @@ -59,7 +59,19 @@ class _BlogPostScreenState extends State { }, builder: (BuildContext context, CommentsState state) { if (blogPostState.isLoading) { - return Scaffold(appBar: AppBar(), body: const Loader()); + return Scaffold( + appBar: AppBar( + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), + ), + body: const Loader()); } isLiked = blogPostState.blogPostModel?.wasLikedByMe ?? false; totalLikes = blogPostState.blogPostModel?.totalLikes ?? 0; @@ -167,6 +179,15 @@ class _BlogPostScreenState extends State { ), appBar: AppBar( title: Text(blogPostState.blogPostModel?.title ?? ''), + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), actions: [ CustomPopup( isAuthor: blogPostState.blogPostModel?.author.authorIsMe ?? false, @@ -242,9 +263,7 @@ class _BlogPostScreenState extends State { isLiked = !isLiked; isLiked ? totalLikes++ : totalLikes--; }); - isLiked - ? await context.read().likePost(widget.id) - : await context.read().unlikedPost(widget.id); + isLiked ? await context.read().likePost(widget.id) : await context.read().unlikedPost(widget.id); sl().updateFeedBlogPostList(blogPostState.blogPostModel, isLiked); }, ), @@ -264,7 +283,8 @@ class _BlogPostScreenState extends State { const Spacer(), Icon(Icons.visibility_rounded, size: 16, color: appTheme.gray500), const SizedBox(width: 4), - Text(blogPostState.blogPostModel?.totalViews.toString() ?? '', style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400)), + Text(blogPostState.blogPostModel?.totalViews.toString() ?? '', + style: TextStyle(fontSize: 12, color: appTheme.gray500, fontWeight: FontWeight.w400)), ], ), ), @@ -285,8 +305,7 @@ class _BlogPostScreenState extends State { const SizedBox(height: 16), if (state is CommentsStateLoading) const Loader(), if (state is CommentsStateLoaded) - CommentsWidget( - id: widget.id.toString(), commentSourceType: CommentSourceType.blogPost, comment: state.comment, replyId: replyId), + CommentsWidget(id: widget.id.toString(), commentSourceType: CommentSourceType.blogPost, comment: state.comment, replyId: replyId), ], ), ), diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index 8a8229b8..13b3b698 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -44,7 +44,7 @@ class _PlanWidgetState extends State { onTap: () => GoRouter.of(context).pushNamed( 'aboutPlanScreen', extra: { - 'plan': widget.plan, + 'slug': widget.plan.slug, 'index': widget.index, 'navigateToComments': false, }, @@ -248,8 +248,10 @@ class _PlanWidgetState extends State { FeedIconButton( icon: Icons.chat_bubble_outline, text: widget.plan.totalComments.toString(), - onTap: () => - GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': true}), + onTap: () { + return GoRouter.of(context) + .pushNamed('aboutPlanScreen', extra: {'slug': widget.plan.slug, 'index': widget.index, 'navigateToComments': true}); + }, ), const SizedBox( width: 12, diff --git a/lib/features/my_plans/presentation/widgets/my_plan_widget.dart b/lib/features/my_plans/presentation/widgets/my_plan_widget.dart index baea0c01..9a49ea9a 100644 --- a/lib/features/my_plans/presentation/widgets/my_plan_widget.dart +++ b/lib/features/my_plans/presentation/widgets/my_plan_widget.dart @@ -45,7 +45,7 @@ class _MyPlanWidgetState extends State { Stack( children: [ InkWell( - onTap: () => GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'navigateToComments': false}), + onTap: () => GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'slug': widget.plan.slug, 'navigateToComments': false}), child: ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), child: CachedNetworkImage( diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 6b8980cf..54f6ce35 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -15,7 +15,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.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/feed/models/plan_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/bloc/feed_cubit/feed_cubit.dart'; @@ -35,8 +34,8 @@ import 'package:go_router/go_router.dart'; import '../../../../core/widgets/option_widget.dart'; class AboutPlanScreen extends StatefulWidget { - const AboutPlanScreen({super.key, required this.plan, required this.navigateToComments, required this.index}); - final PlanModel plan; + const AboutPlanScreen({super.key, required this.slug, required this.navigateToComments, required this.index}); + final String slug; final bool navigateToComments; final int? index; @override @@ -53,7 +52,7 @@ class _AboutPlanScreenState extends State { @override void initState() { super.initState(); - context.read().viewPlan(widget.plan.slug); + context.read().viewPlan(widget.slug); scrollController.addListener(() { if (isMoreOptionsVisible) { setState(() { @@ -73,461 +72,483 @@ class _AboutPlanScreenState extends State { @override Widget build(BuildContext context) { final s = S.of(context); - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => commentsCubit - ..getComments( - widget.plan.slug, - CommentSourceType.plan, - true, - ), - ), - BlocProvider( - create: (context) => sl()..getPlanWithLoading(widget.plan.slug), - ), - ], - child: BlocConsumer( - listener: (BuildContext context, CommentsState commentsState) { - if (commentsState is CommentsStateLoaded) { - setState(() { - replyId = commentsState.replyId; - }); - } - }, - builder: (BuildContext context, CommentsState commentsState) { - return BlocBuilder( - builder: (context, state) { - if (state.isLoading) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text( - widget.plan.name, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - actions: [ - CustomPopup( - isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.slug}', - complainedContentType: ComplainedContent.plan, - slug: widget.plan.slug, - ), - const SizedBox(width: 16), - ], - ), - body: const Loader(), - ); - } - WidgetsBinding.instance.addPostFrameCallback((_) { - if (widget.navigateToComments && !_jumped) { - _scrollToBottom(); - } + return PopScope( + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => commentsCubit + ..getComments( + widget.slug, + CommentSourceType.plan, + true, + ), + ), + BlocProvider( + create: (context) => sl()..getPlanWithLoading(widget.slug), + ), + ], + child: BlocConsumer( + listener: (BuildContext context, CommentsState commentsState) { + if (commentsState is CommentsStateLoaded) { + setState(() { + replyId = commentsState.replyId; }); - if (state.error != null) { + } + }, + builder: (BuildContext context, CommentsState commentsState) { + return BlocBuilder( + builder: (context, state) { + if (state.isLoading) { + return Scaffold( + appBar: AppBar( + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), + centerTitle: true, + title: Text( + '', + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ), + body: const Loader(), + ); + } + WidgetsBinding.instance.addPostFrameCallback((_) { + if (widget.navigateToComments && !_jumped) { + _scrollToBottom(); + } + }); + if (state.error != null) { + return Scaffold( + appBar: AppBar( + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), + centerTitle: true, + title: Text( + state.planModel?.name ?? '', + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + actions: [ + CustomPopup( + isAuthor: state.planModel?.author.authorIsMe ?? false, + path: 'plan/${widget.slug}', + complainedContentType: ComplainedContent.plan, + slug: widget.slug, + ), + const SizedBox(width: 16), + ], + ), + body: DataErrorWidget(onReload: () => context.read().getPlan(widget.slug)), + ); + } return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text( - widget.plan.name, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - actions: [ - CustomPopup( - isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.slug}', - complainedContentType: ComplainedContent.plan, - slug: widget.plan.slug, - ), - const SizedBox(width: 16), - ], - ), - body: DataErrorWidget(onReload: () => context.read().getPlan(widget.plan.slug)), - ); - } - return Scaffold( - backgroundColor: appTheme.gray100, - resizeToAvoidBottomInset: true, - bottomSheet: Container( - padding: const 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), + backgroundColor: appTheme.gray100, + resizeToAvoidBottomInset: true, + bottomSheet: Container( + padding: const 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(16), - ), - backgroundColor: appTheme.greenA700, - onPressed: () async { - final (chosenPlan, error) = await context.read().chosePlan(); - if (error == 'hasPlan') { - showErrorDialog(context, title: s.end_previous_plan, description: s.you_can_not_start_plan_until_end_of_prev_one); - return; - } - if (chosenPlan == null) { + 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(16), + ), + backgroundColor: appTheme.greenA700, + onPressed: () async { + final (chosenPlan, error) = await context.read().chosePlan(); + if (error == 'hasPlan') { + showErrorDialog(context, title: s.end_previous_plan, description: s.you_can_not_start_plan_until_end_of_prev_one); + return; + } + if (chosenPlan == null) { + if (!context.mounted) return; + showErrorDialog(context, title: 'Не удалось заказать план'); + return; + } if (!context.mounted) return; - showErrorDialog(context, title: 'Не удалось заказать план'); - return; - } - if (!context.mounted) return; - GoRouter.of(context).pushReplacementNamed( - 'chatScreen', - extra: { - 'chatId': chosenPlan.chatId, - 'sendInitialMessage': true, - }, - ); - }, - label: Text( - s.to_order, - style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + GoRouter.of(context).pushReplacementNamed( + 'chatScreen', + extra: { + 'chatId': chosenPlan.chatId, + 'sendInitialMessage': true, + }, + ); + }, + label: Text( + s.to_order, + style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + ), ), ), ), - ), - const SizedBox(width: 8), - BookmarkButton( - isFavorite: state.planModel?.isFavorite ?? false, - onTap: () { - final planModel = state.planModel; - if (planModel == null) return; - if (planModel.isFavorite) { - context.read().removeFromFavorite(slug: planModel.slug, index: widget.index); - } else { - context.read().addToFavorite(slug: planModel.slug, index: widget.index); - } - }, - width: 52, - height: 52, - size: 24, - shape: BoxShape.circle, - color: appTheme.gray50, - ), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (replyId != null) + const SizedBox(width: 8), + BookmarkButton( + isFavorite: state.planModel?.isFavorite ?? false, + onTap: () { + final planModel = state.planModel; + if (planModel == null) return; + if (planModel.isFavorite) { + context.read().removeFromFavorite(slug: planModel.slug, index: widget.index); + } else { + context.read().addToFavorite(slug: planModel.slug, index: widget.index); + } + }, + width: 52, + height: 52, + size: 24, + shape: BoxShape.circle, + color: appTheme.gray50, + ), + ], + ), + const 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: const Icon( + Icons.close_rounded, + size: 18, + color: Colors.white, + ), + ), + ), + if (replyId != null) const SizedBox(width: 8), + Expanded( + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 52), + child: TextFormField( + textCapitalization: TextCapitalization.sentences, + onChanged: (_) { + setState(() {}); + }, + controller: textEditingController, + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.black, + height: 1.2, + ), + decoration: InputDecoration( + isDense: false, // убираем true, чтобы padding учитывался корректно + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), // вертикальный паддинг увеличен + hintText: 'Комментарий', + hintStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.2, + color: Color(0xFFAEAEB2), + ), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), + ), + ), + ), + ), + ), + const SizedBox(width: 8), GestureDetector( onTap: () { - context.read().setReplyId(null); - setState(() { - replyId = null; - textEditingController.clear(); - }); - FocusManager.instance.primaryFocus?.unfocus(); + if (textEditingController.text.length > 2) { + commentsCubit.sendComment( + id: widget.slug, + commentText: textEditingController.text, + parentCommentId: replyId, + commentSourceType: CommentSourceType.plan, + ); + context.read().setReplyId(null); + setState(() { + textEditingController.clear(); + replyId = null; + }); + + Future.delayed(const Duration(milliseconds: 500), () { + if (context.mounted) { + context.read().getPlan(widget.slug); + } + }); + FocusManager.instance.primaryFocus?.unfocus(); + } }, child: Container( decoration: BoxDecoration( - color: appTheme.blueA400, + color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, shape: BoxShape.circle, ), height: 32, width: 32, - child: const Icon( - Icons.close_rounded, + child: Icon( + Icons.send_rounded, size: 18, - color: Colors.white, + color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.black, ), ), ), - if (replyId != null) const SizedBox(width: 8), - Expanded( - child: ConstrainedBox( - constraints: const BoxConstraints(minHeight: 52), - child: TextFormField( - textCapitalization: TextCapitalization.sentences, - onChanged: (_) { - setState(() {}); - }, - controller: textEditingController, - maxLines: 5, - minLines: 1, - keyboardType: TextInputType.multiline, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - height: 1.2, - ), - decoration: InputDecoration( - isDense: false, // убираем true, чтобы padding учитывался корректно - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), // вертикальный паддинг увеличен - hintText: 'Комментарий', - hintStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - height: 1.2, - color: Color(0xFFAEAEB2), - ), - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: const BorderSide(color: Color(0xFFD1D1D6), width: 1), - ), - ), - ), - ), - ), - const SizedBox(width: 8), - GestureDetector( - onTap: () { - if (textEditingController.text.length > 2) { - commentsCubit.sendComment( - id: widget.plan.slug, - commentText: textEditingController.text, - parentCommentId: replyId, - commentSourceType: CommentSourceType.plan, - ); - context.read().setReplyId(null); - setState(() { - textEditingController.clear(); - replyId = null; - }); - - Future.delayed(const Duration(milliseconds: 500), () { - if (context.mounted) { - context.read().getPlan(widget.plan.slug); - } - }); - 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.plan.name, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - actions: [ - CustomPopup( - isAuthor: widget.plan.author.authorIsMe, - path: 'plan/${widget.plan.slug}', - complainedContentType: ComplainedContent.plan, - slug: widget.plan.slug, + appBar: AppBar( + centerTitle: true, + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, ), - const SizedBox(width: 16), - ], - ), - body: SingleChildScrollView( - controller: scrollController, - padding: const EdgeInsets.only(bottom: 160), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 8), - MainBackgroundBouble( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - CachedNetworkImage( - imageUrl: '$baseUrl/storage/${widget.plan.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( - backgroundColor: appTheme.whiteA700, - children: [ - SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), - const SizedBox(width: 8), - Text( - widget.plan.totalViews.toString(), - style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), - ), - ], - ), - const SizedBox(width: 12), - GestureDetector( - onTap: () { - context.read().likePlan(slug: widget.plan.slug, index: widget.index); - }, - child: InfoBouble( + title: Text( + state.planModel?.name ?? '', + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + actions: [ + CustomPopup( + isAuthor: state.planModel?.author.authorIsMe ?? false, + path: 'plan/${widget.slug}', + complainedContentType: ComplainedContent.plan, + slug: widget.slug, + ), + const SizedBox(width: 16), + ], + ), + body: SingleChildScrollView( + controller: scrollController, + padding: const EdgeInsets.only(bottom: 160), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + MainBackgroundBouble( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + CachedNetworkImage( + imageUrl: '$baseUrl/storage/${state.planModel?.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( backgroundColor: appTheme.whiteA700, children: [ - SvgPicture.asset( - 'assets/icons/heart.svg', - width: 14, - height: 12, - colorFilter: state.planModel?.wasLikedByMe == true ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, - ), + SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), const SizedBox(width: 8), Text( - state.planModel?.totalLikes.toString() ?? '0', + state.planModel?.totalViews.toString() ?? '', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), ], ), - ), - const SizedBox(width: 12), - InfoBouble( - backgroundColor: appTheme.whiteA700, - children: [ - SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), - const SizedBox(width: 8), - Text( - widget.plan.totalComments.toString(), - style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + const SizedBox(width: 12), + GestureDetector( + onTap: () { + context.read().likePlan(slug: widget.slug, index: widget.index); + }, + child: InfoBouble( + backgroundColor: appTheme.whiteA700, + children: [ + SvgPicture.asset( + 'assets/icons/heart.svg', + width: 14, + height: 12, + colorFilter: state.planModel?.wasLikedByMe == true ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, + ), + const SizedBox(width: 8), + Text( + state.planModel?.totalLikes.toString() ?? '0', + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], ), - ], - ), - ], + ), + const SizedBox(width: 12), + InfoBouble( + backgroundColor: appTheme.whiteA700, + children: [ + SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), + const SizedBox(width: 8), + Text( + state.planModel?.totalComments.toString() ?? '', + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + s.about_plan, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ), + const SizedBox(height: 12), + InfoRow(plan: state.planModel, type: ProductType.plan), + ExpandableText( + text: state.planModel?.details ?? '', + ), + if (state.planModel != null) ...[ + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: AuthorCard( + author: (state.planModel?.author)!, + title: s.plan_author, ), ), ], - ), - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - s.about_plan, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - ), - const SizedBox(height: 12), - InfoRow(plan: state.planModel, type: ProductType.plan), - ExpandableText( - text: widget.plan.details, - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: AuthorCard( - author: widget.plan.author, - title: s.plan_author, - ), - ), - const SizedBox(height: 16), - ], + const SizedBox(height: 16), + ], + ), ), - ), - const SizedBox(height: 16), - if (state.planModel?.packetsCount == 3) ...[ - OptionsTab( - cost: [ - state.planModel?.packet1Options?.price ?? 0, - state.planModel?.packet2Options?.price ?? 0, - state.planModel?.packet3Options?.price ?? 0, - ], + const SizedBox(height: 16), + if (state.planModel?.packetsCount == 3) ...[ + OptionsTab( + cost: [ + state.planModel?.packet1Options?.price ?? 0, + state.planModel?.packet2Options?.price ?? 0, + state.planModel?.packet3Options?.price ?? 0, + ], + ), + const SizedBox(height: 12), + ], + switch (state.chosenOptionIndex) { + 1 => OptionWidget( + option: state.planModel?.packet2Options, + ), + 2 => OptionWidget( + option: state.planModel?.packet3Options, + ), + _ => OptionWidget( + option: state.planModel?.packet1Options, + ), + }, + const SizedBox( + height: 16, ), - const SizedBox(height: 12), - ], - switch (state.chosenOptionIndex) { - 1 => OptionWidget( - option: state.planModel?.packet2Options, - ), - 2 => OptionWidget( - option: state.planModel?.packet3Options, - ), - _ => OptionWidget( - option: state.planModel?.packet1Options, - ), - }, - const SizedBox( - height: 16, - ), - MainBackgroundBouble( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Column( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.centerLeft, - child: Text( - '${state.planModel?.totalComments ?? 0} ${getCommentForm(state.planModel?.totalComments ?? 0, s)}', - style: TextStyles.textMMedium.copyWith( - color: appTheme.gray500, + MainBackgroundBouble( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Column( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Text( + '${state.planModel?.totalComments ?? 0} ${getCommentForm(state.planModel?.totalComments ?? 0, s)}', + style: TextStyles.textMMedium.copyWith( + color: appTheme.gray500, + ), ), ), - ), - const SizedBox(height: 16), - if (commentsState is CommentsStateLoading) const Loader(), - if (commentsState is CommentsStateLoaded) - CommentsWidget( - id: widget.plan.id.toString(), - commentSourceType: CommentSourceType.plan, - comment: commentsState.comment, - replyId: replyId, - ), - ], + const SizedBox(height: 16), + if (commentsState is CommentsStateLoading) const Loader(), + if (commentsState is CommentsStateLoaded) + CommentsWidget( + id: state.planModel?.id.toString() ?? '', + commentSourceType: CommentSourceType.plan, + comment: commentsState.comment, + replyId: replyId, + ), + ], + ), ), - ), - ], + ], + ), ), - ), - ); - }, - ); - }, + ); + }, + ); + }, + ), ), ); } diff --git a/lib/features/recipe/presentation/pages/about_recipe_screen.dart b/lib/features/recipe/presentation/pages/about_recipe_screen.dart index ed2e1230..2a7d489f 100644 --- a/lib/features/recipe/presentation/pages/about_recipe_screen.dart +++ b/lib/features/recipe/presentation/pages/about_recipe_screen.dart @@ -31,6 +31,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:url_launcher/url_launcher.dart'; class AboutRecipeScreen extends StatefulWidget { @@ -203,6 +204,15 @@ class _AboutRecipeScreenState extends State { ), appBar: AppBar( centerTitle: true, + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), title: Text( recipeState.recipe.title ?? '', maxLines: 2, @@ -388,6 +398,15 @@ class _AboutRecipeScreenState extends State { return Scaffold( appBar: AppBar( centerTitle: true, + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), title: Text( '', maxLines: 2, @@ -403,6 +422,15 @@ class _AboutRecipeScreenState extends State { return Scaffold( appBar: AppBar( centerTitle: true, + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/feed-screen'); + }, + ), title: Text( '', maxLines: 2,