import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../design/cookit_colors.dart'; import '../../localization/app_localizations.dart'; import '../../recipes/amount.dart'; import '../../recipes/models/recipe.dart'; import '../../recipes/time_format.dart'; import '../../recipes/unit_catalog.dart'; import '../../recipes/views/recipe_detail_view.dart'; import '../recipe_share_service.dart'; import '../shared_recipe.dart'; /// A Recipe from another cook, shown before anything is saved: what it holds, /// a hint when one of that name exists already, and "Speichern". class SharedRecipeView extends StatefulWidget { const SharedRecipeView({super.key, required this.shared}); static const routeName = '/received'; final SharedRecipe shared; @override State createState() => _SharedRecipeViewState(); } class _SharedRecipeViewState extends State { late final Future _nameTaken = context.read().hasRecipeNamed(widget.shared.recipe.name); var _saving = false; Future _save() async { final l10n = AppLocalizations.of(context); final messenger = ScaffoldMessenger.of(context); final navigator = Navigator.of(context); setState(() => _saving = true); final recipe = await context.read().save(widget.shared); navigator.pushReplacementNamed(RecipeDetailView.routeName, arguments: recipe.id); messenger ..hideCurrentSnackBar() ..showSnackBar(SnackBar(content: Text(l10n.recipeSaved(recipe.name)))); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final c = CookItColors.of(context); final units = context.watch(); final shared = widget.shared; final recipe = shared.recipe; final sentUnits = {for (final u in shared.units) u.key: u.label}; String unitLabel(String key) => units.byKey(key) != null || !sentUnits.containsKey(key) ? units.label(l10n, key) : sentUnits[key]!; final facts = [ if (shared.courseName != null) shared.courseName!, if (recipe.servings != null) '${formatNumber(recipe.servings!)} ${l10n.factServings}', if (recipe.totalMinutes != null) formatMinutes(l10n, recipe.totalMinutes!), ].join(' ยท '); final heading = TextStyle(fontFamily: 'Nunito', fontWeight: FontWeight.w800, fontSize: 17, color: c.green); var number = 0; return Scaffold( appBar: AppBar(title: Text(l10n.receivedRecipe)), bottomNavigationBar: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 16), child: SizedBox( height: 52, child: FilledButton.icon( onPressed: _saving ? null : _save, icon: const Icon(Icons.download_done_rounded), label: Text(l10n.save), ), ), ), ), body: ListView( padding: const EdgeInsets.fromLTRB(20, 8, 20, 24), children: [ if (shared.photos.isNotEmpty) ...[ ClipRRect( borderRadius: BorderRadius.circular(22), child: AspectRatio( aspectRatio: 16 / 10, child: Image.memory(shared.photos.first, fit: BoxFit.cover, cacheWidth: 1000), ), ), const SizedBox(height: 16), ], Text( recipe.name, style: TextStyle(fontFamily: 'Nunito', fontWeight: FontWeight.w900, fontSize: 28, color: c.ink), ), if (facts.isNotEmpty) ...[ const SizedBox(height: 4), Text( facts, style: TextStyle(fontWeight: FontWeight.w700, color: c.inkMuted), ), ], FutureBuilder( future: _nameTaken, builder: (context, snapshot) => snapshot.data != true ? const SizedBox.shrink() : Container( margin: const EdgeInsets.only(top: 14), padding: const EdgeInsets.all(12), decoration: BoxDecoration(color: c.honey, borderRadius: BorderRadius.circular(14)), child: Text( l10n.recipeNameTaken(recipe.name), style: TextStyle(fontWeight: FontWeight.w700, color: c.onHoney), ), ), ), const SizedBox(height: 20), Text(l10n.ingredients, style: heading), if (recipe.ingredientGroups.isEmpty) Text(l10n.noIngredients), for (final group in recipe.ingredientGroups) ...[ if (group.name != null) Padding( padding: const EdgeInsets.only(top: 10, bottom: 2), child: Text( group.name!, style: TextStyle(fontWeight: FontWeight.w800, color: c.ink), ), ), for (final line in group.lines) _IngredientLine(line: line, unitLabel: unitLabel), ], const SizedBox(height: 20), Text(l10n.steps, style: heading), if (recipe.stepSections.isEmpty) Text(l10n.noSteps), for (final section in recipe.stepSections) ...[ if (section.title != null) Padding( padding: const EdgeInsets.only(top: 10, bottom: 2), child: Text( section.title!, style: TextStyle(fontWeight: FontWeight.w800, color: c.ink), ), ), for (final step in section.steps) Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Text('${++number}. $step', style: TextStyle(fontSize: 15, height: 1.4, color: c.ink)), ), ], if (recipe.notes != null) ...[ const SizedBox(height: 20), Text(l10n.notes, style: heading), Text(recipe.notes!, style: TextStyle(fontSize: 15, height: 1.4, color: c.ink)), ], ], ), ); } } class _IngredientLine extends StatelessWidget { const _IngredientLine({required this.line, required this.unitLabel}); final IngredientLine line; final String Function(String key) unitLabel; @override Widget build(BuildContext context) { final c = CookItColors.of(context); final amount = [ formatAmount(line.amount, line.amountMax), if (line.unitKey != null) unitLabel(line.unitKey!), ].where((s) => s.isNotEmpty).join(' '); return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 96, child: Text( amount, style: TextStyle(fontWeight: FontWeight.w800, color: c.ink), ), ), Expanded( child: Text.rich( TextSpan( text: line.name, children: [ if (line.remark != null) TextSpan( text: ' ${line.remark}', style: TextStyle(color: c.inkMuted), ), ], ), style: TextStyle(color: c.ink), ), ), ], ), ); } }