Files
eatvio_app/lib/features/feed/presentation/widgets/recipes_widget.dart
2025-05-19 15:40:00 +03:00

293 lines
12 KiB
Dart

// ignore_for_file: dead_code
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/theme/custom_text_style.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart';
class RecipesWidget extends StatefulWidget {
final int index;
final Recipe recipe;
const RecipesWidget({
super.key,
required this.recipe,
required this.index,
});
@override
State<RecipesWidget> createState() => _RecipesWidgetState();
}
class _RecipesWidgetState extends State<RecipesWidget> {
bool isMoreOptionsVisible = false;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<RecipesCubit, RecipesState>(
builder: (BuildContext context, RecipesState state) {
final isLiked = widget.recipe.wasLikedByMe;
final totalLikes = widget.recipe.likesCount;
final isFavorite = widget.recipe.isFavorite;
final hours = widget.recipe.cookingTime ~/ 60;
final minutes = widget.recipe.cookingTime % 60;
final String timeTitle;
if (hours > 0) {
timeTitle = '$hours ${getHourForm(hours)} $minutes ${getMinuteForm(minutes)}';
} else {
timeTitle = '$minutes ${getMinuteForm(minutes)}';
}
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () => GoRouter.of(context).pushNamed(
'aboutRecipesScreen',
extra: {
'recipe': widget.recipe,
'index': widget.index,
'navigateToComments': false,
},
),
child: Material(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: CachedNetworkImage(
imageUrl: '$baseUrl/storage/${widget.recipe.image}',
fit: BoxFit.fitWidth,
width: MediaQuery.of(context).size.width,
),
),
Positioned(
left: 8,
bottom: 8,
child: Wrap(
runSpacing: 8,
children: [
_iconButton('assets/icons/clock.svg', timeTitle),
const SizedBox(width: 8),
_iconButton('assets/icons/steps.svg', '${widget.recipe.steps?.length ?? 0} ${getStepForm(widget.recipe.steps?.length ?? 0)}'),
const SizedBox(width: 8),
_iconButton('assets/icons/calories.svg', '${widget.recipe.calories.toStringAsFixed(0)} ккал'),
],
),
),
Positioned(
left: 14,
child: BookmarkButton(
onTap: () async {
if (isFavorite) {
await context.read<RecipesCubit>().removeRecipeFromFavorite(widget.recipe.slug, widget.index);
} else {
await context.read<RecipesCubit>().addRecipeToFavorite(widget.recipe.slug, widget.index);
}
},
color: appTheme.whiteA700,
isFavorite: isFavorite,
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(12)),
width: 40,
height: 44,
size: 19,
),
),
// Testing Issue 329
Positioned(
right: 12,
top: 12,
child: SizedBox(
width: 32,
height: 24,
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
child: CustomPopup(
isAuthor: widget.recipe.author.authorIsMe,
path: 'recipe/${widget.recipe.slug}',
slug: widget.recipe.slug,
complainedContentType: ComplainedContent.recipe,
recipe: widget.recipe,
),
),
),
),
],
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
widget.recipe.title ?? '',
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
),
if (widget.recipe.videoUrl != null && widget.recipe.videoUrl!.isNotEmpty) ...[
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.only(left: 15),
child: Row(
children: [
SvgPicture.asset('assets/icons/play_icon.svg', width: 16, height: 16),
const SizedBox(width: 8),
Text(
s.there_is_a_video,
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],
),
),
],
const SizedBox(height: 12),
if (widget.recipe.description.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Html(
data: widget.recipe.description.length > 200 ? widget.recipe.description.substring(0, 200) : widget.recipe.description,
style: {
"*": Style(
color: appTheme.black,
margin: Margins.all(0),
fontFamily: TextStyles.textSMedium.fontFamily,
fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14),
fontWeight: TextStyles.textSMedium.fontWeight,
),
},
),
),
if (widget.recipe.description.length > 200)
const Padding(
padding: EdgeInsets.only(left: 16, top: 8),
child: Text(
'Показать ещё',
style: TextStyle(
height: 1,
color: Color(0xFF8B8B8B),
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
FeedIconButton(
icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded,
text: totalLikes.toString(),
iconColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null,
backgroundColor: isLiked ? Colors.red.withValues(alpha: 0.1) : null,
textColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null,
onTap: () async {
await context.read<RecipesCubit>().likeRecipe(widget.recipe.slug, widget.index);
},
),
const SizedBox(width: 8),
FeedIconButton(
icon: Icons.chat_bubble_outline,
text: widget.recipe.commentsCount.toString(),
onTap: () => GoRouter.of(context).pushNamed(
'aboutRecipesScreen',
extra: {
'recipe': widget.recipe,
'index': widget.index,
'navigateToComments': true,
},
),
),
const SizedBox(width: 8),
FeedIconButton(
svgIcon: SvgPicture.asset(
height: 15,
width: 12,
'assets/icons/repost.svg',
colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn),
),
onTap: () {
final box = context.findRenderObject() as RenderBox?;
Share.shareUri(
Uri.parse(
'$baseUrl/ru/blog_post/${widget.recipe.id}',
),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
},
),
const Spacer(),
Icon(
Icons.visibility_rounded,
size: 16,
color: appTheme.gray500,
),
const SizedBox(width: 4),
Text(
widget.recipe.viewsCount.toString(),
style: TextStyle(
fontSize: 12,
color: appTheme.gray500,
fontWeight: FontWeight.w400,
),
),
],
),
),
const SizedBox(height: 12),
],
),
),
);
},
);
}
_iconButton(String iconPath, [String? text]) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: appTheme.whiteA700,
borderRadius: BorderRadius.circular(16),
),
height: 32,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
iconPath,
),
if (text != null) const SizedBox(width: 8),
if (text != null)
Text(
text,
style: const TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}