Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77bc29c3d5 | |||
| 05e04c444f | |||
| 75f7d52d39 | |||
| becf515acc | |||
| 65c465ab9d | |||
| 6d04d1a688 | |||
| 3e4b683c33 | |||
| f52c929b20 | |||
| c46f612e1d | |||
| bd61687a96 | |||
| 1903896cf6 | |||
| d2371ef8ce | |||
| c7e19718a3 | |||
| ba6c0f06f6 | |||
| 7d0de42488 | |||
| 0091e00060 | |||
| faf0439349 | |||
| 139f57e1b5 | |||
| a6ab577d20 | |||
| 8e49d056c5 | |||
| 397b579e56 | |||
| 57a7f9bd91 | |||
| 2fdf3979da | |||
| 2c2508840d | |||
| f25bbfecbf | |||
| 5118697a02 | |||
| 7da4cb8014 | |||
| 82eca66ae4 | |||
| 7b989076cc | |||
| d3c00cc99f | |||
| e8ff33b4d2 | |||
| e036f4c318 | |||
| 53d6ea56a0 | |||
| 4ec2834a42 |
@@ -28,7 +28,7 @@
|
||||
|
||||
### Как запускать?
|
||||
|
||||
Для запуска на эмуляторе в программе VS Code скомпилируйте и запустите файл main.dart (предварительно необходимо установить эмулятор в Android Studio)
|
||||
Для запуска на эмуляторе в программе VS Code скомпилируйте и запустите файл main.dart (предварительно необходимо установить эмулятор в Android Studio или подключите устройство в режиме разработчика через USB)
|
||||
|
||||
Для тестирования на устройствах Android в корневой папке проекта выполните команду
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 6.2 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 5.9 KiB |
44
ios/Podfile
Normal file
44
ios/Podfile
Normal file
@@ -0,0 +1,44 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '12.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_ios_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
target 'RunnerTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
end
|
||||
end
|
||||
@@ -41,5 +41,9 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -68,6 +68,14 @@ class AuthScreen extends StatelessWidget {
|
||||
Navigator.pushNamed(
|
||||
context, AppRoutes.languageSelectScreen);
|
||||
}),
|
||||
NextStepButton(
|
||||
text: "Тест дневника".toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, AppRoutes.diaryEdit);
|
||||
}),
|
||||
BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
|
||||
return NextStepButton(
|
||||
text: "authentificate".tr.toUpperCase(),
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '/core/app_export.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
part 'diary_edit_event.dart';
|
||||
part 'diary_edit_state.dart';
|
||||
|
||||
/// A bloc that manages the state of a DiaryEdit according to the event that is dispatched to it.
|
||||
class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
DiaryEditBloc(DiaryEditState initialState) : super(initialState) {
|
||||
on<DiaryEditInitialEvent>(_onInitialize);
|
||||
on<ChangeSatiety>(_changeSatiety); // пользователь нажимает на шкалу сытости
|
||||
on<ChangeMood>(_changeMood); // пользователь нажимает на шкалу настроения
|
||||
on<ChangeMealTime>(
|
||||
_changeMealTime); // пользователь изменяет время приема пиши
|
||||
on<ChangeMeal>(_changeMeal);
|
||||
on<WakeupEvent>(_changeWakeup); // пользователь выбирает время пробуждения
|
||||
on<RetireEvent>(_changeRetire); // пользователь выбирает время отхода ко сну
|
||||
on<ToggleMoodExpandedEvent>(_toggleMood);
|
||||
on<ToggleWakeupExpandedEvent>(_toggleWakeup);
|
||||
on<AddPortionEvent>(_addPortion); //пользователь нажимает "Добавить продукт"
|
||||
on<RemovePortionEvent>(
|
||||
_removePortion); // пользователь нажимает на крестик около продукта
|
||||
on<UploadImages>(_uploadImages); //пользователь нажимает "Добавить фото"
|
||||
on<AddComment>(_addComment); //пользователь нажимает "Добавить комментарий"
|
||||
}
|
||||
|
||||
Future<void> _onInitialize(
|
||||
DiaryEditInitialEvent event,
|
||||
Emitter<DiaryEditState> emit,
|
||||
) async {
|
||||
try {
|
||||
final token =
|
||||
"laravel_sanctum_Q22zOtJhQUcJnh0kseZFblFv5XAPaKbG3gTM5hga71ecbb49";
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
if (data is Map<String, dynamic> && data.containsKey('id')) {
|
||||
final int diaryId = data['id'];
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/show/$diaryId'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final diary = jsonDecode(response.body)['diary'];
|
||||
if (diary is Map<String, dynamic>) {
|
||||
print(diary['meals'][1].runtimeType);
|
||||
final String diaryDate =
|
||||
diary['date'] ?? DateTime.now().toString();
|
||||
// Продолжайте обработку данных дневника
|
||||
if (state is DiaryEditLoadedState) {
|
||||
// Если текущее состояние уже загружено, обновляем его
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
emit((currentState).copyWith(
|
||||
currentMeal: currentState.currentMeal,
|
||||
wakeupExpanded: currentState.wakeupExpanded,
|
||||
moodExpanded: currentState.moodExpanded,
|
||||
token: currentState.token,
|
||||
meals: currentState.meals,
|
||||
wakeupController: currentState.wakeupController,
|
||||
retireController: currentState.retireController,
|
||||
commentController: currentState.commentController,
|
||||
mealTimeController: currentState.mealTimeController,
|
||||
productNameController: currentState.productNameController,
|
||||
selectedSatiety: currentState.selectedSatiety,
|
||||
selectedMood: currentState.selectedMood,
|
||||
diaryDate: currentState.diaryDate,
|
||||
diaryId: currentState.diaryId,
|
||||
productQuantityController:
|
||||
currentState.productQuantityController,
|
||||
diaryEditModelObj: currentState.diaryEditModelObj));
|
||||
} else {
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
emit(DiaryEditLoadedState(
|
||||
currentMeal: SelectionPopupModel(
|
||||
id: 0, title: "Прием пищи 1", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
token: token,
|
||||
meals: diary['meals'],
|
||||
portions: diary['meals'][0]['portions'],
|
||||
wakeupController: toTime(diary['wakeup'] ?? "09:00"),
|
||||
retireController: toTime(diary['retire'] ?? "23:00"),
|
||||
mealTimeController:
|
||||
toTime(diary['meals'][0]['time'] ?? "10:00"),
|
||||
selectedSatiety: diary['meals'][0]['satiety'],
|
||||
selectedMood: diary['meals'][0]['mood'],
|
||||
commentController:
|
||||
TextEditingController(text: diary['comment']),
|
||||
productNameController: TextEditingController(),
|
||||
productQuantityController: TextEditingController(),
|
||||
diaryId: diaryId,
|
||||
diaryDate: diaryDate,
|
||||
diaryEditModelObj: state.diaryEditModelObj
|
||||
?.copyWith(mealList: fillMealList())));
|
||||
}
|
||||
} else {
|
||||
// Ошибка при декодировании JSON
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при декодировании данных дневника'));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при получении данных дневника: $e'));
|
||||
}
|
||||
} else {
|
||||
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
|
||||
emit(
|
||||
DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'));
|
||||
}
|
||||
} else {
|
||||
// Статус ответа не равен 200, обрабатываем ошибку
|
||||
emit(DiaryEditErrorState('Ошибка сервера: ${response.statusCode}'));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
emit(DiaryEditErrorState('Ошибка при получении данных дневника: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _changeSatiety(
|
||||
ChangeSatiety event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
currentState.copyWith(selectedSatiety: event.selectedElement);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendSatietyData(
|
||||
currentState.token,
|
||||
event.selectedElement,
|
||||
currentState.currentMeal?.id,
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.selectedSatiety = event.selectedElement;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendSatietyData(
|
||||
String? token,
|
||||
int selectedSatiety,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '$diaryID/meal/$mealID/satiety/fill';
|
||||
final credentials = {
|
||||
'satiety': selectedSatiety,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<void> _changeMood(
|
||||
ChangeMood event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
currentState.copyWith(selectedMood: event.selectedElement);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendMoodData(
|
||||
currentState.token,
|
||||
event.selectedElement,
|
||||
currentState.currentMeal?.id,
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.selectedMood = event.selectedElement;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendMoodData(
|
||||
String? token,
|
||||
int selectedMood,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/mood/fill';
|
||||
final credentials = {
|
||||
'mood': selectedMood,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeWakeup(
|
||||
WakeupEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
currentState.copyWith(wakeupController: event.selectedTime);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendWakeupData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.wakeupController = event.selectedTime;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendWakeupData(
|
||||
String? token,
|
||||
TimeOfDay wakeup,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
|
||||
final credentials = {
|
||||
'wakeup': wakeup,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeRetire(
|
||||
RetireEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
currentState.copyWith(retireController: event.selectedTime);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendRetireData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.retireController = event.selectedTime;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendRetireData(
|
||||
String? token,
|
||||
TimeOfDay retire,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
|
||||
final credentials = {
|
||||
'wakeup': retire,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeMealTime(
|
||||
ChangeMealTime event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState = currentState.copyWith(mealTimeController: event.time);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendMealTimeData(currentState.token, event.time,
|
||||
currentState.currentMeal?.id, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.retireController = event.time;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendMealTimeData(
|
||||
String? token,
|
||||
TimeOfDay mealTime,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
|
||||
final credentials = {
|
||||
'time': mealTime,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeMeal(ChangeMeal event, Emitter<DiaryEditState> emit) {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
int? id = event.value.id;
|
||||
emit(currentState.copyWith(
|
||||
currentMeal: event.value,
|
||||
portions: event.meals?[event.value.id ?? 0]['portions'],
|
||||
mealTimeController:
|
||||
toTime(event.meals?[event.value.id ?? 0]['time'] ?? "1$id:00"),
|
||||
selectedMood: event.meals?[event.value.id ?? 0]['mood'],
|
||||
selectedSatiety: event.meals?[event.value.id ?? 0]['satiety']));
|
||||
currentState.currentMeal = event.value;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _toggleMood(
|
||||
ToggleMoodExpandedEvent event, Emitter<DiaryEditState> emit) {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
emit(currentState.copyWith(moodExpanded: !currentState.moodExpanded));
|
||||
currentState.moodExpanded = !currentState.moodExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _toggleWakeup(
|
||||
ToggleWakeupExpandedEvent event, Emitter<DiaryEditState> emit) {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
emit(currentState.copyWith(wakeupExpanded: !currentState.wakeupExpanded));
|
||||
currentState.wakeupExpanded = !currentState.wakeupExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _addPortion(
|
||||
AddPortionEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
int? id = currentState.currentMeal?.id;
|
||||
var updatedState = currentState.copyWith(
|
||||
currentMeal: currentState.diaryEditModelObj?.mealList[id ?? 0],
|
||||
portions: currentState.meals?[id ?? 0]['portions'],
|
||||
mealTimeController:
|
||||
toTime(currentState.meals?[id ?? 0]['time'] ?? "1$id:00"),
|
||||
selectedMood: currentState.meals?[id ?? 0]['mood'],
|
||||
selectedSatiety: currentState.meals?[id ?? 0]['satiety']);
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendPortionData(
|
||||
currentState.token,
|
||||
event.name,
|
||||
event.portion,
|
||||
event.quantity,
|
||||
currentState.currentMeal!.id,
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendPortionData(
|
||||
String? token,
|
||||
String name,
|
||||
String portion,
|
||||
int quantity,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/portions/create';
|
||||
final credentials = {
|
||||
'name': name,
|
||||
'portion': portion,
|
||||
'quantity': quantity,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _removePortion(
|
||||
RemovePortionEvent event, Emitter<DiaryEditState> emit) {}
|
||||
|
||||
Future<FutureOr<void>> _uploadImages(
|
||||
UploadImages event, Emitter<DiaryEditState> emit) async {
|
||||
String imageUrl = await uploadImageToServer(event.image.path);
|
||||
print(imageUrl);
|
||||
}
|
||||
|
||||
FutureOr<void> _addComment(AddComment event, Emitter<DiaryEditState> emit) {}
|
||||
Future<String> uploadImageToServer(String imagePath) async {
|
||||
var request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('http://your-api-url.com/upload'), // Замените на ваш маршрут API
|
||||
);
|
||||
|
||||
request.files.add(await http.MultipartFile.fromPath('image', imagePath));
|
||||
|
||||
var response = await request.send();
|
||||
if (response.statusCode == 200) {
|
||||
var responseData = await response.stream.bytesToString();
|
||||
return responseData;
|
||||
} else {
|
||||
throw Exception('Ошибка при загрузке изображения');
|
||||
}
|
||||
}
|
||||
|
||||
TimeOfDay toTime(String timeStr) {
|
||||
List<String> timeParts = timeStr.split(":");
|
||||
int hour = int.parse(timeParts[0]);
|
||||
int minute = int.parse(timeParts[1]);
|
||||
|
||||
TimeOfDay timeOfDay = TimeOfDay(hour: hour, minute: minute);
|
||||
return timeOfDay;
|
||||
}
|
||||
|
||||
List<SelectionPopupModel> fillMealList() {
|
||||
return [
|
||||
SelectionPopupModel(id: 0, title: "Прием пищи 1", isSelected: true),
|
||||
SelectionPopupModel(id: 1, title: "Прием пищи 2"),
|
||||
SelectionPopupModel(id: 2, title: "Прием пищи 3"),
|
||||
SelectionPopupModel(id: 3, title: "Прием пищи 4"),
|
||||
SelectionPopupModel(id: 4, title: "Прием пищи 5"),
|
||||
SelectionPopupModel(id: 5, title: "Прием пищи 6"),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'diary_edit_bloc.dart';
|
||||
|
||||
/// Abstract class for all events that can be dispatched from the
|
||||
///DiaryEdit widget.
|
||||
///
|
||||
/// Events must be immutable and implement the [Equatable] interface.
|
||||
@immutable
|
||||
abstract class DiaryEditEvent extends Equatable {}
|
||||
|
||||
/// Event that is dispatched when the DiaryEdit widget is first created.
|
||||
class DiaryEditInitialEvent extends DiaryEditEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ChangeSatiety extends DiaryEditEvent {
|
||||
ChangeSatiety(
|
||||
this.selectedElement,
|
||||
);
|
||||
final int selectedElement;
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedElement];
|
||||
}
|
||||
|
||||
class ChangeMood extends DiaryEditEvent {
|
||||
ChangeMood(
|
||||
this.selectedElement,
|
||||
);
|
||||
final int selectedElement;
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedElement];
|
||||
}
|
||||
|
||||
class ChangeMealTime extends DiaryEditEvent {
|
||||
ChangeMealTime(this.time);
|
||||
final TimeOfDay time;
|
||||
|
||||
@override
|
||||
List<Object> get props => [time];
|
||||
}
|
||||
|
||||
class AddComment extends DiaryEditEvent {
|
||||
AddComment(this.diaryId, this.comment);
|
||||
final String comment;
|
||||
final int diaryId;
|
||||
|
||||
@override
|
||||
List<Object> get props => [comment, diaryId];
|
||||
}
|
||||
|
||||
class WakeupEvent extends DiaryEditEvent {
|
||||
WakeupEvent(this.selectedTime);
|
||||
final TimeOfDay selectedTime;
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedTime];
|
||||
}
|
||||
|
||||
class RetireEvent extends DiaryEditEvent {
|
||||
RetireEvent(this.selectedTime);
|
||||
final TimeOfDay selectedTime;
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedTime];
|
||||
}
|
||||
|
||||
class ToggleMoodExpandedEvent extends DiaryEditEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ToggleWakeupExpandedEvent extends DiaryEditEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class AddPortionEvent extends DiaryEditEvent {
|
||||
AddPortionEvent(this.name, this.quantity, this.portion);
|
||||
final String name;
|
||||
final int quantity;
|
||||
final String portion;
|
||||
@override
|
||||
List<Object> get props => [name, quantity, portion];
|
||||
}
|
||||
|
||||
class RemovePortionEvent extends DiaryEditEvent {
|
||||
RemovePortionEvent(this.portionId);
|
||||
final int portionId;
|
||||
@override
|
||||
List<Object> get props => [portionId];
|
||||
}
|
||||
|
||||
class UploadImages extends DiaryEditEvent {
|
||||
UploadImages(this.image);
|
||||
final XFile image;
|
||||
@override
|
||||
List<Object> get props => [image];
|
||||
}
|
||||
|
||||
class ChangeMeal extends DiaryEditEvent {
|
||||
ChangeMeal({required this.value, required this.meals});
|
||||
|
||||
SelectionPopupModel value;
|
||||
List<dynamic>? meals;
|
||||
@override
|
||||
List<Object?> get props => [value, meals];
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'diary_edit_bloc.dart';
|
||||
|
||||
class DiaryEditState extends Equatable {
|
||||
const DiaryEditState({this.diaryEditModelObj});
|
||||
|
||||
final DiaryEditModel? diaryEditModelObj;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [diaryEditModelObj];
|
||||
}
|
||||
|
||||
class DiaryEditInitialState extends DiaryEditState {
|
||||
DiaryEditInitialState({required DiaryEditModel diaryEditModelObj})
|
||||
: super(diaryEditModelObj: diaryEditModelObj);
|
||||
}
|
||||
|
||||
/// Represents the state of DiaryEdit in the application.
|
||||
class DiaryEditLoadedState extends DiaryEditState {
|
||||
DiaryEditLoadedState(
|
||||
{this.diaryEditModelObj,
|
||||
this.diaryDate,
|
||||
this.token,
|
||||
this.meals,
|
||||
this.portions,
|
||||
this.wakeupController,
|
||||
this.retireController,
|
||||
this.commentController,
|
||||
this.mealTimeController,
|
||||
this.productNameController,
|
||||
this.productQuantityController,
|
||||
this.selectedMood,
|
||||
this.currentMeal,
|
||||
required this.diaryId,
|
||||
required this.selectedSatiety,
|
||||
required this.wakeupExpanded,
|
||||
required this.moodExpanded})
|
||||
: super(diaryEditModelObj: null);
|
||||
|
||||
DiaryEditModel? diaryEditModelObj;
|
||||
String? diaryDate;
|
||||
String? token;
|
||||
List<dynamic>? meals;
|
||||
List<dynamic>? portions;
|
||||
SelectionPopupModel? currentMeal;
|
||||
TimeOfDay? wakeupController;
|
||||
TimeOfDay? retireController;
|
||||
TimeOfDay? mealTimeController;
|
||||
TextEditingController? commentController;
|
||||
TextEditingController? productNameController;
|
||||
TextEditingController? productQuantityController;
|
||||
int? selectedMood;
|
||||
int selectedSatiety;
|
||||
int diaryId;
|
||||
bool wakeupExpanded;
|
||||
bool moodExpanded;
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
diaryEditModelObj,
|
||||
diaryDate,
|
||||
token,
|
||||
meals,
|
||||
wakeupController,
|
||||
retireController,
|
||||
mealTimeController,
|
||||
commentController,
|
||||
productNameController,
|
||||
productQuantityController,
|
||||
selectedMood,
|
||||
selectedSatiety,
|
||||
diaryId,
|
||||
wakeupExpanded,
|
||||
moodExpanded,
|
||||
currentMeal
|
||||
];
|
||||
DiaryEditLoadedState copyWith({
|
||||
DiaryEditModel? diaryEditModelObj,
|
||||
SelectionPopupModel? currentMeal,
|
||||
String? diaryDate,
|
||||
String? token,
|
||||
List<dynamic>? meals,
|
||||
List<dynamic>? portions,
|
||||
TimeOfDay? wakeupController,
|
||||
TimeOfDay? retireController,
|
||||
TimeOfDay? mealTimeController,
|
||||
TextEditingController? commentController,
|
||||
TextEditingController? productNameController,
|
||||
TextEditingController? productQuantityController,
|
||||
int? selectedMood,
|
||||
int? selectedSatiety,
|
||||
int? diaryId,
|
||||
bool? wakeupExpanded,
|
||||
bool? moodExpanded,
|
||||
}) {
|
||||
return DiaryEditLoadedState(
|
||||
diaryEditModelObj: diaryEditModelObj ?? this.diaryEditModelObj,
|
||||
diaryDate: diaryDate ?? this.diaryDate,
|
||||
token: token ?? this.token,
|
||||
meals: meals ?? this.meals,
|
||||
portions: portions ?? this.portions,
|
||||
wakeupController: wakeupController ?? this.wakeupController,
|
||||
retireController: retireController ?? this.retireController,
|
||||
mealTimeController: mealTimeController ?? this.mealTimeController,
|
||||
commentController: commentController ?? this.commentController,
|
||||
productNameController:
|
||||
productNameController ?? this.productNameController,
|
||||
productQuantityController:
|
||||
productQuantityController ?? this.productQuantityController,
|
||||
selectedMood: selectedMood ?? this.selectedMood,
|
||||
selectedSatiety: selectedSatiety ?? this.selectedSatiety,
|
||||
diaryId: diaryId ?? this.diaryId,
|
||||
currentMeal: currentMeal ?? this.currentMeal,
|
||||
wakeupExpanded: wakeupExpanded ?? this.wakeupExpanded,
|
||||
moodExpanded: moodExpanded ?? this.moodExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
class DiaryEditErrorState extends DiaryEditState {
|
||||
final String errorMessage;
|
||||
|
||||
const DiaryEditErrorState(this.errorMessage);
|
||||
|
||||
@override
|
||||
List<Object> get props => [errorMessage];
|
||||
}
|
||||
965
lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart
Normal file
965
lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart
Normal file
@@ -0,0 +1,965 @@
|
||||
import 'bloc/diary_edit_bloc.dart';
|
||||
import 'models/diary_edit_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
|
||||
import 'package:eatvio_app/widgets/custom_edit_drop_down.dart';
|
||||
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/widgets/custom_scale_element.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class DiaryEditScreen extends StatelessWidget {
|
||||
DiaryEditScreen({Key? key})
|
||||
: super(
|
||||
key: key,
|
||||
);
|
||||
|
||||
static Widget builder(BuildContext context) {
|
||||
return BlocProvider<DiaryEditBloc>(
|
||||
create: (context) =>
|
||||
DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))
|
||||
..add(DiaryEditInitialEvent()),
|
||||
child: DiaryEditScreen());
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DiaryEditBloc, DiaryEditState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryEditInitialState) {
|
||||
// Это начальное состояние, данные ещё не загружены.
|
||||
return CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
backgroundColor: Colors.white,
|
||||
);
|
||||
} else if (state is DiaryEditLoadedState) {
|
||||
// Данные загружены, можно строить экран.
|
||||
return _buildDiaryEditScreen(context, state);
|
||||
} else if (state is DiaryEditErrorState) {
|
||||
// Произошла ошибка при загрузке данных.
|
||||
return Center(child: Text(state.errorMessage));
|
||||
} else {
|
||||
// Другие состояния, если они есть.
|
||||
return Container(); // Пустой контейнер для необработанных состояний.
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDiaryEditScreen(
|
||||
BuildContext context, DiaryEditLoadedState state) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
return FormBuilder(
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: Color.fromARGB(255, 240, 242, 245),
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: 5.v),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeaderRow(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildWakupRetire(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
//_buildImageStack(context),
|
||||
_buildMeal(context, state),
|
||||
SizedBox(height: 20.v),
|
||||
_buildMood(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildNutritionChart(context),
|
||||
SizedBox(height: 30.v),
|
||||
_buildComment(context, state),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: _buildEndDay(context),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildHeaderRow(BuildContext context, DiaryEditLoadedState state) {
|
||||
final day = state.diaryId;
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomImageView(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
onTap: () {
|
||||
NavigatorService.goBack();
|
||||
}),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 1.v),
|
||||
child: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
"$day день",
|
||||
style: CustomTextStyles.titleMediumBlack,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 2.v),
|
||||
Text(
|
||||
state.diaryDate.toString(),
|
||||
style: CustomTextStyles.bodyMediumBluegray800,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildAddProduct(BuildContext context) {
|
||||
return CustomOutlinedButton(
|
||||
text: "Добавить продукт",
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildAddPhoto(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
final picker = ImagePicker();
|
||||
List<XFile>? images = await picker.pickMultiImage();
|
||||
if (images.isNotEmpty) {
|
||||
for (var image in images) {
|
||||
context.read<DiaryEditBloc>().add(UploadImages(image));
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 40.h,
|
||||
vertical: 80.v,
|
||||
),
|
||||
decoration: AppDecoration.fillGray.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 7.h,
|
||||
top: 5.v,
|
||||
),
|
||||
child: Text(
|
||||
"Добавить фото",
|
||||
selectionColor: appTheme.blueA400,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Icon(
|
||||
Icons.photo_library,
|
||||
color: appTheme.blueGray300,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildMeal(BuildContext context, DiaryEditLoadedState state) {
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Время приема пищи",
|
||||
style: CustomTextStyles.bodyMediumBlack900,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width - 260,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectMealTime(context, state),
|
||||
child: AbsorbPointer(
|
||||
child: CustomTextFormField(
|
||||
hintText: state.mealTimeController
|
||||
?.format(context)
|
||||
.toString(),
|
||||
autofocus: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: BlocSelector<DiaryEditBloc, DiaryEditState,
|
||||
DiaryEditModel?>(
|
||||
selector: (state) => state.diaryEditModelObj,
|
||||
builder: (context, diaryEditModelObj) {
|
||||
return CustomDropDown(
|
||||
hintText: "Прием пищи",
|
||||
items: diaryEditModelObj?.mealList,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10.h,
|
||||
vertical: 9.v,
|
||||
),
|
||||
defaultValue: diaryEditModelObj?.mealList[0],
|
||||
onChanged: (value) {
|
||||
context.read<DiaryEditBloc>().add(
|
||||
ChangeMeal(meals: state.meals, value: value));
|
||||
},
|
||||
);
|
||||
})),
|
||||
SizedBox(height: 20.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: CustomTextFormField(
|
||||
hintText: "Введите названия продукта",
|
||||
controller: state.productNameController,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
width: 146.h,
|
||||
hintText: "Кол-во",
|
||||
textInputAction: TextInputAction.done,
|
||||
controller: state.productQuantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 30.h),
|
||||
child: Text(
|
||||
"порций",
|
||||
style: CustomTextStyles.bodyMediumBlack900,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30.v),
|
||||
_buildAddProduct(context),
|
||||
SizedBox(height: 30.v),
|
||||
...state.portions!
|
||||
.map((portion) => _buildProduct(context, portion))
|
||||
.toList(),
|
||||
SizedBox(height: 30.v),
|
||||
_buildAddPhoto(context),
|
||||
SizedBox(height: 30.v),
|
||||
Text(
|
||||
"Ваша сытость после приёма пищи",
|
||||
style: CustomTextStyles.bodyMediumBlack900,
|
||||
),
|
||||
SizedBox(height: 15.v),
|
||||
BlocBuilder<DiaryEditBloc, DiaryEditState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("😭"),
|
||||
ScaleElement(
|
||||
color: Colors.red,
|
||||
isSelected: state.selectedSatiety == 0,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(0));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.orange,
|
||||
isSelected: state.selectedSatiety == 1,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(1));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.yellow,
|
||||
isSelected: state.selectedSatiety == 2,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(2));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.lime,
|
||||
isSelected: state.selectedSatiety == 3,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(3));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.lightGreen,
|
||||
isSelected: state.selectedSatiety == 4,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(4));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.green,
|
||||
isSelected: state.selectedSatiety == 5,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(5));
|
||||
},
|
||||
),
|
||||
Text("😊"),
|
||||
// Остальные элементы шкалы
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryEditLoadedState
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
_buildProduct(BuildContext context, Map<String, dynamic> portion) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.h,
|
||||
vertical: 6.v,
|
||||
),
|
||||
decoration: AppDecoration.fillGray.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 7.h,
|
||||
top: 5.v,
|
||||
),
|
||||
child: Text(
|
||||
portion['product_name'],
|
||||
style: CustomTextStyles.titleMedium16,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 20.h,
|
||||
top: 3.v,
|
||||
bottom: 3.v,
|
||||
),
|
||||
child: Text(
|
||||
portion['quantity'] + portion['portion'],
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<DiaryEditBloc>()
|
||||
.add(RemovePortionEvent(portion['id']));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: appTheme.blueGray300,
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNutritionChart(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10.h,
|
||||
vertical: 16.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 3.v),
|
||||
Text(
|
||||
"Ваш КБЖУ",
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
SizedBox(height: 8.v),
|
||||
Text(
|
||||
"От дневной суточной нормы",
|
||||
style: CustomTextStyles.bodyMediumBluegray800,
|
||||
),
|
||||
SizedBox(height: 19.v),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 30.h,
|
||||
right: 12.h,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(height: 101.v),
|
||||
Container(
|
||||
height: 47.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.errorContainer,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 66.v),
|
||||
Container(
|
||||
height: 82.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.lightGreenA700,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 14.v),
|
||||
Container(
|
||||
height: 134.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Container(
|
||||
height: 148.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.circular(
|
||||
1.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 6.h,
|
||||
bottom: 128.v,
|
||||
),
|
||||
child: Text(
|
||||
"*",
|
||||
style: theme.textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 4.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Калории",
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 28.h),
|
||||
child: Text(
|
||||
"Белки",
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
"Жиры",
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 25.h),
|
||||
child: Text(
|
||||
"Углеводы",
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 2.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10.h,
|
||||
right: 2.h,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"255 из 250Ккал",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"50 из 250 гр",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 19.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"270 из 250 гр",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 14.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"400 из 250 гр",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 19.v),
|
||||
Text(
|
||||
"*Превышенная суточная норма углеводов",
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildComment(BuildContext context, DiaryEditLoadedState state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Дополнительная информация для консультанта",
|
||||
style: theme.textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 20.v),
|
||||
Container(
|
||||
width: 320.h,
|
||||
child: CustomTextFormField(
|
||||
autofocus: false,
|
||||
hintText: "Введите информацию для консультанта",
|
||||
maxLines: 7,
|
||||
borderDecoration: InputBorder.none,
|
||||
controller: state.commentController,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildWakupRetire(BuildContext context, DiaryEditLoadedState state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
BlocProvider.of<DiaryEditBloc>(context)
|
||||
.add(ToggleWakeupExpandedEvent());
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
r"Подъем \ отход ко сну",
|
||||
style: CustomTextStyles.bodyMediumBlack900,
|
||||
),
|
||||
Icon(state.wakeupExpanded
|
||||
? Icons.arrow_drop_up
|
||||
: Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.wakeupExpanded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 20.v),
|
||||
GestureDetector(
|
||||
onTap: () => _selectWakeupTime(context, state),
|
||||
child: AbsorbPointer(
|
||||
child: CustomTextFormField(
|
||||
hintText: state.wakeupController
|
||||
?.format(context)
|
||||
.toString(),
|
||||
prefix: Icon(Icons.sunny),
|
||||
autofocus: false,
|
||||
/*initialValue: state.wakeupController
|
||||
?.format(context)
|
||||
.toString()*/
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
GestureDetector(
|
||||
onTap: () => _selectRetireTime(context, state),
|
||||
child: AbsorbPointer(
|
||||
child: CustomTextFormField(
|
||||
hintText: state.retireController
|
||||
?.format(context)
|
||||
.toString(),
|
||||
prefix: Icon(Icons.bed),
|
||||
autofocus: false,
|
||||
/*initialValue: state.retireController
|
||||
?.format(context)
|
||||
.toString(),*/
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildEndDay(BuildContext context) {
|
||||
return CustomOutlinedButton(
|
||||
text: "Закончить день",
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h,
|
||||
right: 16.h,
|
||||
bottom: 41.v,
|
||||
),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
), // Установите прозрачный фон
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMood(BuildContext context, DiaryEditLoadedState state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
BlocProvider.of<DiaryEditBloc>(context)
|
||||
.add(ToggleMoodExpandedEvent());
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Ваше настроение после приема пищи",
|
||||
style: CustomTextStyles.bodyMediumBlack900,
|
||||
),
|
||||
Icon(state.moodExpanded
|
||||
? Icons.arrow_drop_up
|
||||
: Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.moodExpanded)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("😭"),
|
||||
ScaleElement(
|
||||
color: Colors.red,
|
||||
isSelected: state.selectedMood == 0,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(0));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.orange,
|
||||
isSelected: state.selectedMood == 1,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(1));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.yellow,
|
||||
isSelected: state.selectedMood == 2,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(2));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.lime,
|
||||
isSelected: state.selectedMood == 3,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(3));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.lightGreen,
|
||||
isSelected: state.selectedMood == 4,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(4));
|
||||
},
|
||||
),
|
||||
ScaleElement(
|
||||
color: Colors.green,
|
||||
isSelected: state.selectedMood == 5,
|
||||
onTap: () {
|
||||
context.read<DiaryEditBloc>().add(ChangeMood(5));
|
||||
},
|
||||
),
|
||||
Text("😊"),
|
||||
// Остальные элементы шкалы
|
||||
],
|
||||
),
|
||||
])));
|
||||
}
|
||||
|
||||
Future<void> _selectWakeupTime(
|
||||
BuildContext context, DiaryEditLoadedState state) async {
|
||||
final TimeOfDay? pickedTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.wakeupController ?? TimeOfDay.now(),
|
||||
);
|
||||
if (pickedTime != null && pickedTime != state.wakeupController) {
|
||||
state.wakeupController = pickedTime;
|
||||
context.read<DiaryEditBloc>().add(WakeupEvent(
|
||||
pickedTime,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _selectRetireTime(
|
||||
BuildContext context, DiaryEditLoadedState state) async {
|
||||
final TimeOfDay? pickedTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.retireController ?? TimeOfDay.now(),
|
||||
);
|
||||
if (pickedTime != null && pickedTime != state.retireController) {
|
||||
state.retireController = pickedTime;
|
||||
context.read<DiaryEditBloc>().add(RetireEvent(pickedTime));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _selectMealTime(
|
||||
BuildContext context, DiaryEditLoadedState state) async {
|
||||
final TimeOfDay? pickedTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.mealTimeController ?? TimeOfDay.now(),
|
||||
);
|
||||
if (pickedTime != null && pickedTime != state.mealTimeController) {
|
||||
state.mealTimeController = pickedTime;
|
||||
context.read<DiaryEditBloc>().add(ChangeMealTime(pickedTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:eatvio_app/data/models/selectionPopupModel/selection_popup_model.dart';
|
||||
|
||||
/// This class defines the variables used in the [k0_page],
|
||||
/// and is typically used to hold data that is passed between different parts of the application.
|
||||
class DiaryEditModel extends Equatable {
|
||||
DiaryEditModel({this.mealList = const []});
|
||||
|
||||
List<SelectionPopupModel> mealList;
|
||||
DiaryEditModel copyWith({
|
||||
List<SelectionPopupModel>? mealList,
|
||||
}) {
|
||||
return DiaryEditModel(
|
||||
mealList: mealList ?? this.mealList,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [mealList];
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'bloc/my_profile_bloc.dart';
|
||||
import 'models/my_profile_model.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
|
||||
@@ -19,6 +19,7 @@ import 'package:eatvio_app/presentation/registration/app_navigation_screen/app_n
|
||||
import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/my_profile_screen.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/my_settings_screen.dart';
|
||||
import 'package:eatvio_app/presentation/diary/diary_edit_screen/diary_edit_screen.dart';
|
||||
|
||||
class AppRoutes {
|
||||
// REGISTRATION
|
||||
@@ -69,6 +70,8 @@ class AppRoutes {
|
||||
|
||||
static const String mySettings = '/mySettings';
|
||||
|
||||
static const String diaryEdit = '/diaryEdit';
|
||||
|
||||
static Map<String, WidgetBuilder> get routes => {
|
||||
dateOfBirthScreen: DateOfBirthScreen.builder,
|
||||
cityOfLivingScreen: CityOfLivingScreen.builder,
|
||||
@@ -91,5 +94,6 @@ class AppRoutes {
|
||||
initialRoute: SplashScreen.builder,
|
||||
myProfile: MyProfileScreen.builder,
|
||||
mySettings: MySettingsScreen.builder,
|
||||
diaryEdit: DiaryEditScreen.builder,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class CustomButtonStyles {
|
||||
backgroundColor: appTheme.blueA400,
|
||||
side: BorderSide(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 1,
|
||||
width: 0,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15.h),
|
||||
|
||||
147
lib/widgets/custom_edit_drop_down.dart
Normal file
147
lib/widgets/custom_edit_drop_down.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomDropDown extends StatelessWidget {
|
||||
CustomDropDown({
|
||||
Key? key,
|
||||
this.alignment,
|
||||
this.width,
|
||||
this.focusNode,
|
||||
this.icon,
|
||||
this.autofocus = true,
|
||||
this.textStyle,
|
||||
this.items,
|
||||
this.hintText,
|
||||
this.hintStyle,
|
||||
this.prefix,
|
||||
this.prefixConstraints,
|
||||
this.suffix,
|
||||
this.suffixConstraints,
|
||||
this.contentPadding,
|
||||
this.borderDecoration,
|
||||
this.fillColor,
|
||||
this.filled = true,
|
||||
this.validator,
|
||||
this.onChanged,
|
||||
this.defaultValue,
|
||||
}) : super(
|
||||
key: key,
|
||||
);
|
||||
|
||||
final Alignment? alignment;
|
||||
|
||||
final double? width;
|
||||
|
||||
final FocusNode? focusNode;
|
||||
|
||||
final Widget? icon;
|
||||
|
||||
final bool? autofocus;
|
||||
|
||||
final TextStyle? textStyle;
|
||||
|
||||
final List<SelectionPopupModel>? items;
|
||||
|
||||
final String? hintText;
|
||||
|
||||
final TextStyle? hintStyle;
|
||||
|
||||
final Widget? prefix;
|
||||
|
||||
final BoxConstraints? prefixConstraints;
|
||||
|
||||
final Widget? suffix;
|
||||
|
||||
final BoxConstraints? suffixConstraints;
|
||||
|
||||
final EdgeInsets? contentPadding;
|
||||
|
||||
final InputBorder? borderDecoration;
|
||||
|
||||
final Color? fillColor;
|
||||
|
||||
final bool? filled;
|
||||
|
||||
final SelectionPopupModel? defaultValue;
|
||||
|
||||
final FormFieldValidator<SelectionPopupModel>? validator;
|
||||
|
||||
final Function(SelectionPopupModel)? onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: dropDownWidget,
|
||||
)
|
||||
: dropDownWidget;
|
||||
}
|
||||
|
||||
Widget get dropDownWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: DropdownButtonFormField<SelectionPopupModel>(
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
icon: icon,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
items: items?.map((SelectionPopupModel item) {
|
||||
return DropdownMenuItem<SelectionPopupModel>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: hintStyle ?? theme.textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
value: defaultValue,
|
||||
onChanged: (value) {
|
||||
onChanged!(value!);
|
||||
},
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.titleMedium,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ??
|
||||
EdgeInsets.only(
|
||||
left: 6.h,
|
||||
top: 6.v,
|
||||
bottom: 6.v,
|
||||
),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
148
lib/widgets/custom_edit_text_form_field.dart
Normal file
148
lib/widgets/custom_edit_text_form_field.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class CustomTextFormField extends StatelessWidget {
|
||||
CustomTextFormField({
|
||||
Key? key,
|
||||
this.alignment,
|
||||
this.width,
|
||||
this.controller,
|
||||
this.focusNode,
|
||||
this.autofocus = true,
|
||||
this.textStyle,
|
||||
this.obscureText = false,
|
||||
this.textInputAction = TextInputAction.next,
|
||||
this.textInputType = TextInputType.text,
|
||||
this.maxLines,
|
||||
this.hintText,
|
||||
this.hintStyle,
|
||||
this.prefix,
|
||||
this.prefixConstraints,
|
||||
this.suffix,
|
||||
this.suffixConstraints,
|
||||
this.initialValue,
|
||||
this.contentPadding,
|
||||
this.inputFormatters,
|
||||
this.borderDecoration,
|
||||
this.fillColor,
|
||||
this.filled = true,
|
||||
this.validator,
|
||||
this.keyboardType,
|
||||
this.onSaved,
|
||||
}) : super(
|
||||
key: key,
|
||||
);
|
||||
|
||||
final Alignment? alignment;
|
||||
|
||||
final double? width;
|
||||
|
||||
final TextEditingController? controller;
|
||||
|
||||
final FocusNode? focusNode;
|
||||
|
||||
final bool? autofocus;
|
||||
|
||||
final TextStyle? textStyle;
|
||||
|
||||
final bool? obscureText;
|
||||
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
final TextInputAction? textInputAction;
|
||||
|
||||
final TextInputType? textInputType;
|
||||
|
||||
final int? maxLines;
|
||||
|
||||
final String? hintText;
|
||||
|
||||
final TextStyle? hintStyle;
|
||||
|
||||
final Widget? prefix;
|
||||
|
||||
final BoxConstraints? prefixConstraints;
|
||||
|
||||
final Widget? suffix;
|
||||
|
||||
final BoxConstraints? suffixConstraints;
|
||||
|
||||
final EdgeInsets? contentPadding;
|
||||
|
||||
final InputBorder? borderDecoration;
|
||||
|
||||
final Color? fillColor;
|
||||
|
||||
final bool? filled;
|
||||
|
||||
final TextInputType? keyboardType;
|
||||
|
||||
final String? initialValue;
|
||||
|
||||
final FormFieldValidator<String>? validator;
|
||||
|
||||
final void Function(String?)? onSaved;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: textFormFieldWidget,
|
||||
)
|
||||
: textFormFieldWidget;
|
||||
}
|
||||
|
||||
Widget get textFormFieldWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
inputFormatters: inputFormatters,
|
||||
initialValue: initialValue,
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
72
lib/widgets/custom_icon_button.dart
Normal file
72
lib/widgets/custom_icon_button.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomIconButton extends StatelessWidget {
|
||||
CustomIconButton({
|
||||
Key? key,
|
||||
this.alignment,
|
||||
this.height,
|
||||
this.width,
|
||||
this.padding,
|
||||
this.decoration,
|
||||
this.child,
|
||||
this.onTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
);
|
||||
|
||||
final Alignment? alignment;
|
||||
|
||||
final double? height;
|
||||
|
||||
final double? width;
|
||||
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
|
||||
final Widget? child;
|
||||
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: iconButtonWidget,
|
||||
)
|
||||
: iconButtonWidget;
|
||||
}
|
||||
|
||||
Widget get iconButtonWidget => SizedBox(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Container(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
decoration: decoration ??
|
||||
BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
border: Border.all(
|
||||
color: Colors.blueGrey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
onPressed: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Extension on [CustomIconButton] to facilitate inclusion of all types of border style etc
|
||||
extension IconButtonStyleHelper on CustomIconButton {
|
||||
static BoxDecoration get fillBlueA => BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
);
|
||||
}
|
||||
28
lib/widgets/custom_scale_element.dart
Normal file
28
lib/widgets/custom_scale_element.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScaleElement extends StatelessWidget {
|
||||
final Color color;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const ScaleElement({
|
||||
required this.color,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: isSelected ? 40 : 20,
|
||||
height: isSelected ? 40 : 20,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ dependencies:
|
||||
flutter_spinkit: ^5.0.0
|
||||
flutter_dotenv: ^5.1.0
|
||||
carousel_slider: ^4.2.1
|
||||
flutter_form_builder: ^9.2.1
|
||||
image_picker: ^1.0.7
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user