forked from eatvio/eatvio_app
feat(diary): add diary, calendar, meals, products, add product button, modal add product
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DiaryCubit extends Cubit<DiaryState> {
|
||||
final DiaryRepository diaryRepository;
|
||||
final GlossaryRepository glossaryRepository;
|
||||
|
||||
DiaryCubit({
|
||||
required this.diaryRepository,
|
||||
required this.glossaryRepository,
|
||||
}) : super(const DiaryState.loading());
|
||||
|
||||
getDiaryDay(String date) async {
|
||||
print(date);
|
||||
emit(const DiaryState.loading());
|
||||
getDiaryDay(String date, [bool needLoading = true]) async {
|
||||
if (needLoading) emit(const DiaryState.loading());
|
||||
try {
|
||||
emit(DiaryState.loaded(diary: await diaryRepository.getDiaryByDate(date)));
|
||||
} catch (e) {
|
||||
@@ -19,4 +22,26 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
addProductToMeal({
|
||||
required int diaryId,
|
||||
required int mealId,
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
required String date,
|
||||
}) async {
|
||||
await diaryRepository.addPortionToMeal(
|
||||
diaryId: diaryId,
|
||||
mealId: mealId,
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
);
|
||||
await getDiaryDay(date, false);
|
||||
}
|
||||
|
||||
Future<List<GlossaryModel>> getGlossaryListByQuery(String query) async {
|
||||
return await glossaryRepository.getGlossaryListByQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
import 'package:eatvio_app/locator_service.dart';
|
||||
import 'package:eatvio_app/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:rive/rive.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
@@ -21,6 +24,9 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
DateTime? _selectedDay = DateTime.now();
|
||||
int currentSelectedMealNumber = 1;
|
||||
GlossaryModel? selectedGlossary;
|
||||
final productNameController = TextEditingController();
|
||||
final quantityController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -47,6 +53,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
_calendar(state),
|
||||
if (state is DiaryStateLoading) _loader(),
|
||||
if (state is DiaryStateLoaded) _meals(state.diary),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -57,23 +64,12 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
}
|
||||
|
||||
_arrowDown() {
|
||||
return GestureDetector(
|
||||
onTapDown: (_) {
|
||||
setState(() {
|
||||
if (_calendarFormat == CalendarFormat.week) {
|
||||
_calendarFormat = CalendarFormat.month;
|
||||
} else {
|
||||
_calendarFormat = CalendarFormat.week;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 16),
|
||||
width: double.infinity,
|
||||
child: Icon(
|
||||
_calendarFormat == CalendarFormat.month ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 16),
|
||||
width: double.infinity,
|
||||
child: Icon(
|
||||
_calendarFormat == CalendarFormat.month ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -97,6 +93,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
_product(
|
||||
portions[i].productName,
|
||||
portions[i].quantity,
|
||||
portions[i].portion,
|
||||
),
|
||||
i < portions.length - 1 ? const SizedBox(height: 8) : const SizedBox.shrink(),
|
||||
],
|
||||
@@ -104,12 +101,13 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_product(String product, int portion) {
|
||||
_product(String product, int quantity, String portion) {
|
||||
portion == 'gr' ? portion = 'г' : portion = 'шт';
|
||||
return ListTile(
|
||||
onTap: () {},
|
||||
trailing: Icon(Icons.more_vert_rounded),
|
||||
title: Text(product),
|
||||
subtitle: Text('$portion г'),
|
||||
subtitle: Text('$quantity $portion'),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
@@ -161,7 +159,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
const SizedBox(height: 12),
|
||||
_products(diaryDay.meals[currentSelectedMealNumber - 1].portions),
|
||||
const SizedBox(height: 16),
|
||||
_addProductButton(),
|
||||
_addProductButton(diaryDay),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -240,6 +238,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
currentSelectedMealNumber = 1;
|
||||
});
|
||||
diaryCubit.getDiaryDay(_selectedDay!.toString());
|
||||
}
|
||||
@@ -255,6 +254,14 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
_focusedDay = focusedDay;
|
||||
},
|
||||
calendarStyle: CalendarStyle(
|
||||
weekendDecoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
defaultDecoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
selectedDecoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
shape: BoxShape.circle,
|
||||
@@ -267,6 +274,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
color: appTheme.blueA400,
|
||||
width: 2,
|
||||
),
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
@@ -277,9 +285,134 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_addProductButton() {
|
||||
_addProductButton(DiaryDayModel diaryDay) {
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedGlossary = null;
|
||||
productNameController.clear();
|
||||
quantityController.clear();
|
||||
});
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16) + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Добавление блюда',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF323F4B),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TypeAheadField(
|
||||
controller: productNameController,
|
||||
itemBuilder: (context, glossary) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
glossary.name,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemSeparatorBuilder: (context, index) => _divider(context),
|
||||
onSelected: (newSelectedGlossary) {
|
||||
setState(() {
|
||||
selectedGlossary = newSelectedGlossary;
|
||||
productNameController.text = selectedGlossary!.name;
|
||||
});
|
||||
},
|
||||
suggestionsCallback: (query) async {
|
||||
if (query.length < 1) return [];
|
||||
return await diaryCubit.getGlossaryListByQuery(query);
|
||||
},
|
||||
builder: (context, controller, focusNode) {
|
||||
return CustomTextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
autofocus: true,
|
||||
hintText: 'Введите название продукта/блюда',
|
||||
);
|
||||
},
|
||||
emptyBuilder: (context) => const SizedBox.shrink(),
|
||||
errorBuilder: (context, object) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text('Нет подходящих вариантов'),
|
||||
),
|
||||
),
|
||||
if (selectedGlossary != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: CustomTextFormField(
|
||||
controller: quantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
hintText: selectedGlossary!.grInPortion == 0 ? '100' : '1',
|
||||
suffixText: selectedGlossary != null
|
||||
? selectedGlossary!.grInPortion == 0
|
||||
? 'гр'
|
||||
: 'шт'
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
diaryCubit.addProductToMeal(
|
||||
diaryId: diaryDay.id,
|
||||
mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
|
||||
productName: productNameController.text,
|
||||
portion: selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs',
|
||||
quantity: int.parse(quantityController.text),
|
||||
date: _selectedDay.toString(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
padding: const EdgeInsets.all(16),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Добавить',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
|
||||
Reference in New Issue
Block a user