diff --git a/.gitignore b/.gitignore index 6d6c41b2..e167a732 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /.flutter-plugins /.flutter-plugins-dependencies /.packages +/.env \ No newline at end of file diff --git a/README.md b/README.md index a9442762..220ba3a6 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,18 @@ flutter build apk ### Библиотеки и инструменты -- BLoC - контроль состояний - https://bloclibrary.dev -- cached_network_image - кеширование картинок из веба - https://pub.dev/packages/cached_network_image + flutter_bloc - контроль состояний + cached_network_image - кеширование картинок из веба + flutter_svg - работа с изображениями + equatable - сравнение без оператора "==" + validators - валидация + hive - локальная бд (1) + hive_flutter - локальная бд (2) + isar_flutter_libs - локальная бд (3) + path_provider - динамичкские пути до файлов + fl_chart - построение графиков + flutter_spinkit - прелоадеры + flutter_dotenv - переменные окружения ### Поддержка diff --git a/android/app/build.gradle b/android/app/build.gradle index ae7a7c7c..4c483ee5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,7 +41,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.eatvioapp.app" - minSdkVersion 21 + minSdkVersion 23 targetSdkVersion flutter.targetSdkVersion multiDexEnabled true versionCode flutterVersionCode.toInteger() diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8d3a74df..435e0177 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ + + diff --git a/assets/images/img_arrow_left copy.svg b/assets/images/img_arrow_left copy.svg new file mode 100644 index 00000000..686dc1ab --- /dev/null +++ b/assets/images/img_arrow_left copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_close.svg b/assets/images/img_close.svg new file mode 100644 index 00000000..821eccd7 --- /dev/null +++ b/assets/images/img_close.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_close_blue_gray_900.svg b/assets/images/img_close_blue_gray_900.svg new file mode 100644 index 00000000..b0a23bbc --- /dev/null +++ b/assets/images/img_close_blue_gray_900.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_crop.svg b/assets/images/img_crop.svg new file mode 100644 index 00000000..6a6b5b87 --- /dev/null +++ b/assets/images/img_crop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_edit.svg b/assets/images/img_edit.svg new file mode 100644 index 00000000..e7cafed1 --- /dev/null +++ b/assets/images/img_edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_ellipse_1.png b/assets/images/img_ellipse_1.png new file mode 100644 index 00000000..21c05917 Binary files /dev/null and b/assets/images/img_ellipse_1.png differ diff --git a/assets/images/img_ellipse_1_1.png b/assets/images/img_ellipse_1_1.png new file mode 100644 index 00000000..bb5e76a3 Binary files /dev/null and b/assets/images/img_ellipse_1_1.png differ diff --git a/assets/images/img_ellipse_1_25x25.png b/assets/images/img_ellipse_1_25x25.png new file mode 100644 index 00000000..e31c4356 Binary files /dev/null and b/assets/images/img_ellipse_1_25x25.png differ diff --git a/assets/images/img_ellipse_1_32x32.png b/assets/images/img_ellipse_1_32x32.png new file mode 100644 index 00000000..ca0a1bbe Binary files /dev/null and b/assets/images/img_ellipse_1_32x32.png differ diff --git a/assets/images/img_ellipse_1_34x34.png b/assets/images/img_ellipse_1_34x34.png new file mode 100644 index 00000000..ef2f13f8 Binary files /dev/null and b/assets/images/img_ellipse_1_34x34.png differ diff --git a/assets/images/img_ellipse_1_42x42.png b/assets/images/img_ellipse_1_42x42.png new file mode 100644 index 00000000..665db966 Binary files /dev/null and b/assets/images/img_ellipse_1_42x42.png differ diff --git a/assets/images/img_ellipse_2.png b/assets/images/img_ellipse_2.png new file mode 100644 index 00000000..89021c44 Binary files /dev/null and b/assets/images/img_ellipse_2.png differ diff --git a/assets/images/img_ellipse_3.png b/assets/images/img_ellipse_3.png new file mode 100644 index 00000000..3e8a0760 Binary files /dev/null and b/assets/images/img_ellipse_3.png differ diff --git a/assets/images/img_eye.svg b/assets/images/img_eye.svg new file mode 100644 index 00000000..385ec8b0 --- /dev/null +++ b/assets/images/img_eye.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_favorite copy.svg b/assets/images/img_favorite copy.svg new file mode 100644 index 00000000..43cfd6e1 --- /dev/null +++ b/assets/images/img_favorite copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_favorite_white_a700.svg b/assets/images/img_favorite_white_a700.svg new file mode 100644 index 00000000..f5e05ea0 --- /dev/null +++ b/assets/images/img_favorite_white_a700.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_grid.svg b/assets/images/img_grid.svg new file mode 100644 index 00000000..49132101 --- /dev/null +++ b/assets/images/img_grid.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_icon_heart.svg b/assets/images/img_icon_heart.svg new file mode 100644 index 00000000..7dce386f --- /dev/null +++ b/assets/images/img_icon_heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_icon_message_square.svg b/assets/images/img_icon_message_square.svg new file mode 100644 index 00000000..89dced25 --- /dev/null +++ b/assets/images/img_icon_message_square.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_icon_more_horizontal.svg b/assets/images/img_icon_more_horizontal.svg new file mode 100644 index 00000000..6c168c82 --- /dev/null +++ b/assets/images/img_icon_more_horizontal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/img_icon_share_2.svg b/assets/images/img_icon_share_2.svg new file mode 100644 index 00000000..137c94b8 --- /dev/null +++ b/assets/images/img_icon_share_2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/img_like_5.svg b/assets/images/img_like_5.svg new file mode 100644 index 00000000..f07fbfaf --- /dev/null +++ b/assets/images/img_like_5.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/img_like_6.svg b/assets/images/img_like_6.svg new file mode 100644 index 00000000..1124613c --- /dev/null +++ b/assets/images/img_like_6.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/img_line_1.svg b/assets/images/img_line_1.svg new file mode 100644 index 00000000..2928b086 --- /dev/null +++ b/assets/images/img_line_1.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_line_2.svg b/assets/images/img_line_2.svg new file mode 100644 index 00000000..e353d487 --- /dev/null +++ b/assets/images/img_line_2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_line_3.svg b/assets/images/img_line_3.svg new file mode 100644 index 00000000..3aa093a6 --- /dev/null +++ b/assets/images/img_line_3.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_megaphone.svg b/assets/images/img_megaphone.svg new file mode 100644 index 00000000..b1b1ac78 --- /dev/null +++ b/assets/images/img_megaphone.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_nav.svg b/assets/images/img_nav.svg new file mode 100644 index 00000000..e456300f --- /dev/null +++ b/assets/images/img_nav.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/img_nav_blue_gray_400.svg b/assets/images/img_nav_blue_gray_400.svg new file mode 100644 index 00000000..3816ee25 --- /dev/null +++ b/assets/images/img_nav_blue_gray_400.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_nav_blue_gray_400_24x23.svg b/assets/images/img_nav_blue_gray_400_24x23.svg new file mode 100644 index 00000000..28bc4a42 --- /dev/null +++ b/assets/images/img_nav_blue_gray_400_24x23.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_nav_blue_gray_400_24x25.svg b/assets/images/img_nav_blue_gray_400_24x25.svg new file mode 100644 index 00000000..f24329d6 --- /dev/null +++ b/assets/images/img_nav_blue_gray_400_24x25.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_nav_primary.svg b/assets/images/img_nav_primary.svg new file mode 100644 index 00000000..65027e80 --- /dev/null +++ b/assets/images/img_nav_primary.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_overflowmenu.svg b/assets/images/img_overflowmenu.svg new file mode 100644 index 00000000..8f65bc76 --- /dev/null +++ b/assets/images/img_overflowmenu.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/img_rectangle_14.png b/assets/images/img_rectangle_14.png new file mode 100644 index 00000000..fc5220ab Binary files /dev/null and b/assets/images/img_rectangle_14.png differ diff --git a/assets/images/img_rectangle_14_220x341.png b/assets/images/img_rectangle_14_220x341.png new file mode 100644 index 00000000..c5576698 Binary files /dev/null and b/assets/images/img_rectangle_14_220x341.png differ diff --git a/assets/images/img_rectangle_14_220x375.png b/assets/images/img_rectangle_14_220x375.png new file mode 100644 index 00000000..f17e692a Binary files /dev/null and b/assets/images/img_rectangle_14_220x375.png differ diff --git a/assets/images/img_rectangle_14_250x311.png b/assets/images/img_rectangle_14_250x311.png new file mode 100644 index 00000000..9d37ab49 Binary files /dev/null and b/assets/images/img_rectangle_14_250x311.png differ diff --git a/assets/images/img_search.svg b/assets/images/img_search.svg new file mode 100644 index 00000000..589cb1c7 --- /dev/null +++ b/assets/images/img_search.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/img_send_1.svg b/assets/images/img_send_1.svg new file mode 100644 index 00000000..30ae3a66 --- /dev/null +++ b/assets/images/img_send_1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/images/img_statistic.svg b/assets/images/img_statistic.svg new file mode 100644 index 00000000..0d4b6868 --- /dev/null +++ b/assets/images/img_statistic.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/img_statistic_gray_200.svg b/assets/images/img_statistic_gray_200.svg new file mode 100644 index 00000000..35849df9 --- /dev/null +++ b/assets/images/img_statistic_gray_200.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/img_vector.svg b/assets/images/img_vector.svg new file mode 100644 index 00000000..211ce67c --- /dev/null +++ b/assets/images/img_vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index c43ad6c1..004a4f77 100644 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,98 +1,154 @@ { "images" : [ { - "size" : "20x20", "idiom" : "iphone", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" + "scale" : "2x", + "size" : "20x20" }, { - "size" : "40x40", "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" + "scale" : "3x", + "size" : "20x20" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" }, { - "size" : "58x58", "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { - "size" : "87x87", "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { - "size" : "40x40", + "filename" : "Icon-App-20x20@2x.png", "idiom" : "iphone", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" + "scale" : "2x", + "size" : "40x40" }, { - "size" : "80x80", "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" + "scale" : "3x", + "size" : "40x40" }, { - "size" : "120x120", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", "filename" : "", - "scale" : "2x" - }, - { - "size" : "60x60", "idiom" : "iphone", - "filename" : "", - "scale" : "3x" + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "76x76", "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" }, { - "size" : "152x152", "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "Icon-App-20x20@1x.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-40x40@1x.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-29x29@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "58x58" + }, + { + "filename" : "Icon-App-40x40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "80x80" + }, + { + "filename" : "Icon-App-29x29@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "87x87" + }, + { + "filename" : "Icon-App-40x40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "120x120" + }, + { "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "152x152" }, { - "size" : "167x167", - "idiom" : "ipad", "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "167x167" }, { - "size" : "1024x1024", - "idiom" : "ipad", "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "Icon-App-20x20@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60", + "unassigned" : true } ], "info" : { - "version" : 1, - "author" : "ios_icon_set" + "author" : "xcode", + "version" : 1 } -} +} diff --git a/lib/core/utils/image_constant.dart b/lib/core/utils/image_constant.dart index edafef3c..37113a30 100644 --- a/lib/core/utils/image_constant.dart +++ b/lib/core/utils/image_constant.dart @@ -13,4 +13,103 @@ class ImageConstant { static String imgFavorite = '$imagePath/img_favorite.svg'; static String imageNotFound = 'assets/images/image_not_found.png'; + + // Настройки images + + static String imgClose = '$imagePath/img_close.svg'; + + static String imgOverflowmenu = '$imagePath/img_overflowmenu.svg'; + + // Блог images + static String imgRectangle14 = '$imagePath/img_rectangle_14.png'; + + static String imgRectangle14220x341 = + '$imagePath/img_rectangle_14_220x341.png'; + + // Блог - Tab Container images + static String imgCrop = '$imagePath/img_crop.svg'; + + // Статья images + static String imgRectangle14220x375 = + '$imagePath/img_rectangle_14_220x375.png'; + + static String imgVector = '$imagePath/img_vector.svg'; + + static String imgEllipse132x32 = '$imagePath/img_ellipse_1_32x32.png'; + + static String imgLike6 = '$imagePath/img_like_6.svg'; + + static String imgLike5 = '$imagePath/img_like_5.svg'; + + static String imgEllipse11 = '$imagePath/img_ellipse_1_1.png'; + + static String imgCloseBlueGray900 = '$imagePath/img_close_blue_gray_900.svg'; + + static String imgSend1 = '$imagePath/img_send_1.svg'; + + // Личный кабинет One images + static String imgEdit = '$imagePath/img_edit.svg'; + + static String imgStatistic = '$imagePath/img_statistic.svg'; + + static String imgStatisticGray200 = '$imagePath/img_statistic_gray_200.svg'; + + // Личный кабинет Two images + static String imgMegaphone = '$imagePath/img_megaphone.svg'; + + // Common images + + static String imgEllipse1 = '$imagePath/img_ellipse_1.png'; + + static String imgGrid = '$imagePath/img_grid.svg'; + + static String imgSearch = '$imagePath/img_search.svg'; + + static String imgArea1 = '$imagePath/img_area_1.svg'; + + static String imgLine1 = '$imagePath/img_line_1.svg'; + + static String imgLine2 = '$imagePath/img_line_2.svg'; + + static String imgLine3 = '$imagePath/img_line_3.svg'; + + static String imgEllipse125x25 = '$imagePath/img_ellipse_1_25x25.png'; + + static String imgEllipse2 = '$imagePath/img_ellipse_2.png'; + + static String imgEllipse3 = '$imagePath/img_ellipse_3.png'; + + static String imgNav = '$imagePath/img_nav.svg'; + + static String imgNavBlueGray400 = '$imagePath/img_nav_blue_gray_400.svg'; + + static String imgNavBlueGray40024x23 = + '$imagePath/img_nav_blue_gray_400_24x23.svg'; + + static String imgNavPrimary = '$imagePath/img_nav_primary.svg'; + + static String imgNavBlueGray40024x25 = + '$imagePath/img_nav_blue_gray_400_24x25.svg'; + + static String imgEye = '$imagePath/img_eye.svg'; + + static String imgFavoriteWhiteA700 = '$imagePath/img_favorite_white_a700.svg'; + + static String imgEllipse134x34 = '$imagePath/img_ellipse_1_34x34.png'; + + static String img = '$imagePath/img_.png'; + + static String imgEllipse142x42 = '$imagePath/img_ellipse_1_42x42.png'; + + static String imgIconMoreHorizontal = + '$imagePath/img_icon_more_horizontal.svg'; + + static String imgRectangle14250x311 = + '$imagePath/img_rectangle_14_250x311.png'; + + static String imgIconMessageSquare = '$imagePath/img_icon_message_square.svg'; + + static String imgIconHeart = '$imagePath/img_icon_heart.svg'; + + static String imgIconShare2 = '$imagePath/img_icon_share_2.svg'; } diff --git a/lib/data/models/tokenModel/token_model.dart b/lib/data/models/tokenModel/token_model.dart new file mode 100644 index 00000000..17ab28a1 --- /dev/null +++ b/lib/data/models/tokenModel/token_model.dart @@ -0,0 +1,9 @@ +import 'package:hive/hive.dart'; + +@HiveType(typeId: 0) +class TokenModel { + @HiveField(0) + String token; + + TokenModel({required this.token}); +} diff --git a/lib/localization/ru/ru_translations.dart b/lib/localization/ru/ru_translations.dart index 3d328fe0..85794150 100644 --- a/lib/localization/ru/ru_translations.dart +++ b/lib/localization/ru/ru_translations.dart @@ -5,6 +5,7 @@ final Map ru = { "username": "Имя пользователя", "password": "Пароль", "password_confirmation": "Подтвердите пароль", + 'wrong_credentials': "Неверные данные пользователя", //name Screen "first_name": "Имя", @@ -22,6 +23,8 @@ final Map ru = { "waist_circumference": "Обхват талии", "initial_measurements": "Первичные замеры", + "male": "Мужской", + "female": "Женский", // weight Screen "weight_measurements": "Замер веса", "your_weight": "Ваш вес:", "height_measurements": "Замер роста", "your_height": "Ваш рост:", @@ -37,20 +40,20 @@ final Map ru = { // blood_pressure Screen "no": "Нет", - "do_you_have_hypertension_tendency": "Есть ли у вас склонность к гипертонии?", + "do_you_have_hypertension_tendency": "Есть ли у вас гипертония?", "your_blood_pressure": "Ваше артериальное давление?", "average_pressure": "Средние показатели давления:", "up_pressure": "Верхние показатели давления:", "down_pressure": "Нижние показатели давления:", - "do_you_have_hypotension_tendency": "Есть ли у вас склонность к гипотонии?", + "do_you_have_hypotension_tendency": "Есть ли у вас гипотония?", // favorite_sports Screen "your_favorite_sports": "Ваши спортивные увлечения?", // favorite_foods Screen - "favorite_foods": "Любимые продукты:", + "favorite_foods": "Любимые продукты", "favorite_unfavorite_foods": "Ваши любимые/нелюбимые продукты питания?", - "unfavorite_foods": "Нелюбимые продукты:", + "unfavorite_foods": "Нелюбимые продукты", // schedule Screen "your_schedule": "Ваш режим дня?", @@ -58,7 +61,7 @@ final Map ru = { "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:", // additional_information Screen - "additional_information": "Дополнительная информация:", + "additional_information": "Дополнительная информация", "enter_additional_information": "Введите любую дополнительную информацию на своё усмотрение:", @@ -99,4 +102,35 @@ final Map ru = { // Body Circumference "pressure_empty": "Все показатели давления обязательны к заполнению", "pressure_is_not_a_number": "Все показатели давления должны быть числами", + + // Authorization + "no_account": "У меня нет аккаунта", + "authentificate": "Войти", + "user_authorization": "Аутентификация", + + // Личный кабинет Screen + "loading": "Загрузка...", + "weight_chart": "График веса", + "circumferences_chart": "График обхватов", + "city_of_living": "Город проживания", + "bodymass_chart": "График ИМТ", + "chest": "Грудь", + "waist": "Талия", + "hips": "Бедра", + "exit": "Выйти", + "active": "Активен", + "archived": "В архиве", + "draft": "Черновик", + "my_blog": "Мой блог", + "language": "Язык:", + "in_progress": "В процессе", + "add_measure": "Добавить замер", + "weight": "Вес", + "plans_complete": "Пройденные планы", + "my_settings": "Мои настройки", + "profile": "Профиль", + "chat": "Чат", + "admin": "Админ", + "diary": "Дневник", + "save": "Сохранить", }; diff --git a/lib/main.dart b/lib/main.dart index 5db43df0..7b5326dc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,18 +2,22 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'core/app_export.dart'; +import 'package:hive_flutter/hive_flutter.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; var globalMessengerKey = GlobalKey(); -void main() { +void main() async { WidgetsFlutterBinding.ensureInitialized(); - Future.wait([ - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - ]), - PrefUtils().init() - ]).then((value) { - runApp(MyApp()); - }); + final appDocumentDir = await getApplicationDocumentsDirectory(); + final path = appDocumentDir.path; + await dotenv.load(fileName: ".env"); + await Hive.initFlutter(path); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + ]); + await PrefUtils().init(); + runApp(MyApp()); } class MyApp extends StatelessWidget { @@ -29,7 +33,7 @@ class MyApp extends StatelessWidget { builder: (context, state) { return MaterialApp( theme: theme, - title: 'eatvio_app', + title: 'Eatvio', navigatorKey: NavigatorService.navigatorKey, debugShowCheckedModeBanner: false, localizationsDelegates: [ diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart new file mode 100644 index 00000000..15f2d909 --- /dev/null +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -0,0 +1,174 @@ +import 'bloc/auth_bloc.dart'; +import 'models/auth_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart'; +import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; +import 'package:validators/validators.dart'; +import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'dart:convert'; +import 'package:hive/hive.dart'; +import 'package:http/http.dart' as http; + +class AuthScreen extends StatelessWidget { + static Widget builder(BuildContext context) { + return BlocProvider( + create: (context) => AuthBloc( + AuthState( + authModelObj: AuthModel(), + ), + )..add(AuthInitialEvent()), + child: AuthScreen(), + ); + } + + @override + Widget build(BuildContext context) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: _buildAppBar(context), + body: Container( + width: double.maxFinite, + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), + child: Column(children: [ + SizedBox(height: 39.v), + Text("user_authorization".tr, + style: theme.textTheme.headlineSmall), + SizedBox(height: 60.v), + Text("email".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.emailController, + builder: (context, emailController) { + return CustomTextFormField( + controller: emailController, + textInputAction: TextInputAction.done); + }), + SizedBox(height: 22.v), + Text("password".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 5.v), + BlocSelector( + selector: (state) => state.passwordController, + builder: (context, passwordController) { + return CustomTextFormField( + obscureText: true, + controller: passwordController, + ); + }), + Spacer(), + NextStepButton( + text: "no_account".tr.toUpperCase(), + margin: + EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: () { + Navigator.pushNamed( + context, AppRoutes.languageSelectScreen); + }), + 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'), + ), + ], + ), + ); + } + }); + }), + ]))); + } + + /// Section Widget + PreferredSizeWidget _buildAppBar(BuildContext context) { + return CustomAppBar( + leadingWidth: 29.h, + centerTitle: true, + title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); + } + + List validateFields(String email, String password) { + List errors = []; + + if (!isEmail(email)) { + errors.add('email_invalid'.tr); + } + + if (email.isEmpty) { + errors.add('email_empty'.tr); + } + + if (password.isEmpty) { + errors.add('password_empty'.tr); + } + + if (password.length < 6) { + errors.add('password_short'.tr); + } + + return errors; + } +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart b/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart new file mode 100644 index 00000000..cd0203ac --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import '/core/app_export.dart'; +import 'package:eatvio_app/presentation/auth/auth_screen/models/auth_model.dart'; +part 'auth_event.dart'; +part 'auth_state.dart'; + +/// A bloc that manages the state of a Auth according to the event that is dispatched to it. +class AuthBloc extends Bloc { + AuthBloc(AuthState initialState) : super(initialState) { + on(_onInitialize); + } + + _onInitialize( + AuthInitialEvent event, + Emitter emit, + ) async { + emit(state.copyWith( + emailController: TextEditingController(), + passwordController: TextEditingController())); + } +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_event.dart b/lib/presentation/auth/auth_screen/bloc/auth_event.dart new file mode 100644 index 00000000..a395a45b --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_event.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +part of 'auth_bloc.dart'; + +/// Abstract class for all events that can be dispatched from the +///Credentials widget. +/// +/// Events must be immutable and implement the [Equatable] interface. +@immutable +abstract class AuthEvent extends Equatable {} + +/// Event that is dispatched when the Credentials widget is first created. +class AuthInitialEvent extends AuthEvent { + @override + List get props => []; +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_state.dart b/lib/presentation/auth/auth_screen/bloc/auth_state.dart new file mode 100644 index 00000000..504180f0 --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_state.dart @@ -0,0 +1,36 @@ +// ignore_for_file: must_be_immutable + +part of 'auth_bloc.dart'; + +/// Represents the state of Auth in the application. +class AuthState extends Equatable { + AuthState({ + this.emailController, + this.passwordController, + this.authModelObj, + }); + + TextEditingController? emailController; + + TextEditingController? passwordController; + + AuthModel? authModelObj; + + @override + List get props => [ + emailController, + passwordController, + authModelObj, + ]; + AuthState copyWith({ + TextEditingController? emailController, + TextEditingController? passwordController, + AuthModel? authModelObj, + }) { + return AuthState( + emailController: emailController ?? this.emailController, + passwordController: passwordController ?? this.passwordController, + authModelObj: authModelObj ?? this.authModelObj, + ); + } +} diff --git a/lib/presentation/auth/auth_screen/models/auth_model.dart b/lib/presentation/auth/auth_screen/models/auth_model.dart new file mode 100644 index 00000000..829b38c8 --- /dev/null +++ b/lib/presentation/auth/auth_screen/models/auth_model.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +import 'package:equatable/equatable.dart'; + +/// This class defines the variables used in the [auth_screen], +/// and is typically used to hold data that is passed between different parts of the application. +class AuthModel extends Equatable { + AuthModel(); + + AuthModel copyWith() { + return AuthModel(); + } + + @override + List get props => []; +} diff --git a/lib/presentation/credentials_screen/credentials_screen.dart b/lib/presentation/credentials_screen/credentials_screen.dart deleted file mode 100644 index 6ab0eb61..00000000 --- a/lib/presentation/credentials_screen/credentials_screen.dart +++ /dev/null @@ -1,200 +0,0 @@ -import 'bloc/credentials_bloc.dart'; -import 'models/credentials_model.dart'; -import 'package:eatvio_app/core/app_export.dart'; -import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart'; -import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; -import 'package:validators/validators.dart'; -import 'package:eatvio_app/widgets/next_step_button.dart'; -import 'package:eatvio_app/data/models/userRegistrationModel/user_registration_model.dart'; -import 'package:eatvio_app/widgets/custom_text_form_field.dart'; -import 'package:flutter/material.dart'; - -class CredentialsScreen extends StatelessWidget { - static Widget builder(BuildContext context) { - final sheet = - ModalRoute.of(context)!.settings.arguments as UserRegistrationSheet; - return BlocProvider( - create: (context) => CredentialsBloc( - CredentialsState( - credentialsModelObj: CredentialsModel(userRegistrationSheet: sheet), - ), - )..add(CredentialsInitialEvent()), - child: CredentialsScreen(), - ); - } - - @override - Widget build(BuildContext context) { - mediaQueryData = MediaQuery.of(context); - return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: false, - appBar: _buildAppBar(context), - body: Container( - width: double.maxFinite, - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), - child: Column(children: [ - SizedBox(height: 7.v), - 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.05, - backgroundColor: theme.colorScheme.onPrimary, - valueColor: AlwaysStoppedAnimation( - appTheme.blueA400)))), - SizedBox(height: 39.v), - Text("user_registration".tr, - style: theme.textTheme.headlineSmall), - SizedBox(height: 93.v), - Text("email".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 3.v), - BlocSelector( - selector: (state) => state.emailController, - builder: (context, emailController) { - return CustomTextFormField( - controller: emailController, - textInputAction: TextInputAction.done); - }), - SizedBox(height: 22.v), - Text("username".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 3.v), - BlocSelector( - selector: (state) => state.nameController, - builder: (context, nameController) { - return CustomTextFormField( - controller: nameController, - textInputAction: TextInputAction.done); - }), - SizedBox(height: 19.v), - Text("password".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - BlocSelector( - selector: (state) => state.passwordController, - builder: (context, passwordController) { - return CustomTextFormField( - obscureText: true, - controller: passwordController, - ); - }), - SizedBox(height: 21.v), - Text("password_confirmation".tr, - style: theme.textTheme.bodyLarge), - SizedBox(height: 3.v), - BlocSelector( - selector: (state) => state.passwordConfirmationController, - builder: (context, passwordConfirmationController) { - return CustomTextFormField( - obscureText: true, - controller: passwordConfirmationController, - textInputAction: TextInputAction.done); - }), - Spacer(), - BlocBuilder( - builder: (context, state) { - return NextStepButton( - text: "continue".tr.toUpperCase(), - 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'), - ), - ], - ), - ); - } - }); - }), - ])))); - } - - /// Section Widget - PreferredSizeWidget _buildAppBar(BuildContext context) { - return CustomAppBar( - leadingWidth: 29.h, - centerTitle: true, - title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); - } - - List validateFields( - String email, String name, String password, String passwordConfirmation) { - List errors = []; - - if (!isEmail(email)) { - errors.add('email_invalid'.tr); - } - - if (email.isEmpty) { - errors.add('email_empty'.tr); - } - - if (name.length < 6) { - errors.add('name_short'.tr); - } - - if (name.isEmpty) { - errors.add('name_empty'.tr); - } - - if (password.isEmpty) { - errors.add('name_empty'.tr); - } - - if (password.length < 6) { - errors.add('password_short'.tr); - } - - if (password != passwordConfirmation) { - errors.add('password_confirmation_mismatch'.tr); - } - - return errors; - } -} diff --git a/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart b/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart new file mode 100644 index 00000000..03ae81b8 --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart @@ -0,0 +1,246 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import '/core/app_export.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/models/my_profile_model.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:hive/hive.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +part 'my_profile_event.dart'; +part 'my_profile_state.dart'; + +/// A bloc that manages the state of a MyProfile according to the event that is dispatched to it. +class MyProfileBloc extends Bloc { + MyProfileBloc(MyProfileState initialState) : super(initialState) { + on(_onInitialize); + } + + Future _onInitialize( + MyProfileInitialEvent event, + Emitter emit, + ) async { + try { + final box = await Hive.openBox('token'); + final token = box.get('token'); + final response = await http.get( + Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/index'), + headers: { + 'Authorization': 'Bearer $token', + }, + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + // Проверка наличия ключевых полей в JSON + if (data is Map && + data.containsKey('user') && + data.containsKey('userDetails') && + data.containsKey('chartPoints') && + data.containsKey('circumferences')) { + // Все ключевые поля присутствуют, продолжаем обработку + if (state is MyProfileLoadedState) { + // Если текущее состояние уже загружено, обновляем его + var currentState = state as MyProfileLoadedState; + var plans = [] + ..addAll(data["finishedPlans"]) + ..addAll(data["rejectedPlans"]) + ..addAll(data["startedPlan"]) + ..addAll(data["paidPlan"]) + ..addAll(data["requestedPlan"]) + ..addAll(data["initedPlan"]); + + // Обновляем значения контроллеров только если они уже были созданы + + emit((currentState).copyWith( + chartPoints: currentState.chartPoints, + chartDates: currentState.chartDates, + circumferences: currentState.circumferences, + user: data['user'], + userDetails: data['userDetails'], + plans: plans, + token: token, + chestCircumferenceController: TextEditingController(), + waistCircumferenceController: TextEditingController(), + hipCircumferenceController: TextEditingController(), + weightController: TextEditingController(), + chestCircumferenceFormController: + currentState.chestCircumferenceFormController, + waistCircumferenceFormController: + currentState.waistCircumferenceFormController, + hipCircumferenceFormController: + currentState.hipCircumferenceFormController, + firstNameFormController: currentState.firstNameFormController, + lastNameFormController: currentState.lastNameFormController, + countryFormController: currentState.countryFormController, + cityFormController: currentState.cityFormController, + heightFormController: currentState.heightFormController, + upPressureFormController: currentState.upPressureFormController, + downPressureFormController: + currentState.downPressureFormController, + allergiesFormController: currentState.allergiesFormController, + pillsFormController: currentState.pillsFormController, + favoriteFormController: currentState.favoriteFormController, + unfavoriteFormController: currentState.unfavoriteFormController, + additionalFormController: currentState.additionalFormController, + sportsFormController: currentState.sportsFormController, + dateFormController: currentState.dateFormController, + hypoFormController: currentState.hypoFormController, + hyperFormController: currentState.hyperFormController, + selectedCity: currentState.selectedCity, + selectedCountry: currentState.selectedCountry, + )); + } else { + var plans = [] + ..addAll(data["finishedPlans"]) + ..addAll(data["rejectedPlans"]) + ..addAll(data["startedPlan"]) + ..addAll(data["paidPlan"]) + ..addAll(data["requestedPlan"]) + ..addAll(data["initedPlan"]); + + /*print(data["chartPoints"].length); + print(data["chartPoints"].runtimeType); + print(data["circumferences"].length); + print(data["circumferences"].runtimeType);*/ + if (data["chartPoints"].length == 1) { + data["chartPoints"] = []; + } + if (data["circumferences"].length == 1) { + data["circumferences"] = []; + } + var circumferences = data["circumferences"].isEmpty + ? data["circumferences"].asMap().map((index, value) { + return MapEntry(index.toString(), value); + }) + : data["circumferences"]; + var chartPoints = data["chartPoints"].isEmpty + ? data["chartPoints"].asMap().map((index, value) { + return MapEntry(index.toString(), value); + }) + : data["chartPoints"]; + + /*List weights = [0.0, 0.0, 0.0, 0.0]; + List dates = [ + "0000-00-00 00:00:00", + "0000-00-00 00:00:00", + "0000-00-00 00:00:00", + "0000-00-00 00:00:00" + ]; + for (int i = 0; i < data["chartPoints"].length; i++) { + weights[i] = data["chartPoints"][i]["weight"].toDouble(); + dates[i] = data["chartPoints"][i]["created_at"].toString(); + } + print(data);*/ + List dates = [ + "0000-00-00 00:00:00", + "0000-00-00 00:00:00", + "0000-00-00 00:00:00", + "0000-00-00 00:00:00" + ]; + for (int i = 0; i < data["circumferences"].length; i++) { + dates[i] = data["circumferences"]['$i']["created_at"]; + } + if (chartPoints.length < 4) { + for (int i = chartPoints.length; i < 4; i++) { + chartPoints["$i"] = { + "id": 0, + "user_id": 0, + "weight": 0, + "created_at": "0000-00-00 00:00:00", + "updated_at": "0000-00-00 00:00:00" + }; + } + } + + if (circumferences.length < 4) { + for (int i = circumferences.length; i < 4; i++) { + circumferences["$i"] = { + "id": 0, + "user_id": 0, + "chest_circumference": 0, + "waist_circumference": 0, + "hip_circumference": 0, + "created_at": "0000-00-00 00:00:00", + "updated_at": "0000-00-00 00:00:00" + }; + } + } + + /*print(chartPoints); + print(circumferences);*/ + /*print(weights); + print(weights.runtimeType); + print(circumferences); + print(circumferences.runtimeType);*/ + // Если текущее состояние - начальное, создаем новое загруженное состояние + emit(MyProfileLoadedState( + chartPoints: chartPoints, + chartDates: dates, + circumferences: circumferences, + user: data['user'], + userDetails: data['userDetails'], + plans: plans, + token: token, + chestCircumferenceController: TextEditingController(), + waistCircumferenceController: TextEditingController(), + hipCircumferenceController: TextEditingController(), + hypoFormController: data['userDetails']['has_hypo'].toString(), + hyperFormController: data['userDetails']['has_hyper'].toString(), + chestCircumferenceFormController: TextEditingController( + text: data['userDetails']['chest_circumference']?.toString()), + waistCircumferenceFormController: TextEditingController( + text: data['userDetails']['waist_circumference']?.toString()), + hipCircumferenceFormController: TextEditingController( + text: data['userDetails']['hip_circumference']?.toString()), + weightController: TextEditingController( + text: data['userDetails']['weight']?.toString()), + weightFormController: TextEditingController( + text: data['userDetails']['weight']?.toString()), + firstNameFormController: TextEditingController( + text: data['userDetails']['first_name']), + lastNameFormController: + TextEditingController(text: data['userDetails']['last_name']), + countryFormController: + TextEditingController(text: data['userDetails']['country']), + cityFormController: + TextEditingController(text: data['userDetails']['city']), + heightFormController: TextEditingController( + text: data['userDetails']['height']?.toString()), + upPressureFormController: TextEditingController( + text: data['userDetails']['blood_pressure_up']?.toString()), + downPressureFormController: TextEditingController( + text: data['userDetails']['blood_pressure_down']?.toString()), + allergiesFormController: TextEditingController( + text: data['userDetails']['about_alergy']), + pillsFormController: TextEditingController( + text: data['userDetails']['about_taking_pils']), + sportsFormController: TextEditingController( + text: data['userDetails']['about_sport_hobbies']), + favoriteFormController: TextEditingController( + text: data['userDetails']['favorite_foods']), + unfavoriteFormController: TextEditingController( + text: data['userDetails']['unfavorite_foods']), + additionalFormController: TextEditingController( + text: data['userDetails']['additional_info']), + dateFormController: + TextEditingController(text: data['userDetails']['birthdate']), + selectedCity: data['userDetails']['city'], + selectedCountry: data['userDetails']['country'], + )); + } + } else { + // Один или несколько ключевых полей отсутствуют, обрабатываем ошибку + emit( + MyProfileErrorState('Некоторые необходимые данные отсутствуют.')); + } + } else { + // Статус ответа не равен 200, обрабатываем ошибку + emit(MyProfileErrorState('Ошибка сервера: ${response.statusCode}')); + } + } catch (e) { + // Ошибка при декодировании JSON или другая ошибка выполнения + emit(MyProfileErrorState('Ошибка при получении данных профиля: $e')); + } + } +} diff --git a/lib/presentation/profile/my_profile_screen/bloc/my_profile_event.dart b/lib/presentation/profile/my_profile_screen/bloc/my_profile_event.dart new file mode 100644 index 00000000..99c025a4 --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/bloc/my_profile_event.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +part of 'my_profile_bloc.dart'; + +/// Abstract class for all events that can be dispatched from the +///MyProfile widget. +/// +/// Events must be immutable and implement the [Equatable] interface. +@immutable +abstract class MyProfileEvent extends Equatable {} + +/// Event that is dispatched when the MyProfile widget is first created. +class MyProfileInitialEvent extends MyProfileEvent { + @override + List get props => []; +} diff --git a/lib/presentation/profile/my_profile_screen/bloc/my_profile_state.dart b/lib/presentation/profile/my_profile_screen/bloc/my_profile_state.dart new file mode 100644 index 00000000..5350abe5 --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/bloc/my_profile_state.dart @@ -0,0 +1,225 @@ +// ignore_for_file: must_be_immutable + +part of 'my_profile_bloc.dart'; + +class MyProfileState extends Equatable { + const MyProfileState({this.myProfileModelObj}); + + final MyProfileModel? myProfileModelObj; + + @override + List get props => [myProfileModelObj]; +} + +class MyProfileInitialState extends MyProfileState { + MyProfileInitialState({required MyProfileModel myProfileModelObj}) + : super(myProfileModelObj: myProfileModelObj); +} + +/// Represents the state of MyProfile in the application. +class MyProfileLoadedState extends MyProfileState { + MyProfileLoadedState( + {this.chestCircumferenceController, + this.waistCircumferenceController, + this.hipCircumferenceController, + this.myProfileModelObj, + this.weightController, + this.user, + this.userDetails, + this.chartPoints, + this.chartDates, + this.plans, + this.token, + this.selectedCountry, + this.selectedCity, + this.chestCircumferenceFormController, + this.waistCircumferenceFormController, + this.hipCircumferenceFormController, + this.firstNameFormController, + this.lastNameFormController, + this.countryFormController, + this.dateFormController, + this.cityFormController, + this.heightFormController, + this.weightFormController, + this.upPressureFormController, + this.downPressureFormController, + this.allergiesFormController, + this.pillsFormController, + this.sportsFormController, + this.favoriteFormController, + this.unfavoriteFormController, + this.additionalFormController, + this.hypoFormController, + this.hyperFormController, + this.circumferences}) + : super(myProfileModelObj: null); + + MyProfileModel? myProfileModelObj; + Map? user; + Map? userDetails; + Map? circumferences; + Map? chartPoints; + List? chartDates; + List? plans; + TextEditingController? weightController; + TextEditingController? chestCircumferenceController; + TextEditingController? waistCircumferenceController; + TextEditingController? hipCircumferenceController; + TextEditingController? weightFormController; + TextEditingController? chestCircumferenceFormController; + TextEditingController? waistCircumferenceFormController; + TextEditingController? hipCircumferenceFormController; + TextEditingController? firstNameFormController; + TextEditingController? lastNameFormController; + TextEditingController? countryFormController; + TextEditingController? dateFormController; + TextEditingController? cityFormController; + TextEditingController? heightFormController; + TextEditingController? upPressureFormController; + TextEditingController? downPressureFormController; + TextEditingController? allergiesFormController; + TextEditingController? pillsFormController; + TextEditingController? sportsFormController; + TextEditingController? favoriteFormController; + TextEditingController? unfavoriteFormController; + TextEditingController? additionalFormController; + String? token; + String? selectedCountry; + String? selectedCity; + String? hypoFormController; + String? hyperFormController; + @override + List get props => [ + myProfileModelObj, + user, + chartPoints, + chartDates, + circumferences, + plans, + weightController, + chestCircumferenceController, + waistCircumferenceController, + hipCircumferenceController, + token, + chestCircumferenceFormController, + waistCircumferenceFormController, + hipCircumferenceFormController, + firstNameFormController, + lastNameFormController, + countryFormController, + dateFormController, + cityFormController, + heightFormController, + weightFormController, + upPressureFormController, + downPressureFormController, + allergiesFormController, + pillsFormController, + sportsFormController, + favoriteFormController, + unfavoriteFormController, + additionalFormController, + selectedCountry, + selectedCity, + hypoFormController, + hyperFormController, + ]; + MyProfileLoadedState copyWith({ + MyProfileModel? myProfileModelObj, + Map? user, + Map? userDetails, + List? plans, + Map? chartPoints, + List? chartDates, + Map? circumferences, + TextEditingController? weightController, + TextEditingController? chestCircumferenceController, + TextEditingController? waistCircumferenceController, + TextEditingController? hipCircumferenceController, + TextEditingController? chestCircumferenceFormController, + TextEditingController? waistCircumferenceFormController, + TextEditingController? hipCircumferenceFormController, + TextEditingController? firstNameFormController, + TextEditingController? lastNameFormController, + TextEditingController? countryFormController, + TextEditingController? dateFormController, + TextEditingController? cityFormController, + TextEditingController? heightFormController, + TextEditingController? weightFormeFormController, + TextEditingController? allergiesFormController, + TextEditingController? pillsFormController, + TextEditingController? sportsFormController, + TextEditingController? favoriteFormController, + TextEditingController? unfavoriteFormController, + TextEditingController? additionalFormController, + TextEditingController? downPressureFormController, + TextEditingController? upPressureFormController, + String? token, + String? selectedCountry, + String? selectedCity, + String? hypoFormController, + String? hyperFormController, + }) { + return MyProfileLoadedState( + myProfileModelObj: myProfileModelObj ?? this.myProfileModelObj, + user: user ?? this.user, + plans: plans ?? this.plans, + userDetails: userDetails ?? this.userDetails, + circumferences: circumferences ?? this.circumferences, + chartPoints: chartPoints ?? this.chartPoints, + chartDates: chartDates ?? this.chartDates, + weightController: weightController ?? this.weightController, + chestCircumferenceController: + chestCircumferenceController ?? this.chestCircumferenceController, + waistCircumferenceController: + waistCircumferenceController ?? this.waistCircumferenceController, + hipCircumferenceController: + hipCircumferenceController ?? this.hipCircumferenceController, + chestCircumferenceFormController: chestCircumferenceFormController ?? + this.chestCircumferenceFormController, + waistCircumferenceFormController: waistCircumferenceFormController ?? + this.waistCircumferenceFormController, + hipCircumferenceFormController: + hipCircumferenceFormController ?? this.hipCircumferenceFormController, + firstNameFormController: + firstNameFormController ?? this.firstNameFormController, + lastNameFormController: + lastNameFormController ?? this.lastNameFormController, + countryFormController: + countryFormController ?? this.countryFormController, + dateFormController: dateFormController ?? this.dateFormController, + cityFormController: cityFormController ?? this.cityFormController, + heightFormController: heightFormController ?? this.heightFormController, + weightFormController: weightFormController ?? this.weightFormController, + upPressureFormController: + upPressureFormController ?? this.upPressureFormController, + downPressureFormController: + downPressureFormController ?? this.downPressureFormController, + allergiesFormController: + allergiesFormController ?? this.allergiesFormController, + pillsFormController: pillsFormController ?? this.pillsFormController, + sportsFormController: sportsFormController ?? this.sportsFormController, + favoriteFormController: + favoriteFormController ?? this.favoriteFormController, + unfavoriteFormController: + unfavoriteFormController ?? this.unfavoriteFormController, + additionalFormController: + additionalFormController ?? this.additionalFormController, + token: token ?? this.token, + selectedCountry: selectedCountry ?? this.selectedCountry, + selectedCity: selectedCity ?? this.selectedCity, + hypoFormController: hypoFormController ?? this.hypoFormController, + hyperFormController: hyperFormController ?? this.hyperFormController, + ); + } +} + +class MyProfileErrorState extends MyProfileState { + final String errorMessage; + + const MyProfileErrorState(this.errorMessage); + + @override + List get props => [errorMessage]; +} diff --git a/lib/presentation/profile/my_profile_screen/models/my_profile_model.dart b/lib/presentation/profile/my_profile_screen/models/my_profile_model.dart new file mode 100644 index 00000000..ae803e19 --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/models/my_profile_model.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +import 'package:equatable/equatable.dart'; + +/// This class defines the variables used in the [k0_page], +/// and is typically used to hold data that is passed between different parts of the application. +class MyProfileModel extends Equatable { + MyProfileModel(); + + MyProfileModel copyWith() { + return MyProfileModel(); + } + + @override + List get props => []; +} diff --git a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart new file mode 100644 index 00000000..3b65469e --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart @@ -0,0 +1,966 @@ +import 'dart:ffi'; + +import 'bloc/my_profile_bloc.dart'; +import 'models/my_profile_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart'; +import 'package:eatvio_app/widgets/app_bar/appbar_title_circleimage.dart'; +import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; +import 'package:eatvio_app/widgets/custom_bottom_bar.dart'; +import 'package:eatvio_app/widgets/app_bar/exit_button.dart'; +import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:hive/hive.dart'; +import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; +import 'package:fl_chart/fl_chart.dart'; +import 'package:intl/intl.dart'; +import 'dart:math'; +import 'dart:convert'; + +class MyProfileScreen extends StatelessWidget { + static Widget builder(BuildContext context) { + return BlocProvider( + create: (context) => + MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel())) + ..add(MyProfileInitialEvent()), + child: MyProfileScreen()); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is MyProfileInitialState) { + // Это начальное состояние, данные ещё не загружены. + return CircularProgressIndicator( + color: Colors.white, + backgroundColor: Colors.white, + ); + } else if (state is MyProfileLoadedState) { + // Данные загружены, можно строить экран. + return _buildMyProfileScreen(context, state); + } else if (state is MyProfileErrorState) { + // Произошла ошибка при загрузке данных. + return Center(child: Text(state.errorMessage)); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, + ); + } + + Widget _buildMyProfileScreen( + BuildContext context, MyProfileLoadedState state) { + mediaQueryData = MediaQuery.of(context); + return SafeArea( + child: Scaffold( + appBar: _buildAppBar( + context, + ((state.userDetails?['first_name'] ?? '') + + ' ' + + (state.userDetails?['last_name'] ?? '')) ?? + 'loading'.tr), + body: Container( + width: double.maxFinite, + padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 18.v), + child: Column(children: [ + _buildMyBlog(context), + SizedBox(height: 15.v), + _buildSettings(context), + SizedBox(height: 30.v), + _buildCarousel(context, state), + SizedBox(height: 15.v), + _buildPlans( + context, + state.plans?.map((plan) => plan['name']).toList(), + state.plans?.map((plan) => plan['image']).toList(), + state.plans?.map((plan) => plan['state']).toList(), + ), + SizedBox(height: 5.v), + ])), + bottomNavigationBar: _buildBottomBar(context))); + } + + /// Section Widget + PreferredSizeWidget _buildAppBar(BuildContext context, String username) { + return CustomAppBar( + height: 54.v, + leadingWidth: 28.h, + title: Padding( + padding: EdgeInsets.only(left: 20.h), + child: Row(children: [ + AppbarTitleCircleimage(imagePath: ImageConstant.imgEllipse1), + AppbarSubtitle( + text: username, + margin: EdgeInsets.only(left: 35.h, top: 12.v, bottom: 9.v)) + ])), + actions: [ + 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, + ); + } + }, + ) + ]); + } + + /// Section Widget + Widget _buildMyBlog(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 7.h), + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + CustomImageView( + imagePath: ImageConstant.imgGrid, + height: 24.adaptSize, + width: 24.adaptSize, + margin: EdgeInsets.symmetric(vertical: 3.v)), + Expanded( + child: NextStepButton( + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + text: "my_blog".tr.toUpperCase(), + margin: EdgeInsets.only(left: 10.h))) + ])); + } + + /// Section Widget + Widget _buildSettings(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 7.h), + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + CustomImageView( + imagePath: ImageConstant.imgSearch, + height: 24.adaptSize, + width: 24.adaptSize, + margin: EdgeInsets.symmetric(vertical: 3.v)), + Expanded( + child: NextStepButton( + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: () { + Navigator.pushNamed(context, AppRoutes.mySettings); + }, + text: "my_settings".tr.toUpperCase(), + margin: EdgeInsets.only(left: 10.h))), + ])); + } + + Widget _buildBottomBar(BuildContext context) { + return CustomBottomBar(onChanged: (BottomBarEnum type) {}); + } + + Widget _buildCarousel(BuildContext contex, MyProfileLoadedState state) { + return CarouselSlider( + options: CarouselOptions(height: 340.0), + items: [1, 2, 3].map((chartType) { + return Builder( + builder: (BuildContext context) { + switch (chartType) { + case 1: // График веса + return GestureDetector( + onTap: () { + _editWeight(context, state); + }, + child: Column(children: [ + Text( + "weight_chart".tr, + style: CustomTextStyles.titleMediumBlack.copyWith( + color: appTheme.black900, + ), + ), + SizedBox(height: 50.v), + _buildChart( + context, + state.chartPoints?.values + .map((e) => e['weight']) + .toList() + .map((i) => i.toDouble()) + .toList() ?? + [0.0, 0.0, 0.0, 0.0], + state.chartDates, + ) + ])); + case 2: // График ИМТ + return Column(children: [ + Text( + "bodymass_chart".tr, + style: CustomTextStyles.titleMediumBlack.copyWith( + color: appTheme.black900, + ), + ), + SizedBox(height: 50.v), + _buildChart( + context, + _bodyMassIndex( + state.chartPoints?.values + .map((e) => e['weight']) + .toList() + .map((i) => i.toDouble()) + .toList() ?? + [0.0, 0.0, 0.0, 0.0], + state.userDetails?['height'].toInt() ?? 160), + state.chartDates), + ]); + case 3: //График обхватов + return GestureDetector( + onTap: () { + _editCircumference(context, state); + }, + child: Column(children: [ + Text( + "circumferences_chart".tr, + style: CustomTextStyles.titleMediumBlack.copyWith( + color: appTheme.black900, + ), + ), + SizedBox(height: 30.v), + _buildCircumferenceChart( + context, state.circumferences, state.chartDates), + _buildChartLegend() + ])); + default: + return Text('text', style: TextStyle(fontSize: 16.0)); + } + }, + ); + }).toList(), + ); + } + + Widget _buildChartLegend() { + final colors = [Colors.pink, Colors.green, Colors.purple]; + final titles = ["chest".tr, "waist".tr, "hips".tr]; + final style = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + for (var i = 0; i < colors.length; i++) + Container( + child: Row( + children: [ + Container( + width: 16, + height: 16, + color: colors[i], + ), + SizedBox(width: 8), + Text(titles[i], style: style), + ], + ), + ), + ], + ), + ], + ); + } + + /// Section Widget + Widget _buildChart( + BuildContext context, List chartPoints, List? dates) { +// Определение осей графика + List spots = []; + for (int i = 0; i < 4; i++) { + spots.add( + FlSpot((i * 10 / 10) + 1, chartPoints[i]), + ); + } + + Widget bottomTitleWidgets( + double value, TitleMeta meta, double chartWidth, List? dates) { + if (value % 1 != 0) { + return Container(); + } + final style = TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: min(13, 13 * chartWidth / 300), + ); + int index = value.toInt(); + if (index >= 0 && index < dates!.length) { + // Проверка на допустимый индекс в массиве dates + String formattedDate = + DateFormat('dd.MM').format(DateTime.parse(dates[index])); + return SideTitleWidget( + axisSide: meta.axisSide, + space: 16, + child: Text(formattedDate, style: style), + ); + } else { + return Container(); + } + } + + Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) { + final style = TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: min(18, 18 * chartWidth / 300), + ); + return SideTitleWidget( + axisSide: meta.axisSide, + space: 16, + child: Text(meta.formattedValue, style: style), + ); + } + + return Padding( + padding: const EdgeInsets.only( + left: 12, + right: 20, + ), + child: AspectRatio( + aspectRatio: 1, + child: LayoutBuilder( + builder: (context, constraints) { + return LineChart( + LineChartData( + lineTouchData: LineTouchData( + 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(); + }, + ), + handleBuiltInTouches: true, + getTouchLineStart: (data, index) => 0, + ), + lineBarsData: [ + LineChartBarData( + color: Colors.pink, + spots: spots, + isCurved: true, + isStrokeCapRound: true, + barWidth: 3, + belowBarData: BarAreaData( + show: false, + ), + dotData: const FlDotData(show: false), + ), + ], + minY: chartPoints.reduce( + (value, element) => value < element ? value : element), + maxY: chartPoints.reduce( + (value, element) => value > element ? value : element), + minX: 1, + maxX: 4, + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) => + leftTitleWidgets(value, meta, constraints.maxWidth), + reservedSize: 56, + ), + drawBelowEverything: true, + ), + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) => bottomTitleWidgets( + value, meta, constraints.maxWidth, dates), + reservedSize: 36, + interval: 1, + ), + drawBelowEverything: true, + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + ), + gridData: FlGridData( + show: true, + drawHorizontalLine: true, + drawVerticalLine: true, + horizontalInterval: 1.5, + verticalInterval: 5, + checkToShowHorizontalLine: (value) { + return value.toInt() == 0; + }, + getDrawingHorizontalLine: (_) => FlLine( + color: Colors.blue.withOpacity(1), + dashArray: [8, 2], + strokeWidth: 0.8, + ), + getDrawingVerticalLine: (_) => FlLine( + color: Colors.blue.withOpacity(1), + dashArray: [8, 2], + strokeWidth: 0.8, + ), + checkToShowVerticalLine: (value) { + return value.toInt() == 0; + }, + ), + borderData: FlBorderData(show: false), + ), + ); + }, + ), + ), + ); + } + + Widget _buildCircumferenceChart( + BuildContext context, chartPoints, List? dates) { +// Определение осей графика + final chestCircumferences = + chartPoints.values.map((e) => e['chest_circumference']).toList(); + List spotsChest = []; + for (int i = 0; i < 4; i++) { + chestCircumferences[i] = chestCircumferences[i] * 10 / 10; + spotsChest.add( + FlSpot((i * 10 / 10) + 1, chestCircumferences[i] * 10 / 10), + ); + } + final waistCircumferences = + chartPoints.values.map((e) => e['waist_circumference']).toList(); + List spotsWaist = []; + for (int i = 0; i < 4; i++) { + waistCircumferences[i] = waistCircumferences[i] * 10 / 10; + spotsWaist.add( + FlSpot((i * 10 / 10) + 1, waistCircumferences[i] * 10 / 10), + ); + } + final hipCircumferences = + chartPoints.values.map((e) => e['hip_circumference']).toList(); + List spotsHip = []; + for (int i = 0; i < 4; i++) { + hipCircumferences[i] = hipCircumferences[i] * 10 / 10; + spotsHip.add( + FlSpot((i * 10 / 10) + 1, hipCircumferences[i]), + ); + } + + final allCircumferences = [ + ...chestCircumferences, + ...hipCircumferences, + ...waistCircumferences + ]; + + Widget bottomTitleWidgets( + double value, TitleMeta meta, double chartWidth, List? dates) { + if (value % 1 != 0) { + return Container(); + } + final style = TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: min(13, 13 * chartWidth / 300), + ); + int index = value.toInt(); + if (index >= 0 && index < dates!.length) { + // Проверка на допустимый индекс в массиве dates + String formattedDate = + DateFormat('dd.MM').format(DateTime.parse(dates[index])); + return SideTitleWidget( + axisSide: meta.axisSide, + space: 16, + child: Text(formattedDate, style: style), + ); + } else { + return Container(); + } + } + + Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) { + final style = TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: min(18, 18 * chartWidth / 300), + ); + return SideTitleWidget( + axisSide: meta.axisSide, + space: 16, + child: Text(meta.formattedValue, style: style), + ); + } + + return Padding( + padding: const EdgeInsets.only( + left: 12, + right: 20, + ), + child: AspectRatio( + aspectRatio: 1, + child: LayoutBuilder( + builder: (context, constraints) { + return LineChart( + LineChartData( + lineTouchData: LineTouchData( + 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(); + }, + ), + handleBuiltInTouches: true, + getTouchLineStart: (data, index) => 0, + ), + lineBarsData: [ + LineChartBarData( + color: Colors.pink, + spots: spotsChest, + isCurved: true, + isStrokeCapRound: true, + barWidth: 3, + belowBarData: BarAreaData( + show: false, + ), + dotData: const FlDotData(show: false), + ), + LineChartBarData( + color: Colors.green, + spots: spotsWaist, + isCurved: true, + isStrokeCapRound: true, + barWidth: 3, + belowBarData: BarAreaData( + show: false, + ), + dotData: const FlDotData(show: false), + ), + LineChartBarData( + color: Colors.purple, + spots: spotsHip, + isCurved: true, + isStrokeCapRound: true, + barWidth: 3, + belowBarData: BarAreaData( + show: false, + ), + dotData: const FlDotData(show: false), + ), + ], + minY: allCircumferences.reduce( + (value, element) => value < element ? value : element), + maxY: allCircumferences.reduce( + (value, element) => value > element ? value : element), + minX: 1, + maxX: 4, + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) => + leftTitleWidgets(value, meta, constraints.maxWidth), + reservedSize: 56, + ), + drawBelowEverything: true, + ), + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) => bottomTitleWidgets( + value, meta, constraints.maxWidth, dates), + reservedSize: 36, + interval: 1, + ), + drawBelowEverything: true, + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + ), + gridData: FlGridData( + show: true, + drawHorizontalLine: true, + drawVerticalLine: true, + horizontalInterval: 1.5, + verticalInterval: 5, + checkToShowHorizontalLine: (value) { + return value.toInt() == 0; + }, + getDrawingHorizontalLine: (_) => FlLine( + color: Colors.blue.withOpacity(1), + dashArray: [8, 2], + strokeWidth: 0.8, + ), + getDrawingVerticalLine: (_) => FlLine( + color: Colors.blue.withOpacity(1), + dashArray: [8, 2], + strokeWidth: 0.8, + ), + checkToShowVerticalLine: (value) { + return value.toInt() == 0; + }, + ), + borderData: FlBorderData(show: false), + ), + ); + }, + ), + ), + ); + } + + /// Section Widget + Widget _buildPlans(BuildContext context, List? plans, + List? imagePaths, List? states) { + return Padding( + padding: EdgeInsets.only(left: 7.h), + child: Column(children: [ + Padding( + padding: EdgeInsets.only(left: 15.h), + child: Text("plans_complete".tr, + style: CustomTextStyles.titleMediumBlack + .copyWith(color: appTheme.black900))), + SizedBox(height: 5.v), + Container( + decoration: AppDecoration.outlineBlueGray + .copyWith(borderRadius: BorderRadiusStyle.roundedBorder5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 105.v, + width: 343.h, + child: + Stack(alignment: Alignment.bottomCenter, children: [ + Align( + alignment: Alignment.topCenter, + child: ListView.builder( + shrinkWrap: true, + itemCount: plans?.length ?? 0, + itemBuilder: (context, i) { + String state = states?[i]; + var boxColor = appTheme.greenA700; + switch (states?[i]) { + case 'active': + boxColor = appTheme.greenA700; + break; + case 'archived': // График ИМТ + boxColor = appTheme.gray400; + break; + case 'draft': + boxColor = appTheme.deepOrange900; + break; + case 'in_progress': //График обхватов + boxColor = appTheme.lightGreen300; + break; + } + return SizedBox( + height: 40.v, + width: 343.h, + child: Stack( + alignment: Alignment.bottomRight, + children: [ + Align( + alignment: + Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + bottom: 11.v), + padding: + EdgeInsets.symmetric( + horizontal: 6.h, + vertical: 4.v), + decoration: AppDecoration + .fillGray + .copyWith( + borderRadius: + BorderRadiusStyle + .roundedBorder5), + child: Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Padding( + padding: EdgeInsets + .only( + bottom: 1 + .v), + child: Text( + plans![i], + style: CustomTextStyles + .titleSmallBluegray800Bold)), + Container( + height: 19.v, + width: 100.h, + decoration: BoxDecoration( + color: + boxColor, + borderRadius: + BorderRadius.circular( + 5.h))) + ]))), + Align( + alignment: Alignment.center, + child: Container( + margin: EdgeInsets.only( + bottom: 12.h), + child: Image.network( + dotenv.env[ + 'STORAGE_URL']! + + imagePaths?[i], + height: 25.adaptSize, + width: 25.adaptSize, + ))), + Align( + alignment: + Alignment.bottomRight, + child: Container( + width: 70.h, + margin: EdgeInsets.only( + bottom: 14.h, + right: 22.h), + child: Text("$state".tr, + maxLines: 3, + overflow: TextOverflow + .ellipsis, + style: theme.textTheme + .titleSmall))) + ])); + })), + ])) + ])) + ])); + } + + List _bodyMassIndex(List weightPoints, int height) { + List bmiList = []; + for (double weight in weightPoints) { + double bmi = weight / ((height / 100) * (height / 100)); + bmiList.add(bmi); + } + return bmiList; + } + + _editCircumference(BuildContext context, MyProfileLoadedState state) { + showDialog( + context: context, + builder: (context) { + final digitsOnly = FilteringTextInputFormatter.digitsOnly; + return AlertDialog( + title: Center(child: Text("Добавить замер")), + content: SingleChildScrollView( + child: Column( + children: [ + 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, + 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 // Ограничение ввода только цифрами + ], + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(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"); + } + }, + child: Text("Изменить", + style: CustomTextStyles.titleMediumBlack.copyWith( + color: Colors.blue, + )), + ), + ], + backgroundColor: Colors.white, + titlePadding: EdgeInsets.all( + 20.0), // Можете настроить отступы по вашему желанию + ); + }, + ); + } + + _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 // Ограничение ввода только цифрами + ], + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(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"); + } + }, + child: Text("Изменить", + style: CustomTextStyles.titleMediumBlack.copyWith( + color: Colors.blue, + )), + ), + ], + backgroundColor: Colors.white, + titlePadding: EdgeInsets.all( + 20.0), // Можете настроить отступы по вашему желанию + ); + }, + ); + } +} diff --git a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart new file mode 100644 index 00000000..78201e77 --- /dev/null +++ b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart @@ -0,0 +1,667 @@ +import 'bloc/my_profile_bloc.dart'; +import 'models/my_profile_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +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: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) { + return BlocProvider( + create: (context) => + MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel())) + ..add(MyProfileInitialEvent()), + child: MySettingsScreen()); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is MyProfileInitialState) { + // Это начальное состояние, данные ещё не загружены. + return CircularProgressIndicator( + color: Colors.white, + backgroundColor: Colors.white, + ); + } else if (state is MyProfileLoadedState) { + // Данные загружены, можно строить экран. + return _buildSetingsScreen(context, state); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, + ); + } + + Widget _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) { + mediaQueryData = MediaQuery.of(context); + + return 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-н.в.']), + ], + ), + ), + ), + bottomNavigationBar: _buildBottomBar(context))); + } + + /// Section Widget + PreferredSizeWidget _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); + }), + title: Padding( + padding: EdgeInsets.only(left: 20.h), + child: Row(children: [ + AppbarSubtitle( + text: username, + margin: EdgeInsets.only(left: 80.h, top: 12.v, bottom: 9.v)) + ])), + ); + } + + Widget _buildBottomBar(BuildContext context) { + return CustomBottomBar(onChanged: (BottomBarEnum type) {}); + } + + onTapArrowLeft(BuildContext context) { + 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/additional_information_screen/additional_information_screen.dart b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart similarity index 91% rename from lib/presentation/additional_information_screen/additional_information_screen.dart rename to lib/presentation/registration/additional_information_screen/additional_information_screen.dart index f534bf14..0f6e122f 100644 --- a/lib/presentation/additional_information_screen/additional_information_screen.dart +++ b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart @@ -5,10 +5,12 @@ import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart'; import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart'; import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; import 'package:eatvio_app/widgets/next_step_button.dart'; -import 'package:eatvio_app/data/models/userRegistrationModel/user_registration_model.dart'; import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:eatvio_app/data/models/userRegistrationModel/user_registration_model.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter/material.dart'; import 'dart:convert'; +import 'package:hive/hive.dart'; import 'package:http/http.dart' as http; class AdditionalInformationScreen extends StatelessWidget { @@ -70,11 +72,18 @@ class AdditionalInformationScreen extends StatelessWidget { .userRegistrationSheet; sheet.additionalInfo = state.editTextController!.text; final response = await http.post( - Uri.parse('http://10.55.5.126:1092/api/register'), + Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'), headers: {'Content-Type': 'application/json'}, body: json.encode(sheet), ); - print(response.body); + 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, + ); }); }), ])))); diff --git a/lib/presentation/additional_information_screen/bloc/additional_information_bloc.dart b/lib/presentation/registration/additional_information_screen/bloc/additional_information_bloc.dart similarity index 86% rename from lib/presentation/additional_information_screen/bloc/additional_information_bloc.dart rename to lib/presentation/registration/additional_information_screen/bloc/additional_information_bloc.dart index e2b62253..b2405c57 100644 --- a/lib/presentation/additional_information_screen/bloc/additional_information_bloc.dart +++ b/lib/presentation/registration/additional_information_screen/bloc/additional_information_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/additional_information_screen/models/additional_information_model.dart'; +import 'package:eatvio_app/presentation/registration/additional_information_screen/models/additional_information_model.dart'; part 'additional_information_event.dart'; part 'additional_information_state.dart'; diff --git a/lib/presentation/additional_information_screen/bloc/additional_information_event.dart b/lib/presentation/registration/additional_information_screen/bloc/additional_information_event.dart similarity index 100% rename from lib/presentation/additional_information_screen/bloc/additional_information_event.dart rename to lib/presentation/registration/additional_information_screen/bloc/additional_information_event.dart diff --git a/lib/presentation/additional_information_screen/bloc/additional_information_state.dart b/lib/presentation/registration/additional_information_screen/bloc/additional_information_state.dart similarity index 100% rename from lib/presentation/additional_information_screen/bloc/additional_information_state.dart rename to lib/presentation/registration/additional_information_screen/bloc/additional_information_state.dart diff --git a/lib/presentation/additional_information_screen/models/additional_information_model.dart b/lib/presentation/registration/additional_information_screen/models/additional_information_model.dart similarity index 100% rename from lib/presentation/additional_information_screen/models/additional_information_model.dart rename to lib/presentation/registration/additional_information_screen/models/additional_information_model.dart diff --git a/lib/presentation/allergies_screen/allergies_screen.dart b/lib/presentation/registration/allergies_screen/allergies_screen.dart similarity index 100% rename from lib/presentation/allergies_screen/allergies_screen.dart rename to lib/presentation/registration/allergies_screen/allergies_screen.dart diff --git a/lib/presentation/allergies_screen/bloc/allergies_bloc.dart b/lib/presentation/registration/allergies_screen/bloc/allergies_bloc.dart similarity index 86% rename from lib/presentation/allergies_screen/bloc/allergies_bloc.dart rename to lib/presentation/registration/allergies_screen/bloc/allergies_bloc.dart index d529d958..f4cf7a22 100644 --- a/lib/presentation/allergies_screen/bloc/allergies_bloc.dart +++ b/lib/presentation/registration/allergies_screen/bloc/allergies_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/allergies_screen/models/allergies_model.dart'; +import 'package:eatvio_app/presentation/registration/allergies_screen/models/allergies_model.dart'; part 'allergies_event.dart'; part 'allergies_state.dart'; diff --git a/lib/presentation/allergies_screen/bloc/allergies_event.dart b/lib/presentation/registration/allergies_screen/bloc/allergies_event.dart similarity index 100% rename from lib/presentation/allergies_screen/bloc/allergies_event.dart rename to lib/presentation/registration/allergies_screen/bloc/allergies_event.dart diff --git a/lib/presentation/allergies_screen/bloc/allergies_state.dart b/lib/presentation/registration/allergies_screen/bloc/allergies_state.dart similarity index 100% rename from lib/presentation/allergies_screen/bloc/allergies_state.dart rename to lib/presentation/registration/allergies_screen/bloc/allergies_state.dart diff --git a/lib/presentation/allergies_screen/models/allergies_model.dart b/lib/presentation/registration/allergies_screen/models/allergies_model.dart similarity index 100% rename from lib/presentation/allergies_screen/models/allergies_model.dart rename to lib/presentation/registration/allergies_screen/models/allergies_model.dart diff --git a/lib/presentation/app_navigation_screen/app_navigation_screen.dart b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart similarity index 96% rename from lib/presentation/app_navigation_screen/app_navigation_screen.dart rename to lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart index 0a485d18..5dd89c25 100644 --- a/lib/presentation/app_navigation_screen/app_navigation_screen.dart +++ b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart @@ -130,6 +130,12 @@ class _AppNavigationScreenState extends State { onTapScreenTitle: () => onTapScreenTitle( AppRoutes.languageSelectScreen), ), + _buildScreenTitle( + context, + screenTitle: "auth".tr, + onTapScreenTitle: () => + onTapScreenTitle(AppRoutes.authScreen), + ), ], ), ), diff --git a/lib/presentation/app_navigation_screen/bloc/app_navigation_bloc.dart b/lib/presentation/registration/app_navigation_screen/bloc/app_navigation_bloc.dart similarity index 84% rename from lib/presentation/app_navigation_screen/bloc/app_navigation_bloc.dart rename to lib/presentation/registration/app_navigation_screen/bloc/app_navigation_bloc.dart index 3ece4d2b..adb51100 100644 --- a/lib/presentation/app_navigation_screen/bloc/app_navigation_bloc.dart +++ b/lib/presentation/registration/app_navigation_screen/bloc/app_navigation_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/app_navigation_screen/models/app_navigation_model.dart'; +import 'package:eatvio_app/presentation/registration/app_navigation_screen/models/app_navigation_model.dart'; part 'app_navigation_event.dart'; part 'app_navigation_state.dart'; diff --git a/lib/presentation/app_navigation_screen/bloc/app_navigation_event.dart b/lib/presentation/registration/app_navigation_screen/bloc/app_navigation_event.dart similarity index 100% rename from lib/presentation/app_navigation_screen/bloc/app_navigation_event.dart rename to lib/presentation/registration/app_navigation_screen/bloc/app_navigation_event.dart diff --git a/lib/presentation/app_navigation_screen/bloc/app_navigation_state.dart b/lib/presentation/registration/app_navigation_screen/bloc/app_navigation_state.dart similarity index 100% rename from lib/presentation/app_navigation_screen/bloc/app_navigation_state.dart rename to lib/presentation/registration/app_navigation_screen/bloc/app_navigation_state.dart diff --git a/lib/presentation/app_navigation_screen/models/app_navigation_model.dart b/lib/presentation/registration/app_navigation_screen/models/app_navigation_model.dart similarity index 100% rename from lib/presentation/app_navigation_screen/models/app_navigation_model.dart rename to lib/presentation/registration/app_navigation_screen/models/app_navigation_model.dart diff --git a/lib/presentation/blood_pressure_screen/bloc/blood_pressure_bloc.dart b/lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_bloc.dart similarity index 94% rename from lib/presentation/blood_pressure_screen/bloc/blood_pressure_bloc.dart rename to lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_bloc.dart index 9ff02aeb..e5a0c6a0 100644 --- a/lib/presentation/blood_pressure_screen/bloc/blood_pressure_bloc.dart +++ b/lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/blood_pressure_screen/models/blood_pressure_model.dart'; +import 'package:eatvio_app/presentation/registration/blood_pressure_screen/models/blood_pressure_model.dart'; part 'blood_pressure_event.dart'; part 'blood_pressure_state.dart'; diff --git a/lib/presentation/blood_pressure_screen/bloc/blood_pressure_event.dart b/lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_event.dart similarity index 100% rename from lib/presentation/blood_pressure_screen/bloc/blood_pressure_event.dart rename to lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_event.dart diff --git a/lib/presentation/blood_pressure_screen/bloc/blood_pressure_state.dart b/lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_state.dart similarity index 100% rename from lib/presentation/blood_pressure_screen/bloc/blood_pressure_state.dart rename to lib/presentation/registration/blood_pressure_screen/bloc/blood_pressure_state.dart diff --git a/lib/presentation/blood_pressure_screen/blood_pressure_screen.dart b/lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart similarity index 100% rename from lib/presentation/blood_pressure_screen/blood_pressure_screen.dart rename to lib/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart diff --git a/lib/presentation/blood_pressure_screen/models/blood_pressure_model.dart b/lib/presentation/registration/blood_pressure_screen/models/blood_pressure_model.dart similarity index 91% rename from lib/presentation/blood_pressure_screen/models/blood_pressure_model.dart rename to lib/presentation/registration/blood_pressure_screen/models/blood_pressure_model.dart index c002b0a9..328d1c5d 100644 --- a/lib/presentation/blood_pressure_screen/models/blood_pressure_model.dart +++ b/lib/presentation/registration/blood_pressure_screen/models/blood_pressure_model.dart @@ -15,8 +15,7 @@ class BloodPressureModel extends Equatable { BloodPressureModel copyWith({List? dropdownItemList}) { return BloodPressureModel( - userRegistrationSheet: - userRegistrationSheet ?? this.userRegistrationSheet, + userRegistrationSheet: userRegistrationSheet, dropdownItemList: dropdownItemList ?? this.dropdownItemList, ); } diff --git a/lib/presentation/body_circumferences_screen/bloc/body_circumferences_bloc.dart b/lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_bloc.dart similarity index 88% rename from lib/presentation/body_circumferences_screen/bloc/body_circumferences_bloc.dart rename to lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_bloc.dart index 30e0097c..13f1a279 100644 --- a/lib/presentation/body_circumferences_screen/bloc/body_circumferences_bloc.dart +++ b/lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/body_circumferences_screen/models/body_circumferences_model.dart'; +import 'package:eatvio_app/presentation/registration/body_circumferences_screen/models/body_circumferences_model.dart'; part 'body_circumferences_event.dart'; part 'body_circumferences_state.dart'; diff --git a/lib/presentation/body_circumferences_screen/bloc/body_circumferences_event.dart b/lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_event.dart similarity index 100% rename from lib/presentation/body_circumferences_screen/bloc/body_circumferences_event.dart rename to lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_event.dart diff --git a/lib/presentation/body_circumferences_screen/bloc/body_circumferences_state.dart b/lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_state.dart similarity index 100% rename from lib/presentation/body_circumferences_screen/bloc/body_circumferences_state.dart rename to lib/presentation/registration/body_circumferences_screen/bloc/body_circumferences_state.dart diff --git a/lib/presentation/body_circumferences_screen/body_circumferences_screen.dart b/lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart similarity index 100% rename from lib/presentation/body_circumferences_screen/body_circumferences_screen.dart rename to lib/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart diff --git a/lib/presentation/body_circumferences_screen/models/body_circumferences_model.dart b/lib/presentation/registration/body_circumferences_screen/models/body_circumferences_model.dart similarity index 100% rename from lib/presentation/body_circumferences_screen/models/body_circumferences_model.dart rename to lib/presentation/registration/body_circumferences_screen/models/body_circumferences_model.dart diff --git a/lib/presentation/city_of_living_screen/bloc/city_of_living_bloc.dart b/lib/presentation/registration/city_of_living_screen/bloc/city_of_living_bloc.dart similarity index 96% rename from lib/presentation/city_of_living_screen/bloc/city_of_living_bloc.dart rename to lib/presentation/registration/city_of_living_screen/bloc/city_of_living_bloc.dart index c0cad702..0a4fa9fa 100644 --- a/lib/presentation/city_of_living_screen/bloc/city_of_living_bloc.dart +++ b/lib/presentation/registration/city_of_living_screen/bloc/city_of_living_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/city_of_living_screen/models/city_of_living_model.dart'; +import 'package:eatvio_app/presentation/registration/city_of_living_screen/models/city_of_living_model.dart'; part 'city_of_living_event.dart'; part 'city_of_living_state.dart'; diff --git a/lib/presentation/city_of_living_screen/bloc/city_of_living_event.dart b/lib/presentation/registration/city_of_living_screen/bloc/city_of_living_event.dart similarity index 100% rename from lib/presentation/city_of_living_screen/bloc/city_of_living_event.dart rename to lib/presentation/registration/city_of_living_screen/bloc/city_of_living_event.dart diff --git a/lib/presentation/city_of_living_screen/bloc/city_of_living_state.dart b/lib/presentation/registration/city_of_living_screen/bloc/city_of_living_state.dart similarity index 100% rename from lib/presentation/city_of_living_screen/bloc/city_of_living_state.dart rename to lib/presentation/registration/city_of_living_screen/bloc/city_of_living_state.dart diff --git a/lib/presentation/city_of_living_screen/city_of_living_screen.dart b/lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart similarity index 100% rename from lib/presentation/city_of_living_screen/city_of_living_screen.dart rename to lib/presentation/registration/city_of_living_screen/city_of_living_screen.dart diff --git a/lib/presentation/city_of_living_screen/models/city_of_living_model.dart b/lib/presentation/registration/city_of_living_screen/models/city_of_living_model.dart similarity index 100% rename from lib/presentation/city_of_living_screen/models/city_of_living_model.dart rename to lib/presentation/registration/city_of_living_screen/models/city_of_living_model.dart diff --git a/lib/presentation/credentials_screen/bloc/credentials_bloc.dart b/lib/presentation/registration/credentials_screen/bloc/credentials_bloc.dart similarity index 88% rename from lib/presentation/credentials_screen/bloc/credentials_bloc.dart rename to lib/presentation/registration/credentials_screen/bloc/credentials_bloc.dart index 425186a9..91090d2a 100644 --- a/lib/presentation/credentials_screen/bloc/credentials_bloc.dart +++ b/lib/presentation/registration/credentials_screen/bloc/credentials_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/credentials_screen/models/credentials_model.dart'; +import 'package:eatvio_app/presentation/registration/credentials_screen/models/credentials_model.dart'; part 'credentials_event.dart'; part 'credentials_state.dart'; diff --git a/lib/presentation/credentials_screen/bloc/credentials_event.dart b/lib/presentation/registration/credentials_screen/bloc/credentials_event.dart similarity index 100% rename from lib/presentation/credentials_screen/bloc/credentials_event.dart rename to lib/presentation/registration/credentials_screen/bloc/credentials_event.dart diff --git a/lib/presentation/credentials_screen/bloc/credentials_state.dart b/lib/presentation/registration/credentials_screen/bloc/credentials_state.dart similarity index 100% rename from lib/presentation/credentials_screen/bloc/credentials_state.dart rename to lib/presentation/registration/credentials_screen/bloc/credentials_state.dart diff --git a/lib/presentation/registration/credentials_screen/credentials_screen.dart b/lib/presentation/registration/credentials_screen/credentials_screen.dart new file mode 100644 index 00000000..07607b4f --- /dev/null +++ b/lib/presentation/registration/credentials_screen/credentials_screen.dart @@ -0,0 +1,199 @@ +import 'bloc/credentials_bloc.dart'; +import 'models/credentials_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart'; +import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; +import 'package:validators/validators.dart'; +import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'package:eatvio_app/data/models/userRegistrationModel/user_registration_model.dart'; +import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:flutter/material.dart'; + +class CredentialsScreen extends StatelessWidget { + static Widget builder(BuildContext context) { + final sheet = + ModalRoute.of(context)!.settings.arguments as UserRegistrationSheet; + return BlocProvider( + create: (context) => CredentialsBloc( + CredentialsState( + credentialsModelObj: CredentialsModel(userRegistrationSheet: sheet), + ), + )..add(CredentialsInitialEvent()), + child: CredentialsScreen(), + ); + } + + @override + Widget build(BuildContext context) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: _buildAppBar(context), + body: Container( + width: double.maxFinite, + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), + child: Column(children: [ + SizedBox(height: 7.v), + 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.05, + backgroundColor: theme.colorScheme.onPrimary, + valueColor: AlwaysStoppedAnimation( + appTheme.blueA400)))), + SizedBox(height: 39.v), + Text("user_registration".tr, + style: theme.textTheme.headlineSmall), + SizedBox(height: 16.v), + Text("email".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.emailController, + builder: (context, emailController) { + return CustomTextFormField( + controller: emailController, + textInputAction: TextInputAction.done); + }), + SizedBox(height: 22.v), + Text("username".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.nameController, + builder: (context, nameController) { + return CustomTextFormField( + controller: nameController, + textInputAction: TextInputAction.done); + }), + SizedBox(height: 19.v), + Text("password".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 5.v), + BlocSelector( + selector: (state) => state.passwordController, + builder: (context, passwordController) { + return CustomTextFormField( + obscureText: true, + controller: passwordController, + ); + }), + SizedBox(height: 21.v), + Text("password_confirmation".tr, + style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.passwordConfirmationController, + builder: (context, passwordConfirmationController) { + return CustomTextFormField( + obscureText: true, + controller: passwordConfirmationController, + textInputAction: TextInputAction.done); + }), + Spacer(), + BlocBuilder( + builder: (context, state) { + return NextStepButton( + text: "continue".tr.toUpperCase(), + 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'), + ), + ], + ), + ); + } + }); + }), + ]))); + } + + /// Section Widget + PreferredSizeWidget _buildAppBar(BuildContext context) { + return CustomAppBar( + leadingWidth: 29.h, + centerTitle: true, + title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); + } + + List validateFields( + String email, String name, String password, String passwordConfirmation) { + List errors = []; + + if (!isEmail(email)) { + errors.add('email_invalid'.tr); + } + + if (email.isEmpty) { + errors.add('email_empty'.tr); + } + + if (name.length < 6) { + errors.add('name_short'.tr); + } + + if (name.isEmpty) { + errors.add('name_empty'.tr); + } + + if (password.isEmpty) { + errors.add('name_empty'.tr); + } + + if (password.length < 6) { + errors.add('password_short'.tr); + } + + if (password != passwordConfirmation) { + errors.add('password_confirmation_mismatch'.tr); + } + + return errors; + } +} diff --git a/lib/presentation/credentials_screen/models/credentials_model.dart b/lib/presentation/registration/credentials_screen/models/credentials_model.dart similarity index 100% rename from lib/presentation/credentials_screen/models/credentials_model.dart rename to lib/presentation/registration/credentials_screen/models/credentials_model.dart diff --git a/lib/presentation/date_of_birth_screen/bloc/date_of_birth_bloc.dart b/lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_bloc.dart similarity index 84% rename from lib/presentation/date_of_birth_screen/bloc/date_of_birth_bloc.dart rename to lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_bloc.dart index 71ae39b8..78dec540 100644 --- a/lib/presentation/date_of_birth_screen/bloc/date_of_birth_bloc.dart +++ b/lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/date_of_birth_screen/models/date_of_birth_model.dart'; +import 'package:eatvio_app/presentation/registration/date_of_birth_screen/models/date_of_birth_model.dart'; part 'date_of_birth_event.dart'; part 'date_of_birth_state.dart'; diff --git a/lib/presentation/date_of_birth_screen/bloc/date_of_birth_event.dart b/lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_event.dart similarity index 100% rename from lib/presentation/date_of_birth_screen/bloc/date_of_birth_event.dart rename to lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_event.dart diff --git a/lib/presentation/date_of_birth_screen/bloc/date_of_birth_state.dart b/lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_state.dart similarity index 100% rename from lib/presentation/date_of_birth_screen/bloc/date_of_birth_state.dart rename to lib/presentation/registration/date_of_birth_screen/bloc/date_of_birth_state.dart diff --git a/lib/presentation/date_of_birth_screen/date_of_birth_screen.dart b/lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart similarity index 100% rename from lib/presentation/date_of_birth_screen/date_of_birth_screen.dart rename to lib/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart diff --git a/lib/presentation/date_of_birth_screen/models/date_of_birth_model.dart b/lib/presentation/registration/date_of_birth_screen/models/date_of_birth_model.dart similarity index 100% rename from lib/presentation/date_of_birth_screen/models/date_of_birth_model.dart rename to lib/presentation/registration/date_of_birth_screen/models/date_of_birth_model.dart diff --git a/lib/presentation/favorite_foods_screen/bloc/favorite_foods_bloc.dart b/lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_bloc.dart similarity index 87% rename from lib/presentation/favorite_foods_screen/bloc/favorite_foods_bloc.dart rename to lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_bloc.dart index 3fcb2c5b..05f9d908 100644 --- a/lib/presentation/favorite_foods_screen/bloc/favorite_foods_bloc.dart +++ b/lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/favorite_foods_screen/models/favorite_foods_model.dart'; +import 'package:eatvio_app/presentation/registration/favorite_foods_screen/models/favorite_foods_model.dart'; part 'favorite_foods_event.dart'; part 'favorite_foods_state.dart'; diff --git a/lib/presentation/favorite_foods_screen/bloc/favorite_foods_event.dart b/lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_event.dart similarity index 100% rename from lib/presentation/favorite_foods_screen/bloc/favorite_foods_event.dart rename to lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_event.dart diff --git a/lib/presentation/favorite_foods_screen/bloc/favorite_foods_state.dart b/lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_state.dart similarity index 100% rename from lib/presentation/favorite_foods_screen/bloc/favorite_foods_state.dart rename to lib/presentation/registration/favorite_foods_screen/bloc/favorite_foods_state.dart diff --git a/lib/presentation/favorite_foods_screen/favorite_foods_screen.dart b/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart similarity index 98% rename from lib/presentation/favorite_foods_screen/favorite_foods_screen.dart rename to lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart index 7db73fbd..1e189d19 100644 --- a/lib/presentation/favorite_foods_screen/favorite_foods_screen.dart +++ b/lib/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart @@ -64,11 +64,11 @@ class _FavoriteFoodsScreenState extends State { overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: theme.textTheme.headlineSmall)), - SizedBox(height: 23.v), + SizedBox(height: 13.v), _buildTextFormFieldFav(context), - SizedBox(height: 22.v), + SizedBox(height: 12.v), _buildTextFormFieldUnfav(context), - SizedBox(height: 27.v), + SizedBox(height: 17.v), BlocBuilder( builder: (context, state) { return NextStepButton( diff --git a/lib/presentation/favorite_foods_screen/models/favorite_foods_model.dart b/lib/presentation/registration/favorite_foods_screen/models/favorite_foods_model.dart similarity index 100% rename from lib/presentation/favorite_foods_screen/models/favorite_foods_model.dart rename to lib/presentation/registration/favorite_foods_screen/models/favorite_foods_model.dart diff --git a/lib/presentation/favorite_sports_screen/bloc/favorite_sports_bloc.dart b/lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_bloc.dart similarity index 86% rename from lib/presentation/favorite_sports_screen/bloc/favorite_sports_bloc.dart rename to lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_bloc.dart index e90bfcf3..7857f8b4 100644 --- a/lib/presentation/favorite_sports_screen/bloc/favorite_sports_bloc.dart +++ b/lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/favorite_sports_screen/models/favorite_sports_model.dart'; +import 'package:eatvio_app/presentation/registration/favorite_sports_screen/models/favorite_sports_model.dart'; part 'favorite_sports_event.dart'; part 'favorite_sports_state.dart'; diff --git a/lib/presentation/favorite_sports_screen/bloc/favorite_sports_event.dart b/lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_event.dart similarity index 100% rename from lib/presentation/favorite_sports_screen/bloc/favorite_sports_event.dart rename to lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_event.dart diff --git a/lib/presentation/favorite_sports_screen/bloc/favorite_sports_state.dart b/lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_state.dart similarity index 100% rename from lib/presentation/favorite_sports_screen/bloc/favorite_sports_state.dart rename to lib/presentation/registration/favorite_sports_screen/bloc/favorite_sports_state.dart diff --git a/lib/presentation/favorite_sports_screen/favorite_sports_screen.dart b/lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart similarity index 100% rename from lib/presentation/favorite_sports_screen/favorite_sports_screen.dart rename to lib/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart diff --git a/lib/presentation/favorite_sports_screen/models/favorite_sports_model.dart b/lib/presentation/registration/favorite_sports_screen/models/favorite_sports_model.dart similarity index 87% rename from lib/presentation/favorite_sports_screen/models/favorite_sports_model.dart rename to lib/presentation/registration/favorite_sports_screen/models/favorite_sports_model.dart index 11a1e288..25481701 100644 --- a/lib/presentation/favorite_sports_screen/models/favorite_sports_model.dart +++ b/lib/presentation/registration/favorite_sports_screen/models/favorite_sports_model.dart @@ -11,8 +11,7 @@ class FavoriteSportsModel extends Equatable { FavoriteSportsModel copyWith() { return FavoriteSportsModel( - userRegistrationSheet: - userRegistrationSheet ?? this.userRegistrationSheet, + userRegistrationSheet: userRegistrationSheet, ); } diff --git a/lib/presentation/gender_screen/bloc/gender_bloc.dart b/lib/presentation/registration/gender_screen/bloc/gender_bloc.dart similarity index 90% rename from lib/presentation/gender_screen/bloc/gender_bloc.dart rename to lib/presentation/registration/gender_screen/bloc/gender_bloc.dart index 7a2d4092..5f614c27 100644 --- a/lib/presentation/gender_screen/bloc/gender_bloc.dart +++ b/lib/presentation/registration/gender_screen/bloc/gender_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/gender_screen/models/gender_model.dart'; +import 'package:eatvio_app/presentation/registration/gender_screen/models/gender_model.dart'; part 'gender_event.dart'; part 'gender_state.dart'; diff --git a/lib/presentation/gender_screen/bloc/gender_event.dart b/lib/presentation/registration/gender_screen/bloc/gender_event.dart similarity index 100% rename from lib/presentation/gender_screen/bloc/gender_event.dart rename to lib/presentation/registration/gender_screen/bloc/gender_event.dart diff --git a/lib/presentation/gender_screen/bloc/gender_state.dart b/lib/presentation/registration/gender_screen/bloc/gender_state.dart similarity index 100% rename from lib/presentation/gender_screen/bloc/gender_state.dart rename to lib/presentation/registration/gender_screen/bloc/gender_state.dart diff --git a/lib/presentation/gender_screen/gender_screen.dart b/lib/presentation/registration/gender_screen/gender_screen.dart similarity index 100% rename from lib/presentation/gender_screen/gender_screen.dart rename to lib/presentation/registration/gender_screen/gender_screen.dart diff --git a/lib/presentation/gender_screen/models/gender_model.dart b/lib/presentation/registration/gender_screen/models/gender_model.dart similarity index 100% rename from lib/presentation/gender_screen/models/gender_model.dart rename to lib/presentation/registration/gender_screen/models/gender_model.dart diff --git a/lib/presentation/height_screen/bloc/height_bloc.dart b/lib/presentation/registration/height_screen/bloc/height_bloc.dart similarity index 92% rename from lib/presentation/height_screen/bloc/height_bloc.dart rename to lib/presentation/registration/height_screen/bloc/height_bloc.dart index 7136a21a..f9330fd6 100644 --- a/lib/presentation/height_screen/bloc/height_bloc.dart +++ b/lib/presentation/registration/height_screen/bloc/height_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/height_screen/models/height_model.dart'; +import 'package:eatvio_app/presentation/registration/height_screen/models/height_model.dart'; part 'height_event.dart'; part 'height_state.dart'; diff --git a/lib/presentation/height_screen/bloc/height_event.dart b/lib/presentation/registration/height_screen/bloc/height_event.dart similarity index 100% rename from lib/presentation/height_screen/bloc/height_event.dart rename to lib/presentation/registration/height_screen/bloc/height_event.dart diff --git a/lib/presentation/height_screen/bloc/height_state.dart b/lib/presentation/registration/height_screen/bloc/height_state.dart similarity index 100% rename from lib/presentation/height_screen/bloc/height_state.dart rename to lib/presentation/registration/height_screen/bloc/height_state.dart diff --git a/lib/presentation/height_screen/height_screen.dart b/lib/presentation/registration/height_screen/height_screen.dart similarity index 100% rename from lib/presentation/height_screen/height_screen.dart rename to lib/presentation/registration/height_screen/height_screen.dart diff --git a/lib/presentation/height_screen/models/height_model.dart b/lib/presentation/registration/height_screen/models/height_model.dart similarity index 90% rename from lib/presentation/height_screen/models/height_model.dart rename to lib/presentation/registration/height_screen/models/height_model.dart index b108d7d9..caa3b752 100644 --- a/lib/presentation/height_screen/models/height_model.dart +++ b/lib/presentation/registration/height_screen/models/height_model.dart @@ -15,8 +15,7 @@ class HeightModel extends Equatable { HeightModel copyWith({List? dropdownItemList}) { return HeightModel( - userRegistrationSheet: - userRegistrationSheet ?? this.userRegistrationSheet, + userRegistrationSheet: userRegistrationSheet, dropdownItemList: dropdownItemList ?? this.dropdownItemList, ); } diff --git a/lib/presentation/language_select_screen/bloc/language_select_bloc.dart b/lib/presentation/registration/language_select_screen/bloc/language_select_bloc.dart similarity index 91% rename from lib/presentation/language_select_screen/bloc/language_select_bloc.dart rename to lib/presentation/registration/language_select_screen/bloc/language_select_bloc.dart index d6ae042c..e9b6959f 100644 --- a/lib/presentation/language_select_screen/bloc/language_select_bloc.dart +++ b/lib/presentation/registration/language_select_screen/bloc/language_select_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/language_select_screen/models/language_select_model.dart'; +import 'package:eatvio_app/presentation/registration/language_select_screen/models/language_select_model.dart'; part 'language_select_event.dart'; part 'language_select_state.dart'; diff --git a/lib/presentation/language_select_screen/bloc/language_select_event.dart b/lib/presentation/registration/language_select_screen/bloc/language_select_event.dart similarity index 100% rename from lib/presentation/language_select_screen/bloc/language_select_event.dart rename to lib/presentation/registration/language_select_screen/bloc/language_select_event.dart diff --git a/lib/presentation/language_select_screen/bloc/language_select_state.dart b/lib/presentation/registration/language_select_screen/bloc/language_select_state.dart similarity index 100% rename from lib/presentation/language_select_screen/bloc/language_select_state.dart rename to lib/presentation/registration/language_select_screen/bloc/language_select_state.dart diff --git a/lib/presentation/language_select_screen/language_select_screen.dart b/lib/presentation/registration/language_select_screen/language_select_screen.dart similarity index 99% rename from lib/presentation/language_select_screen/language_select_screen.dart rename to lib/presentation/registration/language_select_screen/language_select_screen.dart index 9bae529d..cd8b9b4b 100644 --- a/lib/presentation/language_select_screen/language_select_screen.dart +++ b/lib/presentation/registration/language_select_screen/language_select_screen.dart @@ -82,7 +82,7 @@ class LanguageSelectScreen extends StatelessWidget { .add(ChangeDropDownEvent(value: value)); }); }), - Spacer(flex: 63) + Spacer(flex: 63), ])), bottomNavigationBar: BlocBuilder( diff --git a/lib/presentation/language_select_screen/models/language_select_model.dart b/lib/presentation/registration/language_select_screen/models/language_select_model.dart similarity index 100% rename from lib/presentation/language_select_screen/models/language_select_model.dart rename to lib/presentation/registration/language_select_screen/models/language_select_model.dart diff --git a/lib/presentation/medicine_intake_screen/bloc/medicine_intake_bloc.dart b/lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_bloc.dart similarity index 86% rename from lib/presentation/medicine_intake_screen/bloc/medicine_intake_bloc.dart rename to lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_bloc.dart index 190c1c0d..bd878765 100644 --- a/lib/presentation/medicine_intake_screen/bloc/medicine_intake_bloc.dart +++ b/lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/medicine_intake_screen/models/medicine_intake_model.dart'; +import 'package:eatvio_app/presentation/registration/medicine_intake_screen/models/medicine_intake_model.dart'; part 'medicine_intake_event.dart'; part 'medicine_intake_state.dart'; diff --git a/lib/presentation/medicine_intake_screen/bloc/medicine_intake_event.dart b/lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_event.dart similarity index 100% rename from lib/presentation/medicine_intake_screen/bloc/medicine_intake_event.dart rename to lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_event.dart diff --git a/lib/presentation/medicine_intake_screen/bloc/medicine_intake_state.dart b/lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_state.dart similarity index 100% rename from lib/presentation/medicine_intake_screen/bloc/medicine_intake_state.dart rename to lib/presentation/registration/medicine_intake_screen/bloc/medicine_intake_state.dart diff --git a/lib/presentation/medicine_intake_screen/medicine_intake_screen.dart b/lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart similarity index 100% rename from lib/presentation/medicine_intake_screen/medicine_intake_screen.dart rename to lib/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart diff --git a/lib/presentation/medicine_intake_screen/models/medicine_intake_model.dart b/lib/presentation/registration/medicine_intake_screen/models/medicine_intake_model.dart similarity index 87% rename from lib/presentation/medicine_intake_screen/models/medicine_intake_model.dart rename to lib/presentation/registration/medicine_intake_screen/models/medicine_intake_model.dart index 9828f2d8..f6634de4 100644 --- a/lib/presentation/medicine_intake_screen/models/medicine_intake_model.dart +++ b/lib/presentation/registration/medicine_intake_screen/models/medicine_intake_model.dart @@ -11,8 +11,7 @@ class MedicineIntakeModel extends Equatable { MedicineIntakeModel copyWith() { return MedicineIntakeModel( - userRegistrationSheet: - userRegistrationSheet ?? this.userRegistrationSheet, + userRegistrationSheet: userRegistrationSheet, ); } diff --git a/lib/presentation/name_screen/bloc/name_bloc.dart b/lib/presentation/registration/name_screen/bloc/name_bloc.dart similarity index 87% rename from lib/presentation/name_screen/bloc/name_bloc.dart rename to lib/presentation/registration/name_screen/bloc/name_bloc.dart index 00baf316..f9c47e33 100644 --- a/lib/presentation/name_screen/bloc/name_bloc.dart +++ b/lib/presentation/registration/name_screen/bloc/name_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/name_screen/models/name_model.dart'; +import 'package:eatvio_app/presentation/registration/name_screen/models/name_model.dart'; part 'name_event.dart'; part 'name_state.dart'; diff --git a/lib/presentation/name_screen/bloc/name_event.dart b/lib/presentation/registration/name_screen/bloc/name_event.dart similarity index 100% rename from lib/presentation/name_screen/bloc/name_event.dart rename to lib/presentation/registration/name_screen/bloc/name_event.dart diff --git a/lib/presentation/name_screen/bloc/name_state.dart b/lib/presentation/registration/name_screen/bloc/name_state.dart similarity index 100% rename from lib/presentation/name_screen/bloc/name_state.dart rename to lib/presentation/registration/name_screen/bloc/name_state.dart diff --git a/lib/presentation/name_screen/models/name_model.dart b/lib/presentation/registration/name_screen/models/name_model.dart similarity index 100% rename from lib/presentation/name_screen/models/name_model.dart rename to lib/presentation/registration/name_screen/models/name_model.dart diff --git a/lib/presentation/name_screen/name_screen.dart b/lib/presentation/registration/name_screen/name_screen.dart similarity index 100% rename from lib/presentation/name_screen/name_screen.dart rename to lib/presentation/registration/name_screen/name_screen.dart diff --git a/lib/presentation/splash_screen/bloc/splash_bloc.dart b/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart similarity index 83% rename from lib/presentation/splash_screen/bloc/splash_bloc.dart rename to lib/presentation/registration/splash_screen/bloc/splash_bloc.dart index dc601ef7..a767f763 100644 --- a/lib/presentation/splash_screen/bloc/splash_bloc.dart +++ b/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/splash_screen/models/splash_model.dart'; +import 'package:eatvio_app/presentation/registration/splash_screen/models/splash_model.dart'; part 'splash_event.dart'; part 'splash_state.dart'; @@ -17,7 +17,7 @@ class SplashBloc extends Bloc { ) async { Future.delayed(const Duration(milliseconds: 3000), () { NavigatorService.popAndPushNamed( - AppRoutes.languageSelectScreen, + AppRoutes.authScreen, ); }); } diff --git a/lib/presentation/splash_screen/bloc/splash_event.dart b/lib/presentation/registration/splash_screen/bloc/splash_event.dart similarity index 100% rename from lib/presentation/splash_screen/bloc/splash_event.dart rename to lib/presentation/registration/splash_screen/bloc/splash_event.dart diff --git a/lib/presentation/splash_screen/bloc/splash_state.dart b/lib/presentation/registration/splash_screen/bloc/splash_state.dart similarity index 100% rename from lib/presentation/splash_screen/bloc/splash_state.dart rename to lib/presentation/registration/splash_screen/bloc/splash_state.dart diff --git a/lib/presentation/splash_screen/models/splash_model.dart b/lib/presentation/registration/splash_screen/models/splash_model.dart similarity index 100% rename from lib/presentation/splash_screen/models/splash_model.dart rename to lib/presentation/registration/splash_screen/models/splash_model.dart diff --git a/lib/presentation/splash_screen/splash_screen.dart b/lib/presentation/registration/splash_screen/splash_screen.dart similarity index 100% rename from lib/presentation/splash_screen/splash_screen.dart rename to lib/presentation/registration/splash_screen/splash_screen.dart diff --git a/lib/presentation/weight_screen/bloc/weight_bloc.dart b/lib/presentation/registration/weight_screen/bloc/weight_bloc.dart similarity index 92% rename from lib/presentation/weight_screen/bloc/weight_bloc.dart rename to lib/presentation/registration/weight_screen/bloc/weight_bloc.dart index 507220a4..bf2cc1f9 100644 --- a/lib/presentation/weight_screen/bloc/weight_bloc.dart +++ b/lib/presentation/registration/weight_screen/bloc/weight_bloc.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import '/core/app_export.dart'; -import 'package:eatvio_app/presentation/weight_screen/models/weight_model.dart'; +import 'package:eatvio_app/presentation/registration/weight_screen/models/weight_model.dart'; part 'weight_event.dart'; part 'weight_state.dart'; diff --git a/lib/presentation/weight_screen/bloc/weight_event.dart b/lib/presentation/registration/weight_screen/bloc/weight_event.dart similarity index 100% rename from lib/presentation/weight_screen/bloc/weight_event.dart rename to lib/presentation/registration/weight_screen/bloc/weight_event.dart diff --git a/lib/presentation/weight_screen/bloc/weight_state.dart b/lib/presentation/registration/weight_screen/bloc/weight_state.dart similarity index 100% rename from lib/presentation/weight_screen/bloc/weight_state.dart rename to lib/presentation/registration/weight_screen/bloc/weight_state.dart diff --git a/lib/presentation/weight_screen/models/weight_model.dart b/lib/presentation/registration/weight_screen/models/weight_model.dart similarity index 90% rename from lib/presentation/weight_screen/models/weight_model.dart rename to lib/presentation/registration/weight_screen/models/weight_model.dart index e91ceb10..e26b49a0 100644 --- a/lib/presentation/weight_screen/models/weight_model.dart +++ b/lib/presentation/registration/weight_screen/models/weight_model.dart @@ -15,8 +15,7 @@ class WeightModel extends Equatable { WeightModel copyWith({List? dropdownItemList}) { return WeightModel( - userRegistrationSheet: - userRegistrationSheet ?? this.userRegistrationSheet, + userRegistrationSheet: userRegistrationSheet, dropdownItemList: dropdownItemList ?? this.dropdownItemList, ); } diff --git a/lib/presentation/weight_screen/weight_screen.dart b/lib/presentation/registration/weight_screen/weight_screen.dart similarity index 100% rename from lib/presentation/weight_screen/weight_screen.dart rename to lib/presentation/registration/weight_screen/weight_screen.dart diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 0fd6f29e..b83e7ff9 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -1,21 +1,24 @@ import 'package:flutter/material.dart'; -import 'package:eatvio_app/presentation/date_of_birth_screen/date_of_birth_screen.dart'; -import 'package:eatvio_app/presentation/city_of_living_screen/city_of_living_screen.dart'; -import 'package:eatvio_app/presentation/body_circumferences_screen/body_circumferences_screen.dart'; -import 'package:eatvio_app/presentation/weight_screen/weight_screen.dart'; -import 'package:eatvio_app/presentation/allergies_screen/allergies_screen.dart'; -import 'package:eatvio_app/presentation/medicine_intake_screen/medicine_intake_screen.dart'; -import 'package:eatvio_app/presentation/splash_screen/splash_screen.dart'; -import 'package:eatvio_app/presentation/gender_screen/gender_screen.dart'; -import 'package:eatvio_app/presentation/blood_pressure_screen/blood_pressure_screen.dart'; -import 'package:eatvio_app/presentation/favorite_sports_screen/favorite_sports_screen.dart'; -import 'package:eatvio_app/presentation/favorite_foods_screen/favorite_foods_screen.dart'; -import 'package:eatvio_app/presentation/additional_information_screen/additional_information_screen.dart'; -import 'package:eatvio_app/presentation/language_select_screen/language_select_screen.dart'; -import 'package:eatvio_app/presentation/height_screen/height_screen.dart'; -import 'package:eatvio_app/presentation/credentials_screen/credentials_screen.dart'; -import 'package:eatvio_app/presentation/name_screen/name_screen.dart'; -import 'package:eatvio_app/presentation/app_navigation_screen/app_navigation_screen.dart'; +import 'package:eatvio_app/presentation/registration/date_of_birth_screen/date_of_birth_screen.dart'; +import 'package:eatvio_app/presentation/registration/city_of_living_screen/city_of_living_screen.dart'; +import 'package:eatvio_app/presentation/registration/body_circumferences_screen/body_circumferences_screen.dart'; +import 'package:eatvio_app/presentation/registration/weight_screen/weight_screen.dart'; +import 'package:eatvio_app/presentation/registration/allergies_screen/allergies_screen.dart'; +import 'package:eatvio_app/presentation/registration/medicine_intake_screen/medicine_intake_screen.dart'; +import 'package:eatvio_app/presentation/registration/splash_screen/splash_screen.dart'; +import 'package:eatvio_app/presentation/registration/gender_screen/gender_screen.dart'; +import 'package:eatvio_app/presentation/registration/blood_pressure_screen/blood_pressure_screen.dart'; +import 'package:eatvio_app/presentation/registration/favorite_sports_screen/favorite_sports_screen.dart'; +import 'package:eatvio_app/presentation/registration/favorite_foods_screen/favorite_foods_screen.dart'; +import 'package:eatvio_app/presentation/registration/additional_information_screen/additional_information_screen.dart'; +import 'package:eatvio_app/presentation/registration/language_select_screen/language_select_screen.dart'; +import 'package:eatvio_app/presentation/registration/height_screen/height_screen.dart'; +import 'package:eatvio_app/presentation/registration/credentials_screen/credentials_screen.dart'; +import 'package:eatvio_app/presentation/registration/name_screen/name_screen.dart'; +import 'package:eatvio_app/presentation/registration/app_navigation_screen/app_navigation_screen.dart'; +import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/my_profile_screen.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/my_settings_screen.dart'; class AppRoutes { // REGISTRATION @@ -57,8 +60,15 @@ class AppRoutes { static const String appNavigationScreen = '/app_navigation_screen'; + static const String authScreen = '/auth_screen'; + static const String initialRoute = '/initialRoute'; + //PROFILE + static const String myProfile = '/myProfile'; + + static const String mySettings = '/mySettings'; + static Map get routes => { dateOfBirthScreen: DateOfBirthScreen.builder, cityOfLivingScreen: CityOfLivingScreen.builder, @@ -70,6 +80,7 @@ class AppRoutes { allergiesScreen: AllergiesScreen.builder, medicineIntakeScreen: MedicineIntakeScreen.builder, splashScreen: SplashScreen.builder, + authScreen: AuthScreen.builder, genderScreen: GenderScreen.builder, bloodPressureScreen: BloodPressureScreen.builder, favoriteSportsScreen: FavoriteSportsScreen.builder, @@ -77,6 +88,8 @@ class AppRoutes { additionalInformationScreen: AdditionalInformationScreen.builder, languageSelectScreen: LanguageSelectScreen.builder, appNavigationScreen: AppNavigationScreen.builder, - initialRoute: SplashScreen.builder + initialRoute: SplashScreen.builder, + myProfile: MyProfileScreen.builder, + mySettings: MySettingsScreen.builder, }; } diff --git a/lib/theme/app_decoration.dart b/lib/theme/app_decoration.dart index 027026b8..f304e54d 100644 --- a/lib/theme/app_decoration.dart +++ b/lib/theme/app_decoration.dart @@ -22,6 +22,34 @@ class AppDecoration { width: 1.h, ), ); + static BoxDecoration get fillDeepOrange => BoxDecoration( + color: appTheme.deepOrange900.withOpacity(0.8), + ); + static BoxDecoration get fillGray => BoxDecoration( + color: appTheme.gray50, + ); + static BoxDecoration get fillGray100 => BoxDecoration( + color: appTheme.gray100, + ); + static BoxDecoration get fillGray60002 => BoxDecoration( + color: appTheme.gray60002, + ); + static BoxDecoration get fillGreenA => BoxDecoration( + color: appTheme.greenA700.withOpacity(0.8), + ); + static BoxDecoration get fillPrimary => BoxDecoration( + color: theme.colorScheme.primary, + ); + static BoxDecoration get fillWhiteA => BoxDecoration( + color: appTheme.whiteA700, + ); + static BoxDecoration get outlineBlueGray => BoxDecoration( + border: Border.all( + color: appTheme.blueGray800, + width: 1.h, + strokeAlign: strokeAlignOutside, + ), + ); } class BorderRadiusStyle { @@ -34,6 +62,62 @@ class BorderRadiusStyle { static BorderRadius get roundedBorder1 => BorderRadius.circular( 1.h, ); + + // Outline decorations + static BoxDecoration get outlineBlack => BoxDecoration( + color: appTheme.whiteA700, + boxShadow: [ + BoxShadow( + color: appTheme.black900.withOpacity(0.15), + spreadRadius: 2.h, + blurRadius: 2.h, + offset: Offset( + 0, + 2, + ), + ), + ], + ); + static BoxDecoration get outlineBlueGray => BoxDecoration( + border: Border.all( + color: appTheme.blueGray800, + width: 1.h, + strokeAlign: strokeAlignOutside, + ), + ); + // Circle borders + static BorderRadius get circleBorder17 => BorderRadius.circular( + 17.h, + ); + static BorderRadius get circleBorder20 => BorderRadius.circular( + 20.h, + ); + static BorderRadius get circleBorder62 => BorderRadius.circular( + 62.h, + ); + + // Custom borders + static BorderRadius get customBorderBL8 => BorderRadius.only( + bottomLeft: Radius.circular(8.h), + ); + static BorderRadius get customBorderTL5 => BorderRadius.vertical( + top: Radius.circular(5.h), + ); + static BorderRadius get customBorderTL51 => BorderRadius.only( + topLeft: Radius.circular(5.h), + bottomRight: Radius.circular(5.h), + ); + + // Rounded borders + static BorderRadius get roundedBorder12 => BorderRadius.circular( + 12.h, + ); + static BorderRadius get roundedBorder5 => BorderRadius.circular( + 5.h, + ); + static BorderRadius get roundedBorder8 => BorderRadius.circular( + 8.h, + ); } // Comment/Uncomment the below code based on your Flutter SDK version. diff --git a/lib/theme/custom_text_style.dart b/lib/theme/custom_text_style.dart index a807d9d3..ff969f92 100644 --- a/lib/theme/custom_text_style.dart +++ b/lib/theme/custom_text_style.dart @@ -13,6 +13,138 @@ class CustomTextStyles { theme.textTheme.titleMedium!.segoeUI.copyWith( fontWeight: FontWeight.w600, ); + static get bodyMediumBlack900 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.black900, + ); + static get bodyMediumBlack90013 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.black900, + fontSize: 13.fSize, + ); + static get bodyMediumBluegray800 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.blueGray800, + ); + static get bodyMediumBluegray800_1 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.blueGray800, + ); + static get bodyMediumBluegray800_2 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.blueGray800, + ); + static get bodyMediumWhiteA700 => theme.textTheme.bodyMedium!.copyWith( + color: appTheme.whiteA700, + ); + static get bodySmallMulishBlack900 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.black900, + fontSize: 12.fSize, + ); + static get bodySmallMulishBluegray800 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.blueGray800, + fontSize: 12.fSize, + ); + static get bodySmallMulishBluegray80012 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.blueGray800, + fontSize: 12.fSize, + ); + static get bodySmallMulishBluegray80012_1 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.blueGray800, + fontSize: 12.fSize, + ); + static get bodySmallMulishBluegray900 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.blueGray900, + fontSize: 12.fSize, + ); + static get bodySmallMulishGray600 => + theme.textTheme.bodySmall!.mulish.copyWith( + color: appTheme.gray600, + ); + static get bodySmallMulishPrimary => + theme.textTheme.bodySmall!.mulish.copyWith( + color: theme.colorScheme.primary, + fontSize: 12.fSize, + ); + static get bodySmallSegoeUIBlack900 => + theme.textTheme.bodySmall!.segoeUI.copyWith( + color: appTheme.black900, + fontSize: 12.fSize, + ); + // Label text style + static get labelLargeGray60003 => theme.textTheme.labelLarge!.copyWith( + color: appTheme.gray60003, + ); + static get labelLargeWhiteA700 => theme.textTheme.labelLarge!.copyWith( + color: appTheme.whiteA700, + ); + // Title text style + static get titleMedium16 => theme.textTheme.titleMedium!.copyWith( + fontSize: 16.fSize, + ); + static get titleMediumBlack => theme.textTheme.titleMedium!.copyWith( + fontSize: 17.fSize, + fontWeight: FontWeight.w900, + ); + static get titleMediumBlack900 => theme.textTheme.titleMedium!.copyWith( + color: appTheme.black900, + ); + static get titleMediumSemiBold => theme.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w600, + ); + static get titleMediumWhiteA700 => theme.textTheme.titleMedium!.copyWith( + color: appTheme.whiteA700, + fontWeight: FontWeight.w600, + ); + static get titleSmallBlue800 => theme.textTheme.titleSmall!.copyWith( + color: appTheme.blue800, + fontWeight: FontWeight.w800, + ); + static get titleSmallBluegray800 => theme.textTheme.titleSmall!.copyWith( + color: appTheme.blueGray800, + ); + static get titleSmallBluegray800Bold => theme.textTheme.titleSmall!.copyWith( + color: appTheme.blueGray800, + fontWeight: FontWeight.w700, + ); + static get titleSmallBluegray800ExtraBold => + theme.textTheme.titleSmall!.copyWith( + color: appTheme.blueGray800, + fontWeight: FontWeight.w800, + ); + static get titleSmallBold => theme.textTheme.titleSmall!.copyWith( + fontSize: 15.fSize, + fontWeight: FontWeight.w700, + ); + static get titleSmallGreenA70001 => theme.textTheme.titleSmall!.copyWith( + color: appTheme.greenA70001, + ); + static get titleSmallGreenA70001ExtraBold => + theme.textTheme.titleSmall!.copyWith( + color: appTheme.greenA70001, + fontWeight: FontWeight.w800, + ); + static get titleSmallLightgreen300 => theme.textTheme.titleSmall!.copyWith( + color: appTheme.lightGreen300, + fontWeight: FontWeight.w800, + ); + static get titleSmallRed200 => theme.textTheme.titleSmall!.copyWith( + color: appTheme.red200, + fontWeight: FontWeight.w800, + ); + static get titleSmallSecondaryContainer => + theme.textTheme.titleSmall!.copyWith( + color: theme.colorScheme.secondaryContainer, + fontWeight: FontWeight.w800, + ); + static get titleSmallSecondaryContainer_1 => + theme.textTheme.titleSmall!.copyWith( + color: theme.colorScheme.secondaryContainer, + ); + static get titleSmallSegoeUIOnPrimary => + theme.textTheme.titleSmall!.segoeUI.copyWith( + color: theme.colorScheme.onPrimary, + ); } extension on TextStyle { diff --git a/lib/theme/theme_helper.dart b/lib/theme/theme_helper.dart index e7fb2e95..699e2825 100644 --- a/lib/theme/theme_helper.dart +++ b/lib/theme/theme_helper.dart @@ -40,6 +40,12 @@ class ThemeHelper { var colorScheme = _supportedColorScheme[_appTheme] ?? ColorSchemes.primaryColorScheme; return ThemeData( + pageTransitionsTheme: PageTransitionsTheme( + builders: { + TargetPlatform.android: CupertinoPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + }, + ), visualDensity: VisualDensity.standard, colorScheme: colorScheme, textTheme: TextThemes.textTheme(colorScheme), @@ -98,6 +104,24 @@ class TextThemes { fontFamily: 'Mulish', fontWeight: FontWeight.w700, ), + bodyMedium: TextStyle( + color: appTheme.blueGray400, + fontSize: 14.fSize, + fontFamily: 'Mulish', + fontWeight: FontWeight.w400, + ), + bodySmall: TextStyle( + color: appTheme.gray500, + fontSize: 10.fSize, + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + ), + labelLarge: TextStyle( + color: appTheme.blueGray800, + fontSize: 12.fSize, + fontFamily: 'Mulish', + fontWeight: FontWeight.w600, + ), ); } @@ -121,6 +145,48 @@ class PrimaryColors { // BlueGray Color get blueGray400 => Color(0XFF7B8794); + Color get black900 => Color(0XFF000000); + + // Blue + Color get blue800 => Color(0XFF1F78B4); + + // BlueGray + Color get blueGray300 => Color(0XFF9AA5B1); + Color get blueGray800 => Color(0XFF323F4B); + Color get blueGray900 => Color(0XFF2A2F4C); + + // DeepOrange + Color get deepOrange900 => Color(0XFF9A4A00); + + // Gray + Color get gray100 => Color(0XFFF0F2F5); + Color get gray200 => Color(0XFFF0F0F0); + Color get gray400 => Color(0XFFC1C1C1); + Color get gray40001 => Color(0XFFC8C8C8); + Color get gray50 => Color(0XFFF6FAFF); + Color get gray500 => Color(0XFFA3A3A3); + Color get gray600 => Color(0XFF6D6D6D); + Color get gray60001 => Color(0XFF848484); + Color get gray60002 => Color(0XFF757575); + Color get gray60003 => Color(0XFF707070); + + // Green + Color get greenA700 => Color(0XFF019A3E); + Color get greenA70001 => Color(0XFF00BE13); + + // LightGreen + Color get lightGreen300 => Color(0XFFB2DF8A); + Color get lightGreenA700 => Color(0XFF18FF14); + + // Red + Color get red200 => Color(0XFFE3A6A6); + + // Teal + Color get teal100 => Color(0XFFA6CEE3); + Color get teal900 => Color(0XFF0B3F56); + + // White + Color get whiteA700 => Color(0XFFFFFFFF); } PrimaryColors get appTheme => ThemeHelper().themeColor(); diff --git a/lib/widgets/app_bar/appbar_subtitle.dart b/lib/widgets/app_bar/appbar_subtitle.dart new file mode 100644 index 00000000..69c1b639 --- /dev/null +++ b/lib/widgets/app_bar/appbar_subtitle.dart @@ -0,0 +1,38 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class AppbarSubtitle extends StatelessWidget { + AppbarSubtitle({ + Key? key, + required this.text, + this.margin, + this.onTap, + }) : super( + key: key, + ); + + String text; + + EdgeInsetsGeometry? margin; + + Function? onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onTap!.call(); + }, + child: Padding( + padding: margin ?? EdgeInsets.zero, + child: Text( + text, + style: CustomTextStyles.titleMediumBlack.copyWith( + color: appTheme.black900, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/app_bar/appbar_title_circleimage.dart b/lib/widgets/app_bar/appbar_title_circleimage.dart new file mode 100644 index 00000000..ee412291 --- /dev/null +++ b/lib/widgets/app_bar/appbar_title_circleimage.dart @@ -0,0 +1,42 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class AppbarTitleCircleimage extends StatelessWidget { + AppbarTitleCircleimage({ + Key? key, + this.imagePath, + this.margin, + this.onTap, + }) : super( + key: key, + ); + + String? imagePath; + + EdgeInsetsGeometry? margin; + + Function? onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadiusStyle.circleBorder8, + onTap: () { + onTap!.call(); + }, + child: Padding( + padding: margin ?? EdgeInsets.zero, + child: CustomImageView( + imagePath: imagePath, + height: 40.adaptSize, + width: 40.adaptSize, + fit: BoxFit.contain, + radius: BorderRadius.circular( + 20.h, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/app_bar/exit_button.dart b/lib/widgets/app_bar/exit_button.dart new file mode 100644 index 00000000..a93c9a84 --- /dev/null +++ b/lib/widgets/app_bar/exit_button.dart @@ -0,0 +1,38 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class ExitButton extends StatelessWidget { + ExitButton({ + Key? key, + required this.text, + this.margin, + this.onTap, + }) : super( + key: key, + ); + + String text; + + EdgeInsetsGeometry? margin; + + Function? onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onTap!.call(); + }, + child: Padding( + padding: margin ?? EdgeInsets.zero, + child: Text( + text, + style: CustomTextStyles.titleSmallRed200.copyWith( + color: Colors.red, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/custom_bottom_bar.dart b/lib/widgets/custom_bottom_bar.dart new file mode 100644 index 00000000..67ae4e3f --- /dev/null +++ b/lib/widgets/custom_bottom_bar.dart @@ -0,0 +1,166 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; + +class CustomBottomBar extends StatefulWidget { + CustomBottomBar({this.onChanged}); + + Function(BottomBarEnum)? onChanged; + + @override + CustomBottomBarState createState() => CustomBottomBarState(); +} + +class CustomBottomBarState extends State { + int selectedIndex = 2; + + List bottomMenuList = [ + BottomMenuModel( + icon: ImageConstant.imgNav, + activeIcon: ImageConstant.imgNav, + title: "diary".tr, + type: BottomBarEnum.tf, + ), + BottomMenuModel( + icon: ImageConstant.imgNavBlueGray400, + activeIcon: ImageConstant.imgNavBlueGray400, + title: "my_blog".tr, + type: BottomBarEnum.tf, + ), + BottomMenuModel( + icon: ImageConstant.imgNavBlueGray40024x23, + activeIcon: ImageConstant.imgNavBlueGray40024x23, + title: "profile".tr, + type: BottomBarEnum.tf, + ), + BottomMenuModel( + icon: ImageConstant.imgNavPrimary, + activeIcon: ImageConstant.imgNavPrimary, + title: "admin".tr, + type: BottomBarEnum.tf, + ), + BottomMenuModel( + icon: ImageConstant.imgNavBlueGray40024x25, + activeIcon: ImageConstant.imgNavBlueGray40024x25, + title: "chat".tr, + type: BottomBarEnum.tf, + ) + ]; + + @override + Widget build(BuildContext context) { + return Container( + height: 80.v, + decoration: BoxDecoration( + border: Border.all( + color: appTheme.whiteA700, + width: 1.h, + ), + ), + child: BottomNavigationBar( + backgroundColor: Colors.transparent, + showSelectedLabels: false, + showUnselectedLabels: false, + selectedFontSize: 0, + 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(() {}); + }, + ), + ); + } +} + +enum BottomBarEnum { + tf, +} + +class BottomMenuModel { + BottomMenuModel({ + required this.icon, + required this.activeIcon, + this.title, + required this.type, + }); + + String icon; + + String activeIcon; + + String? title; + + 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_outlined_button.dart b/lib/widgets/custom_outlined_button.dart new file mode 100644 index 00000000..4fb4a85c --- /dev/null +++ b/lib/widgets/custom_outlined_button.dart @@ -0,0 +1,73 @@ +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/widgets/base_button.dart'; +import 'package:flutter/material.dart'; + +class CustomOutlinedButton extends BaseButton { + CustomOutlinedButton({ + Key? key, + this.decoration, + this.leftIcon, + this.rightIcon, + this.label, + VoidCallback? onPressed, + ButtonStyle? buttonStyle, + TextStyle? buttonTextStyle, + bool? isDisabled, + Alignment? alignment, + double? height, + double? width, + EdgeInsets? margin, + required String text, + }) : super( + text: text, + onPressed: onPressed, + buttonStyle: buttonStyle, + isDisabled: isDisabled, + buttonTextStyle: buttonTextStyle, + height: height, + alignment: alignment, + width: width, + margin: margin, + ); + + 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( + 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 ?? CustomTextStyles.titleSmallBold, + ), + rightIcon ?? const SizedBox.shrink(), + ], + ), + ), + ); +} diff --git a/lib/widgets/custom_text_form_field.dart b/lib/widgets/custom_text_form_field.dart index 00bfdf3c..a96c5a1c 100644 --- a/lib/widgets/custom_text_form_field.dart +++ b/lib/widgets/custom_text_form_field.dart @@ -27,6 +27,7 @@ class CustomTextFormField extends StatelessWidget { this.fillColor, this.filled = true, this.validator, + this.keyboardType, this.onSaved, }) : super( key: key, @@ -74,6 +75,8 @@ class CustomTextFormField extends StatelessWidget { final bool? filled; + final TextInputType? keyboardType; + final FormFieldValidator? validator; final void Function(String?)? onSaved; @@ -97,10 +100,10 @@ class CustomTextFormField extends StatelessWidget { style: textStyle ?? theme.textTheme.titleMedium, obscureText: obscureText!, textInputAction: textInputAction, - keyboardType: textInputType, maxLines: maxLines ?? 1, decoration: decoration, validator: validator, + keyboardType: keyboardType, onSaved: onSaved, inputFormatters: inputFormatters, ), diff --git a/pubspec.yaml b/pubspec.yaml index 79669938..5bc2b5fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,14 @@ dependencies: flutter_svg: ^2.0.9 equatable: ^2.0.0 validators: ^3.0.0 + hive: ^2.2.3 + hive_flutter: ^1.1.0 + isar_flutter_libs: ^4.0.0-dev.13 + path_provider: ^2.1.0 + fl_chart: ^0.66.0 + flutter_spinkit: ^5.0.0 + flutter_dotenv: ^5.1.0 + carousel_slider: ^4.2.1 dev_dependencies: flutter_test: @@ -24,6 +32,7 @@ flutter: assets: - assets/ - assets/images/ + - .env fonts: - family: Segoe UI fonts: