155 lines
5.7 KiB
Dart
155 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../../../design/cookit_colors.dart';
|
|
import '../../../localization/app_localizations.dart';
|
|
import '../../../recipes/recipe_list_provider.dart';
|
|
import '../../../recipes/views/widgets/recipe_photo.dart';
|
|
import '../../day.dart';
|
|
import '../../day_format.dart';
|
|
import '../../models/meal.dart';
|
|
import '../../plan_repository.dart';
|
|
|
|
/// "+" on a Meal: search the collection for a Recipe, or keep what was typed as a note.
|
|
Future<void> showAddEntrySheet(BuildContext context, DateTime day, Meal meal) => showModalBottomSheet<void>(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
showDragHandle: true,
|
|
builder: (_) => _AddEntrySheet(day: day, meal: meal),
|
|
);
|
|
|
|
class _AddEntrySheet extends StatefulWidget {
|
|
const _AddEntrySheet({required this.day, required this.meal});
|
|
|
|
final DateTime day;
|
|
final Meal meal;
|
|
|
|
@override
|
|
State<_AddEntrySheet> createState() => _AddEntrySheetState();
|
|
}
|
|
|
|
class _AddEntrySheetState extends State<_AddEntrySheet> {
|
|
final _query = TextEditingController();
|
|
|
|
/// Only Recipes of this Course; null shows all.
|
|
String? _courseId;
|
|
|
|
@override
|
|
void dispose() {
|
|
_query.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _add({String? recipeId, String? note}) async {
|
|
final plan = context.read<PlanRepository>();
|
|
Navigator.pop(context);
|
|
await plan.addEntry(widget.day, widget.meal.id, recipeId: recipeId, note: note);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context);
|
|
final c = CookItColors.of(context);
|
|
final provider = context.watch<RecipeListProvider>();
|
|
final text = _query.text.trim();
|
|
final recipes = provider.allRecipes
|
|
.where((r) => _courseId == null || r.courseId == _courseId)
|
|
.where((r) => r.name.toLowerCase().contains(text.toLowerCase()))
|
|
.toList();
|
|
|
|
return Padding(
|
|
padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom),
|
|
child: SizedBox(
|
|
height: MediaQuery.sizeOf(context).height * 0.75,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 12),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text(
|
|
l10n.addEntryTitle(widget.meal.name, dayLabel(l10n, widget.day, dayOf(DateTime.now()))),
|
|
style: TextStyle(fontFamily: 'Nunito', fontWeight: FontWeight.w900, fontSize: 22, color: c.ink),
|
|
),
|
|
const SizedBox(height: 12),
|
|
TextField(
|
|
controller: _query,
|
|
autofocus: true,
|
|
textCapitalization: TextCapitalization.sentences,
|
|
textInputAction: TextInputAction.done,
|
|
onChanged: (_) => setState(() {}),
|
|
onSubmitted: (value) {
|
|
if (value.trim().isNotEmpty) _add(note: value.trim());
|
|
},
|
|
decoration: InputDecoration(
|
|
hintText: l10n.addEntryHint,
|
|
prefixIcon: Icon(Icons.search_rounded, color: c.green),
|
|
fillColor: c.background,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 40,
|
|
child: ListView(
|
|
scrollDirection: Axis.horizontal,
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
children: [
|
|
for (final course in [null, ...provider.courses])
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: ChoiceChip(
|
|
label: Text(course == null ? l10n.filterAll : course.label),
|
|
selected: _courseId == course?.id,
|
|
showCheckmark: false,
|
|
onSelected: (_) => setState(() => _courseId = course?.id),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Expanded(
|
|
child: ListView(
|
|
children: [
|
|
if (text.isNotEmpty)
|
|
ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundColor: c.tiles[0],
|
|
child: Icon(Icons.sticky_note_2_outlined, color: c.green),
|
|
),
|
|
title: Text(l10n.addAsNote(text), style: const TextStyle(fontWeight: FontWeight.w800)),
|
|
onTap: () => _add(note: text),
|
|
),
|
|
for (final recipe in recipes)
|
|
ListTile(
|
|
leading: ClipRRect(
|
|
borderRadius: BorderRadius.circular(10),
|
|
child: SizedBox(
|
|
width: 44,
|
|
height: 44,
|
|
child: RecipePhoto(
|
|
photo: recipe.mainPhoto,
|
|
seed: recipe.id,
|
|
placeholderEmoji: provider.courseOf(recipe)?.emoji,
|
|
cacheWidth: 132,
|
|
),
|
|
),
|
|
),
|
|
title: Text(recipe.name),
|
|
subtitle: provider.courseOf(recipe) == null ? null : Text(provider.courseOf(recipe)!.name),
|
|
onTap: () => _add(recipeId: recipe.id),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|