Merge pull request 'profile' (#3) from profile into main
Reviewed-on: maxxkim/eatvio_app#3
1
.gitignore
vendored
@@ -6,3 +6,4 @@
|
||||
/.flutter-plugins
|
||||
/.flutter-plugins-dependencies
|
||||
/.packages
|
||||
/.env
|
||||
16
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 - переменные окружения
|
||||
|
||||
### Поддержка
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
|
||||
<application
|
||||
android:label="eatvio_app"
|
||||
android:label="Eatvio"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
BIN
assets/images/image_not_found copy.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/images/img_.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
3
assets/images/img_area_1.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="305" height="88" viewBox="0 0 305 88" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M101 9C50 9 0 0 0 0V88H304.5V25C304.5 25 253 18 202 18C151 18 151 9 101 9Z" fill="#A6CEE3" fill-opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 226 B |
3
assets/images/img_arrow_left copy.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="13" height="26" viewBox="0 0 13 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.843 25.9999C10.5655 26.0008 10.2913 25.9396 10.0406 25.8207C9.78988 25.7018 9.56899 25.5282 9.39417 25.3128L0.422554 14.171C0.149353 13.8387 0 13.4219 0 12.9918C0 12.5617 0.149353 12.1449 0.422554 11.8126L9.70994 0.670834C10.0252 0.29161 10.4783 0.0531301 10.9695 0.00785776C11.4606 -0.0374146 11.9497 0.114229 12.329 0.429428C12.7083 0.744627 12.9469 1.19756 12.9921 1.68859C13.0374 2.17962 12.8857 2.66853 12.5705 3.04775L4.26753 13.0011L12.2918 22.9544C12.519 23.227 12.6633 23.5589 12.7076 23.9109C12.752 24.2629 12.6945 24.6202 12.5421 24.9406C12.3897 25.261 12.1487 25.531 11.8476 25.7187C11.5464 25.9065 11.1978 26.004 10.843 25.9999Z" fill="#323F4B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 776 B |
3
assets/images/img_close.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="22" height="22" rx="11" fill="#2F80ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 157 B |
3
assets/images/img_close_blue_gray_900.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.57391 7.99998L16.4098 1.43756C16.7523 1.10869 16.7523 0.575499 16.4098 0.246656C16.0672 -0.0822186 15.5118 -0.0822186 15.1692 0.246656L8.33332 6.80908L1.49746 0.246656C1.15488 -0.0822186 0.599478 -0.0822186 0.256933 0.246656C-0.0856118 0.57553 -0.0856444 1.10872 0.256933 1.43756L7.09279 7.99998L0.256933 14.5624C-0.0856444 14.8913 -0.0856444 15.4245 0.256933 15.7533C0.599511 16.0822 1.15491 16.0822 1.49746 15.7533L8.33332 9.19089L15.1691 15.7533C15.5117 16.0822 16.0672 16.0822 16.4097 15.7533C16.7522 15.4244 16.7522 14.8913 16.4097 14.5624L9.57391 7.99998Z" fill="#2A2F4C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 695 B |
4
assets/images/img_crop.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.52588e-05 18.3333V26.6667C1.52588e-05 27.5507 0.351206 28.3986 0.976327 29.0237C1.60145 29.6488 2.44929 30 3.33335 30H26.6667C27.5507 30 28.3986 29.6488 29.0237 29.0237C29.6488 28.3986 30 27.5507 30 26.6667V3.33333C30 2.44928 29.6488 1.60143 29.0237 0.97631C28.3986 0.351189 27.5507 0 26.6667 0H18.3333V3.33333H26.6667V26.6667H3.33335V18.3333H1.52588e-05Z" fill="white"/>
|
||||
<path d="M3.8147e-06 6.66666H6.66667V0H10V6.66666H16.6667V9.99999H10V16.6667H6.66667V9.99999H3.8147e-06" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 607 B |
4
assets/images/img_edit.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.296 0.0665879C15.1167 0.136927 14.7967 0.44642 10.9816 4.25881C8.71364 6.52374 6.84301 8.41939 6.82543 8.46863C6.76565 8.61634 6.29448 11.043 6.29448 11.1943C6.29448 11.4686 6.53007 11.7042 6.80433 11.7042C6.95553 11.7042 9.38172 11.233 9.5294 11.1732C9.57863 11.1556 11.4739 9.28456 13.7383 7.01612C17.7292 3.02084 17.8558 2.8872 17.9332 2.68321C17.9964 2.50736 18.007 2.41944 17.9964 2.15567C17.9754 1.68088 17.9085 1.57888 17.1631 0.833287C16.4106 0.0771388 16.3192 0.0208672 15.8234 0.00328238C15.5492 -0.00726854 15.4542 0.00679935 15.296 0.0665879ZM16.3755 1.61757C16.7165 1.95872 16.9135 2.18029 16.9135 2.22601C16.9135 2.26821 16.7869 2.42296 16.597 2.61287L16.2805 2.9294L15.6757 2.32097L15.0674 1.71605L15.3839 1.39952C15.5703 1.2096 15.7285 1.08299 15.7707 1.08299C15.8129 1.08299 16.0414 1.28346 16.3755 1.61757ZM12.272 6.93875L9.03713 10.1779L8.29169 10.3256C7.88381 10.4065 7.54274 10.4663 7.5357 10.4592C7.52867 10.4522 7.59548 10.1111 7.67987 9.69957L7.83458 8.95045L11.066 5.71836L14.2939 2.48978L14.8987 3.0947L15.507 3.70313L12.272 6.93875Z" fill="#2F80ED"/>
|
||||
<path d="M1.33265 2.95725C0.699727 3.17178 0.246135 3.64657 0.0562594 4.27963C0.00703243 4.45196 0 5.05336 0 10.4414C0 16.2725 0 16.4132 0.0703243 16.6312C0.256684 17.2432 0.717308 17.7074 1.33265 17.9184L1.56472 17.9958L7.5458 17.9993C12.9432 17.9993 13.5445 17.9923 13.7168 17.9431C14.3673 17.7496 14.849 17.2713 15.0599 16.6101L15.1373 16.3639V12.2667C15.1373 8.2327 15.1373 8.16588 15.067 8.05334C14.8701 7.72626 14.3602 7.70516 14.1422 8.01465C14.086 8.09202 14.0824 8.4191 14.0649 12.1787L14.0473 16.2584L13.9523 16.4343C13.8433 16.6418 13.7519 16.7297 13.5374 16.8422L13.3792 16.9267H7.55986H1.74053L1.56472 16.8317C1.35726 16.7227 1.26935 16.6312 1.15683 16.4167L1.07245 16.2584V10.4378V4.61726L1.16738 4.44141C1.27639 4.23391 1.36781 4.14598 1.5823 4.03344L1.74053 3.94903L5.81934 3.93145C9.57817 3.91386 9.90518 3.91035 9.98253 3.85407C10.1302 3.74857 10.197 3.6114 10.197 3.4039C10.197 3.18585 10.1267 3.05221 9.96495 2.94318C9.8665 2.87987 9.71882 2.87636 5.71385 2.87636L1.56472 2.87987L1.33265 2.95725Z" fill="#2F80ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/images/img_ellipse_1.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/images/img_ellipse_1_1.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/images/img_ellipse_1_25x25.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
assets/images/img_ellipse_1_32x32.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/images/img_ellipse_1_34x34.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
assets/images/img_ellipse_1_42x42.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/images/img_ellipse_2.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
assets/images/img_ellipse_3.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
3
assets/images/img_eye.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.06407 7C7.06407 7.63782 7.32457 8.24952 7.78828 8.70053C8.25199 9.15154 8.88091 9.40491 9.53669 9.40491C10.1925 9.40491 10.8214 9.15154 11.2851 8.70053C11.7488 8.24952 12.0093 7.63782 12.0093 7C12.0093 6.36218 11.7488 5.75048 11.2851 5.29947C10.8214 4.84847 10.1925 4.59509 9.53669 4.59509C8.88091 4.59509 8.25199 4.84847 7.78828 5.29947C7.32457 5.75048 7.06407 6.36218 7.06407 7ZM19.1225 6.44601C17.0296 2.15798 13.866 0 9.62499 0C5.3818 0 2.22038 2.15798 0.127484 6.44816C0.0435372 6.62105 0 6.80987 0 7.00107C0 7.19227 0.0435372 7.3811 0.127484 7.55399C2.22038 11.842 5.38401 14 9.62499 14C13.8682 14 17.0296 11.842 19.1225 7.55184C19.2925 7.20399 19.2925 6.80031 19.1225 6.44601ZM9.53669 10.7791C7.39081 10.7791 5.65114 9.08712 5.65114 7C5.65114 4.91288 7.39081 3.22086 9.53669 3.22086C11.6826 3.22086 13.4222 4.91288 13.4222 7C13.4222 9.08712 11.6826 10.7791 9.53669 10.7791Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1012 B |
3
assets/images/img_favorite copy.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="9" height="8" viewBox="0 0 9 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.36798 7.07248C4.96514 7.7104 4.03486 7.7104 3.63202 7.07248L0.160242 1.5746C-0.271405 0.89105 0.219787 -5.85952e-05 1.02822 -5.85306e-05L7.97178 -5.79756e-05C8.78021 -5.7911e-05 9.27141 0.89105 8.83976 1.5746L5.36798 7.07248Z" fill="#0C4056"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 355 B |
3
assets/images/img_favorite_white_a700.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.05654 2.15152C5.3923 -1.93687 0 0.290355 0 4.74398C0 8.08832 7.39255 13.3198 8.05654 14C8.72508 13.3198 15.75 8.08832 15.75 4.74398C15.75 0.32412 10.7257 -1.93687 8.05654 2.15152Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 311 B |
3
assets/images/img_grid.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.70596 14.6708C9.86516 14.6708 10.806 15.7146 10.806 17.0038V21.667C10.806 22.2857 10.5847 22.8791 10.1909 23.3166C9.79705 23.7541 9.26291 23.9999 8.70596 23.9999H2.09999C1.54304 23.9999 1.0089 23.7541 0.615073 23.3166C0.221249 22.8791 0 22.2857 0 21.667V17.0038C0 15.7146 0.940796 14.6708 2.09999 14.6708H8.70596ZM21.8999 14.6708C23.0591 14.6708 23.9999 15.7146 23.9999 17.0038V21.667C23.9999 22.2857 23.7786 22.8791 23.3848 23.3166C22.991 23.7541 22.4569 23.9999 21.8999 23.9999H15.2939C14.737 23.9999 14.2028 23.7541 13.809 23.3166C13.4152 22.8791 13.1939 22.2857 13.1939 21.667V17.0038C13.1939 15.7146 14.1347 14.6708 15.2939 14.6708H21.8999ZM8.70596 16.6705H2.09999C2.02043 16.6705 1.94412 16.7056 1.88786 16.7681C1.8316 16.8306 1.79999 16.9154 1.79999 17.0038V21.667C1.79999 21.8523 1.93439 22.0002 2.09999 22.0002H8.70596C8.78553 22.0002 8.86183 21.9651 8.91809 21.9026C8.97435 21.8401 9.00596 21.7554 9.00596 21.667V17.0038C9.00596 16.9154 8.97435 16.8306 8.91809 16.7681C8.86183 16.7056 8.78553 16.6705 8.70596 16.6705ZM21.8999 16.6705H15.2939C15.2144 16.6705 15.1381 16.7056 15.0818 16.7681C15.0255 16.8306 14.9939 16.9154 14.9939 17.0038V21.667C14.9939 21.8523 15.1283 22.0002 15.2939 22.0002H21.8999C21.9795 22.0002 22.0558 21.9651 22.112 21.9026C22.1683 21.8401 22.1999 21.7554 22.1999 21.667V17.0038C22.1999 16.9154 22.1683 16.8306 22.112 16.7681C22.0558 16.7056 21.9795 16.6705 21.8999 16.6705ZM21.8999 0C23.0591 0 23.9999 1.04515 23.9999 2.33293V9.66501C23.9999 10.2837 23.7786 10.8771 23.3848 11.3146C22.991 11.7522 22.4569 11.9979 21.8999 11.9979H2.09999C1.54304 11.9979 1.0089 11.7522 0.615073 11.3146C0.221249 10.8771 0 10.2837 0 9.66501V2.33293C1.96854e-05 1.74745 0.198204 1.18337 0.555213 0.752683C0.912222 0.321992 1.40195 0.0561759 1.92719 0.00799869L2.09999 0H21.8999ZM21.8999 1.99966H2.09999L2.03159 2.00899C1.96578 2.02611 1.90711 2.06741 1.86507 2.12621C1.82304 2.18501 1.8001 2.25785 1.79999 2.33293V9.66501C1.79999 9.84898 1.93439 9.99829 2.09999 9.99829H21.8999C21.9795 9.99829 22.0558 9.96318 22.112 9.90067C22.1683 9.83817 22.1999 9.7534 22.1999 9.66501V2.33293C22.1999 2.24454 22.1683 2.15977 22.112 2.09727C22.0558 2.03477 21.9795 1.99966 21.8999 1.99966Z" fill="#323F4B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
3
assets/images/img_icon_heart.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M29.2913 12.6118C28.7805 12.1008 28.1741 11.6955 27.5066 11.4189C26.8392 11.1423 26.1238 11 25.4013 11C24.6788 11 23.9634 11.1423 23.2959 11.4189C22.6285 11.6955 22.022 12.1008 21.5113 12.6118L20.4513 13.6718L19.3913 12.6118C18.3596 11.5801 16.9603 11.0005 15.5013 11.0005C14.0423 11.0005 12.643 11.5801 11.6113 12.6118C10.5796 13.6435 10 15.0428 10 16.5018C10 17.9609 10.5796 19.3601 11.6113 20.3918L12.6713 21.4518L20.4513 29.2318L28.2313 21.4518L29.2913 20.3918C29.8023 19.8811 30.2076 19.2746 30.4842 18.6072C30.7608 17.9397 30.9031 17.2243 30.9031 16.5018C30.9031 15.7793 30.7608 15.0639 30.4842 14.3965C30.2076 13.729 29.8023 13.1226 29.2913 12.6118V12.6118Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 861 B |
3
assets/images/img_icon_message_square.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M29 23C29 23.5304 28.7893 24.0391 28.4142 24.4142C28.0391 24.7893 27.5304 25 27 25H15L11 29V13C11 12.4696 11.2107 11.9609 11.5858 11.5858C11.9609 11.2107 12.4696 11 13 11H27C27.5304 11 28.0391 11.2107 28.4142 11.5858C28.7893 11.9609 29 12.4696 29 13V23Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 450 B |
5
assets/images/img_icon_more_horizontal.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 21C20.5523 21 21 20.5523 21 20C21 19.4477 20.5523 19 20 19C19.4477 19 19 19.4477 19 20C19 20.5523 19.4477 21 20 21Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M27 21C27.5523 21 28 20.5523 28 20C28 19.4477 27.5523 19 27 19C26.4477 19 26 19.4477 26 20C26 20.5523 26.4477 21 27 21Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13 21C13.5523 21 14 20.5523 14 20C14 19.4477 13.5523 19 13 19C12.4477 19 12 19.4477 12 20C12 20.5523 12.4477 21 13 21Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
7
assets/images/img_icon_share_2.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M26 16C27.6569 16 29 14.6569 29 13C29 11.3431 27.6569 10 26 10C24.3431 10 23 11.3431 23 13C23 14.6569 24.3431 16 26 16Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 23C15.6569 23 17 21.6569 17 20C17 18.3431 15.6569 17 14 17C12.3431 17 11 18.3431 11 20C11 21.6569 12.3431 23 14 23Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26 30C27.6569 30 29 28.6569 29 27C29 25.3431 27.6569 24 26 24C24.3431 24 23 25.3431 23 27C23 28.6569 24.3431 30 26 30Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.59 21.5098L23.42 25.4898" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M23.41 14.5098L16.59 18.4898" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 986 B |
11
assets/images/img_like_5.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_147_85)">
|
||||
<path d="M14 8.31257C14 7.962 13.8606 7.63532 13.6202 7.3944C13.8927 7.09632 14.0315 6.69672 13.9936 6.28082C13.9253 5.5394 13.2574 4.9584 12.4722 4.9584H8.869C9.0475 4.41647 9.33332 3.42307 9.33332 2.62507C9.33332 1.35982 8.25825 0.291748 7.58332 0.291748C6.97782 0.291748 6.5444 0.632998 6.52632 0.646998C6.4569 0.702424 6.41665 0.786424 6.41665 0.875072V2.85315L4.73607 6.49257L4.66665 6.52815V6.41672C4.66665 6.25572 4.53597 6.12505 4.37497 6.12505H1.45832C0.653926 6.12507 0 6.779 0 7.5834V12.2501C0 13.0545 0.653926 13.7084 1.45832 13.7084H3.20832C3.8389 13.7084 4.3779 13.3059 4.5809 12.7441C5.06625 12.9938 5.71957 13.1251 6.125 13.1251H11.4794C12.1147 13.1251 12.6706 12.6969 12.8013 12.1066C12.8683 11.8021 12.8293 11.4958 12.6962 11.2298C13.1267 11.0134 13.4167 10.5683 13.4167 10.0626C13.4167 9.85607 13.3694 9.65832 13.2802 9.47982C13.7107 9.26282 14 8.81832 14 8.31257ZM12.7697 9.0324C12.6572 9.04582 12.5621 9.12222 12.5259 9.23015C12.4904 9.33807 12.5201 9.45647 12.6029 9.53465C12.7511 9.67407 12.8334 9.8619 12.8334 10.0626C12.8334 10.4306 12.5551 10.7398 12.187 10.7824C12.0745 10.7958 11.9794 10.8722 11.9432 10.9801C11.9076 11.0881 11.9374 11.2065 12.0202 11.2846C12.2121 11.4655 12.2891 11.7192 12.2314 11.9806C12.1596 12.3061 11.8435 12.5417 11.4795 12.5417H6.125C5.65132 12.5417 4.85682 12.3195 4.5815 12.0436C4.49807 11.9607 4.37207 11.9362 4.26357 11.9806C4.1545 12.0255 4.08332 12.1322 4.08332 12.2501C4.08332 12.7325 3.69075 13.1251 3.20832 13.1251H1.45832C0.975898 13.1251 0.583324 12.7325 0.583324 12.2501V7.5834C0.583324 7.10097 0.975898 6.7084 1.45832 6.7084H4.08332V7.00007C4.08332 7.101 4.13582 7.1949 4.22215 7.24857C4.30732 7.2999 4.41465 7.30515 4.50565 7.26082L5.08897 6.96915C5.1479 6.93997 5.19515 6.89097 5.22315 6.8309L6.97315 3.03922C6.99065 3.00072 6.99997 2.95872 6.99997 2.91672V1.03315C7.12132 0.966072 7.33193 0.875072 7.58332 0.875072C7.903 0.875072 8.75 1.66957 8.75 2.62507C8.75 3.65175 8.19118 5.13165 8.18593 5.14625C8.1521 5.2355 8.16375 5.33642 8.218 5.41575C8.27282 5.4945 8.36268 5.54175 8.45832 5.54175H12.4722C12.9587 5.54175 13.3717 5.89 13.4126 6.3345C13.4435 6.667 13.2836 6.98317 12.9972 7.15992C12.908 7.21475 12.8549 7.31392 12.859 7.4195C12.8631 7.52507 12.9237 7.61957 13.0171 7.668C13.2638 7.79342 13.4166 8.04075 13.4166 8.31257C13.4167 8.68065 13.1384 8.98982 12.7697 9.0324Z" fill="#717171"/>
|
||||
<path d="M4.375 6.70825C4.214 6.70825 4.08333 6.83893 4.08333 6.99993V12.2499C4.08333 12.4109 4.214 12.5416 4.375 12.5416C4.536 12.5416 4.66668 12.4109 4.66668 12.2499V6.99993C4.66668 6.83893 4.536 6.70825 4.375 6.70825Z" fill="#717171"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_147_85">
|
||||
<rect width="14" height="14" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
19
assets/images/img_like_6.svg
Normal file
|
After Width: | Height: | Size: 18 KiB |
3
assets/images/img_line_1.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="305" height="38" viewBox="0 0 305 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.28894 21C0.28894 21 38.2889 9.5 76.2889 9.5C114.289 9.5 114.289 1 152.289 1C190.289 1 190.289 37 228.289 37C266.289 37 304.289 21 304.289 21" stroke="#E3A6A6" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 295 B |
3
assets/images/img_line_2.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="305" height="30" viewBox="0 0 305 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.217651 22C0.217651 22 38.2177 13.5 76.2177 13.5C114.218 13.5 114.218 1 152.218 1C190.218 1 190.218 29 228.218 29C266.218 29 304.218 22 304.218 22" stroke="#1F78B4" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
3
assets/images/img_line_3.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="305" height="14" viewBox="0 0 305 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.0262456 0.999773C0.0262456 0.999773 37.5269 1.99979 75.5276 1.99979C113.528 1.99979 113.528 1.99979 151.529 1.99979C189.53 1.99979 189.53 13 227.53 13C265.531 13 304.032 0.999773 304.032 0.999773" stroke="#B2DF8A" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 350 B |
4
assets/images/img_megaphone.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.625 1.1875C16.9399 1.1875 17.242 1.31261 17.4647 1.53531C17.6874 1.75801 17.8125 2.06006 17.8125 2.375V11.875C17.8125 12.1899 17.6874 12.492 17.4647 12.7147C17.242 12.9374 16.9399 13.0625 16.625 13.0625H5.24162C4.61179 13.0626 4.0078 13.3129 3.5625 13.7584L1.1875 16.1334V2.375C1.1875 2.06006 1.31261 1.75801 1.53531 1.53531C1.75801 1.31261 2.06006 1.1875 2.375 1.1875H16.625ZM2.375 0C1.74511 0 1.14102 0.250223 0.695621 0.695621C0.250223 1.14102 0 1.74511 0 2.375L0 17.5667C2.49552e-05 17.6842 0.0349037 17.799 0.100222 17.8967C0.16554 17.9943 0.258361 18.0704 0.366937 18.1153C0.475513 18.1602 0.594962 18.1718 0.710168 18.1488C0.825373 18.1258 0.931155 18.0691 1.01412 17.9859L4.40206 14.5979C4.62471 14.3752 4.92671 14.2501 5.24162 14.25H16.625C17.2549 14.25 17.859 13.9998 18.3044 13.5544C18.7498 13.109 19 12.5049 19 11.875V2.375C19 1.74511 18.7498 1.14102 18.3044 0.695621C17.859 0.250223 17.2549 0 16.625 0L2.375 0Z" fill="white"/>
|
||||
<path d="M3.5625 4.15625C3.5625 3.99878 3.62506 3.84776 3.73641 3.73641C3.84776 3.62506 3.99878 3.5625 4.15625 3.5625H14.8438C15.0012 3.5625 15.1522 3.62506 15.2636 3.73641C15.3749 3.84776 15.4375 3.99878 15.4375 4.15625C15.4375 4.31372 15.3749 4.46474 15.2636 4.57609C15.1522 4.68744 15.0012 4.75 14.8438 4.75H4.15625C3.99878 4.75 3.84776 4.68744 3.73641 4.57609C3.62506 4.46474 3.5625 4.31372 3.5625 4.15625ZM3.5625 7.125C3.5625 6.96753 3.62506 6.81651 3.73641 6.70516C3.84776 6.59381 3.99878 6.53125 4.15625 6.53125H14.8438C15.0012 6.53125 15.1522 6.59381 15.2636 6.70516C15.3749 6.81651 15.4375 6.96753 15.4375 7.125C15.4375 7.28247 15.3749 7.4335 15.2636 7.54484C15.1522 7.65619 15.0012 7.71875 14.8438 7.71875H4.15625C3.99878 7.71875 3.84776 7.65619 3.73641 7.54484C3.62506 7.4335 3.5625 7.28247 3.5625 7.125ZM3.5625 10.0938C3.5625 9.93628 3.62506 9.78526 3.73641 9.67391C3.84776 9.56256 3.99878 9.5 4.15625 9.5H10.0938C10.2512 9.5 10.4022 9.56256 10.5136 9.67391C10.6249 9.78526 10.6875 9.93628 10.6875 10.0938C10.6875 10.2512 10.6249 10.4022 10.5136 10.5136C10.4022 10.6249 10.2512 10.6875 10.0938 10.6875H4.15625C3.99878 10.6875 3.84776 10.6249 3.73641 10.5136C3.62506 10.4022 3.5625 10.2512 3.5625 10.0938Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
9
assets/images/img_nav.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H20.6366V24.9999H1V1Z" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.818 6.33325H11.3633" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.818 13H11.3633" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.818 19H11.3633" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.81793 6.45477L5.90859 7.54544L8.63591 4.81812" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.81793 12.9999L5.90859 14.0906L8.63591 11.3633" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="6.45459" cy="19.5461" r="1.63666" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1016 B |
3
assets/images/img_nav_blue_gray_400.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.70596 14.6708C9.86516 14.6708 10.806 15.7146 10.806 17.0038V21.667C10.806 22.2857 10.5847 22.8791 10.1909 23.3166C9.79705 23.7541 9.26291 23.9999 8.70596 23.9999H2.09999C1.54304 23.9999 1.0089 23.7541 0.615073 23.3166C0.221249 22.8791 0 22.2857 0 21.667V17.0038C0 15.7146 0.940796 14.6708 2.09999 14.6708H8.70596ZM21.8999 14.6708C23.0591 14.6708 23.9999 15.7146 23.9999 17.0038V21.667C23.9999 22.2857 23.7786 22.8791 23.3848 23.3166C22.991 23.7541 22.4569 23.9999 21.8999 23.9999H15.2939C14.737 23.9999 14.2028 23.7541 13.809 23.3166C13.4152 22.8791 13.1939 22.2857 13.1939 21.667V17.0038C13.1939 15.7146 14.1347 14.6708 15.2939 14.6708H21.8999ZM8.70596 16.6705H2.09999C2.02043 16.6705 1.94412 16.7056 1.88786 16.7681C1.8316 16.8306 1.79999 16.9154 1.79999 17.0038V21.667C1.79999 21.8523 1.93439 22.0002 2.09999 22.0002H8.70596C8.78553 22.0002 8.86183 21.9651 8.91809 21.9026C8.97435 21.8401 9.00596 21.7554 9.00596 21.667V17.0038C9.00596 16.9154 8.97435 16.8306 8.91809 16.7681C8.86183 16.7056 8.78553 16.6705 8.70596 16.6705ZM21.8999 16.6705H15.2939C15.2144 16.6705 15.1381 16.7056 15.0818 16.7681C15.0255 16.8306 14.9939 16.9154 14.9939 17.0038V21.667C14.9939 21.8523 15.1283 22.0002 15.2939 22.0002H21.8999C21.9795 22.0002 22.0558 21.9651 22.112 21.9026C22.1683 21.8401 22.1999 21.7554 22.1999 21.667V17.0038C22.1999 16.9154 22.1683 16.8306 22.112 16.7681C22.0558 16.7056 21.9795 16.6705 21.8999 16.6705ZM21.8999 0C23.0591 0 23.9999 1.04515 23.9999 2.33293V9.66501C23.9999 10.2837 23.7786 10.8771 23.3848 11.3146C22.991 11.7522 22.4569 11.9979 21.8999 11.9979H2.09999C1.54304 11.9979 1.0089 11.7522 0.615073 11.3146C0.221249 10.8771 0 10.2837 0 9.66501V2.33293C1.96854e-05 1.74745 0.198204 1.18337 0.555213 0.752683C0.912222 0.321992 1.40195 0.0561759 1.92719 0.00799869L2.09999 0H21.8999ZM21.8999 1.99966H2.09999L2.03159 2.00899C1.96578 2.02611 1.90711 2.06741 1.86507 2.12621C1.82304 2.18501 1.8001 2.25785 1.79999 2.33293V9.66501C1.79999 9.84898 1.93439 9.99829 2.09999 9.99829H21.8999C21.9795 9.99829 22.0558 9.96318 22.112 9.90067C22.1683 9.83817 22.1999 9.7534 22.1999 9.66501V2.33293C22.1999 2.24454 22.1683 2.15977 22.112 2.09727C22.0558 2.03477 21.9795 1.99966 21.8999 1.99966Z" fill="#7B8794"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
4
assets/images/img_nav_blue_gray_400_24x23.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.0926 13.2793V24.9999H14.9533V19.4186H10.4866V24.9999H4.34863V13.2793" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1 11.6046L12.7206 1L24.4419 11.6046" stroke="#7B8794" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 399 B |
4
assets/images/img_nav_blue_gray_400_24x25.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.9709 1.56935C22.3871 1.56935 22.7863 1.73469 23.0806 2.029C23.3749 2.32331 23.5402 2.72248 23.5402 3.1387V15.6935C23.5402 16.1097 23.3749 16.5089 23.0806 16.8032C22.7863 17.0975 22.3871 17.2628 21.9709 17.2628H6.92711C6.09474 17.263 5.29653 17.5938 4.70805 18.1825L1.56935 21.3212V3.1387C1.56935 2.72248 1.73469 2.32331 2.029 2.029C2.32331 1.73469 2.72248 1.56935 3.1387 1.56935H21.9709ZM3.1387 0C2.30626 0 1.50792 0.330683 0.919304 0.919304C0.330683 1.50792 0 2.30626 0 3.1387L0 23.2154C3.29797e-05 23.3706 0.0461272 23.5224 0.132449 23.6515C0.21877 23.7805 0.341439 23.8811 0.484928 23.9404C0.628417 23.9997 0.786277 24.0151 0.938527 23.9847C1.09078 23.9542 1.23057 23.8793 1.34022 23.7694L5.81758 19.292C6.11182 18.9977 6.51092 18.8323 6.92711 18.8322H21.9709C22.8033 18.8322 23.6017 18.5015 24.1903 17.9129C24.7789 17.3243 25.1096 16.5259 25.1096 15.6935V3.1387C25.1096 2.30626 24.7789 1.50792 24.1903 0.919304C23.6017 0.330683 22.8033 0 21.9709 0L3.1387 0Z" fill="#7B8794"/>
|
||||
<path d="M4.70807 5.49268C4.70807 5.28457 4.79074 5.08499 4.93789 4.93783C5.08505 4.79068 5.28463 4.70801 5.49274 4.70801H19.6169C19.825 4.70801 20.0246 4.79068 20.1717 4.93783C20.3189 5.08499 20.4016 5.28457 20.4016 5.49268C20.4016 5.70079 20.3189 5.90038 20.1717 6.04753C20.0246 6.19469 19.825 6.27736 19.6169 6.27736H5.49274C5.28463 6.27736 5.08505 6.19469 4.93789 6.04753C4.79074 5.90038 4.70807 5.70079 4.70807 5.49268ZM4.70807 9.41606C4.70807 9.20795 4.79074 9.00836 4.93789 8.86121C5.08505 8.71405 5.28463 8.63138 5.49274 8.63138H19.6169C19.825 8.63138 20.0246 8.71405 20.1717 8.86121C20.3189 9.00836 20.4016 9.20795 20.4016 9.41606C20.4016 9.62416 20.3189 9.82375 20.1717 9.9709C20.0246 10.1181 19.825 10.2007 19.6169 10.2007H5.49274C5.28463 10.2007 5.08505 10.1181 4.93789 9.9709C4.79074 9.82375 4.70807 9.62416 4.70807 9.41606ZM4.70807 13.3394C4.70807 13.1313 4.79074 12.9317 4.93789 12.7846C5.08505 12.6374 5.28463 12.5548 5.49274 12.5548H13.3395C13.5476 12.5548 13.7472 12.6374 13.8943 12.7846C14.0415 12.9317 14.1242 13.1313 14.1242 13.3394C14.1242 13.5475 14.0415 13.7471 13.8943 13.8943C13.7472 14.0414 13.5476 14.1241 13.3395 14.1241H5.49274C5.28463 14.1241 5.08505 14.0414 4.93789 13.8943C4.79074 13.7471 4.70807 13.5475 4.70807 13.3394Z" fill="#7B8794"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
3
assets/images/img_nav_primary.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9999 0C8.32396 0 5.33331 2.83325 5.33331 6.31576C5.33331 9.79827 8.32396 12.6315 11.9999 12.6315C15.6759 12.6315 18.6666 9.79827 18.6666 6.31576C18.6666 2.83325 15.6759 0 11.9999 0ZM11.9999 10.1052C9.79462 10.1052 7.99996 8.40502 7.99996 6.31576C7.99996 4.22651 9.79462 2.5263 11.9999 2.5263C14.2053 2.5263 15.9999 4.22651 15.9999 6.31576C15.9999 8.40502 14.2053 10.1052 11.9999 10.1052ZM23.9999 23.9999V22.7367C23.9999 17.8622 19.8119 13.8947 14.6666 13.8947H9.33329C4.18665 13.8947 0 17.8622 0 22.7367V23.9999H2.66665V22.7367C2.66665 19.2542 5.65731 16.421 9.33329 16.421H14.6666C18.3426 16.421 21.3332 19.2542 21.3332 22.7367V23.9999H23.9999Z" fill="#2F80ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 780 B |
3
assets/images/img_overflowmenu.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 3.59955C8.16667 3.98445 8.16667 4.9467 7.5 5.3316L1.5 8.7957C0.833334 9.1806 1.22038e-07 8.69948 1.12858e-07 7.92968L3.02403e-08 1.00147C2.10605e-08 0.231675 0.833333 -0.249451 1.5 0.135449L7.5 3.59955Z" fill="#0C4056"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
BIN
assets/images/img_rectangle_14.png
Normal file
|
After Width: | Height: | Size: 398 KiB |
BIN
assets/images/img_rectangle_14_220x341.png
Normal file
|
After Width: | Height: | Size: 497 KiB |
BIN
assets/images/img_rectangle_14_220x375.png
Normal file
|
After Width: | Height: | Size: 646 KiB |
BIN
assets/images/img_rectangle_14_250x311.png
Normal file
|
After Width: | Height: | Size: 996 KiB |
4
assets/images/img_search.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9991 12.9995C24.9991 11.7945 24.0222 10.8176 22.8171 10.8176H21.4402C21.2374 10.0294 20.9247 9.27368 20.5112 8.57268L21.4842 7.59971C22.0566 7.05306 22.288 6.23932 22.089 5.47327C21.89 4.70722 21.2918 4.10899 20.5258 3.91C19.7597 3.71102 18.946 3.94247 18.3993 4.51483L17.4263 5.48779C16.7255 5.07409 15.9697 4.76135 15.1814 4.55883V3.18188C15.1814 1.97684 14.2046 0.999962 12.9995 0.999962C11.7945 0.999962 10.8176 1.97684 10.8176 3.18188V4.55883C10.0294 4.76159 9.27368 5.07432 8.57268 5.48779L7.59971 4.51383C6.74412 3.68258 5.37956 3.69253 4.53618 4.53618C3.69281 5.37982 3.6833 6.74439 4.51483 7.59971L5.48779 8.57268C5.07409 9.27356 4.76135 10.0293 4.55883 10.8176H3.18188C1.97684 10.8176 0.999962 11.7945 0.999962 12.9995C0.999962 14.2046 1.97684 15.1814 3.18188 15.1814H4.55883C4.76159 15.9696 5.07432 16.7253 5.48779 17.4263L4.51483 18.3993C3.94247 18.946 3.71102 19.7597 3.91 20.5258C4.10899 21.2918 4.70722 21.89 5.47327 22.089C6.23932 22.288 7.05306 22.0566 7.59971 21.4842L8.57268 20.5112C9.27356 20.9249 10.0293 21.2377 10.8176 21.4402V22.8181C10.8176 24.0232 11.7945 25.0001 12.9995 25.0001C14.2046 25.0001 15.1814 24.0232 15.1814 22.8181V21.4402C15.9696 21.2374 16.7253 20.9247 17.4263 20.5112L18.3993 21.4842C18.946 22.0566 19.7597 22.288 20.5258 22.089C21.2918 21.89 21.89 21.2918 22.089 20.5258C22.288 19.7597 22.0566 18.946 21.4842 18.3993L20.5112 17.4263C20.9249 16.7255 21.2377 15.9697 21.4402 15.1814H22.8181C24.0226 15.1803 24.9985 14.2039 24.9991 12.9995Z" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="12.9996" cy="12.9996" r="3.27288" stroke="#323F4B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
13
assets/images/img_send_1.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_147_71)">
|
||||
<path d="M16.6557 0.534608C16.655 0.531941 16.654 0.529274 16.6533 0.526606C16.6415 0.473599 16.6213 0.422258 16.596 0.372585C16.5894 0.359916 16.5828 0.347581 16.5755 0.334913C16.5456 0.284572 16.5109 0.236232 16.4661 0.193226C16.4213 0.15022 16.3706 0.116882 16.3182 0.0882115C16.3057 0.0812105 16.2932 0.0752096 16.28 0.0688754C16.2268 0.0438719 16.1723 0.0235357 16.1154 0.0122008C16.1136 0.0118674 16.1126 0.011534 16.1112 0.0112006C16.0518 -0.000134303 15.9914 -0.0018012 15.931 0.00186598C15.9178 0.00253274 15.9046 0.00386626 15.8914 0.00553316C15.8303 0.0125341 15.7695 0.0248692 15.7115 0.0475391L0.431919 6.04838C0.159312 6.1554 -0.0132821 6.41577 0.000955993 6.69781C0.0151941 6.98018 0.213139 7.22322 0.49547 7.30423L7.08251 9.20149L9.05848 15.525C9.14321 15.7958 9.39637 15.9858 9.69017 15.9994C9.70162 15.9998 9.71274 16.0001 9.7242 16.0001C10.0048 16.0001 10.2597 15.8374 10.367 15.5857L16.6179 0.916995C16.6179 0.916661 16.6179 0.916661 16.6179 0.916328C16.6411 0.861654 16.6536 0.804646 16.6609 0.746971C16.6627 0.732635 16.6644 0.718633 16.6651 0.704298C16.6686 0.647623 16.6672 0.590615 16.6557 0.534608ZM2.776 6.56846L13.0427 2.53622L7.44263 7.91264L2.776 6.56846ZM9.82456 13.3357L8.42471 8.85544L14.0245 3.47969L9.82456 13.3357Z" fill="#2A2F4C"/>
|
||||
<path d="M6.04749 10.1945C5.77627 9.93417 5.33663 9.93417 5.06541 10.1945L3.32905 11.8615C3.05784 12.1218 3.05784 12.5439 3.32905 12.8043C3.46484 12.9346 3.64229 12.9996 3.8201 12.9996C3.9979 12.9996 4.17535 12.9346 4.31114 12.8043L6.04749 11.1374C6.31871 10.877 6.31871 10.4549 6.04749 10.1945Z" fill="#2A2F4C"/>
|
||||
<path d="M2.22749 10.1945C1.95627 9.93418 1.51663 9.93418 1.24541 10.1945L0.203597 11.1947C-0.0676216 11.4551 -0.0676216 11.8771 0.203597 12.1375C0.33938 12.2678 0.516836 12.3328 0.694638 12.3328C0.872441 12.3328 1.0499 12.2678 1.18568 12.1375L2.22749 11.1373C2.49871 10.877 2.49871 10.4549 2.22749 10.1945Z" fill="#2A2F4C"/>
|
||||
<path d="M6.04752 13.8617C5.7763 13.6013 5.33665 13.6013 5.06543 13.8617L4.02362 14.8618C3.7524 15.1222 3.7524 15.5442 4.02362 15.8046C4.1594 15.9349 4.33686 16 4.51466 16C4.69246 16 4.86992 15.9349 5.0057 15.8046L6.04752 14.8045C6.31873 14.5441 6.31873 14.122 6.04752 13.8617Z" fill="#2A2F4C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_147_71">
|
||||
<rect width="16.6667" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
28
assets/images/img_statistic.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg width="389" height="109" viewBox="0 0 389 109" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.500125 0.472775V108.5" stroke="#F0F0F0"/>
|
||||
<path d="M75.5189 0.472775L75.522 107.5" stroke="#F0F0F0"/>
|
||||
<path d="M203.551 0.472775L203.468 107.5" stroke="#F0F0F0"/>
|
||||
<path d="M278.57 0.472775V108.5" stroke="#F0F0F0"/>
|
||||
<path d="M0.500125 56.4868L278.57 56.4868" stroke="#F0F0F0"/>
|
||||
<path d="M0.828821 0.500125H278.57" stroke="#F0F0F0"/>
|
||||
<path d="M0.828821 108.5H278.57" stroke="#F0F0F0"/>
|
||||
<path d="M15.1007 64.0532H266.437" stroke="#F0F0F0"/>
|
||||
<path d="M15.1007 57.9416C15.1007 57.9416 46.5177 46.2296 77.9347 46.2296C109.352 46.2296 109.352 37.5729 140.769 37.5729C172.186 37.5729 172.186 74.2366 203.603 74.2366C235.02 74.2366 266.437 57.9416 266.437 57.9416" stroke="#E3A6A6" stroke-width="2"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 10.5026 53.4314)" fill="#A6CEE3"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 104.714 38.664)" fill="#E3A6A6"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 199.306 68.5074)" fill="#E3A6A6"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 272.568 55.4866)" fill="#E3A6A6"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 388.49 53.868)" fill="#A6CEE3"/>
|
||||
<path d="M15.1007 57.9413C15.1007 57.9413 46.5177 49.2846 77.9347 49.2846C109.352 49.2846 109.352 36.5541 140.769 36.5541C172.186 36.5541 172.186 65.0703 203.603 65.0703C235.02 65.0703 266.437 57.9413 266.437 57.9413" stroke="#1F78B4" stroke-width="2"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 10.5026 53.4314)" fill="#1F78B4"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 104.714 41.7195)" fill="#1F78B4"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 199.306 58.4873)" fill="#1F78B4"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 272.568 52.4858)" fill="#1F78B4"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 388.49 53.868)" fill="#1F78B4"/>
|
||||
<path d="M15.1007 57.9423C15.1007 57.9423 46.1043 58.9607 77.5213 58.9607C108.938 58.9607 108.938 58.9607 140.355 58.9607C171.772 58.9607 171.772 70.1635 203.189 70.1635C234.606 70.1635 266.437 57.9423 266.437 57.9423" stroke="#B2DF8A" stroke-width="2"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 10.5026 53.4314)" fill="#B2DF8A"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 104.714 54.8868)" fill="#B2DF8A"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 199.306 64.8893)" fill="#B2DF8A"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 272.568 54.4863)" fill="#B2DF8A"/>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" transform="matrix(-1.00025 0 0 1.00025 388.49 53.868)" fill="#B2DF8A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
9
assets/images/img_statistic_gray_200.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="279" height="109" viewBox="0 0 279 109" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.5 0.473145L0.5 108.473" stroke="#F0F0F0"/>
|
||||
<path d="M75.5 0.473145L75.503 107.473" stroke="#F0F0F0"/>
|
||||
<path d="M203.5 0.473145L203.417 107.473" stroke="#F0F0F0"/>
|
||||
<path d="M278.5 0.473145V108.473" stroke="#F0F0F0"/>
|
||||
<path d="M0.5 56.4731L278.5 56.4731" stroke="#F0F0F0"/>
|
||||
<path d="M0.828613 0.5H278.5" stroke="#F0F0F0"/>
|
||||
<path d="M92.7102 32.1663C46.1486 32.1663 0.5 23.4731 0.5 23.4731V108.473H278.5V47.6209C278.5 47.6209 231.482 40.8595 184.92 40.8595C138.359 40.8595 138.359 32.1663 92.7102 32.1663Z" fill="#A6CEE3" fill-opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 659 B |
3
assets/images/img_vector.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.9 14C4.71435 14 4.5363 13.9263 4.40503 13.795C4.27375 13.6637 4.2 13.4857 4.2 13.3V11.2H1.4C1.0287 11.2 0.672601 11.0525 0.41005 10.7899C0.1475 10.5274 0 10.1713 0 9.8V1.4C0 1.0287 0.1475 0.672601 0.41005 0.41005C0.672601 0.1475 1.0287 0 1.4 0H12.6C12.9713 0 13.3274 0.1475 13.5899 0.41005C13.8525 0.672601 14 1.0287 14 1.4V9.8C14 10.1713 13.8525 10.5274 13.5899 10.7899C13.3274 11.0525 12.9713 11.2 12.6 11.2H8.33L5.74 13.797C5.6 13.93 5.425 14 5.25 14H4.9ZM5.6 9.8V11.956L7.756 9.8H12.6V1.4H1.4V9.8H5.6Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 637 B |
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
9
lib/data/models/tokenModel/token_model.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
class TokenModel {
|
||||
@HiveField(0)
|
||||
String token;
|
||||
|
||||
TokenModel({required this.token});
|
||||
}
|
||||
@@ -5,6 +5,7 @@ final Map<String, String> ru = {
|
||||
"username": "Имя пользователя",
|
||||
"password": "Пароль",
|
||||
"password_confirmation": "Подтвердите пароль",
|
||||
'wrong_credentials': "Неверные данные пользователя",
|
||||
|
||||
//name Screen
|
||||
"first_name": "Имя",
|
||||
@@ -22,6 +23,8 @@ final Map<String, String> ru = {
|
||||
"waist_circumference": "Обхват талии",
|
||||
"initial_measurements": "Первичные замеры",
|
||||
|
||||
"male": "Мужской",
|
||||
"female": "Женский",
|
||||
// weight Screen
|
||||
"weight_measurements": "Замер веса", "your_weight": "Ваш вес:",
|
||||
"height_measurements": "Замер роста", "your_height": "Ваш рост:",
|
||||
@@ -37,20 +40,20 @@ final Map<String, String> 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<String, String> ru = {
|
||||
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",
|
||||
|
||||
// additional_information Screen
|
||||
"additional_information": "Дополнительная информация:",
|
||||
"additional_information": "Дополнительная информация",
|
||||
"enter_additional_information":
|
||||
"Введите любую дополнительную информацию на своё усмотрение:",
|
||||
|
||||
@@ -99,4 +102,35 @@ final Map<String, String> 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": "Сохранить",
|
||||
};
|
||||
|
||||
@@ -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<ScaffoldMessengerState>();
|
||||
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: [
|
||||
|
||||
174
lib/presentation/auth/auth_screen/auth_screen.dart
Normal file
@@ -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<AuthBloc>(
|
||||
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<AuthBloc, AuthState, TextEditingController?>(
|
||||
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<AuthBloc, AuthState, TextEditingController?>(
|
||||
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<AuthBloc, AuthState>(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<String> 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<String> validateFields(String email, String password) {
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
22
lib/presentation/auth/auth_screen/bloc/auth_bloc.dart
Normal file
@@ -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<AuthEvent, AuthState> {
|
||||
AuthBloc(AuthState initialState) : super(initialState) {
|
||||
on<AuthInitialEvent>(_onInitialize);
|
||||
}
|
||||
|
||||
_onInitialize(
|
||||
AuthInitialEvent event,
|
||||
Emitter<AuthState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(
|
||||
emailController: TextEditingController(),
|
||||
passwordController: TextEditingController()));
|
||||
}
|
||||
}
|
||||
16
lib/presentation/auth/auth_screen/bloc/auth_event.dart
Normal file
@@ -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<Object?> get props => [];
|
||||
}
|
||||
36
lib/presentation/auth/auth_screen/bloc/auth_state.dart
Normal file
@@ -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<Object?> 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
16
lib/presentation/auth/auth_screen/models/auth_model.dart
Normal file
@@ -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<Object?> get props => [];
|
||||
}
|
||||
@@ -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<CredentialsBloc>(
|
||||
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<Color>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
selector: (state) => state.passwordConfirmationController,
|
||||
builder: (context, passwordConfirmationController) {
|
||||
return CustomTextFormField(
|
||||
obscureText: true,
|
||||
controller: passwordConfirmationController,
|
||||
textInputAction: TextInputAction.done);
|
||||
}),
|
||||
Spacer(),
|
||||
BlocBuilder<CredentialsBloc, CredentialsState>(
|
||||
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<String> 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<String> validateFields(
|
||||
String email, String name, String password, String passwordConfirmation) {
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<MyProfileEvent, MyProfileState> {
|
||||
MyProfileBloc(MyProfileState initialState) : super(initialState) {
|
||||
on<MyProfileInitialEvent>(_onInitialize);
|
||||
}
|
||||
|
||||
Future<void> _onInitialize(
|
||||
MyProfileInitialEvent event,
|
||||
Emitter<MyProfileState> 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<String, dynamic> &&
|
||||
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<double> weights = [0.0, 0.0, 0.0, 0.0];
|
||||
List<String> 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<String> 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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Object?> get props => [];
|
||||
}
|
||||
@@ -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<Object?> 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<String, dynamic>? user;
|
||||
Map<String, dynamic>? userDetails;
|
||||
Map<dynamic, dynamic>? circumferences;
|
||||
Map<dynamic, dynamic>? chartPoints;
|
||||
List<String>? chartDates;
|
||||
List<dynamic>? 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<Object?> 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<String, dynamic>? user,
|
||||
Map<String, dynamic>? userDetails,
|
||||
List<dynamic>? plans,
|
||||
Map<dynamic, dynamic>? chartPoints,
|
||||
List<String>? chartDates,
|
||||
Map<dynamic, dynamic>? 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<Object> get props => [errorMessage];
|
||||
}
|
||||
@@ -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<Object?> get props => [];
|
||||
}
|
||||
@@ -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<MyProfileBloc>(
|
||||
create: (context) =>
|
||||
MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))
|
||||
..add(MyProfileInitialEvent()),
|
||||
child: MyProfileScreen());
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<MyProfileBloc, MyProfileState>(
|
||||
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<int>((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<int>((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<double> chartPoints, List<String>? dates) {
|
||||
// Определение осей графика
|
||||
List<FlSpot> 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<String>? 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<String>? dates) {
|
||||
// Определение осей графика
|
||||
final chestCircumferences =
|
||||
chartPoints.values.map((e) => e['chest_circumference']).toList();
|
||||
List<FlSpot> 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<FlSpot> 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<FlSpot> 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<String>? 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<dynamic>? plans,
|
||||
List<dynamic>? imagePaths, List<dynamic>? 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<double> _bodyMassIndex(List<double> weightPoints, int height) {
|
||||
List<double> 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: <TextInputFormatter>[
|
||||
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: <TextInputFormatter>[
|
||||
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: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Map<String, dynamic> 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: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Map<String, dynamic> 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), // Можете настроить отступы по вашему желанию
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<MyProfileBloc>(
|
||||
create: (context) =>
|
||||
MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))
|
||||
..add(MyProfileInitialEvent()),
|
||||
child: MySettingsScreen());
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<MyProfileBloc, MyProfileState>(
|
||||
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<LanguageDropdown> {
|
||||
String selectedLanguage = 'Русский';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownButton<String>(
|
||||
value: selectedLanguage,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
selectedLanguage = newValue!;
|
||||
});
|
||||
},
|
||||
isDense: false, // Установите isDense в false
|
||||
selectedItemBuilder: (BuildContext context) {
|
||||
return ['Русский'].map<Widget>((String language) {
|
||||
return Align(
|
||||
alignment: Alignment.center, // Выравнивание текста по центру
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
items: ['Русский'].map((String language) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: language,
|
||||
child: Text(language),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
NotificationSwitch(this.title);
|
||||
|
||||
@override
|
||||
_NotificationSwitchState createState() => _NotificationSwitchState();
|
||||
}
|
||||
|
||||
class _NotificationSwitchState extends State<NotificationSwitch> {
|
||||
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<HyperSwitch> {
|
||||
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<HypoSwitch> {
|
||||
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<ExpandableList> {
|
||||
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<MyProfileBloc, MyProfileState>(
|
||||
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<String> cities = [
|
||||
'Москва',
|
||||
'Санкт-Петербург',
|
||||
'Новосибирск',
|
||||
'Екатеринбург',
|
||||
'Нижний Новгород',
|
||||
'Казань',
|
||||
'Челябинск',
|
||||
'Омск',
|
||||
'Самара',
|
||||
'Ростов-на-Дону',
|
||||
'Уфа',
|
||||
'Красноярск',
|
||||
'Воронеж',
|
||||
'Пермь',
|
||||
'Волгоград',
|
||||
'Краснодар',
|
||||
'Саратов',
|
||||
'Тюмень',
|
||||
'Тольятти',
|
||||
'Ижевск',
|
||||
'Барнаул',
|
||||
'Ульяновск',
|
||||
'Иркутск',
|
||||
'Хабаровск',
|
||||
'Ярославль',
|
||||
'Владивосток',
|
||||
'Махачкала',
|
||||
'Томск',
|
||||
'Оренбург',
|
||||
'Кемерово',
|
||||
];
|
||||
List<String> 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<String>(
|
||||
value: state.selectedCountry,
|
||||
hint: Text(state.selectedCountry ?? "Не выбрано"),
|
||||
onChanged: (newValue) {
|
||||
// Обработчик изменения выбранной страны
|
||||
setSelectedCountry(state, newValue);
|
||||
},
|
||||
items: countries.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(labelText: 'country'.tr),
|
||||
),
|
||||
DropdownButtonFormField<String>(
|
||||
hint: Text(state.selectedCity ?? "Не выбрано"),
|
||||
value: state.selectedCity,
|
||||
onChanged: (newValue) {
|
||||
// Обработчик изменения выбранного города
|
||||
setSelectedCity(state, newValue);
|
||||
},
|
||||
items: cities.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
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: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.heightFormController,
|
||||
decoration: InputDecoration(labelText: 'height_measurements'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.chestCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'chest_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.waistCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'waist_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.hipCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'hip_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.upPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'up_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.downPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'down_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[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<void> sendProfileData(MyProfileLoadedState? state) async {
|
||||
try {
|
||||
// Создаем JSON объект с данными профиля
|
||||
Map<String, dynamic> 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<String> items;
|
||||
|
||||
AboutList(this.title, this.items);
|
||||
|
||||
@override
|
||||
_AboutListState createState() => _AboutListState();
|
||||
}
|
||||
|
||||
class _AboutListState extends State<AboutList> {
|
||||
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(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
}),
|
||||
]))));
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -130,6 +130,12 @@ class _AppNavigationScreenState extends State<AppNavigationScreen> {
|
||||
onTapScreenTitle: () => onTapScreenTitle(
|
||||
AppRoutes.languageSelectScreen),
|
||||
),
|
||||
_buildScreenTitle(
|
||||
context,
|
||||
screenTitle: "auth".tr,
|
||||
onTapScreenTitle: () =>
|
||||
onTapScreenTitle(AppRoutes.authScreen),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -15,8 +15,7 @@ class BloodPressureModel extends Equatable {
|
||||
|
||||
BloodPressureModel copyWith({List<SelectionPopupModel>? dropdownItemList}) {
|
||||
return BloodPressureModel(
|
||||
userRegistrationSheet:
|
||||
userRegistrationSheet ?? this.userRegistrationSheet,
|
||||
userRegistrationSheet: userRegistrationSheet,
|
||||
dropdownItemList: dropdownItemList ?? this.dropdownItemList,
|
||||
);
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<CredentialsBloc>(
|
||||
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<Color>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
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<CredentialsBloc, CredentialsState,
|
||||
TextEditingController?>(
|
||||
selector: (state) => state.passwordConfirmationController,
|
||||
builder: (context, passwordConfirmationController) {
|
||||
return CustomTextFormField(
|
||||
obscureText: true,
|
||||
controller: passwordConfirmationController,
|
||||
textInputAction: TextInputAction.done);
|
||||
}),
|
||||
Spacer(),
|
||||
BlocBuilder<CredentialsBloc, CredentialsState>(
|
||||
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<String> 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<String> validateFields(
|
||||
String email, String name, String password, String passwordConfirmation) {
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||