Files
eatvio_app/lib/features/feed/presentation/widgets/recipes_widget.dart

254 lines
11 KiB
Dart

// ignore_for_file: dead_code
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/locator_service.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:eatvio_app/core/widgets/markdown_html_widget.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.slug, '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: '${sl<AppConfig>().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,
userSlug: widget.recipe.author.authorSlug,
),
),
),
),
],
),
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: Builder(
builder: (context) {
String description = widget.recipe.description.length > 200
? widget.recipe.description.substring(0, 200)
: widget.recipe.description;
return MarkdownHtmlWidget(
markdownText: description,
fontSize: TextStyles.textSMedium.fontSize ?? 14,
textColor: appTheme.black,
);
},
),
),
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.slug, '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?;
SharePlus.instance.share(
ShareParams(
uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/recipe/${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),
],
),
),
);
},
);
}
Container _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),
),
],
),
);
}
}