diff --git a/.env b/.env new file mode 100644 index 00000000..98d26074 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +SERVER_ROOT='https://ohngeeri3ien-dev3.eatvio.ru/api' +STORAGE_URL='https://ohngeeri3ien-dev3.eatvio.ru/storage/' \ No newline at end of file diff --git a/lib/core/utils/pref_utils.dart b/lib/core/utils/pref_utils.dart index 5238073f..9c49a3d2 100644 --- a/lib/core/utils/pref_utils.dart +++ b/lib/core/utils/pref_utils.dart @@ -1,5 +1,3 @@ -//ignore: unused_import -import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; class PrefUtils { diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart index ddc30061..a6de3e67 100644 --- a/lib/core/utils/size_utils.dart +++ b/lib/core/utils/size_utils.dart @@ -1,9 +1,9 @@ -import 'dart:ui' as ui; import 'package:flutter/material.dart'; // This functions are responsible to make UI responsive across all the mobile devices. -MediaQueryData mediaQueryData = MediaQueryData.fromWindow(ui.window); +MediaQueryData mediaQueryData = MediaQueryData.fromView( + WidgetsBinding.instance.platformDispatcher.views.single); // These are the Viewport values of your Figma Design. // These are used in the code as a reference to create your UI Responsively. diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart index 4cbef203..483e0ff5 100644 --- a/lib/presentation/auth/auth_screen/auth_screen.dart +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -64,94 +64,106 @@ class AuthScreen extends StatelessWidget { margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - Navigator.pushNamed( - context, AppRoutes.languageSelectScreen); - }), + onPressed: onTapNoAccount(context)), NextStepButton( text: "Тест дневника".toUpperCase(), margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - Navigator.pushNamed(context, AppRoutes.diaryEdit); - }), + onPressed: onTapDiaryTest(context)), BlocBuilder(builder: (context, state) { return NextStepButton( text: "authentificate".tr.toUpperCase(), margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () async { - List errors = validateFields( - state.emailController!.text, - state.passwordController!.text); - if (errors.isEmpty) { - final credentials = { - 'email': state.emailController!.text, - 'password': state.passwordController!.text, - }; - final response = await http.post( - Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'), - headers: {'Content-Type': 'application/json'}, - body: json.encode(credentials), - encoding: utf8); - if (response.statusCode == 200) { - var res = await jsonDecode(response.body); - final box = await Hive.openBox('token'); - final token = res['token']; - box.put('token', token); - Navigator.pushNamed( - context, - AppRoutes.myProfile, - ); - } else { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text('wrong_credentials'.tr), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - } else { - String errorMessage = ''; - - for (var error in errors) { - errorMessage += error + '\n\n'; - } - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text(errorMessage), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - }); + onPressed: onTapAuth(state, context)); }), ]))); } + onTapAuth(AuthState state, BuildContext context) { + return () async { + List errors = validateFields( + state.emailController!.text, state.passwordController!.text); + if (errors.isEmpty) { + final Map credentials = { + 'email': state.emailController!.text, + 'password': state.passwordController!.text, + }; + final response = await http.post( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'), + headers: {'Content-Type': 'application/json'}, + body: json.encode(credentials), + encoding: utf8); + if (response.statusCode == 200) { + var res = await jsonDecode(response.body); + final box = await Hive.openBox('token'); + final token = res['token']; + box.put('token', token); + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + } else { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text('wrong_credentials'.tr), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + } + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + } + }; + } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } + + onTapDiaryTest(BuildContext context) { + return () { + Navigator.pushNamed(context, AppRoutes.diaryEdit); + }; + } + + onTapNoAccount(BuildContext context) { + return () { + Navigator.pushNamed(context, AppRoutes.languageSelectScreen); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, centerTitle: true, diff --git a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart index 1796418f..569164b1 100644 --- a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart +++ b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart @@ -1,5 +1,5 @@ import 'dart:async'; - +import 'package:intl/intl.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; @@ -40,10 +40,11 @@ class DiaryEditBloc extends Bloc { ) async { try { final token = - "laravel_sanctum_Q22zOtJhQUcJnh0kseZFblFv5XAPaKbG3gTM5hga71ecbb49"; + "laravel_sanctum_1lPFtd9tpGmHYS243C4K7YF8BrVhxxgXm8cTmjuZda1da694"; final response = await http.get( Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, ); @@ -56,12 +57,12 @@ class DiaryEditBloc extends Bloc { final response = await http.get( Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/show/$diaryId'), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, ); final diary = jsonDecode(response.body)['diary']; if (diary is Map) { - print(diary['meals'][1].runtimeType); final String diaryDate = diary['date'] ?? DateTime.now().toString(); // Продолжайте обработку данных дневника @@ -91,7 +92,7 @@ class DiaryEditBloc extends Bloc { // Если текущее состояние - начальное, создаем новое загруженное состояние emit(DiaryEditLoadedState( currentMeal: SelectionPopupModel( - id: 0, title: "Прием пищи 1", isSelected: true), + id: 1, title: "Прием пищи 1", isSelected: true), wakeupExpanded: true, moodExpanded: true, token: token, @@ -145,8 +146,6 @@ class DiaryEditBloc extends Bloc { var updatedState = currentState.copyWith(selectedSatiety: event.selectedElement); - emit(updatedState); // Передаем обновленное состояние в emit - // Отправляем данные по HTTP маршруту и ждем ответ var response = await sendSatietyData( currentState.token, @@ -155,8 +154,8 @@ class DiaryEditBloc extends Bloc { currentState.diaryId); if (response.statusCode == 200) { - currentState.selectedSatiety = event.selectedElement; - emit(currentState); // Передаем обновленное состояние в emit + updatedState.selectedSatiety = event.selectedElement; + emit(updatedState); // Передаем обновленное состояние в emit } } } @@ -167,21 +166,18 @@ class DiaryEditBloc extends Bloc { int? mealID, int diaryID, ) async { - String url = - dotenv.env['SERVER_ROOT']! + '$diaryID/meal/$mealID/satiety/fill'; - final credentials = { + String url = dotenv.env['SERVER_ROOT']! + + '/diary/$diaryID/meal/$mealID/satiety/fill'; + final Map credentials = { 'satiety': selectedSatiety, - 'meal_id': mealID, - 'diary_id': diaryID, }; - var response = await http.post(Uri.parse(url), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, body: json.encode(credentials), encoding: utf8); - return response; } @@ -213,15 +209,14 @@ class DiaryEditBloc extends Bloc { int diaryID, ) async { String url = - dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/mood/fill'; - final credentials = { + dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill'; + final Map credentials = { 'mood': selectedMood, - 'meal_id': mealID, - 'diary_id': diaryID, }; var response = await http.post(Uri.parse(url), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, body: json.encode(credentials), @@ -234,7 +229,7 @@ class DiaryEditBloc extends Bloc { WakeupEvent event, Emitter emit) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; - var updatedState = + var updatedState = //инициализируем новое состояние currentState.copyWith(wakeupController: event.selectedTime); // Отправляем данные по HTTP маршруту и ждем ответ @@ -242,6 +237,7 @@ class DiaryEditBloc extends Bloc { currentState.token, event.selectedTime, currentState.diaryId); if (response.statusCode == 200) { + //Чекаем статус ответа updatedState.wakeupController = event.selectedTime; emit(updatedState); // Передаем обновленное состояние в emit } @@ -253,19 +249,23 @@ class DiaryEditBloc extends Bloc { TimeOfDay wakeup, int diaryID, ) async { - String url = dotenv.env['SERVER_ROOT']! + '/diary/???'; - final credentials = { - 'wakeup': wakeup, - 'diary_id': diaryID, + String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/wakeUp/fill'; + final formatter = DateFormat('HH:mm'); + final wakeupString = + formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute)); + final Map credentials = { + 'wakeup': wakeupString, }; - - var response = await http.post(Uri.parse(url), - headers: { - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8); - + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); + print(response.body); return response; } @@ -290,12 +290,16 @@ class DiaryEditBloc extends Bloc { Future sendRetireData( String? token, TimeOfDay retire, int diaryID) async { String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill'; - final credentials = { - 'retire': retire, + final formatter = DateFormat('HH:mm'); + final retireString = + formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute)); + final Map credentials = { + 'retire': retireString, }; var response = await http.post(Uri.parse(url), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, body: json.encode(credentials), @@ -310,16 +314,7 @@ class DiaryEditBloc extends Bloc { 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 - } + emit(updatedState); } } @@ -330,7 +325,7 @@ class DiaryEditBloc extends Bloc { int diaryID, ) async { String url = dotenv.env['SERVER_ROOT']! + '/diary/???'; - final credentials = { + final Map credentials = { 'time': mealTime, 'meal_id': mealID, 'diary_id': diaryID, @@ -338,6 +333,7 @@ class DiaryEditBloc extends Bloc { var response = await http.post(Uri.parse(url), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, body: json.encode(credentials), @@ -385,56 +381,51 @@ class DiaryEditBloc extends Bloc { AddPortionEvent event, Emitter 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.productNameController?.text, + 'pcs', + int.parse(currentState.productQuantityController!.text), currentState.currentMeal!.id, currentState.diaryId); - if (response.statusCode == 200) { - emit(currentState); // Передаем обновленное состояние в emit + var updatedState = currentState.copyWith( + portions: currentState.meals?[id ?? 0]['portions'], + ); + emit(updatedState); // Передаем обновленное состояние в emit } } } Future sendPortionData( String? token, - String name, + String? name, String portion, - int quantity, + int? quantity, int? mealID, int diaryID, ) async { - String url = - dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/portions/create'; - final credentials = { - 'name': name, + String url = dotenv.env['SERVER_ROOT']! + + '/diary/$diaryID/meal/$mealID/portions/create'; + final Map credentials = { + 'product_name': name, 'portion': portion, 'quantity': quantity, - 'meal_id': mealID, - 'diary_id': diaryID, }; - + print(credentials); var response = await http.post(Uri.parse(url), headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, body: json.encode(credentials), encoding: utf8); + print(response.body); return response; } @@ -443,7 +434,7 @@ class DiaryEditBloc extends Bloc { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; String? token = currentState.token; - final credentials = { + final Map credentials = { 'name': event.input, }; final uri = Uri.parse(dotenv.env['SERVER_ROOT']! + @@ -452,18 +443,22 @@ class DiaryEditBloc extends Bloc { final response = await http.get( uri, headers: { + 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, ); final productNutritionalValues = - jsonDecode(response.body)['productNutritionalValue']; - if (productNutritionalValues is Map) { + jsonDecode(response.body)['product_nutritional_value']; + if (productNutritionalValues is List) { var updatedState = currentState.copyWith( - autocompleteList: productNutritionalValues.values - .map((value) => value['name']) + productNameController: TextEditingController(text: event.input), + autocompleteList: productNutritionalValues + .map((product) => product['name']) .toList()); emit(updatedState); + } else { + print("Ошибка в декодировании ответа из API"); } } } @@ -477,7 +472,42 @@ class DiaryEditBloc extends Bloc { print(imageUrl); } - FutureOr _addComment(AddComment event, Emitter emit) {} + FutureOr _addComment( + AddComment event, Emitter emit) async { + if (state is DiaryEditLoadedState) { + var currentState = state as DiaryEditLoadedState; + var updatedState = currentState.copyWith( + commentController: TextEditingController(text: event.comment), + ); + + // Отправляем данные по HTTP маршруту и ждем ответ + var response = await sendCommentData( + currentState.token, event.comment, currentState.diaryId); + + if (response.statusCode == 200) { + emit(updatedState); // Передаем обновленное состояние в emit + } + } + } + + Future sendCommentData( + String? token, String comment, int diaryID) async { + String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill'; + final Map credentials = { + 'comment': comment, + }; + + var response = await http.post(Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8); + + return response; + } + Future uploadImageToServer(String imagePath) async { var request = http.MultipartRequest( 'POST', @@ -507,12 +537,12 @@ class DiaryEditBloc extends Bloc { List 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"), + SelectionPopupModel(id: 1, title: "Прием пищи 1", isSelected: true), + SelectionPopupModel(id: 2, title: "Прием пищи 2"), + SelectionPopupModel(id: 3, title: "Прием пищи 3"), + SelectionPopupModel(id: 4, title: "Прием пищи 4"), + SelectionPopupModel(id: 5, title: "Прием пищи 5"), + SelectionPopupModel(id: 6, title: "Прием пищи 6"), ]; } } diff --git a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_event.dart b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_event.dart index 206e5957..ed6f8e2f 100644 --- a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_event.dart +++ b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_event.dart @@ -79,12 +79,9 @@ class ToggleWakeupExpandedEvent extends DiaryEditEvent { } class AddPortionEvent extends DiaryEditEvent { - AddPortionEvent(this.name, this.quantity, this.portion); - final String name; - final int quantity; - final String portion; + AddPortionEvent(); @override - List get props => [name, quantity, portion]; + List get props => []; } class RemovePortionEvent extends DiaryEditEvent { diff --git a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart index 2bc07d97..1629dd1c 100644 --- a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart +++ b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart @@ -3,6 +3,7 @@ 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'; @@ -91,7 +92,7 @@ class DiaryEditScreen extends StatelessWidget { } /// Section Widget - Widget _buildHeaderRow(BuildContext context, DiaryEditLoadedState state) { + _buildHeaderRow(BuildContext context, DiaryEditLoadedState state) { final day = state.diaryId; return Container( margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h), @@ -101,7 +102,7 @@ class DiaryEditScreen extends StatelessWidget { ), decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(12), + borderRadius: const BorderRadius.all(Radius.circular(12)), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), @@ -115,10 +116,7 @@ class DiaryEditScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ CustomImageView( - imagePath: ImageConstant.imgArrowLeft, - onTap: () { - NavigatorService.goBack(); - }), + imagePath: ImageConstant.imgArrowLeft, onTap: onTapBack()), Padding( padding: EdgeInsets.only(top: 1.v), child: Column( @@ -143,27 +141,32 @@ class DiaryEditScreen extends StatelessWidget { ); } + onTapBack() { + return () { + NavigatorService.goBack(); + }; + } + /// Section Widget - Widget _buildAddProduct(BuildContext context) { + _buildAddProduct(BuildContext context, DiaryEditLoadedState state) { return CustomOutlinedButton( text: "Добавить продукт", margin: EdgeInsets.symmetric(horizontal: 10.h), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: addProduct(context), ); } + addProduct(BuildContext context) { + return () { + context.read().add(AddPortionEvent()); + }; + } + /// Section Widget - Widget _buildAddPhoto(BuildContext context) { + _buildAddPhoto(BuildContext context) { return GestureDetector( - onTap: () async { - final picker = ImagePicker(); - List? images = await picker.pickMultiImage(); - if (images.isNotEmpty) { - for (var image in images) { - context.read().add(UploadImages(image)); - } - } - }, + onTap: addImage(context), child: Container( margin: EdgeInsets.symmetric(horizontal: 10.h), padding: EdgeInsets.symmetric( @@ -198,195 +201,265 @@ class DiaryEditScreen extends StatelessWidget { ); } + addImage(BuildContext context) { + return () async { + final picker = ImagePicker(); + List? images = await picker.pickMultiImage(); + if (images.isNotEmpty) { + for (var image in images) { + context.read().add(UploadImages(image)); + } + } + }; + } + _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, - ), + List _options = + state.autocompleteList!.map((dynamic item) => item as String).toList(); + print(_options); + return Container( + margin: EdgeInsets.symmetric(horizontal: 16.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.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( - 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().add( - ChangeMeal(meals: state.meals, value: value)); - }, - ); - })), - SizedBox(height: 20.v), - Padding( + ), + SizedBox(height: 20.v), + Padding( padding: EdgeInsets.symmetric(horizontal: 10.h), - child: CustomTextFormField( - hintText: "Введите названия продукта", - controller: state.productNameController, - onChanged: (value) { - if (value.length > 3) { - context.read().add(ProductInput(value)); - } - }, - ), - ), - 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: [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( - 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().add(ChangeSatiety(0)); - }, + child: BlocSelector( + selector: (state) => state.diaryEditModelObj, + builder: (context, diaryEditModelObj) { + return CustomDropDown( + hintText: "Прием пищи", + items: diaryEditModelObj?.mealList, + contentPadding: EdgeInsets.symmetric( + horizontal: 10.h, + vertical: 9.v, ), - ScaleElement( - color: Colors.orange, - isSelected: state.selectedSatiety == 1, - onTap: () { - context.read().add(ChangeSatiety(1)); - }, - ), - ScaleElement( - color: Colors.yellow, - isSelected: state.selectedSatiety == 2, - onTap: () { - context.read().add(ChangeSatiety(2)); - }, - ), - ScaleElement( - color: Colors.lime, - isSelected: state.selectedSatiety == 3, - onTap: () { - context.read().add(ChangeSatiety(3)); - }, - ), - ScaleElement( - color: Colors.lightGreen, - isSelected: state.selectedSatiety == 4, - onTap: () { - context.read().add(ChangeSatiety(4)); - }, - ), - ScaleElement( - color: Colors.green, - isSelected: state.selectedSatiety == 5, - onTap: () { - context.read().add(ChangeSatiety(5)); - }, - ), - Text("😊"), - // Остальные элементы шкалы - ], + defaultValue: diaryEditModelObj?.mealList[0], + onChanged: changeMeal(context, state), + ); + })), + SizedBox(height: 20.v), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.h), + child: RawAutocomplete( + optionsBuilder: buildOptions(_options), + fieldViewBuilder: ( + BuildContext context, + TextEditingController textEditingController, + FocusNode focusNode, + VoidCallback onFieldSubmitted, + ) { + return CustomTextFormField( + hintText: "Введите название продукта", + controller: textEditingController, + focusNode: focusNode, + onChanged: getAutocomplete(context), + /*onFieldSubmitted: (String value) { + onFieldSubmitted(); + },*/ ); - } else { - return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryEditLoadedState - } - }, - ) - ], - ), + }, + optionsViewBuilder: ( + BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options, + ) { + return Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 4.0, + child: SizedBox( + height: 200.0, + child: ListView.builder( + padding: const EdgeInsets.all(8.0), + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final String option = options.elementAt(index); + return GestureDetector( + onTap: selectProduct(onSelected, option), + child: ListTile( + title: Text(option), + ), + ); + }, + ), + ), + ), + ); + }), + ), + 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: [digitsOnly], + ), + Padding( + padding: EdgeInsets.only(right: 30.h), + child: Text( + "порций", + style: CustomTextStyles.bodyMediumBlack900, + )), + ], + ), + ), + SizedBox(height: 30.v), + _buildAddProduct(context, state), + 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( + builder: (context, state) { + if (state is DiaryEditLoadedState) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("😭"), + ScaleElement( + color: Colors.red, + isSelected: state.selectedSatiety == 0, + onTap: changeSatiety(context, 0), + ), + ScaleElement( + color: Colors.orange, + isSelected: state.selectedSatiety == 1, + onTap: changeSatiety(context, 1), + ), + ScaleElement( + color: Colors.yellow, + isSelected: state.selectedSatiety == 2, + onTap: changeSatiety(context, 2), + ), + ScaleElement( + color: Colors.lime, + isSelected: state.selectedSatiety == 3, + onTap: changeSatiety(context, 3), + ), + ScaleElement( + color: Colors.lightGreen, + isSelected: state.selectedSatiety == 4, + onTap: changeSatiety(context, 4), + ), + ScaleElement( + color: Colors.green, + isSelected: state.selectedSatiety == 5, + onTap: changeSatiety(context, 5), + ), + Text("😊"), + // Остальные элементы шкалы + ], + ); + } else { + return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryEditLoadedState + } + }, + ) + ], )); } + changeSatiety(BuildContext context, int satiety) { + return () { + context.read().add(ChangeSatiety(satiety)); + }; + } + + selectProduct(AutocompleteOnSelected onSelected, String option) { + return () { + onSelected(option); + }; + } + + getAutocomplete(BuildContext context) { + return (value) { + /*print(state.autocompleteList + ?.map((dynamic item) => item as String) + .toList());*/ + if (value.length > 2) { + context.read().add(ProductInput(value)); + } + }; + } + + buildOptions(List _options) { + return (TextEditingValue textEditingValue) { + return _options.where((String option) { + return true; + }); + }; + } + + changeMeal(BuildContext context, DiaryEditLoadedState state) { + return (value) { + context + .read() + .add(ChangeMeal(meals: state.meals, value: value)); + }; + } + _buildProduct(BuildContext context, Map portion) { return Container( margin: EdgeInsets.symmetric(horizontal: 10.h), @@ -418,16 +491,14 @@ class DiaryEditScreen extends StatelessWidget { bottom: 3.v, ), child: Text( - portion['quantity'] + portion['portion'], + portion['quantity'].toString() + + ' ' + + portion['portion'].toString(), ), ), Spacer(), GestureDetector( - onTap: () { - context - .read() - .add(RemovePortionEvent(portion['id'])); - }, + onTap: removePortion(context, portion), child: Padding( padding: const EdgeInsets.only(right: 10), child: Icon( @@ -440,7 +511,13 @@ class DiaryEditScreen extends StatelessWidget { ); } - Widget _buildNutritionChart(BuildContext context) { + removePortion(BuildContext context, Map portion) { + return () { + context.read().add(RemovePortionEvent(portion['id'])); + }; + } + + _buildNutritionChart(BuildContext context) { return Container( margin: EdgeInsets.symmetric(horizontal: 16.h), padding: EdgeInsets.symmetric( @@ -449,7 +526,7 @@ class DiaryEditScreen extends StatelessWidget { ), decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(12), + borderRadius: const BorderRadius.all(Radius.circular(12)), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), @@ -680,148 +757,132 @@ class DiaryEditScreen extends StatelessWidget { } /// 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, + _buildComment(BuildContext context, DiaryEditLoadedState state) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 16.h), + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), ), - 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, ), - 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, - ), - ), - ], - ), - ), - ], + 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(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), - ], - ), - ), - ], - )), + ], + ), ); } + _buildWakupRetire(BuildContext context, DiaryEditLoadedState state) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 16.h), + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: Column( + children: [ + GestureDetector( + onTap: toggleWakeup(context), + 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, + ), + ), + ), + 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, + ), + ), + ), + SizedBox(height: 20.v), + ], + ), + ), + ], + ), + ); + } + + toggleWakeup(BuildContext context) { + return () { + BlocProvider.of(context).add(ToggleWakeupExpandedEvent()); + }; + } + /// Section Widget - Widget _buildEndDay(BuildContext context) { + _buildEndDay(BuildContext context) { return CustomOutlinedButton( text: "Закончить день", margin: EdgeInsets.only( @@ -836,97 +897,93 @@ class DiaryEditScreen extends StatelessWidget { ); } - 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, + _buildMood(BuildContext context, DiaryEditLoadedState state) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 16.h), + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), ), - 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: toggleMood(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Ваше настроение после приема пищи", + style: CustomTextStyles.bodyMediumBlack900, ), + Icon(state.moodExpanded + ? Icons.arrow_drop_up + : Icons.arrow_drop_down), ], ), - child: Column(children: [ - GestureDetector( - onTap: () { - BlocProvider.of(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: changeMood(context, 0), ), - ), - if (state.moodExpanded) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("😭"), - ScaleElement( - color: Colors.red, - isSelected: state.selectedMood == 0, - onTap: () { - context.read().add(ChangeMood(0)); - }, - ), - ScaleElement( - color: Colors.orange, - isSelected: state.selectedMood == 1, - onTap: () { - context.read().add(ChangeMood(1)); - }, - ), - ScaleElement( - color: Colors.yellow, - isSelected: state.selectedMood == 2, - onTap: () { - context.read().add(ChangeMood(2)); - }, - ), - ScaleElement( - color: Colors.lime, - isSelected: state.selectedMood == 3, - onTap: () { - context.read().add(ChangeMood(3)); - }, - ), - ScaleElement( - color: Colors.lightGreen, - isSelected: state.selectedMood == 4, - onTap: () { - context.read().add(ChangeMood(4)); - }, - ), - ScaleElement( - color: Colors.green, - isSelected: state.selectedMood == 5, - onTap: () { - context.read().add(ChangeMood(5)); - }, - ), - Text("😊"), - // Остальные элементы шкалы - ], + ScaleElement( + color: Colors.orange, + isSelected: state.selectedMood == 1, + onTap: changeMood(context, 1), ), - ]))); + ScaleElement( + color: Colors.yellow, + isSelected: state.selectedMood == 2, + onTap: changeMood(context, 2), + ), + ScaleElement( + color: Colors.lime, + isSelected: state.selectedMood == 3, + onTap: changeMood(context, 3), + ), + ScaleElement( + color: Colors.lightGreen, + isSelected: state.selectedMood == 4, + onTap: changeMood(context, 4), + ), + ScaleElement( + color: Colors.green, + isSelected: state.selectedMood == 5, + onTap: changeMood(context, 5), + ), + Text("😊"), + // Остальные элементы шкалы + ], + ), + ])); + } + + changeMood(BuildContext context, int satiety) { + return () { + context.read().add(ChangeMood(satiety)); + }; + } + + toggleMood(BuildContext context) { + return () { + BlocProvider.of(context).add(ToggleMoodExpandedEvent()); + }; } Future _selectWakeupTime( diff --git a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart index 3fe87cf2..77929fd5 100644 --- a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart @@ -51,8 +51,7 @@ class MyProfileScreen extends StatelessWidget { ); } - Widget _buildMyProfileScreen( - BuildContext context, MyProfileLoadedState state) { + _buildMyProfileScreen(BuildContext context, MyProfileLoadedState state) { mediaQueryData = MediaQuery.of(context); return SafeArea( child: Scaffold( @@ -84,7 +83,7 @@ class MyProfileScreen extends StatelessWidget { } /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context, String username) { + _buildAppBar(BuildContext context, String username) { return CustomAppBar( height: 54.v, leadingWidth: 28.h, @@ -100,31 +99,35 @@ class MyProfileScreen extends StatelessWidget { ExitButton( text: "exit".tr, margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 17.v), - onTap: () async { - final box = await Hive.openBox('token'); - final token = box.get('token'); - final response = await http.post( - Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/logout'), - headers: { - 'Authorization': 'Bearer $token', - }, - ); - - if (response.statusCode == 204) { - print(response.statusCode); - box.delete('token'); // Удаляем токен из Hive - Navigator.pushNamed( - context, - AppRoutes.authScreen, - ); - } - }, + onTap: _onTapLogout(context), ) ]); } + Function _onTapLogout(BuildContext context) { + return () async { + final box = await Hive.openBox('token'); + final token = box.get('token'); + final response = await http.post( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/logout'), + headers: { + 'Authorization': 'Bearer $token', + }, + ); + + if (response.statusCode == 204) { + print(response.statusCode); + box.delete('token'); // Удаляем токен из Hive + Navigator.pushNamed( + context, + AppRoutes.authScreen, + ); + } + }; + } + /// Section Widget - Widget _buildMyBlog(BuildContext context) { + _buildMyBlog(BuildContext context) { return Padding( padding: EdgeInsets.symmetric(horizontal: 7.h), child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -142,7 +145,7 @@ class MyProfileScreen extends StatelessWidget { } /// Section Widget - Widget _buildSettings(BuildContext context) { + _buildSettings(BuildContext context) { return Padding( padding: EdgeInsets.symmetric(horizontal: 7.h), child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -154,19 +157,23 @@ class MyProfileScreen extends StatelessWidget { Expanded( child: NextStepButton( buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - Navigator.pushNamed(context, AppRoutes.mySettings); - }, + onPressed: onTapSettings(context), text: "my_settings".tr.toUpperCase(), margin: EdgeInsets.only(left: 10.h))), ])); } - Widget _buildBottomBar(BuildContext context) { + onTapSettings(BuildContext context) { + return () { + Navigator.pushNamed(context, AppRoutes.mySettings); + }; + } + + _buildBottomBar(BuildContext context) { return CustomBottomBar(onChanged: (BottomBarEnum type) {}); } - Widget _buildCarousel(BuildContext contex, MyProfileLoadedState state) { + _buildCarousel(BuildContext contex, MyProfileLoadedState state) { return CarouselSlider( options: CarouselOptions(height: 340.0), items: [1, 2, 3].map((chartType) { @@ -175,9 +182,7 @@ class MyProfileScreen extends StatelessWidget { switch (chartType) { case 1: // График веса return GestureDetector( - onTap: () { - _editWeight(context, state); - }, + onTap: _editWeight(context, state), child: Column(children: [ Text( "weight_chart".tr, @@ -220,9 +225,7 @@ class MyProfileScreen extends StatelessWidget { ]); case 3: //График обхватов return GestureDetector( - onTap: () { - _editCircumference(context, state); - }, + onTap: _editCircumference(context, state), child: Column(children: [ Text( "circumferences_chart".tr, @@ -244,7 +247,7 @@ class MyProfileScreen extends StatelessWidget { ); } - Widget _buildChartLegend() { + _buildChartLegend() { final colors = [Colors.pink, Colors.green, Colors.purple]; final titles = ["chest".tr, "waist".tr, "hips".tr]; final style = TextStyle( @@ -277,7 +280,7 @@ class MyProfileScreen extends StatelessWidget { } /// Section Widget - Widget _buildChart( + _buildChart( BuildContext context, List chartPoints, List? dates) { // Определение осей графика List spots = []; @@ -340,20 +343,7 @@ class MyProfileScreen extends StatelessWidget { touchTooltipData: LineTouchTooltipData( maxContentWidth: 100, tooltipBgColor: Colors.black, - getTooltipItems: (touchedSpots) { - return touchedSpots.map((LineBarSpot touchedSpot) { - final textStyle = TextStyle( - color: touchedSpot.bar.gradient?.colors[0] ?? - touchedSpot.bar.color, - fontWeight: FontWeight.bold, - fontSize: 14, - ); - return LineTooltipItem( - '${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}', - textStyle, - ); - }).toList(); - }, + getTooltipItems: mapSpots(), ), handleBuiltInTouches: true, getTouchLineStart: (data, index) => 0, @@ -410,9 +400,7 @@ class MyProfileScreen extends StatelessWidget { drawVerticalLine: true, horizontalInterval: 1.5, verticalInterval: 5, - checkToShowHorizontalLine: (value) { - return value.toInt() == 0; - }, + checkToShowHorizontalLine: valuteToInt(), getDrawingHorizontalLine: (_) => FlLine( color: Colors.blue.withOpacity(1), dashArray: [8, 2], @@ -423,9 +411,7 @@ class MyProfileScreen extends StatelessWidget { dashArray: [8, 2], strokeWidth: 0.8, ), - checkToShowVerticalLine: (value) { - return value.toInt() == 0; - }, + checkToShowVerticalLine: valuteToInt(), ), borderData: FlBorderData(show: false), ), @@ -436,7 +422,29 @@ class MyProfileScreen extends StatelessWidget { ); } - Widget _buildCircumferenceChart( + valuteToInt() { + return (value) { + return value.toInt() == 0; + }; + } + + mapSpots() { + return (touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + final textStyle = TextStyle( + color: touchedSpot.bar.gradient?.colors[0] ?? touchedSpot.bar.color, + fontWeight: FontWeight.bold, + fontSize: 14, + ); + return LineTooltipItem( + '${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}', + textStyle, + ); + }).toList(); + }; + } + + _buildCircumferenceChart( BuildContext context, chartPoints, List? dates) { // Определение осей графика final chestCircumferences = @@ -526,20 +534,7 @@ class MyProfileScreen extends StatelessWidget { touchTooltipData: LineTouchTooltipData( maxContentWidth: 100, tooltipBgColor: Colors.black, - getTooltipItems: (touchedSpots) { - return touchedSpots.map((LineBarSpot touchedSpot) { - final textStyle = TextStyle( - color: touchedSpot.bar.gradient?.colors[0] ?? - touchedSpot.bar.color, - fontWeight: FontWeight.bold, - fontSize: 14, - ); - return LineTooltipItem( - '${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}', - textStyle, - ); - }).toList(); - }, + getTooltipItems: mapTouchedSpots(), ), handleBuiltInTouches: true, getTouchLineStart: (data, index) => 0, @@ -618,9 +613,7 @@ class MyProfileScreen extends StatelessWidget { drawVerticalLine: true, horizontalInterval: 1.5, verticalInterval: 5, - checkToShowHorizontalLine: (value) { - return value.toInt() == 0; - }, + checkToShowHorizontalLine: valuteToInt(), getDrawingHorizontalLine: (_) => FlLine( color: Colors.blue.withOpacity(1), dashArray: [8, 2], @@ -631,9 +624,7 @@ class MyProfileScreen extends StatelessWidget { dashArray: [8, 2], strokeWidth: 0.8, ), - checkToShowVerticalLine: (value) { - return value.toInt() == 0; - }, + checkToShowVerticalLine: valuteToInt(), ), borderData: FlBorderData(show: false), ), @@ -644,8 +635,24 @@ class MyProfileScreen extends StatelessWidget { ); } + mapTouchedSpots() { + return (touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + final textStyle = TextStyle( + color: touchedSpot.bar.gradient?.colors[0] ?? touchedSpot.bar.color, + fontWeight: FontWeight.bold, + fontSize: 14, + ); + return LineTooltipItem( + '${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}', + textStyle, + ); + }).toList(); + }; + } + /// Section Widget - Widget _buildPlans(BuildContext context, List? plans, + _buildPlans(BuildContext context, List? plans, List? imagePaths, List? states) { return Padding( padding: EdgeInsets.only(left: 7.h), @@ -670,7 +677,6 @@ class MyProfileScreen extends StatelessWidget { Align( alignment: Alignment.topCenter, child: ListView.builder( - shrinkWrap: true, itemCount: plans?.length ?? 0, itemBuilder: (context, i) { String state = states?[i]; @@ -781,94 +787,62 @@ class MyProfileScreen extends StatelessWidget { showDialog( context: context, builder: (context) { - final digitsOnly = FilteringTextInputFormatter.digitsOnly; + //final digitsOnly = FilteringTextInputFormatter.digitsOnly; return AlertDialog( title: Center(child: Text("Добавить замер")), - content: SingleChildScrollView( - child: Column( - children: [ - Text("chest_circumference".tr, + content: ListView( + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 5.v), + child: Text("chest_circumference".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - CustomTextFormField( - controller: state.chestCircumferenceController, - keyboardType: TextInputType.number, - inputFormatters: [ - digitsOnly // Ограничение ввода только цифрами - ], - ), - SizedBox(height: 15.v), - Text("waist_circumference".tr, + ), + SizedBox(height: 5.v), + CustomTextFormField( + controller: state.chestCircumferenceController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter + .digitsOnly, // Ограничение ввода только цифрами + ], + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 15.v), + child: Text("waist_circumference".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - CustomTextFormField( - controller: state.waistCircumferenceController, - keyboardType: TextInputType.number, - inputFormatters: [ - digitsOnly // Ограничение ввода только цифрами - ], - ), - SizedBox(height: 15.v), - Text("hip_circumference".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - CustomTextFormField( - controller: state.hipCircumferenceController, - keyboardType: TextInputType.number, - inputFormatters: [ - digitsOnly // Ограничение ввода только цифрами - ], - ), - ], - ), + ), + SizedBox(height: 5.v), + CustomTextFormField( + controller: state.waistCircumferenceController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter + .digitsOnly, // Ограничение ввода только цифрами + ], + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 15.v), + child: Text("hip_circumference".tr, + style: theme.textTheme.bodyLarge), + ), + SizedBox(height: 5.v), + CustomTextFormField( + controller: state.hipCircumferenceController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter + .digitsOnly, // Ограничение ввода только цифрами + ], + ), + ], ), actions: [ TextButton( - onPressed: () { - Navigator.pop(context); - }, + onPressed: onTapOk(context), child: Text("Отмена", style: theme.textTheme.bodyLarge), ), TextButton( - onPressed: () async { - Map data = { - "chest_circumference": - state.chestCircumferenceController?.text, - "waist_circumference": - state.waistCircumferenceController?.text, - "hip_circumference": state.hipCircumferenceController?.text, - }; - - // Преобразование Map в JSON - String jsonData = json.encode(data); - - // Отправка HTTP-запроса - try { - final token = state.token; - final response = await http.post( - Uri.parse(dotenv.env['SERVER_ROOT']! + - '/profile/addCircumference'), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: jsonData, - ); - - if (response.statusCode == 201) { - Navigator.pushNamed( - context, - AppRoutes.myProfile, - ); - } else { - // Ошибка от сервера - print( - "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); - } - } catch (e) { - // Обработка ошибок при отправке запроса - print("Ошибка при отправке запроса: $e"); - } - }, + onPressed: sendEditData(state, context), child: Text("Изменить", style: CustomTextStyles.titleMediumBlack.copyWith( color: Colors.blue, @@ -883,71 +857,76 @@ class MyProfileScreen extends StatelessWidget { ); } + sendEditData(MyProfileLoadedState state, BuildContext context) { + return () async { + Map data = { + "chest_circumference": state.chestCircumferenceController?.text, + "waist_circumference": state.waistCircumferenceController?.text, + "hip_circumference": state.hipCircumferenceController?.text, + }; + + // Преобразование Map в JSON + String jsonData = json.encode(data); + + // Отправка HTTP-запроса + try { + final token = state.token; + final response = await http.post( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addCircumference'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: jsonData, + ); + + if (response.statusCode == 201) { + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + } else { + // Ошибка от сервера + print( + "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); + } + } catch (e) { + // Обработка ошибок при отправке запроса + print("Ошибка при отправке запроса: $e"); + } + }; + } + _editWeight(BuildContext context, MyProfileLoadedState state) { showDialog( context: context, builder: (context) { - final digitsOnly = FilteringTextInputFormatter.digitsOnly; return AlertDialog( title: Center(child: Text("add_measure".tr)), - content: SingleChildScrollView( - child: Column( - children: [ - Text("weight".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - CustomTextFormField( - controller: state.weightController, - keyboardType: TextInputType.number, - inputFormatters: [ - digitsOnly // Ограничение ввода только цифрами - ], - ), - ], - ), + content: ListView( + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 5.v), + child: Text("weight".tr, style: theme.textTheme.bodyLarge), + ), + SizedBox(height: 5.v), + CustomTextFormField( + controller: state.weightController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter + .digitsOnly, // Ограничение ввода только цифрами + ], + ), + ], ), actions: [ TextButton( - onPressed: () { - Navigator.pop(context); - }, + onPressed: onTapOk(context), child: Text("Отмена", style: theme.textTheme.bodyLarge), ), TextButton( - onPressed: () async { - Map data = { - "weight": state.weightController!.text, - }; - // Преобразование Map в JSON - String jsonData = json.encode(data); - - // Отправка HTTP-запроса - try { - final token = state.token; - final response = await http.post( - Uri.parse( - dotenv.env['SERVER_ROOT']! + '/profile/addWeight'), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: jsonData, - ); - - if (response.statusCode == 201) { - Navigator.pushNamed( - context, - AppRoutes.myProfile, - ); - } else { - // Ошибка от сервера - print( - "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); - } - } catch (e) { - // Обработка ошибок при отправке запроса - print("Ошибка при отправке запроса: $e"); - } - }, + onPressed: addWeight(state, context), child: Text("Изменить", style: CustomTextStyles.titleMediumBlack.copyWith( color: Colors.blue, @@ -961,4 +940,47 @@ class MyProfileScreen extends StatelessWidget { }, ); } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } + + addWeight(MyProfileLoadedState state, BuildContext context) { + return () async { + Map data = { + "weight": state.weightController!.text, + }; + // Преобразование Map в JSON + String jsonData = json.encode(data); + + // Отправка HTTP-запроса + try { + final token = state.token; + final response = await http.post( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addWeight'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: jsonData, + ); + + if (response.statusCode == 201) { + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + } else { + // Ошибка от сервера + print( + "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); + } + } catch (e) { + // Обработка ошибок при отправке запроса + print("Ошибка при отправке запроса: $e"); + } + }; + } } diff --git a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart index 78201e77..a95b0434 100644 --- a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart @@ -5,14 +5,12 @@ import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart'; import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart'; import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; import 'package:eatvio_app/widgets/custom_bottom_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'package:eatvio_app/widgets/custom_about_list.dart'; +import 'package:eatvio_app/widgets/custom_expandable_list.dart'; +import 'package:eatvio_app/widgets/custom_notification_screen.dart'; +import 'package:eatvio_app/widgets/custom_section_title.dart'; +import 'package:eatvio_app/widgets/custom_language_drop_down.dart'; import 'package:flutter/material.dart'; -import 'dart:convert'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:hive/hive.dart'; -import 'package:flutter/services.dart'; -import 'package:http/http.dart' as http; class MySettingsScreen extends StatelessWidget { static Widget builder(BuildContext context) { @@ -43,43 +41,39 @@ class MySettingsScreen extends StatelessWidget { ); } - Widget _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) { + _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) { mediaQueryData = MediaQuery.of(context); - return SafeArea( + SafeArea( child: Scaffold( appBar: _buildAppBar(context, "my_settings".tr), body: Padding( padding: const EdgeInsets.all(16.0), - child: SingleChildScrollView( - child: Column( - children: [ - SectionTitle('Язык'), - LanguageDropdown(), - SectionTitle('Уведомления'), - NotificationSwitch('Уведомление об обновлениях'), - NotificationSwitch('Напоминания о заполнении'), - SectionTitle('Дополнительная информация'), - ExpandableList('Персональные данные'), - AboutList('О нас', ['ООО Итвио', '2023-н.в.']), - ], - ), + child: ListView( + children: [ + SectionTitle('Язык'), + LanguageDropdown(), + SectionTitle('Уведомления'), + NotificationSwitch('Уведомление об обновлениях'), + NotificationSwitch('Напоминания о заполнении'), + SectionTitle('Дополнительная информация'), + ExpandableList('Персональные данные'), + AboutList('О нас', ['ООО Итвио', '2023-н.в.']), + ], ), ), bottomNavigationBar: _buildBottomBar(context))); } /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context, String username) { + _buildAppBar(BuildContext context, String username) { return CustomAppBar( height: 54.v, leadingWidth: 28.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), title: Padding( padding: EdgeInsets.only(left: 20.h), child: Row(children: [ @@ -90,7 +84,13 @@ class MySettingsScreen extends StatelessWidget { ); } - Widget _buildBottomBar(BuildContext context) { + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + + _buildBottomBar(BuildContext context) { return CustomBottomBar(onChanged: (BottomBarEnum type) {}); } @@ -98,570 +98,3 @@ class MySettingsScreen extends StatelessWidget { NavigatorService.goBack(); } } - -class SectionTitle extends StatelessWidget { - final String title; - - SectionTitle(this.title); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - title, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18), - ), - ); - } -} - -class LanguageDropdown extends StatefulWidget { - @override - _LanguageDropdownState createState() => _LanguageDropdownState(); -} - -class _LanguageDropdownState extends State { - String selectedLanguage = 'Русский'; - - @override - Widget build(BuildContext context) { - return DropdownButton( - value: selectedLanguage, - onChanged: (newValue) { - setState(() { - selectedLanguage = newValue!; - }); - }, - isDense: false, // Установите isDense в false - selectedItemBuilder: (BuildContext context) { - return ['Русский'].map((String language) { - return Align( - alignment: Alignment.center, // Выравнивание текста по центру - child: Text( - language, - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - ); - }).toList(); - }, - items: ['Русский'].map((String language) { - return DropdownMenuItem( - value: language, - child: Text(language), - ); - }).toList(), - ); - } -} - -class NotificationSwitch extends StatefulWidget { - final String title; - - NotificationSwitch(this.title); - - @override - _NotificationSwitchState createState() => _NotificationSwitchState(); -} - -class _NotificationSwitchState extends State { - bool switchValue = false; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: TextStyle( - color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), - ), - Switch( - value: switchValue, - onChanged: (value) { - setState(() { - switchValue = value; - }); - }, - inactiveThumbColor: appTheme.blueA400, - inactiveTrackColor: Colors.white, - activeTrackColor: appTheme.blueA400, - activeColor: Colors.white, - ), - ], - ); - } -} - -class HyperSwitch extends StatefulWidget { - final String title; - final MyProfileLoadedState? state; - HyperSwitch(this.title, this.state); - - @override - _HyperState createState() => _HyperState(); -} - -class _HyperState extends State { - bool switchValue = false; - - @override - void initState() { - super.initState(); - if (widget.state?.hyperFormController == '1') { - switchValue = true; - } - } - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: TextStyle( - color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), - ), - Switch( - value: switchValue, - onChanged: (value) { - setState(() { - switchValue = value; - widget.state!.hyperFormController = value ? "1" : "0"; - }); - }, - inactiveThumbColor: appTheme.blueA400, - inactiveTrackColor: Colors.white, - activeTrackColor: appTheme.blueA400, - activeColor: Colors.white, - ), - ], - ); - } -} - -class HypoSwitch extends StatefulWidget { - final String title; - final MyProfileLoadedState? state; - HypoSwitch(this.title, this.state); - - @override - _HypoState createState() => _HypoState(); -} - -class _HypoState extends State { - bool switchValue = false; - - @override - void initState() { - super.initState(); - if (widget.state?.hypoFormController == '1') { - switchValue = true; - } - } - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: TextStyle( - color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), - ), - Switch( - value: switchValue, - onChanged: (value) { - setState(() { - switchValue = value; - widget.state!.hypoFormController = value ? "1" : "0"; - }); - }, - inactiveThumbColor: appTheme.blueA400, - inactiveTrackColor: Colors.white, - activeTrackColor: appTheme.blueA400, - activeColor: Colors.white, - ), - ], - ); - } -} - -class ExpandableList extends StatefulWidget { - final String title; - - ExpandableList(this.title); - - @override - _ExpandableListState createState() => _ExpandableListState(); -} - -class _ExpandableListState extends State { - bool isExpanded = false; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - GestureDetector( - onTap: () { - setState(() { - isExpanded = !isExpanded; - }); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.bold), - ), - Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), - ], - ), - ), - if (isExpanded) - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: BlocBuilder( - builder: (context, state) { - if (state is MyProfileInitialState) { - // Это начальное состояние, данные ещё не загружены. - return CircularProgressIndicator( - color: Colors.white, - backgroundColor: Colors.white, - ); - } else if (state is MyProfileLoadedState) { - // Данные загружены, можно строить форму. - return _buildForm(context, state); - } else if (state is MyProfileErrorState) { - // Произошла ошибка при загрузке данных. - return Center(child: Text(state.errorMessage)); - } else { - // Другие состояния, если они есть. - return Container(); // Пустой контейнер для необработанных состояний. - } - }), - ), - Divider(), - ], - ); - } - - Widget _buildForm(BuildContext context, MyProfileLoadedState state) { - List cities = [ - 'Москва', - 'Санкт-Петербург', - 'Новосибирск', - 'Екатеринбург', - 'Нижний Новгород', - 'Казань', - 'Челябинск', - 'Омск', - 'Самара', - 'Ростов-на-Дону', - 'Уфа', - 'Красноярск', - 'Воронеж', - 'Пермь', - 'Волгоград', - 'Краснодар', - 'Саратов', - 'Тюмень', - 'Тольятти', - 'Ижевск', - 'Барнаул', - 'Ульяновск', - 'Иркутск', - 'Хабаровск', - 'Ярославль', - 'Владивосток', - 'Махачкала', - 'Томск', - 'Оренбург', - 'Кемерово', - ]; - List countries = [ - 'Россия', - ]; - final digitsOnly = FilteringTextInputFormatter.digitsOnly; - return Column(children: [ - TextField( - controller: state.firstNameFormController, - decoration: InputDecoration(labelText: 'first_name'.tr), - ), - TextField( - controller: state.lastNameFormController, - decoration: InputDecoration(labelText: 'last_name'.tr), - ), - TextField( - decoration: InputDecoration(labelText: 'date_of_birth'.tr), - controller: state.dateFormController, - onTap: () { - showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(1900), - lastDate: DateTime.now(), - ).then((selectedDate) { - if (selectedDate != null) { - setState(() { - state.dateFormController!.text = - DateFormat('dd-MM-yyyy').format(selectedDate); - }); - } - }); - }), - DropdownButtonFormField( - value: state.selectedCountry, - hint: Text(state.selectedCountry ?? "Не выбрано"), - onChanged: (newValue) { - // Обработчик изменения выбранной страны - setSelectedCountry(state, newValue); - }, - items: countries.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - decoration: InputDecoration(labelText: 'country'.tr), - ), - DropdownButtonFormField( - hint: Text(state.selectedCity ?? "Не выбрано"), - value: state.selectedCity, - onChanged: (newValue) { - // Обработчик изменения выбранного города - setSelectedCity(state, newValue); - }, - items: cities.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - decoration: InputDecoration(labelText: 'city_of_living'.tr), - ), - TextField( - controller: state.weightFormController, - decoration: InputDecoration(labelText: 'weight_measurements'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.heightFormController, - decoration: InputDecoration(labelText: 'height_measurements'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.chestCircumferenceFormController, - decoration: InputDecoration(labelText: 'chest_circumference'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.waistCircumferenceFormController, - decoration: InputDecoration(labelText: 'waist_circumference'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.hipCircumferenceFormController, - decoration: InputDecoration(labelText: 'hip_circumference'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.upPressureFormController, - decoration: InputDecoration(labelText: 'up_pressure'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.downPressureFormController, - decoration: InputDecoration(labelText: 'down_pressure'.tr), - keyboardType: TextInputType.number, - inputFormatters: [digitsOnly]), - TextField( - controller: state.allergiesFormController, - decoration: InputDecoration(labelText: 'allergies_presence'.tr), - maxLines: null, - ), - TextField( - controller: state.pillsFormController, - decoration: InputDecoration(labelText: 'do_you_take_pills'.tr), - maxLines: null, - ), - TextField( - controller: state.sportsFormController, - decoration: InputDecoration(labelText: 'your_favorite_sports'.tr), - maxLines: null, - ), - TextField( - controller: state.favoriteFormController, - decoration: InputDecoration(labelText: 'favorite_foods'.tr), - maxLines: null, - ), - TextField( - controller: state.unfavoriteFormController, - decoration: InputDecoration(labelText: 'unfavorite_foods'.tr), - maxLines: null, - ), - TextField( - controller: state.additionalFormController, - decoration: InputDecoration(labelText: 'additional_information'.tr), - maxLines: null, - ), - Row( - children: [ - HypoSwitch('do_you_have_hypotension_tendency'.tr, state), - ], - ), - Row( - children: [ - HyperSwitch('do_you_have_hypertension_tendency'.tr, state), - ], - ), - NextStepButton( - onPressed: () { - sendProfileData(state); - }, - buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - text: "save".tr.toUpperCase(), - margin: EdgeInsets.only(top: 20.h, bottom: 20.h)), - ]); - } - - void setSelectedCity(MyProfileLoadedState? state, String? newValue) { - setState(() { - state!.selectedCity = newValue; - }); - } - - void setSelectedCountry(MyProfileLoadedState? state, String? newValue) { - setState(() { - state!.selectedCountry = newValue; - }); - } - - Future sendProfileData(MyProfileLoadedState? state) async { - try { - // Создаем JSON объект с данными профиля - Map profileData = { - 'first_name': state?.firstNameFormController?.text, - 'last_name': state?.lastNameFormController?.text, - 'chest_circumference': state?.chestCircumferenceFormController?.text, - 'waist_circumference': state?.waistCircumferenceFormController?.text, - 'hip_circumference': state?.hipCircumferenceFormController?.text, - 'birthdate': state?.dateFormController?.text, - 'height': state?.heightFormController?.text, - 'weight': state?.weightFormController?.text, - 'blood_pressure_up': state?.upPressureFormController?.text, - 'blood_pressure_down': state?.downPressureFormController?.text, - 'about_alergy': state?.allergiesFormController?.text, - 'about_taking_pils': state?.pillsFormController?.text, - 'about_sport_hobbies': state?.sportsFormController?.text, - 'favorite_foods': state?.favoriteFormController?.text, - 'unfavorite_foods': state?.unfavoriteFormController?.text, - 'additional_info': state?.additionalFormController?.text, - 'country': state?.selectedCountry ?? 'Россия', - 'city': state?.selectedCity ?? 'Москва', - 'has_hypo': state?.hypoFormController, - 'has_hyper': state?.hyperFormController, - }; - final box = await Hive.openBox('token'); - final token = box.get('token'); - String apiUrl = dotenv.env['SERVER_ROOT']! + '/profile/update'; - http.Response response = await http.post( - Uri.parse(apiUrl), - body: json.encode(profileData), - encoding: utf8, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - ); - - print(response.body); - if (response.statusCode == 200) { - Navigator.pushNamed( - context, - AppRoutes.myProfile, - ); - } else { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text('Ошибка при отправке данных профиля'.tr), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - throw Exception('Ошибка при отправке данных профиля'); - } - } catch (error) { - throw Exception('Ошибка при отправке данных профиля: $error'); - } - } -} - -class AboutList extends StatefulWidget { - final String title; - final List items; - - AboutList(this.title, this.items); - - @override - _AboutListState createState() => _AboutListState(); -} - -class _AboutListState extends State { - bool isExpanded = false; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - GestureDetector( - onTap: () { - setState(() { - isExpanded = !isExpanded; - }); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.bold), - ), - Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), - ], - ), - ), - if (isExpanded) - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Column( - //children: widget.items.map((item) => Text(item)).toList(), - children: [Text(widget.items.join('\n'))]), - ), - Divider(), - ], - ); - } -} diff --git a/lib/presentation/registration/additional_information_screen/additional_information_screen.dart b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart index 0f6e122f..345af298 100644 --- a/lib/presentation/registration/additional_information_screen/additional_information_screen.dart +++ b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart @@ -66,45 +66,52 @@ class AdditionalInformationScreen extends StatelessWidget { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () async { - UserRegistrationSheet sheet = state - .additionalInformationModelObj! - .userRegistrationSheet; - sheet.additionalInfo = state.editTextController!.text; - final response = await http.post( - Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'), - headers: {'Content-Type': 'application/json'}, - body: json.encode(sheet), - ); - var res = await jsonDecode(response.body); - final box = await Hive.openBox('token'); - final token = res['token']; - box.put('token', token); - Navigator.pushNamed( - context, - AppRoutes.myProfile, - ); - }); + onPressed: onTapFinish(state, context)); }), ])))); } + onTapFinish(AdditionalInformationState state, BuildContext context) { + return () async { + UserRegistrationSheet sheet = + state.additionalInformationModelObj!.userRegistrationSheet; + sheet.additionalInfo = state.editTextController!.text; + final response = await http.post( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'), + headers: {'Content-Type': 'application/json'}, + body: json.encode(sheet), + ); + var res = await jsonDecode(response.body); + final box = await Hive.openBox('token'); + final token = res['token']; + box.put('token', token); + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 31.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildTextFormField(BuildContext context) { + _buildTextFormField(BuildContext context) { return SizedBox( height: 257.v, width: 343.h, @@ -147,12 +154,14 @@ class AdditionalInformationScreen extends StatelessWidget { title: const Text('Регистрация завершена!'), content: const Text('1'), actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Ok')) + TextButton(onPressed: onTapOk(context), child: const Text('Ok')) ], )); } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } } diff --git a/lib/presentation/registration/allergies_screen/allergies_screen.dart b/lib/presentation/registration/allergies_screen/allergies_screen.dart index ee78724c..9f4c6b03 100644 --- a/lib/presentation/registration/allergies_screen/allergies_screen.dart +++ b/lib/presentation/registration/allergies_screen/allergies_screen.dart @@ -67,52 +67,63 @@ class _AllergiesScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.allergiesModelObj!.userRegistrationSheet; - sheet.aboutAllergy = - state.enterInformationController!.text; - Navigator.pushNamed( - context, - AppRoutes.medicineIntakeScreen, - arguments: sheet, - ); - }); + onPressed: onTapContinue(state, context)); }), BlocBuilder( builder: (context, state) { return GestureDetector( - onTap: () { - UserRegistrationSheet sheet = - state.allergiesModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.medicineIntakeScreen, - arguments: sheet, - ); - }, + onTap: onTapSkip(state, context), child: Text("skip".tr, style: theme.textTheme.bodyLarge), ); }), ])))); } + onTapSkip(AllergiesState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.allergiesModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.medicineIntakeScreen, + arguments: sheet, + ); + }; + } + + onTapContinue(AllergiesState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.allergiesModelObj!.userRegistrationSheet; + sheet.aboutAllergy = state.enterInformationController!.text; + Navigator.pushNamed( + context, + AppRoutes.medicineIntakeScreen, + arguments: sheet, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 31.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildTextFormField(BuildContext context) { + _buildTextFormField(BuildContext context) { return Column(children: [ Text("fill_in_information".tr, style: CustomTextStyles.titleMediumSegoeUI), diff --git a/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart index 5dd89c25..ec34e364 100644 --- a/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart +++ b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart @@ -151,7 +151,7 @@ class _AppNavigationScreenState extends State { } /// Section Widget - Widget _buildAppNavigation(BuildContext context) { + _buildAppNavigation(BuildContext context) { return Container( decoration: BoxDecoration( color: Color(0XFFFFFFFF), @@ -205,15 +205,13 @@ class _AppNavigationScreenState extends State { } /// Common widget - Widget _buildScreenTitle( + _buildScreenTitle( BuildContext context, { required String screenTitle, Function? onTapScreenTitle, }) { return GestureDetector( - onTap: () { - onTapScreenTitle!.call(); - }, + onTap: onTapScreenTitle!.call(), child: Container( decoration: BoxDecoration( color: Color(0XFFFFFFFF), diff --git a/lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart b/lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart index 47c83a35..f0c26a10 100644 --- a/lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart +++ b/lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart @@ -43,24 +43,30 @@ class _BloodPressureScreenState extends State { padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 9.v), child: Column(children: [ SizedBox(height: 7.v), - Padding( - padding: EdgeInsets.symmetric(horizontal: 4.h), - child: Container( - height: 16.v, - width: 343.h, - decoration: BoxDecoration( - color: theme.colorScheme.onPrimary, - borderRadius: BorderRadius.circular(8.h), - border: Border.all( - color: theme.colorScheme.primary, - width: 1.h)), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.h), - child: LinearProgressIndicator( - value: 0.62, - backgroundColor: theme.colorScheme.onPrimary, - valueColor: AlwaysStoppedAnimation( - appTheme.blueA400))))), + Container( + height: 16.v, + width: 343.h, + padding: EdgeInsets.symmetric( + horizontal: + 4.h), // Apply padding directly to the Container + decoration: BoxDecoration( + color: theme.colorScheme.onPrimary, + borderRadius: BorderRadius.circular(8.h), + border: Border.all( + color: theme.colorScheme.primary, + width: 1.h, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.h), + child: LinearProgressIndicator( + value: 0.62, + backgroundColor: theme.colorScheme.onPrimary, + valueColor: + AlwaysStoppedAnimation(appTheme.blueA400), + ), + ), + ), SizedBox(height: 21.v), SizedBox( width: 224.h, @@ -130,11 +136,7 @@ class _BloodPressureScreenState extends State { items: bloodPressureModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context - .read() - .add(ChangeHypoDownEvent(value: value)); - }); + onChanged: onChangeHyper(context)); })), SizedBox(height: 22.v), Text("do_you_have_hypertension_tendency".tr, @@ -161,10 +163,7 @@ class _BloodPressureScreenState extends State { items: bloodPressureModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context.read().add( - ChangeHyperDropDownEvent(value: value)); - }); + onChanged: onChangeHypo(context)); })), Spacer(), BlocBuilder( @@ -174,79 +173,106 @@ class _BloodPressureScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - List errors = validateFields( - state.upPressureController!.text, - state.downPressureController!.text); - if (errors.isEmpty) { - UserRegistrationSheet sheet = state - .bloodPressureModelObj!.userRegistrationSheet; - sheet.bloodPressureUp = - state.upPressureController!.text; - sheet.bloodPressureDown = - state.downPressureController!.text; - Navigator.pushNamed( - context, - AppRoutes.favoriteSportsScreen, - arguments: sheet, - ); - } else { - String errorMessage = ''; - - for (var error in errors) { - errorMessage += error + '\n\n'; - } - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text(errorMessage), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - }); + onPressed: onTapContinue(state, context)); }), BlocBuilder( builder: (context, state) { return GestureDetector( - onTap: () { - UserRegistrationSheet sheet = - state.bloodPressureModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.allergiesScreen, - arguments: sheet, - ); - }, + onTap: onTapSkip(state, context), child: Text("skip".tr, style: theme.textTheme.bodyLarge), ); }), ])))); } + onTapSkip(BloodPressureState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.bloodPressureModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.allergiesScreen, + arguments: sheet, + ); + }; + } + + onTapContinue(BloodPressureState state, BuildContext context) { + return () { + List errors = validateFields( + state.upPressureController!.text, state.downPressureController!.text); + if (errors.isEmpty) { + UserRegistrationSheet sheet = + state.bloodPressureModelObj!.userRegistrationSheet; + sheet.bloodPressureUp = state.upPressureController!.text; + sheet.bloodPressureDown = state.downPressureController!.text; + Navigator.pushNamed( + context, + AppRoutes.favoriteSportsScreen, + arguments: sheet, + ); + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: onTapOK(context), + child: Text('OK'), + ), + ], + ), + ); + } + }; + } + + onTapOK(BuildContext context) { + return () { + Navigator.pop(context); + }; + } + + onChangeHypo(BuildContext context) { + return (value) { + context + .read() + .add(ChangeHyperDropDownEvent(value: value)); + }; + } + + onChangeHyper(BuildContext context) { + return (value) { + context.read().add(ChangeHypoDownEvent(value: value)); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); diff --git a/lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart b/lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart index 77f48b43..17ce3dfb 100644 --- a/lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart +++ b/lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart @@ -105,68 +105,76 @@ class BodyCircumferencesScreen extends StatelessWidget { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - List errors = validateFields( - state.chestCircumferenceController!.text, - state.waistCircumferenceController!.text, - state.hipCircumferenceController!.text); - if (errors.isEmpty) { - UserRegistrationSheet sheet = state - .bodyCircumferencesModelObj! - .userRegistrationSheet; - sheet.chestCircumference = - state.chestCircumferenceController!.text; - sheet.waistCircumference = - state.waistCircumferenceController!.text; - sheet.hipCircumference = - state.hipCircumferenceController!.text; - Navigator.pushNamed( - context, - AppRoutes.weightScreen, - arguments: sheet, - ); - } else { - String errorMessage = ''; - - for (var error in errors) { - errorMessage += error + '\n\n'; - } - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text(errorMessage), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - }); + onPressed: onTapContinue(state, context)); }), ])))); } + onTapContinue(BodyCircumferencesState state, BuildContext context) { + return () { + List errors = validateFields( + state.chestCircumferenceController!.text, + state.waistCircumferenceController!.text, + state.hipCircumferenceController!.text); + if (errors.isEmpty) { + UserRegistrationSheet sheet = + state.bodyCircumferencesModelObj!.userRegistrationSheet; + sheet.chestCircumference = state.chestCircumferenceController!.text; + sheet.waistCircumference = state.waistCircumferenceController!.text; + sheet.hipCircumference = state.hipCircumferenceController!.text; + Navigator.pushNamed( + context, + AppRoutes.weightScreen, + arguments: sheet, + ); + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + } + }; + } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); diff --git a/lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart b/lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart index 1e14a9c1..eac52021 100644 --- a/lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart +++ b/lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart @@ -71,11 +71,7 @@ class CityOfLivingScreen extends StatelessWidget { width: 12.adaptSize)), hintText: "Не выбрано".tr, items: cityOfLivingModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context - .read() - .add(ChangeDropDownEvent(value: value)); - }); + onChanged: onDropDownChange(context)); }), SizedBox(height: 22.v), Text("city".tr, style: theme.textTheme.bodyLarge), @@ -97,11 +93,7 @@ class CityOfLivingScreen extends StatelessWidget { hintText: "Не выбрано".tr, items: cityOfLivingModelObj?.dropdownItemList1 ?? [], - onChanged: (value) { - context - .read() - .add(ChangeDropDown1Event(value: value)); - }); + onChanged: onDropDownChangeCity(context)); }), Spacer(flex: 65), BlocBuilder( @@ -111,33 +103,53 @@ class CityOfLivingScreen extends StatelessWidget { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.cityOfLivingModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.bodyCircumferencesScreen, - arguments: sheet, - ); - }); + onPressed: onTapContinue(state, context)); }), ])))); } + onTapContinue(CityOfLivingState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.cityOfLivingModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.bodyCircumferencesScreen, + arguments: sheet, + ); + }; + } + + onDropDownChangeCity(BuildContext context) { + return (value) { + context.read().add(ChangeDropDown1Event(value: value)); + }; + } + + onDropDownChange(BuildContext context) { + return (value) { + context.read().add(ChangeDropDownEvent(value: value)); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); diff --git a/lib/presentation/registration/credentials_screen/credentials_screen.dart b/lib/presentation/registration/credentials_screen/credentials_screen.dart index 07607b4f..48979999 100644 --- a/lib/presentation/registration/credentials_screen/credentials_screen.dart +++ b/lib/presentation/registration/credentials_screen/credentials_screen.dart @@ -107,55 +107,62 @@ class CredentialsScreen extends StatelessWidget { margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - List errors = validateFields( - state.emailController!.text, - state.nameController!.text, - state.passwordController!.text, - state.passwordConfirmationController!.text); - if (errors.isEmpty) { - UserRegistrationSheet sheet = - state.credentialsModelObj!.userRegistrationSheet; - sheet.email = state.emailController!.text; - sheet.name = state.nameController!.text; - sheet.password = state.passwordController!.text; - sheet.passwordConfirmation = - state.passwordConfirmationController!.text; - Navigator.pushNamed( - context, - AppRoutes.nameScreen, - arguments: sheet, - ); - } else { - String errorMessage = ''; - - for (var error in errors) { - errorMessage += error + '\n\n'; - } - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text(errorMessage), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - }); + onPressed: onTapContinue(state, context)); }), ]))); } + onTapContinue(CredentialsState state, BuildContext context) { + return () { + List errors = validateFields( + state.emailController!.text, + state.nameController!.text, + state.passwordController!.text, + state.passwordConfirmationController!.text); + if (errors.isEmpty) { + UserRegistrationSheet sheet = + state.credentialsModelObj!.userRegistrationSheet; + sheet.email = state.emailController!.text; + sheet.name = state.nameController!.text; + sheet.password = state.passwordController!.text; + sheet.passwordConfirmation = state.passwordConfirmationController!.text; + Navigator.pushNamed( + context, + AppRoutes.nameScreen, + arguments: sheet, + ); + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + } + }; + } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, centerTitle: true, diff --git a/lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart b/lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart index 49ef2848..df859872 100644 --- a/lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart +++ b/lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart @@ -69,37 +69,45 @@ class _DateOfBirthScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = state - .dateOfBirthModelObj!.userRegistrationSheet; - sheet.birthdate = selectedDate.toString(); - Navigator.pushNamed( - context, - AppRoutes.cityOfLivingScreen, - arguments: sheet, - ); - }); + onPressed: onTapContinue(state, context)); }), ])))); }); } + onTapContinue(DateOfBirthState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.dateOfBirthModelObj!.userRegistrationSheet; + sheet.birthdate = selectedDate.toString(); + Navigator.pushNamed( + context, + AppRoutes.cityOfLivingScreen, + arguments: sheet, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildDateInput(BuildContext context) { + _buildDateInput(BuildContext context) { return Container( width: 343.h, padding: EdgeInsets.symmetric(horizontal: 4.h, vertical: 1.v), @@ -119,9 +127,7 @@ class _DateOfBirthScreenState extends State { Align( alignment: Alignment.center, child: GestureDetector( - onTap: () { - openDatePickerDialog(context); - }, + onTap: onTapCalendar(context), child: Text( '${selectedDate.day}.${selectedDate.month}.${selectedDate.year}', style: theme.textTheme.titleMedium))), @@ -129,6 +135,12 @@ class _DateOfBirthScreenState extends State { ])); } + onTapCalendar(BuildContext context) { + return () { + openDatePickerDialog(context); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); @@ -139,15 +151,21 @@ class _DateOfBirthScreenState extends State { /// This function returns a `Future` that completes with `void`. Future openDatePickerDialog(BuildContext context) async { DateTime? dateTime = await showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(1924), - lastDate: DateTime( - DateTime.now().year, DateTime.now().month, DateTime.now().day)); + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(1924), + lastDate: DateTime( + DateTime.now().year, + DateTime.now().month, + DateTime.now().day, + ), + ); if (dateTime != null) { - setState(() { - selectedDate = dateTime; - }); + if (mounted) { + setState(() { + selectedDate = dateTime; + }); + } } } } diff --git a/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart b/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart index 1e189d19..4dc1ce60 100644 --- a/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart +++ b/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart @@ -76,54 +76,64 @@ class _FavoriteFoodsScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 40.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = state - .favoriteFoodsModelObj!.userRegistrationSheet; - sheet.favoriteFoods = - state.favoriteFoodsController!.text; - sheet.unfavoriteFoods = - state.unfavoriteFoodsController!.text; - Navigator.pushNamed( - context, - AppRoutes.additionalInformationScreen, - arguments: sheet, - ); - }); + onPressed: onTapContinue(state, context)); }), BlocBuilder( builder: (context, state) { return GestureDetector( - onTap: () { - UserRegistrationSheet sheet = - state.favoriteFoodsModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.additionalInformationScreen, - arguments: sheet, - ); - }, + onTap: onTapSkip(state, context), child: Text("skip".tr, style: theme.textTheme.bodyLarge), ); }), ])))); } + onTapSkip(FavoriteFoodsState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.favoriteFoodsModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.additionalInformationScreen, + arguments: sheet, + ); + }; + } + + onTapContinue(FavoriteFoodsState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.favoriteFoodsModelObj!.userRegistrationSheet; + sheet.favoriteFoods = state.favoriteFoodsController!.text; + sheet.unfavoriteFoods = state.unfavoriteFoodsController!.text; + Navigator.pushNamed( + context, + AppRoutes.additionalInformationScreen, + arguments: sheet, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 31.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildTextFormFieldFav(BuildContext context) { + _buildTextFormFieldFav(BuildContext context) { return Column(children: [ Text("favorite_foods".tr, style: CustomTextStyles.titleMediumSegoeUI), SizedBox(height: 2.v), @@ -140,7 +150,7 @@ class _FavoriteFoodsScreenState extends State { } /// Section Widget - Widget _buildTextFormFieldUnfav(BuildContext context) { + _buildTextFormFieldUnfav(BuildContext context) { return Column(children: [ Text("unfavorite_foods".tr, style: CustomTextStyles.titleMediumSegoeUI), SizedBox(height: 2.v), diff --git a/lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart b/lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart index 8374c74b..cb23b28f 100644 --- a/lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart +++ b/lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart @@ -71,31 +71,12 @@ class _FavoriteSportsScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = state - .favoriteSportsModelObj! - .userRegistrationSheet; - sheet.aboutSportHobbies = - state.enterinformationController!.text; - Navigator.pushNamed( - context, - AppRoutes.favoriteFoodsScreen, - arguments: sheet, - ); - }); + onPressed: onTapContinue(state, context)); }), BlocBuilder( builder: (context, state) { return GestureDetector( - onTap: () { - UserRegistrationSheet sheet = state - .favoriteSportsModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.favoriteFoodsScreen, - arguments: sheet, - ); - }, + onTap: onTapSkip(state, context), child: Text("skip".tr, style: theme.textTheme.bodyLarge), ); @@ -103,21 +84,50 @@ class _FavoriteSportsScreenState extends State { ])))); } - PreferredSizeWidget _buildAppBar(BuildContext context) { + onTapSkip(FavoriteSportsState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.favoriteSportsModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.favoriteFoodsScreen, + arguments: sheet, + ); + }; + } + + onTapContinue(FavoriteSportsState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.favoriteSportsModelObj!.userRegistrationSheet; + sheet.aboutSportHobbies = state.enterinformationController!.text; + Navigator.pushNamed( + context, + AppRoutes.favoriteFoodsScreen, + arguments: sheet, + ); + }; + } + + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildTextFormFieldSection(BuildContext context) { + _buildTextFormFieldSection(BuildContext context) { return Column(children: [ Text("fill_in_information".tr, style: CustomTextStyles.titleMediumSegoeUI), diff --git a/lib/presentation/registration/gender_screen/gender_screen.dart b/lib/presentation/registration/gender_screen/gender_screen.dart index 34dcc859..1db99ac0 100644 --- a/lib/presentation/registration/gender_screen/gender_screen.dart +++ b/lib/presentation/registration/gender_screen/gender_screen.dart @@ -63,10 +63,7 @@ class _GenderScreenState extends State { title: Text('Мужской'), value: 'male', groupValue: state.selectedRadioValue, - onChanged: (value) { - BlocProvider.of(context) - .add(GenderSelectEvent(value: 'male')); - }, + onChanged: onMale(context), secondary: Image.asset( 'assets/images/img_male.png', width: 40, @@ -77,10 +74,7 @@ class _GenderScreenState extends State { title: Text('Женский'), value: 'female', groupValue: state.selectedRadioValue, - onChanged: (value) { - BlocProvider.of(context) - .add(GenderSelectEvent(value: 'female')); - }, + onChanged: onFemale(context), secondary: Image.asset( 'assets/images/img_female.png', width: 40, @@ -95,37 +89,58 @@ class _GenderScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.genderModelObj!.userRegistrationSheet; - sheet.gender = state.selectedRadioValue!; - Navigator.pushNamed( - context, - AppRoutes.dateOfBirthScreen, - arguments: sheet, - ); - }); + onPressed: onTapNext(state, context)); }), ])))); }); } + onTapNext(GenderState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = state.genderModelObj!.userRegistrationSheet; + sheet.gender = state.selectedRadioValue!; + Navigator.pushNamed( + context, + AppRoutes.dateOfBirthScreen, + arguments: sheet, + ); + }; + } + + onMale(BuildContext context) { + return (value) { + BlocProvider.of(context) + .add(GenderSelectEvent(value: 'male')); + }; + } + + onFemale(BuildContext context) { + return (value) { + BlocProvider.of(context) + .add(GenderSelectEvent(value: 'female')); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - NavigatorService.pushNamed( - AppRoutes.dateOfBirthScreen, - ); - }), + onTap: onTapBack()), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack() { + return () { + NavigatorService.pushNamed( + AppRoutes.dateOfBirthScreen, + ); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); diff --git a/lib/presentation/registration/height_screen/height_screen.dart b/lib/presentation/registration/height_screen/height_screen.dart index 574bf488..9cb66c29 100644 --- a/lib/presentation/registration/height_screen/height_screen.dart +++ b/lib/presentation/registration/height_screen/height_screen.dart @@ -77,11 +77,7 @@ class _HeightScreenState extends State { width: 12.adaptSize)), hintText: "170", items: heightModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context - .read() - .add(ChangeDropDownEvent(value: value)); - }); + onChanged: newMethod(context)); }) ]) ]), @@ -93,33 +89,46 @@ class _HeightScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.heightModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.bloodPressureScreen, - arguments: sheet, - ); - }); + onPressed: onTapNext(state, context)); }), ])))); } + newMethod(BuildContext context) { + return (value) { + context.read().add(ChangeDropDownEvent(value: value)); + }; + } + + onTapNext(HeightState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = state.heightModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.bloodPressureScreen, + arguments: sheet, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Navigates to the previous screen. onTapArrowLeft(BuildContext context) { NavigatorService.goBack(); diff --git a/lib/presentation/registration/language_select_screen/language_select_screen.dart b/lib/presentation/registration/language_select_screen/language_select_screen.dart index cd8b9b4b..56e644a0 100644 --- a/lib/presentation/registration/language_select_screen/language_select_screen.dart +++ b/lib/presentation/registration/language_select_screen/language_select_screen.dart @@ -64,23 +64,22 @@ class LanguageSelectScreen extends StatelessWidget { languageSelectModelObj, ) { return CustomDropDown( - icon: Container( - margin: - EdgeInsets.fromLTRB(30.h, 11.v, 17.h, 11.v), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(1.h)), - child: CustomImageView( - imagePath: ImageConstant.imgFavorite, - height: 12.adaptSize, - width: 12.adaptSize)), - hintText: "Не выбрано".tr, - items: - languageSelectModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context - .read() - .add(ChangeDropDownEvent(value: value)); - }); + icon: Container( + margin: EdgeInsets.fromLTRB(30.h, 11.v, 17.h, 11.v), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.h), + ), + child: CustomImageView( + imagePath: ImageConstant.imgFavorite, + height: 12.adaptSize, + width: 12.adaptSize, + ), + ), + hintText: "Не выбрано".tr, + items: languageSelectModelObj?.dropdownItemList ?? [], + onChanged: (value) => + _onDropDownChanged(context, value), + ); }), Spacer(flex: 63), ])), @@ -92,15 +91,24 @@ class LanguageSelectScreen extends StatelessWidget { margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.languageSelectModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.credentialsScreen, - arguments: sheet, - ); - }); + onPressed: _saveAndPush(state, context)); }))); } + + void Function()? _saveAndPush( + LanguageSelectState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.languageSelectModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.credentialsScreen, + arguments: sheet, + ); + }; + } + + void _onDropDownChanged(BuildContext context, value) { + context.read().add(ChangeDropDownEvent(value: value)); + } } diff --git a/lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart b/lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart index fc0766f6..51df64aa 100644 --- a/lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart +++ b/lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart @@ -74,52 +74,63 @@ class _MedicineIntakeScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = state - .medicineIntakeModelObj!.userRegistrationSheet; - sheet.aboutTakingPils = - state.enterInformationController!.text; - Navigator.pushNamed( - context, - AppRoutes.bloodPressureScreen, - arguments: sheet, - ); - }); + onPressed: onTapNext(state, context)); }), BlocBuilder( builder: (context, state) { return GestureDetector( - onTap: () { - UserRegistrationSheet sheet = - state.medicineIntakeModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.bloodPressureScreen, - arguments: sheet, - ); - }, + onTap: onTapSkip(state, context), child: Text("skip".tr, style: theme.textTheme.bodyLarge), ); }), ])))); } + onTapSkip(MedicineIntakeState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.medicineIntakeModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.bloodPressureScreen, + arguments: sheet, + ); + }; + } + + onTapNext(MedicineIntakeState state, BuildContext context) { + return () { + UserRegistrationSheet sheet = + state.medicineIntakeModelObj!.userRegistrationSheet; + sheet.aboutTakingPils = state.enterInformationController!.text; + Navigator.pushNamed( + context, + AppRoutes.bloodPressureScreen, + arguments: sheet, + ); + }; + } + /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 31.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapBack(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + /// Section Widget - Widget _buildTextFormField(BuildContext context) { + _buildTextFormField(BuildContext context) { return Column(children: [ Text("fill_in_information".tr, style: CustomTextStyles.titleMediumSegoeUI), diff --git a/lib/presentation/registration/name_screen/name_screen.dart b/lib/presentation/registration/name_screen/name_screen.dart index cc234a77..05759a47 100644 --- a/lib/presentation/registration/name_screen/name_screen.dart +++ b/lib/presentation/registration/name_screen/name_screen.dart @@ -82,58 +82,19 @@ class NameScreen extends StatelessWidget { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - List errors = validateFields( - state.firstNameController!.text, - state.lastNameController!.text); - if (errors.isEmpty) { - UserRegistrationSheet sheet = - state.nameModelObj!.userRegistrationSheet; - sheet.firstName = state.firstNameController!.text; - sheet.lastName = state.lastNameController!.text; - Navigator.pushNamed( - context, - AppRoutes.genderScreen, - arguments: sheet, - ); - } else { - String errorMessage = ''; - - for (var error in errors) { - errorMessage += error + '\n\n'; - } - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Ошибка'), - content: Text(errorMessage), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ), - ); - } - }); + onPressed: _onTapNext(context, state)); }), ])))); } /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapArrowLeft(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } @@ -143,6 +104,45 @@ class NameScreen extends StatelessWidget { NavigatorService.goBack(); } + _onTapNext(BuildContext context, state) { + List errors = validateFields( + state.firstNameController!.text, state.lastNameController!.text); + if (errors.isEmpty) { + UserRegistrationSheet sheet = state.nameModelObj!.userRegistrationSheet; + sheet.firstName = state.firstNameController!.text; + sheet.lastName = state.lastNameController!.text; + Navigator.pushNamed( + context, + AppRoutes.genderScreen, + arguments: sheet, + ); + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + } + } + + onTapOk(BuildContext context) { + Navigator.pop(context); + } + List validateFields( String firstName, String lastName, diff --git a/lib/presentation/registration/weight_screen/weight_screen.dart b/lib/presentation/registration/weight_screen/weight_screen.dart index ab936350..64cec0a3 100644 --- a/lib/presentation/registration/weight_screen/weight_screen.dart +++ b/lib/presentation/registration/weight_screen/weight_screen.dart @@ -77,11 +77,7 @@ class _WeightScreenState extends State { width: 12.adaptSize)), hintText: "Не выбрано", items: weightModelObj?.dropdownItemList ?? [], - onChanged: (value) { - context - .read() - .add(ChangeDropDownEvent(value: value)); - }); + onChanged: _onDropDownChanged); }) ]) ]), @@ -93,29 +89,19 @@ class _WeightScreenState extends State { margin: EdgeInsets.only( left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: () { - UserRegistrationSheet sheet = - state.weightModelObj!.userRegistrationSheet; - Navigator.pushNamed( - context, - AppRoutes.heightScreen, - arguments: sheet, - ); - }); + onPressed: onTapNext(context, state)); }), ])))); } /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { + _buildAppBar(BuildContext context) { return CustomAppBar( leadingWidth: 29.h, leading: AppbarLeadingImage( imagePath: ImageConstant.imgArrowLeft, margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v), - onTap: () { - onTapArrowLeft(context); - }), + onTap: onTapArrowLeft(context)), centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } @@ -125,10 +111,23 @@ class _WeightScreenState extends State { NavigatorService.goBack(); } + onTapNext(BuildContext context, state) { + UserRegistrationSheet sheet = state.weightModelObj!.userRegistrationSheet; + Navigator.pushNamed( + context, + AppRoutes.heightScreen, + arguments: sheet, + ); + } + /// Navigates to the allergiesScreen when the action is triggered. navigateToHeight(BuildContext context) { NavigatorService.pushNamed( AppRoutes.heightScreen, ); } + + void _onDropDownChanged(value) { + context.read().add(ChangeDropDownEvent(value: value)); + } } diff --git a/lib/widgets/app_bar/appbar_leading_image.dart b/lib/widgets/app_bar/appbar_leading_image.dart index 5c9ff8e4..003d9f03 100644 --- a/lib/widgets/app_bar/appbar_leading_image.dart +++ b/lib/widgets/app_bar/appbar_leading_image.dart @@ -21,9 +21,7 @@ class AppbarLeadingImage extends StatelessWidget { @override Widget build(BuildContext context) { return InkWell( - onTap: () { - onTap!.call(); - }, + onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: CustomImageView( @@ -35,4 +33,8 @@ class AppbarLeadingImage extends StatelessWidget { ), ); } + + void onTapCall() { + onTap!.call(); + } } diff --git a/lib/widgets/app_bar/appbar_subtitle.dart b/lib/widgets/app_bar/appbar_subtitle.dart index 69c1b639..58779324 100644 --- a/lib/widgets/app_bar/appbar_subtitle.dart +++ b/lib/widgets/app_bar/appbar_subtitle.dart @@ -21,9 +21,7 @@ class AppbarSubtitle extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: () { - onTap!.call(); - }, + onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: Text( @@ -35,4 +33,8 @@ class AppbarSubtitle extends StatelessWidget { ), ); } + + void onTapCall() { + onTap!.call(); + } } diff --git a/lib/widgets/app_bar/appbar_title_circleimage.dart b/lib/widgets/app_bar/appbar_title_circleimage.dart index ee412291..66f82ef9 100644 --- a/lib/widgets/app_bar/appbar_title_circleimage.dart +++ b/lib/widgets/app_bar/appbar_title_circleimage.dart @@ -22,9 +22,7 @@ class AppbarTitleCircleimage extends StatelessWidget { Widget build(BuildContext context) { return InkWell( borderRadius: BorderRadiusStyle.circleBorder8, - onTap: () { - onTap!.call(); - }, + onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: CustomImageView( @@ -39,4 +37,8 @@ class AppbarTitleCircleimage extends StatelessWidget { ), ); } + + void onTapCall() { + onTap!.call(); + } } diff --git a/lib/widgets/app_bar/appbar_title_image.dart b/lib/widgets/app_bar/appbar_title_image.dart index 3ccfd581..e15f1c52 100644 --- a/lib/widgets/app_bar/appbar_title_image.dart +++ b/lib/widgets/app_bar/appbar_title_image.dart @@ -21,9 +21,7 @@ class AppbarTitleImage extends StatelessWidget { @override Widget build(BuildContext context) { return InkWell( - onTap: () { - onTap!.call(); - }, + onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: CustomImageView( @@ -35,4 +33,8 @@ class AppbarTitleImage extends StatelessWidget { ), ); } + + void onTapCall() { + onTap!.call(); + } } diff --git a/lib/widgets/app_bar/exit_button.dart b/lib/widgets/app_bar/exit_button.dart index a93c9a84..ee0d27dd 100644 --- a/lib/widgets/app_bar/exit_button.dart +++ b/lib/widgets/app_bar/exit_button.dart @@ -21,9 +21,7 @@ class ExitButton extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: () { - onTap!.call(); - }, + onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: Text( @@ -35,4 +33,8 @@ class ExitButton extends StatelessWidget { ), ); } + + void onTapCall() { + onTap!.call(); + } } diff --git a/lib/widgets/custom_about_list.dart b/lib/widgets/custom_about_list.dart new file mode 100644 index 00000000..f4b91b2c --- /dev/null +++ b/lib/widgets/custom_about_list.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +class AboutList extends StatefulWidget { + final String title; + final List items; + + AboutList(this.title, this.items); + + @override + _AboutListState createState() => _AboutListState(); +} + +class _AboutListState extends State { + bool isExpanded = false; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + GestureDetector( + onTap: toggleList(), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: TextStyle( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.bold), + ), + Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), + ], + ), + ), + if (isExpanded) + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Column( + //children: widget.items.map((item) => Text(item)).toList(), + children: [Text(widget.items.join('\n'))]), + ), + Divider(), + ], + ); + } + + toggleList() { + return () { + setState(() { + isExpanded = !isExpanded; + }); + }; + } +} diff --git a/lib/widgets/custom_autocomplete.dart b/lib/widgets/custom_autocomplete.dart new file mode 100644 index 00000000..66e042dc --- /dev/null +++ b/lib/widgets/custom_autocomplete.dart @@ -0,0 +1,80 @@ +/* +class CustomTextFormField extends StatelessWidget { + // ... + + final AutocompleteOptions autocompleteOptions; + + const CustomTextFormField({ + // ... (остальные параметры) + required this.autocompleteOptions, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return alignment != null + ? Align( + alignment: alignment ?? Alignment.center, + child: autocompleteFieldWidget, + ) + : autocompleteFieldWidget; + } + + Widget get autocompleteFieldWidget => SizedBox( + width: width ?? double.maxFinite, + child: Autocomplete( + optionsBuilder: (TextEditingValue textEditingValue) { + if (textEditingValue.text == '') { + return const Iterable.empty(); + } + return autocompleteOptions.options.where((String option) { + return option + .toLowerCase() + .contains(textEditingValue.text.toLowerCase()); + }); + }, + onSelected: (String value) { + controller?.text = value; + onChanged?.call(value); + }, + fieldViewBuilder: (BuildContext context, + TextEditingController textEditingController, + FocusNode focusNode, + VoidCallback onFieldSubmitted) { + return TextFormField( + controller: textEditingController, + focusNode: focusNode, + autofocus: autofocus!, + style: textStyle ?? theme.textTheme.titleMedium, + obscureText: obscureText!, + textInputAction: textInputAction, + maxLines: maxLines ?? 1, + decoration: decoration, + validator: validator, + keyboardType: keyboardType, + onSaved: onSaved, + onChanged: onChanged, + inputFormatters: inputFormatters, + initialValue: initialValue, + onFieldSubmitted: (_) => onFieldSubmitted(), + ); + }, + optionsViewBuilder: (BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options) { + return Material( + elevation: 4.0, + child: ListView( + children: options.map((String option) { + return ListTile( + title: Text(option), + onTap: () { + onSelected(option); + }, + ); + }).toList(), + ), + ); + }, + ), + ); +}*/ diff --git a/lib/widgets/custom_bottom_bar.dart b/lib/widgets/custom_bottom_bar.dart index 67ae4e3f..45928ebd 100644 --- a/lib/widgets/custom_bottom_bar.dart +++ b/lib/widgets/custom_bottom_bar.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/app_export.dart'; import 'package:flutter/material.dart'; +// ignore: must_be_immutable class CustomBottomBar extends StatefulWidget { CustomBottomBar({this.onChanged}); @@ -64,60 +65,63 @@ class CustomBottomBarState extends State { elevation: 0, currentIndex: selectedIndex, type: BottomNavigationBarType.fixed, - items: List.generate(bottomMenuList.length, (index) { - return BottomNavigationBarItem( - icon: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CustomImageView( - imagePath: bottomMenuList[index].icon, - height: 24.v, - width: 19.h, - color: appTheme.blueGray400, - ), - Padding( - padding: EdgeInsets.only(top: 8.v), - child: Text( - bottomMenuList[index].title ?? "", - style: - CustomTextStyles.bodySmallMulishBluegray80012.copyWith( - color: appTheme.blueGray800, - ), - ), - ), - ], - ), - activeIcon: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CustomImageView( - imagePath: bottomMenuList[index].activeIcon, - height: 24.adaptSize, - width: 24.adaptSize, - color: theme.colorScheme.primary, - ), - Padding( - padding: EdgeInsets.only(top: 9.v), - child: Text( - bottomMenuList[index].title ?? "", - style: CustomTextStyles.bodySmallMulishPrimary.copyWith( - color: theme.colorScheme.primary, - ), - ), - ), - ], - ), - label: '', - ); - }), - onTap: (index) { - selectedIndex = index; - widget.onChanged?.call(bottomMenuList[index].type); - setState(() {}); - }, + items: List.generate(bottomMenuList.length, bottomMenu), + onTap: changeMeny, ), ); } + + void changeMeny(index) { + selectedIndex = index; + widget.onChanged?.call(bottomMenuList[index].type); + setState(() {}); + } + + BottomNavigationBarItem bottomMenu(index) { + return BottomNavigationBarItem( + icon: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomImageView( + imagePath: bottomMenuList[index].icon, + height: 24.v, + width: 19.h, + color: appTheme.blueGray400, + ), + Padding( + padding: EdgeInsets.only(top: 8.v), + child: Text( + bottomMenuList[index].title ?? "", + style: CustomTextStyles.bodySmallMulishBluegray80012.copyWith( + color: appTheme.blueGray800, + ), + ), + ), + ], + ), + activeIcon: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomImageView( + imagePath: bottomMenuList[index].activeIcon, + height: 24.adaptSize, + width: 24.adaptSize, + color: theme.colorScheme.primary, + ), + Padding( + padding: EdgeInsets.only(top: 9.v), + child: Text( + bottomMenuList[index].title ?? "", + style: CustomTextStyles.bodySmallMulishPrimary.copyWith( + color: theme.colorScheme.primary, + ), + ), + ), + ], + ), + label: '', + ); + } } enum BottomBarEnum { @@ -140,27 +144,3 @@ class BottomMenuModel { BottomBarEnum type; } - -class DefaultWidget extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - color: Colors.white, - padding: EdgeInsets.all(10), - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Please replace the respective Widget here', - style: TextStyle( - fontSize: 18, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/widgets/custom_drop_down.dart b/lib/widgets/custom_drop_down.dart index dd5afc7d..55f1231f 100644 --- a/lib/widgets/custom_drop_down.dart +++ b/lib/widgets/custom_drop_down.dart @@ -70,74 +70,72 @@ class CustomDropDown extends StatelessWidget { return alignment != null ? Align( alignment: alignment ?? Alignment.center, - child: dropDownWidget, + child: _buildDropDownWidget(context), ) - : dropDownWidget; + : _buildDropDownWidget(context); } - Widget get dropDownWidget => SizedBox( - width: width ?? double.maxFinite, - child: DropdownButtonFormField( - focusNode: focusNode ?? FocusNode(), - icon: icon, - autofocus: autofocus!, - style: textStyle ?? theme.textTheme.titleMedium, - items: items?.map((SelectionPopupModel item) { - return DropdownMenuItem( - value: item, - child: Text( - item.title, - overflow: TextOverflow.ellipsis, - style: hintStyle ?? theme.textTheme.titleMedium, - ), - ); - }).toList(), - decoration: decoration, - validator: validator, - 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, + _buildDropDownWidget(BuildContext context) { + return SizedBox( + width: width ?? double.maxFinite, + child: DropdownButtonFormField( + focusNode: focusNode ?? FocusNode(), + icon: icon, + autofocus: autofocus!, + style: textStyle ?? Theme.of(context).textTheme.titleMedium, + items: items?.map((SelectionPopupModel item) { + return DropdownMenuItem( + value: item, + child: Text( + item.title, + overflow: TextOverflow.ellipsis, + style: hintStyle ?? Theme.of(context).textTheme.titleMedium, ), - fillColor: fillColor ?? theme.colorScheme.onPrimary, - filled: filled, - border: borderDecoration ?? - OutlineInputBorder( - borderRadius: BorderRadius.circular(8.h), - borderSide: BorderSide( - color: appTheme.blueA400, - width: 1, - ), - ), - enabledBorder: borderDecoration ?? - OutlineInputBorder( - borderRadius: BorderRadius.circular(8.h), - borderSide: BorderSide( - color: appTheme.blueA400, - width: 1, - ), - ), - focusedBorder: borderDecoration ?? - OutlineInputBorder( - borderRadius: BorderRadius.circular(8.h), - borderSide: BorderSide( - color: appTheme.blueA400, - width: 1, - ), - ), - ); + ); + }).toList(), + decoration: _buildInputDecoration(context), + validator: validator, + onChanged: onDropDownChanged, + ), + ); + } + + void onDropDownChanged(value) { + onChanged!(value!); + } + + InputDecoration _buildInputDecoration(BuildContext context) { + return InputDecoration( + hintText: hintText ?? "", + hintStyle: hintStyle ?? Theme.of(context).textTheme.titleMedium, + prefixIcon: prefix, + prefixIconConstraints: prefixConstraints, + suffixIcon: suffix, + suffixIconConstraints: suffixConstraints, + isDense: true, + contentPadding: contentPadding ?? + EdgeInsets.only( + left: 6, + top: 6, + bottom: 6, + ), + fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary, + filled: filled, + border: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: appTheme.blueA400, width: 1), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: appTheme.blueA400, width: 1), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: appTheme.blueA400, width: 1), + ), + ); + } } diff --git a/lib/widgets/custom_edit_drop_down.dart b/lib/widgets/custom_edit_drop_down.dart index 59c58593..7f56bbd8 100644 --- a/lib/widgets/custom_edit_drop_down.dart +++ b/lib/widgets/custom_edit_drop_down.dart @@ -73,75 +73,73 @@ class CustomDropDown extends StatelessWidget { return alignment != null ? Align( alignment: alignment ?? Alignment.center, - child: dropDownWidget, + child: _buildDropDownWidget(context), ) - : dropDownWidget; + : _buildDropDownWidget(context); } - Widget get dropDownWidget => SizedBox( - width: width ?? double.maxFinite, - child: DropdownButtonFormField( - focusNode: focusNode ?? FocusNode(), - icon: icon, - autofocus: autofocus!, - style: textStyle ?? theme.textTheme.titleMedium, - items: items?.map((SelectionPopupModel item) { - return DropdownMenuItem( - 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, + _buildDropDownWidget(BuildContext context) { + return SizedBox( + width: width ?? double.maxFinite, + child: DropdownButtonFormField( + focusNode: focusNode ?? FocusNode(), + icon: icon, + autofocus: autofocus!, + style: textStyle ?? Theme.of(context).textTheme.titleMedium, + items: items?.map((SelectionPopupModel item) { + return DropdownMenuItem( + value: item, + child: Text( + item.title, + overflow: TextOverflow.ellipsis, + style: hintStyle ?? Theme.of(context).textTheme.titleMedium, ), - 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, - ), - ), - ); + ); + }).toList(), + decoration: _buildInputDecoration(context), + validator: validator, + value: defaultValue, + onChanged: onDropDownChanged, + ), + ); + } + + void onDropDownChanged(value) { + onChanged!(value!); + } + + InputDecoration _buildInputDecoration(BuildContext context) { + return InputDecoration( + hintText: hintText ?? "", + hintStyle: hintStyle ?? Theme.of(context).textTheme.titleMedium, + prefixIcon: prefix, + prefixIconConstraints: prefixConstraints, + suffixIcon: suffix, + suffixIconConstraints: suffixConstraints, + isDense: true, + contentPadding: contentPadding ?? + EdgeInsets.only( + left: 6, + top: 6, + bottom: 6, + ), + fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary, + filled: filled, + border: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + ); + } } diff --git a/lib/widgets/custom_edit_text_form_field.dart b/lib/widgets/custom_edit_text_form_field.dart index 5b27a43f..aeeb19e7 100644 --- a/lib/widgets/custom_edit_text_form_field.dart +++ b/lib/widgets/custom_edit_text_form_field.dart @@ -1,4 +1,3 @@ -import 'package:eatvio_app/core/app_export.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -92,61 +91,60 @@ class CustomTextFormField extends StatelessWidget { return alignment != null ? Align( alignment: alignment ?? Alignment.center, - child: textFormFieldWidget, + child: _buildTextFormFieldWidget(context), ) - : textFormFieldWidget; + : _buildTextFormFieldWidget(context); } - 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, - onChanged: onChanged, - 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, - ), - ), - ); + _buildTextFormFieldWidget(BuildContext context) { + return SizedBox( + width: width ?? double.maxFinite, + child: TextFormField( + controller: controller, + focusNode: focusNode ?? FocusNode(), + autofocus: autofocus!, + style: textStyle ?? Theme.of(context).textTheme.titleMedium, + obscureText: obscureText!, + textInputAction: textInputAction, + maxLines: maxLines ?? 1, + decoration: _buildInputDecoration(context), + validator: validator, + keyboardType: keyboardType, + onSaved: onSaved, + onChanged: onChanged, + inputFormatters: inputFormatters, + initialValue: initialValue, + ), + ); + } + + InputDecoration _buildInputDecoration(BuildContext context) { + return InputDecoration( + hintText: hintText ?? "", + hintStyle: hintStyle ?? Theme.of(context).textTheme.bodyLarge, + prefixIcon: prefix, + prefixIconConstraints: prefixConstraints, + suffixIcon: suffix, + suffixIconConstraints: suffixConstraints, + isDense: true, + contentPadding: contentPadding ?? EdgeInsets.all(6), + fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary, + filled: filled, + border: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + ); + } } diff --git a/lib/widgets/custom_expandable_list.dart b/lib/widgets/custom_expandable_list.dart new file mode 100644 index 00000000..f20ac19e --- /dev/null +++ b/lib/widgets/custom_expandable_list.dart @@ -0,0 +1,360 @@ +import 'package:flutter/material.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart'; +import 'package:eatvio_app/widgets/custom_hyper_switch.dart'; +import 'package:eatvio_app/widgets/custom_hypo_switch.dart'; +import 'package:intl/intl.dart'; +import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'dart:convert'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:hive/hive.dart'; +import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; + +class ExpandableList extends StatefulWidget { + final String title; + + ExpandableList(this.title); + + @override + _ExpandableListState createState() => _ExpandableListState(); +} + +class _ExpandableListState extends State { + bool isExpanded = false; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + GestureDetector( + onTap: toggleExpand(), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: TextStyle( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.bold), + ), + Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), + ], + ), + ), + if (isExpanded) + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: BlocBuilder( + builder: (context, state) { + if (state is MyProfileInitialState) { + // Это начальное состояние, данные ещё не загружены. + return CircularProgressIndicator( + color: Colors.white, + backgroundColor: Colors.white, + ); + } else if (state is MyProfileLoadedState) { + // Данные загружены, можно строить форму. + return _buildForm(context, state); + } else if (state is MyProfileErrorState) { + // Произошла ошибка при загрузке данных. + return Center(child: Text(state.errorMessage)); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }), + ), + Divider(), + ], + ); + } + + toggleExpand() { + return () { + setState(() { + isExpanded = !isExpanded; + }); + }; + } + + _buildForm(BuildContext context, MyProfileLoadedState state) { + List cities = [ + 'Москва', + 'Санкт-Петербург', + 'Новосибирск', + 'Екатеринбург', + 'Нижний Новгород', + 'Казань', + 'Челябинск', + 'Омск', + 'Самара', + 'Ростов-на-Дону', + 'Уфа', + 'Красноярск', + 'Воронеж', + 'Пермь', + 'Волгоград', + 'Краснодар', + 'Саратов', + 'Тюмень', + 'Тольятти', + 'Ижевск', + 'Барнаул', + 'Ульяновск', + 'Иркутск', + 'Хабаровск', + 'Ярославль', + 'Владивосток', + 'Махачкала', + 'Томск', + 'Оренбург', + 'Кемерово', + ]; + List countries = [ + 'Россия', + ]; + final digitsOnly = FilteringTextInputFormatter.digitsOnly; + return Column(children: [ + TextField( + controller: state.firstNameFormController, + decoration: InputDecoration(labelText: 'first_name'.tr), + ), + TextField( + controller: state.lastNameFormController, + decoration: InputDecoration(labelText: 'last_name'.tr), + ), + TextField( + decoration: InputDecoration(labelText: 'date_of_birth'.tr), + controller: state.dateFormController, + onTap: inputTime(context, state)), + DropdownButtonFormField( + value: state.selectedCountry, + hint: Text(state.selectedCountry ?? "Не выбрано"), + onChanged: setCountry(state), + items: countries.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + decoration: InputDecoration(labelText: 'country'.tr), + ), + DropdownButtonFormField( + hint: Text(state.selectedCity ?? "Не выбрано"), + value: state.selectedCity, + onChanged: setCity(state), + items: cities.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + decoration: InputDecoration(labelText: 'city_of_living'.tr), + ), + TextField( + controller: state.weightFormController, + decoration: InputDecoration(labelText: 'weight_measurements'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.heightFormController, + decoration: InputDecoration(labelText: 'height_measurements'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.chestCircumferenceFormController, + decoration: InputDecoration(labelText: 'chest_circumference'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.waistCircumferenceFormController, + decoration: InputDecoration(labelText: 'waist_circumference'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.hipCircumferenceFormController, + decoration: InputDecoration(labelText: 'hip_circumference'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.upPressureFormController, + decoration: InputDecoration(labelText: 'up_pressure'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.downPressureFormController, + decoration: InputDecoration(labelText: 'down_pressure'.tr), + keyboardType: TextInputType.number, + inputFormatters: [digitsOnly]), + TextField( + controller: state.allergiesFormController, + decoration: InputDecoration(labelText: 'allergies_presence'.tr), + maxLines: null, + ), + TextField( + controller: state.pillsFormController, + decoration: InputDecoration(labelText: 'do_you_take_pills'.tr), + maxLines: null, + ), + TextField( + controller: state.sportsFormController, + decoration: InputDecoration(labelText: 'your_favorite_sports'.tr), + maxLines: null, + ), + TextField( + controller: state.favoriteFormController, + decoration: InputDecoration(labelText: 'favorite_foods'.tr), + maxLines: null, + ), + TextField( + controller: state.unfavoriteFormController, + decoration: InputDecoration(labelText: 'unfavorite_foods'.tr), + maxLines: null, + ), + TextField( + controller: state.additionalFormController, + decoration: InputDecoration(labelText: 'additional_information'.tr), + maxLines: null, + ), + Row( + children: [ + HypoSwitch('do_you_have_hypotension_tendency'.tr, state), + ], + ), + Row( + children: [ + HyperSwitch('do_you_have_hypertension_tendency'.tr, state), + ], + ), + NextStepButton( + onPressed: sendData(state), + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + text: "save".tr.toUpperCase(), + margin: EdgeInsets.only(top: 20.h, bottom: 20.h)), + ]); + } + + sendData(MyProfileLoadedState state) { + return () { + sendProfileData(state); + }; + } + + setCity(MyProfileLoadedState state) { + return (newValue) { + // Обработчик изменения выбранного города + setSelectedCity(state, newValue); + }; + } + + setCountry(MyProfileLoadedState state) { + return (newValue) { + // Обработчик изменения выбранной страны + setSelectedCountry(state, newValue); + }; + } + + inputTime(BuildContext context, MyProfileLoadedState state) { + return () { + showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(1900), + lastDate: DateTime.now(), + ).then((selectedDate) { + if (selectedDate != null) { + setState(() { + state.dateFormController!.text = + DateFormat('dd-MM-yyyy').format(selectedDate); + }); + } + }); + }; + } + + void setSelectedCity(MyProfileLoadedState? state, String? newValue) { + setState(() { + state!.selectedCity = newValue; + }); + } + + void setSelectedCountry(MyProfileLoadedState? state, String? newValue) { + setState(() { + state!.selectedCountry = newValue; + }); + } + + Future sendProfileData(MyProfileLoadedState? state) async { + try { + // Создаем JSON объект с данными профиля + Map profileData = { + 'first_name': state?.firstNameFormController?.text, + 'last_name': state?.lastNameFormController?.text, + 'chest_circumference': state?.chestCircumferenceFormController?.text, + 'waist_circumference': state?.waistCircumferenceFormController?.text, + 'hip_circumference': state?.hipCircumferenceFormController?.text, + 'birthdate': state?.dateFormController?.text, + 'height': state?.heightFormController?.text, + 'weight': state?.weightFormController?.text, + 'blood_pressure_up': state?.upPressureFormController?.text, + 'blood_pressure_down': state?.downPressureFormController?.text, + 'about_alergy': state?.allergiesFormController?.text, + 'about_taking_pils': state?.pillsFormController?.text, + 'about_sport_hobbies': state?.sportsFormController?.text, + 'favorite_foods': state?.favoriteFormController?.text, + 'unfavorite_foods': state?.unfavoriteFormController?.text, + 'additional_info': state?.additionalFormController?.text, + 'country': state?.selectedCountry ?? 'Россия', + 'city': state?.selectedCity ?? 'Москва', + 'has_hypo': state?.hypoFormController, + 'has_hyper': state?.hyperFormController, + }; + final box = await Hive.openBox('token'); + final token = box.get('token'); + String apiUrl = dotenv.env['SERVER_ROOT']! + '/profile/update'; + http.Response response = await http.post( + Uri.parse(apiUrl), + body: json.encode(profileData), + encoding: utf8, + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + ); + + print(response.body); + if (response.statusCode == 200) { + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + } else { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text('Ошибка при отправке данных профиля'.tr), + actions: [ + TextButton( + onPressed: onTapOk(context), + child: Text('OK'), + ), + ], + ), + ); + throw Exception('Ошибка при отправке данных профиля'); + } + } catch (error) { + throw Exception('Ошибка при отправке данных профиля: $error'); + } + } + + onTapOk(BuildContext context) { + return () { + Navigator.pop(context); + }; + } +} diff --git a/lib/widgets/custom_hyper_switch.dart b/lib/widgets/custom_hyper_switch.dart new file mode 100644 index 00000000..92ebf000 --- /dev/null +++ b/lib/widgets/custom_hyper_switch.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart'; + +class HyperSwitch extends StatefulWidget { + final String title; + final MyProfileLoadedState? state; + HyperSwitch(this.title, this.state); + + @override + _HyperState createState() => _HyperState(); +} + +class _HyperState extends State { + bool switchValue = false; + + @override + void initState() { + super.initState(); + if (widget.state?.hyperFormController == '1') { + switchValue = true; + } + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: TextStyle( + color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), + ), + Switch( + value: switchValue, + onChanged: toggleSwitch(), + inactiveThumbColor: appTheme.blueA400, + inactiveTrackColor: Colors.white, + activeTrackColor: appTheme.blueA400, + activeColor: Colors.white, + ), + ], + ); + } + + toggleSwitch() { + return (value) { + setState(() { + switchValue = value; + widget.state!.hyperFormController = value ? "1" : "0"; + }); + }; + } +} diff --git a/lib/widgets/custom_hypo_switch.dart b/lib/widgets/custom_hypo_switch.dart new file mode 100644 index 00000000..1c625feb --- /dev/null +++ b/lib/widgets/custom_hypo_switch.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart'; + +class HypoSwitch extends StatefulWidget { + final String title; + final MyProfileLoadedState? state; + HypoSwitch(this.title, this.state); + + @override + _HypoState createState() => _HypoState(); +} + +class _HypoState extends State { + bool switchValue = false; + + @override + void initState() { + super.initState(); + if (widget.state?.hypoFormController == '1') { + switchValue = true; + } + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: TextStyle( + color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), + ), + Switch( + value: switchValue, + onChanged: toggleSwitch(), + inactiveThumbColor: appTheme.blueA400, + inactiveTrackColor: Colors.white, + activeTrackColor: appTheme.blueA400, + activeColor: Colors.white, + ), + ], + ); + } + + toggleSwitch() { + return (value) { + setState(() { + switchValue = value; + widget.state!.hypoFormController = value ? "1" : "0"; + }); + }; + } +} diff --git a/lib/widgets/custom_icon_button.dart b/lib/widgets/custom_icon_button.dart index cdf5b5b5..55766f18 100644 --- a/lib/widgets/custom_icon_button.dart +++ b/lib/widgets/custom_icon_button.dart @@ -10,22 +10,14 @@ class CustomIconButton extends StatelessWidget { this.decoration, this.child, this.onTap, - }) : super( - key: key, - ); + }) : 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 @@ -33,40 +25,40 @@ class CustomIconButton extends StatelessWidget { return alignment != null ? Align( alignment: alignment ?? Alignment.center, - child: iconButtonWidget, + child: _buildIconButtonWidget(), ) - : iconButtonWidget; + : _buildIconButtonWidget(); } - 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, + _buildIconButtonWidget() { + return 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: const BorderRadius.all(Radius.circular(9)), + border: const Border.fromBorderSide( + BorderSide(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), + borderRadius: const BorderRadius.all(Radius.circular(14)), ); } diff --git a/lib/widgets/custom_image_view.dart b/lib/widgets/custom_image_view.dart index 5bed7a28..07b9a5d9 100644 --- a/lib/widgets/custom_image_view.dart +++ b/lib/widgets/custom_image_view.dart @@ -42,19 +42,21 @@ class CustomImageView extends StatelessWidget { return alignment != null ? Align( alignment: alignment!, - child: _buildWidget(), + child: Padding( + padding: margin ?? EdgeInsets.zero, + child: InkWell( + onTap: onTap, + child: _buildCircleImage(), + ), + ), ) - : _buildWidget(); - } - - Widget _buildWidget() { - return Padding( - padding: margin ?? EdgeInsets.zero, - child: InkWell( - onTap: onTap, - child: _buildCircleImage(), - ), - ); + : Padding( + padding: margin ?? EdgeInsets.zero, + child: InkWell( + onTap: onTap, + child: _buildCircleImage(), + ), + ); } ///build the image with border radius @@ -84,7 +86,7 @@ class CustomImageView extends StatelessWidget { } } - Widget _buildImageView() { + _buildImageView() { if (imagePath != null) { switch (imagePath!.imageType) { case ImageType.svg: @@ -96,7 +98,6 @@ class CustomImageView extends StatelessWidget { height: height, width: width, fit: fit ?? BoxFit.contain, - color: color, ), ); case ImageType.file: diff --git a/lib/widgets/custom_language_drop_down.dart b/lib/widgets/custom_language_drop_down.dart new file mode 100644 index 00000000..7f0e3e0f --- /dev/null +++ b/lib/widgets/custom_language_drop_down.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class LanguageDropdown extends StatefulWidget { + @override + _LanguageDropdownState createState() => _LanguageDropdownState(); +} + +class _LanguageDropdownState extends State { + String selectedLanguage = 'Русский'; + + @override + Widget build(BuildContext context) { + return DropdownButton( + value: selectedLanguage, + onChanged: selectLanguage(), + isDense: false, // Установите isDense в false + selectedItemBuilder: getList(), + items: ['Русский'].map((String language) { + return DropdownMenuItem( + value: language, + child: Text(language), + ); + }).toList(), + ); + } + + getList() { + return (BuildContext context) { + return ['Русский'].map((String language) { + return Align( + alignment: Alignment.center, // Выравнивание текста по центру + child: Text( + language, + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ); + }).toList(); + }; + } + + selectLanguage() { + return (newValue) { + setState(() { + selectedLanguage = newValue!; + }); + }; + } +} diff --git a/lib/widgets/custom_notification_screen.dart b/lib/widgets/custom_notification_screen.dart new file mode 100644 index 00000000..bb778441 --- /dev/null +++ b/lib/widgets/custom_notification_screen.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:eatvio_app/core/app_export.dart'; + +class NotificationSwitch extends StatefulWidget { + final String title; + + NotificationSwitch(this.title); + + @override + _NotificationSwitchState createState() => _NotificationSwitchState(); +} + +class _NotificationSwitchState extends State { + bool switchValue = false; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: TextStyle( + color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold), + ), + Switch( + value: switchValue, + onChanged: toggleSwitch(), + inactiveThumbColor: appTheme.blueA400, + inactiveTrackColor: Colors.white, + activeTrackColor: appTheme.blueA400, + activeColor: Colors.white, + ), + ], + ); + } + + toggleSwitch() { + return (value) { + setState(() { + switchValue = value; + }); + }; + } +} diff --git a/lib/widgets/custom_outlined_button.dart b/lib/widgets/custom_outlined_button.dart index 4fb4a85c..38a77041 100644 --- a/lib/widgets/custom_outlined_button.dart +++ b/lib/widgets/custom_outlined_button.dart @@ -31,43 +31,86 @@ class CustomOutlinedButton extends BaseButton { ); final BoxDecoration? decoration; - final Widget? leftIcon; - final Widget? rightIcon; - final Widget? label; @override Widget build(BuildContext context) { - return alignment != null - ? Align( - alignment: alignment ?? Alignment.center, - child: buildOutlinedButtonWidget, - ) - : buildOutlinedButtonWidget; - } - - Widget get buildOutlinedButtonWidget => Container( + if (alignment != null) { + return Align( + alignment: alignment ?? Alignment.center, + child: Container( + height: this.height ?? 30.v, + width: this.width ?? double.maxFinite, + margin: margin, + decoration: decoration, + child: OutlinedButton( + style: buttonStyle!, + onPressed: isDisabled ?? false ? null : onPressed ?? () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + leftIcon ?? SizedBox.shrink(), + Text( + text, + style: buttonTextStyle ?? CustomTextStyles.titleSmallBold, + ), + rightIcon ?? SizedBox.shrink(), + ], + ), + ), + ), + ); + } else { + return Container( height: this.height ?? 30.v, width: this.width ?? double.maxFinite, margin: margin, decoration: decoration, child: OutlinedButton( - style: buttonStyle, + style: buttonStyle!, onPressed: isDisabled ?? false ? null : onPressed ?? () {}, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - leftIcon ?? const SizedBox.shrink(), + leftIcon ?? SizedBox.shrink(), Text( text, style: buttonTextStyle ?? CustomTextStyles.titleSmallBold, ), - rightIcon ?? const SizedBox.shrink(), + rightIcon ?? SizedBox.shrink(), ], ), ), ); + } + } + + /*_buildAppBarbuildOutlinedButtonWidget() { + return Container( + height: this.height ?? 30.v, + width: this.width ?? double.maxFinite, + margin: margin, + decoration: decoration, + child: OutlinedButton( + style: buttonStyle!, + onPressed: isDisabled ?? false ? null : onPressed ?? () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + leftIcon ?? SizedBox.shrink(), + Text( + text, + style: buttonTextStyle ?? CustomTextStyles.titleSmallBold, + ), + rightIcon ?? SizedBox.shrink(), + ], + ), + ), + ); + }*/ } diff --git a/lib/widgets/custom_section_title.dart b/lib/widgets/custom_section_title.dart new file mode 100644 index 00000000..282f3d6a --- /dev/null +++ b/lib/widgets/custom_section_title.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class SectionTitle extends StatelessWidget { + final String title; + + SectionTitle(this.title); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + title, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ); + } +} diff --git a/lib/widgets/custom_text_form_field.dart b/lib/widgets/custom_text_form_field.dart index a96c5a1c..0d11d1f3 100644 --- a/lib/widgets/custom_text_form_field.dart +++ b/lib/widgets/custom_text_form_field.dart @@ -34,51 +34,28 @@ class CustomTextFormField extends StatelessWidget { ); final Alignment? alignment; - final double? width; - final TextEditingController? controller; - final FocusNode? focusNode; - final bool? autofocus; - final TextStyle? textStyle; - final bool? obscureText; - final List? 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 FormFieldValidator? validator; - final void Function(String?)? onSaved; @override @@ -86,62 +63,149 @@ class CustomTextFormField extends StatelessWidget { return alignment != null ? Align( alignment: alignment ?? Alignment.center, - child: textFormFieldWidget, + child: 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: 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: appTheme.blueA400, + width: 1, + ), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + ), + keyboardType: keyboardType, + validator: validator, + onSaved: onSaved, + inputFormatters: inputFormatters, + ), + ), ) - : 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: 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: appTheme.blueA400, + width: 1, + ), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + ), + keyboardType: keyboardType, + validator: validator, + onSaved: onSaved, + inputFormatters: inputFormatters, + ), + ); } - - 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, - ), - ); - 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: appTheme.blueA400, - width: 1, - ), - ), - enabledBorder: borderDecoration ?? - OutlineInputBorder( - borderRadius: BorderRadius.circular(8.h), - borderSide: BorderSide( - color: appTheme.blueA400, - width: 1, - ), - ), - focusedBorder: borderDecoration ?? - OutlineInputBorder( - borderRadius: BorderRadius.circular(8.h), - borderSide: BorderSide( - color: appTheme.blueA400, - width: 1, - ), - ), - ); } + /* InputDecoration _buildInputDecoration() { + return 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: appTheme.blueA400, + width: 1, + ), + ), + enabledBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + focusedBorder: borderDecoration ?? + OutlineInputBorder( + borderRadius: BorderRadius.circular(8.h), + borderSide: BorderSide( + color: appTheme.blueA400, + width: 1, + ), + ), + ); + } +}*/ \ No newline at end of file diff --git a/lib/widgets/next_step_button.dart b/lib/widgets/next_step_button.dart index ce972835..455f8f94 100644 --- a/lib/widgets/next_step_button.dart +++ b/lib/widgets/next_step_button.dart @@ -31,43 +31,59 @@ class NextStepButton extends BaseButton { ); final BoxDecoration? decoration; - final Widget? leftIcon; - final Widget? rightIcon; - final Widget? label; @override Widget build(BuildContext context) { - return alignment != null + return (alignment != null) ? Align( - alignment: alignment ?? Alignment.center, - child: buildOutlinedButtonWidget, - ) - : buildOutlinedButtonWidget; - } - - Widget get buildOutlinedButtonWidget => Container( - height: this.height ?? 30.v, - width: this.width ?? double.maxFinite, - margin: margin, - decoration: decoration, - child: OutlinedButton( - style: buttonStyle, - onPressed: isDisabled ?? false ? null : onPressed ?? () {}, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - leftIcon ?? const SizedBox.shrink(), - Text( - text, - style: buttonTextStyle ?? theme.textTheme.titleSmall, + alignment: alignment!, + child: Container( + height: this.height ?? 30.v, + width: this.width ?? double.maxFinite, + margin: margin, + decoration: decoration, + child: OutlinedButton( + style: buttonStyle, + onPressed: isDisabled ?? false ? null : onPressed ?? () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + leftIcon ?? const SizedBox.shrink(), + Text( + text, + style: buttonTextStyle ?? theme.textTheme.titleSmall, + ), + rightIcon ?? const SizedBox.shrink(), + ], + ), ), - rightIcon ?? const SizedBox.shrink(), - ], - ), - ), - ); + ), + ) + : Container( + height: this.height ?? 30.v, + width: this.width ?? double.maxFinite, + margin: margin, + decoration: decoration, + child: OutlinedButton( + style: buttonStyle, + onPressed: isDisabled ?? false ? null : onPressed ?? () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + leftIcon ?? const SizedBox.shrink(), + Text( + text, + style: buttonTextStyle ?? theme.textTheme.titleSmall, + ), + rightIcon ?? const SizedBox.shrink(), + ], + ), + ), + ); + } } diff --git a/test/launch_test.dart b/test/launch_test.dart new file mode 100644 index 00000000..4f1eafee --- /dev/null +++ b/test/launch_test.dart @@ -0,0 +1,15 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:eatvio_app/main.dart' as app; + +void main() { + testWidgets('Smoke test', (WidgetTester tester) async { + // Запустить приложение + app.main(); + + // Подождать, пока приложение загрузится + await tester.pumpAndSettle(); + + // Проверить, что нет ошибок + expect(tester.takeException(), isNull); + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index e93dc93b..00000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:eatvio_app/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}