Merge pull request 'merged_login_home' (#3) from merged_login_home into main
Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/3
@@ -43,3 +43,6 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"dart.flutterSdkPath": ".fvm/versions/3.29.3"
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.4297 5.92969L20.4997 11.9997L14.4297 18.0697" stroke="#9E9E9E" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.5 12H20.33" stroke="#9E9E9E" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 402 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="31" height="32" viewBox="0 0 31 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.89324 8.32365C3.00187 7.17818 3.73102 6.27444 4.82381 6.04606C6.4137 5.7138 9.30388 5.33398 14.209 5.33398C19.1141 5.33398 22.0043 5.7138 23.5942 6.04606C24.6869 6.27444 25.4161 7.17818 25.5248 8.32365C25.673 9.88758 25.834 12.6611 25.834 17.334C25.834 22.0069 25.673 24.7804 25.5248 26.3443C25.4161 27.4898 24.6869 28.3935 23.5942 28.6219C22.0043 28.9542 19.1141 29.334 14.209 29.334C9.30388 29.334 6.4137 28.9542 4.82381 28.6219C3.73102 28.3935 3.00187 27.4898 2.89324 26.3443C2.74491 24.7804 2.58398 22.0069 2.58398 17.334C2.58398 12.6611 2.74491 9.88758 2.89324 8.32365Z" fill="#0164FF"/>
|
||||
<path d="M6.12175 4.98967C6.23039 3.84422 6.95956 2.94045 8.05231 2.71208C9.64223 2.37981 12.5324 2 17.4375 2C22.3426 2 25.2328 2.37981 26.8227 2.71208C27.9154 2.94045 28.6446 3.84422 28.7533 4.98967C28.9016 6.55357 29.0625 9.32713 29.0625 14C29.0625 18.6729 28.9016 21.4464 28.7533 23.0103C28.6446 24.1558 27.9154 25.0595 26.8227 25.2879C25.2328 25.6202 22.3426 26 17.4375 26C12.5324 26 9.64223 25.6202 8.05231 25.2879C6.95956 25.0595 6.23039 24.1558 6.12175 23.0103C5.97343 21.4464 5.8125 18.6729 5.8125 14C5.8125 9.32713 5.97343 6.55357 6.12175 4.98967Z" fill="#B4D1FF"/>
|
||||
<path d="M20.226 20.1993C19.2473 21.0582 18.0352 20.546 17.9853 19.2666C17.9563 18.5249 17.9355 17.5827 17.9332 16.394C15.4348 16.3271 12.72 16.1671 10.4057 15.9789C9.74648 15.9253 9.25551 15.3759 9.26608 14.7127L9.28459 13.5466C9.2951 12.8843 9.80245 12.3205 10.4626 12.2459C12.784 11.9835 15.5052 11.7365 18.0072 11.5896C18.0465 10.3975 18.0965 9.4523 18.1484 8.70809C18.238 7.42615 19.4668 6.87625 20.4196 7.70546C20.9868 8.19902 21.6836 8.83796 22.5198 9.65803C23.9905 11.1001 24.8068 12.1746 25.2525 12.8771C25.6431 13.493 25.6315 14.2468 25.2215 14.875C24.7539 15.5915 23.904 16.6917 22.3881 18.1801C21.5259 19.0268 20.8088 19.6879 20.226 20.1993Z" fill="#0164FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0007 6C13.4233 6 11.334 8.08933 11.334 10.6667V21.3333C11.334 23.9107 13.4233 26 16.0007 26C18.578 26 20.6673 23.9107 20.6673 21.3333V14.6667C20.6673 13.9303 20.0704 13.3333 19.334 13.3333C18.5976 13.3333 18.0007 13.9303 18.0007 14.6667V21.5621C18.0007 22.2693 17.7117 22.9785 17.0352 23.1849C16.7525 23.2712 16.4076 23.3333 16.0007 23.3333C15.5937 23.3333 15.2489 23.2712 14.9661 23.1849C14.2897 22.9785 14.0007 22.2693 14.0007 21.5621V14.6667C14.0007 11.7211 16.3885 9.33333 19.334 9.33333C22.2795 9.33333 24.6673 11.7211 24.6673 14.6667V21.3333C24.6673 26.1198 20.7871 30 16.0007 30C11.2142 30 7.33398 26.1198 7.33398 21.3333V10.6667C7.33398 5.8802 11.2142 2 16.0007 2C17.9498 2 19.7539 2.64583 21.2027 3.73435L21.2743 3.78037C21.9337 4.2038 22.4003 4.92708 22.1132 5.65622C22.0052 5.93048 21.8429 6.23093 21.6003 6.53468C21.2898 6.92333 20.9807 7.17633 20.7135 7.34087C20.2345 7.6358 19.6563 7.48227 19.1831 7.17833L18.8 6.93233C18.0201 6.34641 17.0537 6 16.0007 6Z" fill="#9E9E9E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_71_2102)">
|
||||
<path d="M9.02 2.83998L3.63 7.03998C2.73 7.73998 2 9.22998 2 10.36V17.77C2 20.09 3.89 21.99 6.21 21.99H17.79C20.11 21.99 22 20.09 22 17.78V10.5C22 9.28998 21.19 7.73998 20.2 7.04998L14.02 2.71998C12.62 1.73998 10.37 1.78998 9.02 2.83998Z" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 17.99V14.99" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_71_2102">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 680 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_70_3561)">
|
||||
<path d="M9.02 2.83998L3.63 7.03998C2.73 7.73998 2 9.22998 2 10.36V17.77C2 20.09 3.89 21.99 6.21 21.99H17.79C20.11 21.99 22 20.09 22 17.78V10.5C22 9.28998 21.19 7.73998 20.2 7.04998L14.02 2.71998C12.62 1.73998 10.37 1.78998 9.02 2.83998Z" fill="#0164FF"/>
|
||||
<path d="M12 17.99V14.99" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_70_3561">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00187 8.95688C9.15063 8.95688 10.0819 8.02563 10.0819 6.87688C10.0819 5.72812 9.15063 4.79688 8.00187 4.79688C6.85312 4.79688 5.92188 5.72812 5.92188 6.87688C5.92188 8.02563 6.85312 8.95688 8.00187 8.95688Z" stroke="#888888" stroke-width="1.5"/>
|
||||
<path d="M2.41184 5.66261C3.72517 -0.110728 12.2785 -0.104061 13.5852 5.66927C14.3518 9.05594 12.2452 11.9226 10.3985 13.6959C9.0585 14.9893 6.9385 14.9893 5.59184 13.6959C3.75184 11.9226 1.64517 9.04927 2.41184 5.66261Z" stroke="#888888" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 621 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.12 12.78C12.05 12.77 11.96 12.77 11.88 12.78C10.12 12.72 8.71997 11.28 8.71997 9.50998C8.71997 7.69998 10.18 6.22998 12 6.22998C13.81 6.22998 15.28 7.69998 15.28 9.50998C15.27 11.28 13.88 12.72 12.12 12.78Z" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18.74 19.38C16.96 21.01 14.6 22 12 22C9.40001 22 7.04001 21.01 5.26001 19.38C5.36001 18.44 5.96001 17.52 7.03001 16.8C9.77001 14.98 14.25 14.98 16.97 16.8C18.04 17.52 18.64 18.44 18.74 19.38Z" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 906 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.12 12.78C12.05 12.77 11.96 12.77 11.88 12.78C10.12 12.72 8.71997 11.28 8.71997 9.50998C8.71997 7.69998 10.18 6.22998 12 6.22998C13.81 6.22998 15.28 7.69998 15.28 9.50998C15.27 11.28 13.88 12.72 12.12 12.78Z" fill="#0164FF"/>
|
||||
<path d="M18.74 19.38C16.96 21.01 14.6 22 12 22C9.40001 22 7.04001 21.01 5.26001 19.38C5.36001 18.44 5.96001 17.52 7.03001 16.8C9.77001 14.98 14.25 14.98 16.97 16.8C18.04 17.52 18.64 18.44 18.74 19.38Z" fill="#0164FF"/>
|
||||
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#0164FF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 770 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.10194 5.71479C3.22932 4.61652 3.89562 3.70984 4.97518 3.4711C6.65908 3.0987 9.92302 2.66602 15.9993 2.66602C22.0757 2.66602 25.3396 3.0987 27.0235 3.4711C28.1031 3.70984 28.7693 4.61652 28.8967 5.71479C29.0894 7.37562 29.3327 10.1109 29.3327 13.3327C29.3327 19.3516 26.1561 24.9836 20.8043 27.7379C19.0165 28.658 17.2718 29.3327 15.9993 29.3327C14.7269 29.3327 12.9821 28.658 11.1944 27.7379C5.84264 24.9836 2.66602 19.3516 2.66602 13.3327C2.66602 10.1109 2.9093 7.37562 3.10194 5.71479Z" fill="#F6D7AB"/>
|
||||
<path d="M15.481 19.6932C16.3103 19.6932 16.9093 19.5482 17.2779 19.2582C17.6619 18.9683 17.8539 18.613 17.8539 18.1925C17.8539 17.8156 17.708 17.5111 17.4162 17.2792C17.1243 17.0471 16.6867 16.8442 16.103 16.6702L14.8359 16.2787C14.2062 16.0902 13.6379 15.8873 13.1311 15.6697C12.6243 15.4523 12.1942 15.1913 11.8409 14.8868C11.4877 14.5823 11.2113 14.2199 11.0116 13.7994C10.8119 13.3789 10.7121 12.8787 10.7121 12.2988C10.7121 11.3129 11.0346 10.4937 11.6797 9.84128C12.3247 9.18882 13.2463 8.75382 14.4443 8.53635V7.74162C14.4443 7.20055 14.8165 6.71795 15.3562 6.67968C15.4832 6.67068 15.6097 6.66602 15.7344 6.66602C16.3027 6.66602 16.7097 6.76028 16.9554 6.94875C17.2165 7.13722 17.3471 7.47795 17.3471 7.97088V8.44935C18.33 8.55088 19.1287 8.77562 19.743 9.12355C20.3574 9.45702 20.6645 9.89928 20.6645 10.4502C20.6645 10.7349 20.5801 10.9952 20.4375 11.2265C20.1523 11.6891 19.5153 11.6589 19.0054 11.4712C18.8155 11.4013 18.6083 11.3364 18.3838 11.2766C17.8001 11.1027 17.1627 11.0157 16.4716 11.0157C15.7037 11.0157 15.1277 11.1389 14.7437 11.3853C14.3751 11.6173 14.1909 11.9218 14.1909 12.2988C14.1909 12.6033 14.3137 12.8497 14.5595 13.0382C14.8205 13.2267 15.2122 13.4007 15.7344 13.5602L17.0245 13.9299C18.4068 14.3359 19.4666 14.8723 20.2038 15.5393C20.9564 16.2062 21.3327 17.1051 21.3327 18.2361C21.3327 19.2365 20.9948 20.0847 20.319 20.7806C19.6432 21.4621 18.6525 21.9187 17.3471 22.1507V22.9237C17.3471 23.4648 16.9749 23.9475 16.4351 23.9857C16.3082 23.9947 16.1817 23.9993 16.0569 23.9993C15.4887 23.9993 15.0739 23.9051 14.8129 23.7166C14.5671 23.5282 14.4443 23.1874 14.4443 22.6945V22.2595C13.3231 22.1435 12.4092 21.8898 11.7027 21.4983C11.0116 21.0924 10.666 20.5849 10.666 19.9759C10.666 19.5835 10.8267 19.2479 11.0781 18.9726C11.4348 18.5819 12.0348 18.6763 12.4975 18.9328C12.7443 19.0695 13.0169 19.1997 13.3154 19.3235C13.9451 19.5699 14.6669 19.6932 15.481 19.6932Z" fill="#E5821E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 22H15C20 22 22 20 22 15V9C22 4 20 2 15 2H9C4 2 2 4 2 9V15C2 20 4 22 9 22Z" fill="#0164FF"/>
|
||||
<path d="M12.3701 8.88H17.6201" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.38 8.88L7.13 9.63L9.38 7.38" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.37 15.88H17.62" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.38 15.88L7.13 16.63L9.38 14.38" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.37 8.88H17.62" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.38 8.88L7.13 9.63L9.38 7.38" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.37 15.88H17.62" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.38 15.88L7.13 16.63L9.38 14.38" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 22H15C20 22 22 20 22 15V9C22 4 20 2 15 2H9C4 2 2 4 2 9V15C2 20 4 22 9 22Z" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 757 B |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 94 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.4031 2.67191C15.9698 2.7192 16.636 2.80976 17.2782 2.98049C18.6545 3.34634 19.3715 4.71426 19.4579 6.14168C19.5666 7.93935 19.5007 9.76542 19.4111 11.1145C21.5493 10.9227 23.6703 10.771 25.5795 10.6795C27.5952 10.583 29.2814 12.1097 29.3225 14.1363C29.3901 17.4725 29.179 22.5522 27.652 26.6666C27.0525 28.282 25.4638 29.2352 23.7483 29.2913C15.72 29.5536 8 28.484 8 28.484V12.572C8.89627 12.4032 9.93513 12.2344 11.0698 12.0705C12.0847 9.22415 13.0445 5.89077 13.5565 4.043C13.7897 3.20108 14.5361 2.59956 15.4031 2.67191Z" fill="#99DDE5"/>
|
||||
<path d="M2.76118 28.2007C2.77899 28.9048 3.17598 29.5444 3.85621 29.7273C4.39368 29.8717 5.11976 30.0007 5.99935 30.0007C6.87895 30.0007 7.60502 29.8717 8.14248 29.7273C8.82275 29.5444 9.21968 28.9048 9.23755 28.2007C9.27515 26.7136 9.33268 23.9087 9.33268 20.6673C9.33268 17.426 9.27515 14.6211 9.23755 13.1339C9.21968 12.4298 8.82275 11.7903 8.14248 11.6074C7.60502 11.4629 6.87895 11.334 5.99935 11.334C5.11976 11.334 4.39368 11.4629 3.85621 11.6074C3.17598 11.7903 2.77899 12.4298 2.76118 13.1339C2.72356 14.6211 2.66602 17.426 2.66602 20.6673C2.66602 23.9087 2.72356 26.7136 2.76118 28.2007Z" fill="#24848E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.7887 14.9303C17.7287 16.9803 14.7787 17.6103 12.1887 16.8003L7.47868 21.5003C7.13868 21.8503 6.46868 22.0603 5.98868 21.9903L3.80868 21.6903C3.08868 21.5903 2.41868 20.9103 2.30868 20.1903L2.00868 18.0103C1.93868 17.5303 2.16868 16.8603 2.49868 16.5203L7.19868 11.8203C6.39868 9.22031 7.01868 6.27031 9.07868 4.22031C12.0287 1.27031 16.8187 1.27031 19.7787 4.22031C22.7387 7.17031 22.7387 11.9803 19.7887 14.9303Z" stroke="#888888" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.89062 17.4922L9.19062 19.7922" stroke="#888888" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.5 11C15.3284 11 16 10.3284 16 9.5C16 8.67157 15.3284 8 14.5 8C13.6716 8 13 8.67157 13 9.5C13 10.3284 13.6716 11 14.5 11Z" stroke="#888888" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1010 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.5819 12.0019C15.5819 13.9819 13.9819 15.5819 12.0019 15.5819C10.0219 15.5819 8.42188 13.9819 8.42188 12.0019C8.42188 10.0219 10.0219 8.42188 12.0019 8.42188C13.9819 8.42188 15.5819 10.0219 15.5819 12.0019Z" stroke="#888888" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.9998 20.2688C15.5298 20.2688 18.8198 18.1887 21.1098 14.5887C22.0098 13.1787 22.0098 10.8087 21.1098 9.39875C18.8198 5.79875 15.5298 3.71875 11.9998 3.71875C8.46984 3.71875 5.17984 5.79875 2.88984 9.39875C1.98984 10.8087 1.98984 13.1787 2.88984 14.5887C5.17984 18.1887 8.46984 20.2688 11.9998 20.2688Z" stroke="#888888" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 809 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12C14.7614 12 17 9.76142 17 7C17 4.23858 14.7614 2 12 2C9.23858 2 7 4.23858 7 7C7 9.76142 9.23858 12 12 12Z" stroke="#888888" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20.5901 22C20.5901 18.13 16.7402 15 12.0002 15C7.26015 15 3.41016 18.13 3.41016 22" stroke="#888888" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 489 B |
@@ -0,0 +1,97 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import 'size_config.dart';
|
||||
|
||||
class BottomNavigation extends StatefulWidget {
|
||||
const BottomNavigation({super.key});
|
||||
|
||||
@override
|
||||
State<BottomNavigation> createState() => _BottomNavigationState();
|
||||
}
|
||||
|
||||
class _BottomNavigationState extends State<BottomNavigation> {
|
||||
int activeTab = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 72.0.h(),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFE3E3E3),
|
||||
border: Border.fromBorderSide(BorderSide(color: Color(0xFFE3E3E3))),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_bottomNavigationItem(
|
||||
text: 'Home',
|
||||
isActive: activeTab == 0,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
activeTab = 0;
|
||||
});
|
||||
},
|
||||
iconPath: 'assets/icons/home.svg',
|
||||
activeIconPath: 'assets/icons/homeActive.svg',
|
||||
),
|
||||
_bottomNavigationItem(
|
||||
text: 'Tenders',
|
||||
isActive: activeTab == 1,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
activeTab = 1;
|
||||
});
|
||||
},
|
||||
iconPath: 'assets/icons/task_square.svg',
|
||||
activeIconPath: 'assets/icons/task-square_active.svg',
|
||||
),
|
||||
_bottomNavigationItem(
|
||||
text: 'Profile',
|
||||
isActive: activeTab == 2,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
activeTab = 2;
|
||||
});
|
||||
},
|
||||
iconPath: 'assets/icons/profile-circle.svg',
|
||||
activeIconPath: 'assets/icons/profile-circle_active.svg',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bottomNavigationItem({
|
||||
required String text,
|
||||
required bool isActive,
|
||||
required VoidCallback onTap,
|
||||
required String iconPath,
|
||||
required String activeIconPath,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 64.0.w(),
|
||||
height: 72.0.h(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 12.0.h(), bottom: 14.0.h()),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(isActive ? activeIconPath : iconPath),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: isActive ? Color(0xFF0164FF) : Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class AppStrings {
|
||||
AppStrings._();
|
||||
|
||||
static const String home = 'Home';
|
||||
|
||||
static const String partnership = 'Partnership';
|
||||
static const String selfApply = 'Self-apply';
|
||||
static const String contracting = 'Contracting';
|
||||
static const String tenderSubmitting = 'Tender submitting';
|
||||
static const String approvedTenders = 'Approved tenders';
|
||||
static const String tenderValue = 'Tender Value';
|
||||
static const String thunderStatus = 'Thunder status';
|
||||
static const String yourTenders = 'Your tenders';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class AssetsManager {
|
||||
AssetsManager._();
|
||||
|
||||
// login page
|
||||
static const logo = 'assets/pngs/logo.png';
|
||||
static const userIcon = 'assets/svgs/user_icon.svg';
|
||||
static const passwordIcon = 'assets/svgs/password_icon.svg';
|
||||
static const passwordVisibility = 'assets/svgs/password_visibility.svg';
|
||||
static const passwordVisibilityOff = 'assets/svgs/password_visibility.svg';
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppColors {
|
||||
static const Color mainBlue = Color(0xff0164FF);
|
||||
static const Color borderBlue = Color(0xffA1C6FF);
|
||||
// Primary Colors
|
||||
static const Color primaryColor = Color(0xFF007BFF);
|
||||
static const Color accentColor = Color(0xFFE83E8C);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class TextManager {
|
||||
static const signInTitle = 'Sign In';
|
||||
static const signInSubtitle = 'Enter valid username & password to continue';
|
||||
static const usernameLabel = 'User Name';
|
||||
static const passwordLabel = 'Password';
|
||||
static const loginButton = 'Login';
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../bottom_navigation.dart';
|
||||
import '../../constants/app_strings.dart';
|
||||
import '../../size_config.dart';
|
||||
import 'widgets.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: _body(),
|
||||
bottomNavigationBar: BottomNavigation(),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea _body() {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
24.0.w(),
|
||||
26.0.h(),
|
||||
24.0.w(),
|
||||
24.0.h(),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_appbar(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_progressBarsRow(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_firstStatisticsRow(),
|
||||
SizedBox(height: 8.0.h()),
|
||||
_secondStatisticsRow(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
_yourTenderText(),
|
||||
SizedBox(height: 28.0.h()),
|
||||
_bottomListView(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appbar() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
AppStrings.home,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF222222),
|
||||
),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/icons/tenderLogo.png',
|
||||
width: 59.0.w(),
|
||||
height: 28.0.h(),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _progressBarsRow() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ProgressBarColumn(
|
||||
text: AppStrings.partnership,
|
||||
value: 0.75,
|
||||
amount: '75%',
|
||||
),
|
||||
ProgressBarColumn(
|
||||
text: AppStrings.selfApply,
|
||||
value: 0.88,
|
||||
amount: '12',
|
||||
),
|
||||
ProgressBarColumn(
|
||||
text: AppStrings.contracting,
|
||||
value: 0.36,
|
||||
amount: '36',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _firstStatisticsRow() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
StatisticsCard(
|
||||
backgroundColor: Color(0xFFE5EFFF),
|
||||
iconPath: 'assets/icons/arrows.svg',
|
||||
title: AppStrings.tenderSubmitting,
|
||||
amount: '12',
|
||||
textColor: Color(0xFF0164FF),
|
||||
enableTap: true,
|
||||
onTap: () {},
|
||||
),
|
||||
StatisticsCard(
|
||||
backgroundColor: Color(0xFFEAF8F9),
|
||||
iconPath: 'assets/icons/thumb.svg',
|
||||
title: AppStrings.approvedTenders,
|
||||
amount: '03',
|
||||
textColor: Color(0xFF24848E),
|
||||
enableTap: true,
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _secondStatisticsRow() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
StatisticsCard(
|
||||
backgroundColor: Color(0xFFFFF2E0),
|
||||
iconPath: 'assets/icons/shield.svg',
|
||||
title: AppStrings.tenderValue,
|
||||
amount: '\$250,000',
|
||||
textColor: Color(0xFFE5821E),
|
||||
enableTap: true,
|
||||
onTap: () {},
|
||||
),
|
||||
StatisticsCard(
|
||||
backgroundColor: Color(0xFFF4F4F4),
|
||||
iconPath: 'assets/icons/edit.svg',
|
||||
title: AppStrings.thunderStatus,
|
||||
amount: '23',
|
||||
textColor: Color(0xFF9E9E9E),
|
||||
enableTap: false,
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _yourTenderText() {
|
||||
return Text(
|
||||
AppStrings.yourTenders,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0.sp(),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF777777),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bottomListView() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 310.0.h(),
|
||||
child: ListView.builder(
|
||||
itemCount: 10,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
return TendersListItem();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../size_config.dart';
|
||||
|
||||
class ProgressBarColumn extends StatefulWidget {
|
||||
const ProgressBarColumn({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.value,
|
||||
required this.amount,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final double value;
|
||||
final String amount;
|
||||
|
||||
@override
|
||||
State<ProgressBarColumn> createState() => _ProgressBarColumnState();
|
||||
}
|
||||
|
||||
class _ProgressBarColumnState extends State<ProgressBarColumn>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Initialize the animation controller
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 1500), // duration of the animation
|
||||
);
|
||||
|
||||
// Create a Tween animation from 0.0 to 0.75
|
||||
_animation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: widget.value,
|
||||
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
|
||||
|
||||
// Start the animation
|
||||
_controller.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose(); // clean up controller
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [_progressIndicator(), _centerText()],
|
||||
),
|
||||
SizedBox(height: 12.0.h()),
|
||||
_titleText(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _progressIndicator() {
|
||||
return SizedBox(
|
||||
width: 96.0.w(),
|
||||
height: 96.0.w(),
|
||||
child: AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder:
|
||||
(context, child) => CircularProgressIndicator(
|
||||
backgroundColor: Color(0xFFE1E1E1),
|
||||
strokeCap: StrokeCap.round,
|
||||
strokeWidth: 8.0,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF34BCCB)),
|
||||
value: _animation.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _centerText() {
|
||||
return Center(
|
||||
child: Text(
|
||||
widget.amount,
|
||||
style: TextStyle(fontSize: 24.0.sp(), fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _titleText() {
|
||||
return Text(
|
||||
widget.text,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF222222),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../size_config.dart';
|
||||
|
||||
class StatisticsCard extends StatelessWidget {
|
||||
const StatisticsCard({
|
||||
super.key,
|
||||
required this.backgroundColor,
|
||||
required this.iconPath,
|
||||
required this.title,
|
||||
required this.amount,
|
||||
required this.textColor,
|
||||
required this.onTap,
|
||||
required this.enableTap,
|
||||
});
|
||||
|
||||
final Color backgroundColor;
|
||||
final String iconPath;
|
||||
final String title;
|
||||
final String amount;
|
||||
final Color textColor;
|
||||
final VoidCallback onTap;
|
||||
final bool enableTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: enableTap ? onTap : null,
|
||||
child: Container(
|
||||
width: 178.0.w(),
|
||||
height: 148.0.h(),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 12.0.h()),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(iconPath),
|
||||
SizedBox(height: 4.0.h()),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
amount,
|
||||
style: TextStyle(
|
||||
fontSize: 24.0.sp(),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
enableTap
|
||||
? SvgPicture.asset('assets/icons/arrow-right.svg')
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../size_config.dart';
|
||||
|
||||
class TendersListItem extends StatelessWidget {
|
||||
const TendersListItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 327.0.w(),
|
||||
height: 309.0.h(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'2025-05-21',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 91.0.w(),
|
||||
height: 24.0.h(),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFDAF8D6),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Completed',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF289744),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12.0.h()),
|
||||
Text(
|
||||
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF222222),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. Suscipit ...',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF222222),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24.0.h()),
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/location.svg'),
|
||||
SizedBox(width: 1.0.w()),
|
||||
Text(
|
||||
'Location',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.0.w()),
|
||||
SizedBox(
|
||||
width: 32.0.w(),
|
||||
height: 21.0.h(),
|
||||
child: Image.asset('assets/icons/SE.png', fit: BoxFit.cover),
|
||||
),
|
||||
Spacer(),
|
||||
Container(
|
||||
width: 96.0.w(),
|
||||
height: 24.0.h(),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFE4E4E4),
|
||||
borderRadius: BorderRadius.circular(88),
|
||||
border: Border.all(color: Color(0xFFDADADA)),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Self Control',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF777777),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'progress_bar_column.dart';
|
||||
export 'statistics_card.dart';
|
||||
export 'tenders_list_item.dart';
|
||||
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
||||
|
||||
import 'data/repositories/auth_repository.dart';
|
||||
import 'data/services/auth_service.dart';
|
||||
import 'size_config.dart';
|
||||
import 'view_models/auth_view_model.dart';
|
||||
import 'views/login_screen.dart';
|
||||
|
||||
@@ -32,6 +33,13 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(home: LoginScreen());
|
||||
SizeConfig.init(context);
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
),
|
||||
home: LoginScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SizeConfig {
|
||||
static double screenWidth = 0.0;
|
||||
static double screenHeight = 0.0;
|
||||
static late TextScaler textScaler;
|
||||
|
||||
static void init(BuildContext context) {
|
||||
screenWidth = MediaQuery.sizeOf(context).width;
|
||||
screenHeight = MediaQuery.sizeOf(context).height;
|
||||
textScaler = MediaQuery.of(context).textScaler;
|
||||
}
|
||||
}
|
||||
|
||||
extension SizeDoubleExt on double {
|
||||
double h() {
|
||||
final double screenHeight = SizeConfig.screenHeight;
|
||||
final double res = (this / 917.0) * screenHeight;
|
||||
return res;
|
||||
}
|
||||
|
||||
double w() {
|
||||
final double screenWidth = SizeConfig.screenWidth;
|
||||
final double res = (this / 412.0) * screenWidth;
|
||||
return res;
|
||||
}
|
||||
|
||||
double sp() {
|
||||
return h();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
// lib/views/login_screen.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../view_models/auth_view_model.dart';
|
||||
import '../core/constants/assets.dart';
|
||||
import '../core/constants/texts.dart';
|
||||
import '../size_config.dart';
|
||||
import '../views/widget/login_button.dart';
|
||||
import '../views/widget/login_logo.dart';
|
||||
import '../views/widget/login_textfield.dart';
|
||||
import '../views/widget/login_title.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@@ -12,58 +17,81 @@ class LoginScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
final _emailController = TextEditingController();
|
||||
final _usernameController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
final _usernameFocus = FocusNode();
|
||||
final _passwordFocus = FocusNode();
|
||||
|
||||
bool _obscurePassword = true;
|
||||
|
||||
bool get _isLoginEnabled =>
|
||||
_usernameController.text.isNotEmpty &&
|
||||
_passwordController.text.isNotEmpty;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_usernameController.addListener(_updateState);
|
||||
_passwordController.addListener(_updateState);
|
||||
_usernameFocus.addListener(_updateState);
|
||||
_passwordFocus.addListener(_updateState);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_usernameController.dispose();
|
||||
_passwordController.dispose();
|
||||
_usernameFocus.dispose();
|
||||
_passwordFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _updateState() => setState(() {});
|
||||
|
||||
void _togglePasswordVisibility() =>
|
||||
setState(() => _obscurePassword = !_obscurePassword);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final authViewModel = Provider.of<AuthViewModel>(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Login')),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(labelText: 'Email'),
|
||||
),
|
||||
TextField(
|
||||
controller: _passwordController,
|
||||
decoration: const InputDecoration(labelText: 'Password'),
|
||||
obscureText: true,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (authViewModel.isLoading)
|
||||
const CircularProgressIndicator()
|
||||
else
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
authViewModel.login(
|
||||
_emailController.text,
|
||||
_passwordController.text,
|
||||
);
|
||||
},
|
||||
child: const Text('Login'),
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const LoginLogo(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
LoginTextField(
|
||||
controller: _usernameController,
|
||||
focusNode: _usernameFocus,
|
||||
label: TextManager.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
),
|
||||
if (authViewModel.errorMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
authViewModel.errorMessage!,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _passwordController,
|
||||
focusNode: _passwordFocus,
|
||||
label: TextManager.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
isPassword: true,
|
||||
obscureText: _obscurePassword,
|
||||
onToggleVisibility: _togglePasswordVisibility,
|
||||
),
|
||||
if (authViewModel.loggedInUser != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
'Welcome, ${authViewModel.loggedInUser!.name}!',
|
||||
style: const TextStyle(color: Colors.green),
|
||||
),
|
||||
|
||||
SizedBox(height: 32.0.h()),
|
||||
LoginButton(
|
||||
isEnabled: _isLoginEnabled,
|
||||
onPressed: _isLoginEnabled ? () {} : null,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/constants/colors.dart';
|
||||
import '../../core/constants/texts.dart';
|
||||
import '../../size_config.dart';
|
||||
|
||||
class LoginButton extends StatelessWidget {
|
||||
final bool isEnabled;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
const LoginButton({
|
||||
super.key,
|
||||
required this.isEnabled,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 55.0.h(),
|
||||
child: ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: isEnabled ? AppColors.mainBlue : Colors.grey[300],
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(28.0.w()),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
TextManager.loginButton,
|
||||
style: TextStyle(fontSize: 16.0.sp(), fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/constants/assets.dart';
|
||||
import '../../size_config.dart';
|
||||
|
||||
class LoginLogo extends StatelessWidget {
|
||||
const LoginLogo({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Image.asset(AssetsManager.logo, width: 190.0.w(), height: 88.0.h());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../core/constants/assets.dart';
|
||||
import '../../core/constants/colors.dart';
|
||||
import '../../size_config.dart';
|
||||
|
||||
class LoginTextField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final FocusNode focusNode;
|
||||
final String label;
|
||||
final String iconPath;
|
||||
final bool isPassword;
|
||||
final bool obscureText;
|
||||
final VoidCallback? onToggleVisibility;
|
||||
|
||||
const LoginTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.focusNode,
|
||||
required this.label,
|
||||
required this.iconPath,
|
||||
this.isPassword = false,
|
||||
this.obscureText = false,
|
||||
this.onToggleVisibility,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
obscureText: isPassword ? obscureText : false,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.all(12.0.w()),
|
||||
child: SvgPicture.asset(
|
||||
iconPath,
|
||||
width: 24.0.w(),
|
||||
height: 24.0.h(),
|
||||
colorFilter: ColorFilter.mode(
|
||||
focusNode.hasFocus ? Colors.black87 : Colors.grey,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
suffixIcon:
|
||||
isPassword
|
||||
? IconButton(
|
||||
onPressed: onToggleVisibility,
|
||||
icon: SvgPicture.asset(
|
||||
obscureText
|
||||
? AssetsManager.passwordVisibility
|
||||
: AssetsManager.passwordVisibilityOff,
|
||||
width: 24.0.w(),
|
||||
height: 24.0.h(),
|
||||
colorFilter: ColorFilter.mode(
|
||||
focusNode.hasFocus ? Colors.black87 : Colors.grey,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12.0.w()),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: AppColors.borderBlue),
|
||||
borderRadius: BorderRadius.circular(12.0.w()),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/constants/texts.dart';
|
||||
import '../../size_config.dart';
|
||||
|
||||
class LoginTitle extends StatelessWidget {
|
||||
const LoginTitle({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
TextManager.signInTitle,
|
||||
style: TextStyle(fontSize: 28.0.sp(), fontWeight: FontWeight.w700),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
TextManager.signInSubtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,12 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
provider: ^6.1.5
|
||||
flutter_svg: ^2.2.0
|
||||
freezed: ^3.1.0
|
||||
freezed_annotation: ^3.1.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.8
|
||||
@@ -48,8 +54,10 @@ flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
assets:
|
||||
- assets/icons/
|
||||
- assets/svgs/
|
||||
- assets/pngs/
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
|
||||