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 в корневой папке проекта выполните команду
|
Для тестирования на устройствах 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>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ class AuthScreen extends StatelessWidget {
|
|||||||
Navigator.pushNamed(
|
Navigator.pushNamed(
|
||||||
context, AppRoutes.languageSelectScreen);
|
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) {
|
BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
|
||||||
return NextStepButton(
|
return NextStepButton(
|
||||||
text: "authentificate".tr.toUpperCase(),
|
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 'bloc/my_profile_bloc.dart';
|
||||||
import 'models/my_profile_model.dart';
|
import 'models/my_profile_model.dart';
|
||||||
import 'package:eatvio_app/core/app_export.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/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_profile_screen.dart';
|
||||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/my_settings_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 {
|
class AppRoutes {
|
||||||
// REGISTRATION
|
// REGISTRATION
|
||||||
@@ -69,6 +70,8 @@ class AppRoutes {
|
|||||||
|
|
||||||
static const String mySettings = '/mySettings';
|
static const String mySettings = '/mySettings';
|
||||||
|
|
||||||
|
static const String diaryEdit = '/diaryEdit';
|
||||||
|
|
||||||
static Map<String, WidgetBuilder> get routes => {
|
static Map<String, WidgetBuilder> get routes => {
|
||||||
dateOfBirthScreen: DateOfBirthScreen.builder,
|
dateOfBirthScreen: DateOfBirthScreen.builder,
|
||||||
cityOfLivingScreen: CityOfLivingScreen.builder,
|
cityOfLivingScreen: CityOfLivingScreen.builder,
|
||||||
@@ -91,5 +94,6 @@ class AppRoutes {
|
|||||||
initialRoute: SplashScreen.builder,
|
initialRoute: SplashScreen.builder,
|
||||||
myProfile: MyProfileScreen.builder,
|
myProfile: MyProfileScreen.builder,
|
||||||
mySettings: MySettingsScreen.builder,
|
mySettings: MySettingsScreen.builder,
|
||||||
|
diaryEdit: DiaryEditScreen.builder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class CustomButtonStyles {
|
|||||||
backgroundColor: appTheme.blueA400,
|
backgroundColor: appTheme.blueA400,
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
width: 1,
|
width: 0,
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(15.h),
|
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_spinkit: ^5.0.0
|
||||||
flutter_dotenv: ^5.1.0
|
flutter_dotenv: ^5.1.0
|
||||||
carousel_slider: ^4.2.1
|
carousel_slider: ^4.2.1
|
||||||
|
flutter_form_builder: ^9.2.1
|
||||||
|
image_picker: ^1.0.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user