Health Calculators
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path d="M26.582 9.25C26.1253 9.25 25.7623 9.44409 25.4564 9.6775C25.4426 9.68802 25.4288 9.69876 25.4149 9.70972C25.1988 9.87986 25.0908 9.96493 25.0829 10.0995C25.0749 10.234 25.1799 10.3391 25.39 10.5492L29.4509 14.6101C29.661 14.8202 29.7661 14.9252 29.9006 14.9172C30.0352 14.9093 30.1202 14.8013 30.2904 14.5853C30.3013 14.5713 30.3121 14.5575 30.3226 14.5437C30.556 14.2378 30.7501 13.8748 30.7501 13.4181C30.7501 12.9614 30.556 12.5984 30.3226 12.2925C30.1683 12.0902 29.9664 11.8751 29.7389 11.6439L30.4645 10.9183C30.8461 10.5367 30.8461 9.91789 30.4645 9.53624C30.0828 9.15459 29.4641 9.15459 29.0824 9.53624L28.3568 10.2619C28.1254 10.0341 27.9101 9.83199 27.7076 9.6775C27.4017 9.44409 27.0387 9.25 26.582 9.25Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M23.8025 10.5545C23.4544 10.4103 23.0732 10.3772 22.7099 10.455C22.3791 10.5259 22.1459 10.7182 22.0063 10.8577L20.9207 11.9434C20.7811 12.083 20.5888 12.3162 20.5179 12.647C20.4401 13.0103 20.4732 13.3915 20.6174 13.7396C20.7033 13.9468 20.8276 14.1127 20.9534 14.2569L22.8296 16.1331C22.6603 16.3768 22.4295 16.5648 22.1399 16.6931C19.6325 17.8047 17.8054 19.6318 16.6939 22.1392C16.5656 22.4286 16.3776 22.6594 16.1341 22.8286L14.2565 20.951C14.2565 20.951 13.9464 20.7009 13.7391 20.6151C13.391 20.4709 13.0098 20.4377 12.6465 20.5155C12.3157 20.5864 12.0825 20.7787 11.943 20.9183L10.8573 22.004C10.7178 22.1435 10.5254 22.3767 10.4546 22.7075C10.3767 23.0708 10.4099 23.452 10.5541 23.8001C10.6399 24.0074 10.7642 24.1733 10.89 24.3175L15.6813 29.1088C15.8255 29.2345 15.9914 29.3589 16.1987 29.4447C16.5468 29.5889 16.928 29.6221 17.2913 29.5442C17.6221 29.4734 17.8553 29.281 17.9948 29.1415L19.0805 28.0558C19.22 27.9162 19.4124 27.6831 19.4832 27.3522C19.5611 26.989 19.5279 26.6078 19.3837 26.2597C19.2979 26.0524 19.1736 25.8865 19.0478 25.7423L17.5583 24.2528C17.9649 23.9054 18.2931 23.4666 18.5223 22.9497C19.4326 20.8962 20.8969 19.4319 22.9504 18.5215C23.4675 18.2923 23.9063 17.964 24.2537 17.5573L25.7446 19.0482C25.7446 19.0482 26.0548 19.2983 26.262 19.3842C26.6101 19.5284 26.9913 19.5615 27.3546 19.4837C27.6854 19.4128 27.9186 19.2205 28.0582 19.0809L29.1438 17.9952C29.2834 17.8557 29.4757 17.6225 29.5466 17.2917C29.6244 16.9284 29.5913 16.5472 29.4471 16.1991C29.3612 15.9919 29.2369 15.8259 29.1111 15.6817L24.3199 10.8905C24.1757 10.7647 24.0097 10.6404 23.8025 10.5545Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M13.4191 30.7492C13.8759 30.7492 14.2388 30.5551 14.5447 30.3217C14.5585 30.3112 14.5724 30.3005 14.5863 30.2895C14.8023 30.1194 14.9103 30.0343 14.9183 29.8997C14.9262 29.7652 14.8212 29.6601 14.6111 29.45L10.5502 25.3892C10.3401 25.1791 10.2351 25.074 10.1005 25.082C9.96598 25.0899 9.8809 25.1979 9.71076 25.414C9.69981 25.4279 9.68906 25.4417 9.67854 25.4555C9.44513 25.7614 9.25104 26.1244 9.25104 26.5811C9.25104 27.0378 9.44513 27.4008 9.67854 27.7067C9.83274 27.9088 10.0344 28.1237 10.2616 28.3546L9.53697 29.0793C9.15532 29.4609 9.15532 30.0797 9.53697 30.4613C9.91862 30.843 10.5374 30.843 10.919 30.4613L11.6437 29.7367C11.8753 29.9647 12.0909 30.1671 12.2935 30.3217C12.5995 30.5551 12.9624 30.7492 13.4191 30.7492Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.2 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path d="M16 19.75C15.3096 19.75 14.75 20.3096 14.75 21C14.75 21.6904 15.3096 22.25 16 22.25H16.009C16.6993 22.25 17.259 21.6904 17.259 21C17.259 20.3096 16.6993 19.75 16.009 19.75H16Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M19.9955 19.75C19.3052 19.75 18.7455 20.3096 18.7455 21C18.7455 21.6904 19.3052 22.25 19.9955 22.25H20.0045C20.6948 22.25 21.2545 21.6904 21.2545 21C21.2545 20.3096 20.6948 19.75 20.0045 19.75H19.9955Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M23.991 19.75C23.3007 19.75 22.741 20.3096 22.741 21C22.741 21.6904 23.3007 22.25 23.991 22.25H24C24.6904 22.25 25.25 21.6904 25.25 21C25.25 20.3096 24.6904 19.75 24 19.75H23.991Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M16 23.75C15.3096 23.75 14.75 24.3096 14.75 25C14.75 25.6904 15.3096 26.25 16 26.25H16.009C16.6993 26.25 17.259 25.6904 17.259 25C17.259 24.3096 16.6993 23.75 16.009 23.75H16Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M19.9955 23.75C19.3052 23.75 18.7455 24.3096 18.7455 25C18.7455 25.6904 19.3052 26.25 19.9955 26.25H20.0045C20.6948 26.25 21.2545 25.6904 21.2545 25C21.2545 24.3096 20.6948 23.75 20.0045 23.75H19.9955Z" fill="#8F9AA3"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 10C15 9.44772 14.5523 9 14 9C13.4477 9 13 9.44772 13 10V10.4489C12.2388 10.6903 11.5773 11.0699 11.018 11.6746C10.1895 12.5703 9.83279 13.6927 9.66416 15.0487C9.49997 16.3689 9.49998 18.0541 9.5 20.1739V20.8261C9.49998 22.9459 9.49997 24.6311 9.66416 25.9513C9.83279 27.3073 10.1895 28.4297 11.018 29.3254C11.8568 30.2322 12.9253 30.6329 14.2153 30.8204C15.4514 31.0001 17.0229 31 18.9712 31H21.0288C22.977 31 24.5486 31.0001 25.7847 30.8204C27.0747 30.6329 28.1432 30.2322 28.982 29.3254C29.8105 28.4297 30.1672 27.3073 30.3358 25.9513C30.5 24.6311 30.5 22.9459 30.5 20.8261V20.1739C30.5 18.0541 30.5 16.3689 30.3358 15.0487C30.1672 13.6927 29.8105 12.5703 28.982 11.6746C28.4227 11.0699 27.7612 10.6903 27 10.4489V10C27 9.44772 26.5523 9 26 9C25.4477 9 25 9.44772 25 10V10.0917C23.903 9.99995 22.5863 9.99998 21.0288 10L18.9712 10C17.4138 9.99998 16.097 9.99995 15 10.0917V10ZM12.4978 17C12.0392 17 11.8099 17 11.6639 17.1442C11.5178 17.2883 11.5149 17.5147 11.509 17.9675C11.5003 18.6407 11.5 19.3942 11.5 20.2432V20.7568C11.5 22.9616 11.5018 24.5221 11.6489 25.7045C11.7933 26.8656 12.0626 27.5094 12.4862 27.9673C12.8996 28.4142 13.4647 28.6903 14.503 28.8412C15.5786 28.9975 17.0043 29 19.05 29H20.95C22.9957 29 24.4214 28.9975 25.497 28.8412C26.5353 28.6903 27.1004 28.4142 27.5138 27.9673C27.9374 27.5094 28.2067 26.8656 28.3511 25.7045C28.4982 24.5221 28.5 22.9616 28.5 20.7568V20.2432C28.5 19.3942 28.4997 18.6407 28.491 17.9675C28.4851 17.5147 28.4822 17.2883 28.3362 17.1442C28.1901 17 27.9608 17 27.5022 17L12.4978 17Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path d="M18.2215 12.1982C18.7926 12.0686 19.3878 12 20.0002 12C24.4185 12 28.0002 15.5817 28.0002 20C28.0002 22.0411 27.2363 23.904 25.9769 25.3181V24C25.9769 23.4477 25.5292 23 24.9769 23C24.4246 23 23.9769 23.4477 23.9769 24V27.5C23.9769 28.0523 24.4246 28.5 24.9769 28.5H28.5002C29.0525 28.5 29.5002 28.0523 29.5002 27.5C29.5002 26.9477 29.0525 26.5 28.5002 26.5H27.5996C29.0955 24.7526 30.0002 22.482 30.0002 20C30.0002 14.4772 25.5231 10 20.0002 10C19.2379 10 18.4943 10.0855 17.7789 10.2478C17.2403 10.37 16.9028 10.9057 17.025 11.4443C17.1472 11.9829 17.6829 12.3204 18.2215 12.1982Z" fill="#8F9AA3"/>
|
||||||
|
<path d="M11.5 11.5156C10.9477 11.5156 10.5 11.9633 10.5 12.5156C10.5 13.0679 10.9477 13.5156 11.5 13.5156H12.3873C10.8993 15.261 10 17.5254 10 20.0001C10 25.5229 14.4772 30.0001 20 30.0001C20.7623 30.0001 21.5059 29.9146 22.2213 29.7523C22.7599 29.6301 23.0974 29.0944 22.9752 28.5558C22.853 28.0172 22.3173 27.6796 21.7787 27.8019C21.2076 27.9314 20.6124 28.0001 20 28.0001C15.5817 28.0001 12 24.4184 12 20.0001C12 17.9716 12.7545 16.1191 14 14.7082L14 16.0001C14 16.5524 14.4477 17.0001 15 17.0001C15.5523 17.0001 16 16.5524 16 16.0001V12.5156C16 11.9633 15.5523 11.5156 15 11.5156L11.5 11.5156Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 30.75C17.7007 30.75 17.4476 30.6611 17.2239 30.5507C17.0235 30.4518 16.8014 30.3117 16.5667 30.1637L14.6291 28.9417C14.3742 28.7809 14.2283 28.6899 14.114 28.6327C14.0083 28.5747 13.9326 28.6033 13.9079 28.6249C13.7948 28.678 13.6438 28.7696 13.371 28.9417L12.8622 29.2911C12.6515 29.4359 12.3974 29.6033 12.1271 29.7447C11.655 29.9917 10.8144 30.3299 10.0558 29.8414C9.54247 29.5107 9.38152 28.9658 9.3149 28.487C9.24992 28.0198 9.24996 27.4095 9.25 26.7008L9.25 15.9998C9.24998 14.6191 9.24997 13.5075 9.36641 12.6336C9.48716 11.7274 9.74526 10.9648 10.346 10.3587C10.9476 9.75161 11.706 9.48992 12.6074 9.36764C13.4748 9.24997 14.5775 9.24998 15.9446 9.25L28.016 9.25C28.4911 9.25 28.8802 9.4793 29.1687 9.7609C29.4541 10.0395 29.6847 10.4083 29.8722 10.8101C30.2537 11.6272 30.5229 12.7386 30.6684 13.9644C30.7158 14.3637 30.7614 14.7478 30.7473 15.0722C30.7309 15.4502 30.6325 15.8306 30.3326 16.1652C30.0239 16.5095 29.6366 16.6432 29.2576 16.6998C28.921 16.7501 28.5084 16.7501 28.0646 16.75L26.75 16.75L26.75 26.7008C26.75 27.4095 26.7501 28.0198 26.6851 28.487C26.6185 28.9658 26.4575 29.5107 25.9441 29.8414C25.1856 30.3299 24.345 29.9917 23.8729 29.7447C23.6025 29.6033 23.3485 29.4359 23.1377 29.2911L22.629 28.9417C22.3562 28.7696 22.2052 28.678 22.0921 28.6249C22.0674 28.6033 21.9917 28.5747 21.8859 28.6327C21.7717 28.6899 21.6258 28.7809 21.3709 28.9417L19.4333 30.1636C19.1987 30.3116 18.9765 30.4518 18.7761 30.5507C18.5524 30.6611 18.2993 30.75 18 30.75ZM28.121 10.8343C28.0611 10.7759 27.9721 10.7748 27.9121 10.8329C27.8853 10.8591 27.854 10.8934 27.8186 10.938C27.652 11.1477 27.469 11.4974 27.3014 11.9962C27.0311 12.8006 26.836 13.8859 26.7724 15.1219C26.7702 15.1644 26.7682 15.2071 26.7663 15.25H28.016C28.5254 15.25 28.8236 15.248 29.0358 15.2163C29.2003 15.1917 29.2417 15.1699 29.2487 15.0072C29.2569 14.8192 29.2283 14.5588 29.173 14.0921C29.0404 12.9728 28.7989 12.0569 28.513 11.4445C28.3694 11.1368 28.2313 10.942 28.121 10.8343ZM18.75 15C18.75 14.5858 18.4142 14.25 18 14.25C17.5858 14.25 17.25 14.5858 17.25 15V15.3336C16.8358 15.4281 16.4543 15.6005 16.1358 15.8393C15.6318 16.2173 15.25 16.7968 15.25 17.5C15.25 18.2032 15.6318 18.7827 16.1358 19.1607C16.6396 19.5385 17.3011 19.75 18 19.75C18.4057 19.75 18.7441 19.8743 18.9642 20.0393C19.1841 20.2042 19.25 20.3748 19.25 20.5C19.25 20.6252 19.1841 20.7958 18.9642 20.9607C18.7441 21.1257 18.4057 21.25 18 21.25C17.3316 21.25 16.9095 20.929 16.7922 20.6803C16.6157 20.3056 16.1688 20.145 15.7941 20.3216C15.4194 20.4981 15.2588 20.945 15.4354 21.3197C15.7653 22.0197 16.4613 22.4848 17.25 22.6655V23C17.25 23.4142 17.5858 23.75 18 23.75C18.4142 23.75 18.75 23.4142 18.75 23V22.6664C19.1642 22.5719 19.5457 22.3995 19.8642 22.1607C20.3682 21.7827 20.75 21.2032 20.75 20.5C20.75 19.7968 20.3682 19.2173 19.8642 18.8393C19.3604 18.4615 18.6989 18.25 18 18.25C17.5943 18.25 17.2559 18.1257 17.0358 17.9607C16.8159 17.7958 16.75 17.6252 16.75 17.5C16.75 17.3748 16.8159 17.2042 17.0358 17.0393C17.2559 16.8743 17.5943 16.75 18 16.75C18.6684 16.75 19.0905 17.071 19.2078 17.3197C19.3843 17.6944 19.8312 17.855 20.2059 17.6784C20.5806 17.5019 20.7412 17.055 20.5646 16.6803C20.2347 15.9803 19.5387 15.5152 18.75 15.3345V15Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.999 9.25C18.3178 9.25 16.9509 9.81794 15.7588 10.4355C15.3986 10.6222 15.0639 10.8068 14.7441 10.9832C13.9617 11.4148 13.2681 11.7974 12.5066 12.0404C12.0593 12.1831 11.6631 12.3095 11.3821 12.4196C11.1177 12.5232 10.8035 12.6652 10.5805 12.913C10.3802 13.1356 10.2773 13.3855 10.2091 13.6245C10.1472 13.8415 10.0943 14.1352 10.0435 14.417C8.80553 21.2827 11.5125 27.787 18.123 30.3179C18.7727 30.5667 19.2513 30.75 20.0023 30.75C20.7534 30.75 21.2319 30.5667 21.8816 30.3179C28.4919 27.7869 31.1962 21.2824 29.9579 14.417C29.9071 14.1352 29.8541 13.8413 29.7922 13.6243C29.7239 13.3853 29.621 13.1354 29.4207 12.9128C29.1977 12.665 28.8835 12.523 28.619 12.4195C28.3381 12.3094 27.9419 12.1831 27.4946 12.0404C26.7327 11.7974 26.0386 11.4148 25.2555 10.9831C24.9356 10.8067 24.6007 10.6221 24.2405 10.4355C23.0477 9.81795 21.6803 9.25 19.999 9.25ZM25.3338 17.9429C25.8544 17.7585 26.127 17.187 25.9426 16.6664C25.7582 16.1458 25.1868 15.8733 24.6662 16.0576C23.788 16.3686 22.9486 16.9618 22.212 17.6078C21.465 18.2629 20.7666 19.0235 20.1733 19.7347C19.734 20.2613 19.3456 20.7689 19.0304 21.1997C18.7416 20.8538 18.454 20.6011 18.1797 20.4182C17.9053 20.2353 17.5523 20.0003 17 20.0003C16.4477 20.0003 16 20.448 16 21.0003C16 21.521 16.398 21.9487 16.9064 21.9959C17.1104 22.0149 17.6649 22.5661 18.1056 23.4475C18.2668 23.7698 18.5889 23.9805 18.9489 23.999C19.3087 24.0174 19.6508 23.8406 19.8441 23.5365C19.8441 23.5365 20.1922 23.0141 20.3574 22.7801C20.6885 22.3111 21.1572 21.6774 21.7091 21.0159C22.2628 20.3521 22.888 19.6751 23.5307 19.1115C24.1837 18.5388 24.8002 18.1319 25.3338 17.9429Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 9.25C14.0629 9.25 9.25 14.0629 9.25 20C9.25 25.9371 14.0629 30.75 20 30.75C25.9371 30.75 30.75 25.9371 30.75 20C30.75 14.0629 25.9371 9.25 20 9.25ZM19.9916 14.25C18.1958 14.25 16.7381 15.7041 16.7381 17.5C16.7381 19.2959 18.1958 20.75 19.9916 20.75C21.7875 20.75 23.2452 19.2959 23.2452 17.5C23.2452 15.7041 21.7875 14.25 19.9916 14.25ZM25.0409 24.4802C22.3735 21.6002 17.5747 21.7487 14.9638 24.4756L14.7763 24.6631C14.631 24.8084 14.5517 25.0069 14.5569 25.2123C14.562 25.4177 14.6512 25.612 14.8036 25.7498C16.1777 26.9923 18.0013 27.75 20.0001 27.75C21.9989 27.75 23.8225 26.9923 25.1966 25.7498C25.349 25.612 25.4382 25.4177 25.4433 25.2123C25.4485 25.0069 25.3692 24.8084 25.2239 24.6631L25.0409 24.4802Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 939 B |
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#18C273"/>
|
||||||
|
<path d="M20.6464 12.8584C20.9445 12.4867 21.1271 12.0198 21.1271 11.5C21.1271 10.2029 20.0081 9.25 18.7543 9.25H13.2452C11.9914 9.25 10.8724 10.2029 10.8724 11.5C10.8724 12.0198 11.055 12.4867 11.3532 12.8584C11.5208 13.0674 11.6046 13.1719 11.6136 13.2601C11.6225 13.3484 11.5724 13.4461 11.4721 13.6417C10.1525 16.2149 9.24963 18.6245 9.24963 21.7771V26C9.24963 27.6296 9.41033 28.9586 10.3117 29.7918C10.764 30.2098 11.3378 30.4427 11.9913 30.5754C12.6394 30.707 13.4172 30.75 14.3271 30.75H17.6724C18.0971 30.75 18.536 30.7311 18.9492 30.6976C19.458 30.6563 19.7124 30.6356 19.7825 30.466C19.8525 30.2965 19.6681 30.0801 19.2993 29.6473C18.355 28.5392 17.7496 27.1021 17.7496 25.3845C17.7496 23.5039 18.3683 21.8652 19.2997 20.6615C19.9146 19.867 20.7887 19.1221 21.8271 18.7559C22.1207 18.6524 22.2674 18.6007 22.3223 18.4975C22.3772 18.3943 22.343 18.2627 22.2747 17.9996C21.8827 16.4907 21.2713 15.0921 20.5274 13.6416C20.4271 13.4461 20.377 13.3484 20.386 13.2601C20.3949 13.1719 20.4787 13.0674 20.6464 12.8584Z" fill="white"/>
|
||||||
|
<path d="M28.7264 18C28.7264 17.4477 28.2787 17 27.7264 17C26.4312 17 25.4699 17.6734 24.8679 18.5139C24.515 19.0066 24.2718 19.5713 24.1343 20.1362C23.845 20.0691 23.5182 20.0195 23.1816 20.0195C22.1838 20.0195 21.1925 20.667 20.4862 21.5798C19.7591 22.5195 19.2498 23.8349 19.2498 25.3848C19.2498 28.6682 22.1829 30.7503 24.9998 30.7503C27.8167 30.7503 30.7498 28.6682 30.7498 25.3848C30.7498 23.8349 30.2406 22.5195 29.5134 21.5798C28.8071 20.667 27.8158 20.0195 26.818 20.0196C26.6312 20.0195 26.4473 20.0348 26.2712 20.0599C26.3347 19.9256 26.409 19.7969 26.4939 19.6785C26.801 19.2497 27.2033 19 27.7264 19C28.2787 19 28.7264 18.5523 28.7264 18Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path d="M23.5378 27.5227C23.7479 27.3066 23.8082 26.9857 23.6909 26.7081C23.5736 26.4304 23.3014 26.25 23 26.25H21L21 13.75L23 13.75C23.3014 13.75 23.5736 13.5696 23.6909 13.2919C23.8082 13.0143 23.7479 12.6934 23.5378 12.4773C23.4246 12.3607 23.2441 12.135 22.9904 11.812C22.7621 11.5211 22.4399 11.1107 22.1661 10.7876C21.8723 10.4408 21.5478 10.086 21.2266 9.81244C21.0657 9.67538 20.888 9.54343 20.6999 9.44275C20.5177 9.34525 20.2758 9.25 20 9.25C19.7242 9.25 19.4824 9.34525 19.3002 9.44275C19.112 9.54343 18.9344 9.67538 18.7734 9.81244C18.4522 10.086 18.1277 10.4408 17.8339 10.7876C17.5601 11.1107 17.238 11.5211 17.0096 11.812C16.7559 12.135 16.5755 12.3607 16.4622 12.4773C16.2521 12.6934 16.1918 13.0143 16.3092 13.2919C16.4265 13.5696 16.6986 13.75 17 13.75H19L19 26.25H17C16.6986 26.25 16.4265 26.4304 16.3092 26.7081C16.1918 26.9857 16.2521 27.3066 16.4622 27.5227C16.5755 27.6393 16.7559 27.865 17.0096 28.188C17.238 28.4789 17.5601 28.8893 17.8339 29.2124C18.1277 29.5592 18.4522 29.914 18.7734 30.1876C18.9344 30.3246 19.112 30.4566 19.3002 30.5573C19.4824 30.6547 19.7242 30.75 20 30.75C20.2758 30.75 20.5177 30.6547 20.6999 30.5572C20.888 30.4566 21.0657 30.3246 21.2266 30.1876C21.5478 29.914 21.8723 29.5592 22.1661 29.2124C22.4399 28.8893 22.7621 28.4789 22.9904 28.188C23.2441 27.865 23.4246 27.6393 23.5378 27.5227Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path d="M14.0748 9.25021C13.2108 9.25016 12.4627 9.25011 11.8629 9.33132C11.2158 9.41894 10.5992 9.61694 10.103 10.1166C9.61189 10.6111 9.42205 11.2171 9.33738 11.8513C9.25772 12.448 9.25775 13.1949 9.25779 14.0698L9.25779 14.1308C9.2578 14.3712 9.25561 14.6022 9.25353 14.8221C9.24814 15.3915 9.24345 15.8867 9.27924 16.2795C9.33494 16.8907 9.4987 17.4598 9.99534 17.9599C10.3697 18.3368 10.9883 18.3482 11.3772 17.9854C11.766 17.6225 11.7778 17.0228 11.4035 16.6458C11.3281 16.5699 11.2597 16.48 11.2262 16.1128C11.1992 15.8167 11.2026 15.4635 11.2075 14.9542C11.2098 14.717 11.2124 14.4458 11.2124 14.1308C11.2124 13.1779 11.2143 12.5551 11.2758 12.0945C11.3341 11.6576 11.4303 11.5121 11.5111 11.4306C11.587 11.3542 11.7168 11.2642 12.1332 11.2078C12.5818 11.1471 13.1917 11.145 14.141 11.145L25.8588 11.145C26.8081 11.145 27.418 11.1471 27.8665 11.2078C28.283 11.2642 28.4128 11.3542 28.4886 11.4306C28.5695 11.5121 28.6657 11.6576 28.724 12.0945C28.7855 12.5551 28.7874 13.1779 28.7874 14.1308C28.7874 14.444 28.79 14.7139 28.7922 14.9501C28.797 15.4613 28.8004 15.8159 28.7732 16.1125C28.7397 16.4796 28.6712 16.5697 28.5957 16.6458C28.2213 17.0228 28.2331 17.6225 28.6219 17.9854C29.0108 18.3482 29.6295 18.3368 30.0038 17.9599C30.5002 17.4599 30.6643 16.8911 30.7202 16.2798C30.7562 15.8861 30.7515 15.3898 30.7462 14.8191C30.7441 14.6001 30.742 14.3702 30.742 14.1308L30.742 14.0698C30.742 13.1949 30.7421 12.448 30.6624 11.8513C30.5777 11.2171 30.3879 10.6111 29.8968 10.1166C29.4006 9.61695 28.784 9.41894 28.1369 9.33132C27.5371 9.25012 26.7889 9.25016 25.925 9.25021L14.0748 9.25021Z" fill="#8F9AA3"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9842 29.8165C15.9781 31.0614 17.9976 31.0614 18.9916 29.8165C19.2101 29.5428 19.6483 29.2974 19.9998 29.2974C20.3513 29.2974 20.7895 29.5428 21.0081 29.8165C22.002 31.0614 24.0216 31.0614 25.0155 29.8165L25.5666 29.1263C25.6362 29.0391 25.7678 28.9739 25.9356 28.9818C26.1037 28.9898 26.2183 29.0662 26.2708 29.1496C26.6929 29.8193 27.4137 30.0602 28.0404 30.0126C28.6496 29.9663 29.3157 29.6346 29.625 28.9757C29.8184 28.6746 29.7447 27.999 29.6837 27.6989L27.7527 18.5617C27.5201 17.4608 27.3313 16.5674 27.0921 15.8695C26.8427 15.1417 26.5126 14.5436 25.9474 14.0868C25.3827 13.6304 24.7284 13.4325 23.9638 13.3395C23.2296 13.2502 22.3137 13.2502 21.1836 13.2502L18.8161 13.2502C17.6859 13.2502 16.7701 13.2502 16.0358 13.3395C15.2712 13.4325 14.617 13.6304 14.0523 14.0868C13.4871 14.5436 13.157 15.1417 12.9076 15.8695C12.6683 16.5674 12.4796 17.4608 12.2469 18.5617L10.3159 27.6989C10.2549 27.999 10.1813 28.6746 10.3746 28.9757C10.6839 29.6346 11.3501 29.9663 11.9592 30.0126C12.586 30.0602 13.3067 29.8193 13.7289 29.1496C13.7814 29.0662 13.896 28.9898 14.064 28.9818C14.2318 28.9739 14.3634 29.0391 14.4331 29.1263L14.9842 29.8165ZM19.9998 17.2502C20.414 17.2502 20.7498 17.586 20.7498 18.0002C20.7498 18.4144 20.414 18.7502 19.9998 18.7502L16.9998 18.7502C16.5856 18.7502 16.2498 18.4144 16.2498 18.0002C16.2498 17.586 16.5856 17.2502 16.9998 17.2502L19.9998 17.2502ZM22.7498 22.0002C22.7498 21.586 22.414 21.2502 21.9998 21.2502L15.9998 21.2502C15.5856 21.2502 15.2498 21.586 15.2498 22.0002C15.2498 22.4144 15.5856 22.7502 15.9998 22.7502L21.9998 22.7502C22.414 22.7502 22.7498 22.4144 22.7498 22.0002Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9241 16.3828C20.8753 16.5007 20.803 16.6112 20.7071 16.7071C20.524 16.8902 20.2877 16.9874 20.048 16.9989C20.032 16.9996 20.016 17 20 17H15C14.4477 17 14 16.5523 14 16C14 15.4477 14.4477 15 15 15H17.5858L14.2929 11.7071C13.8341 11.2483 13.5517 10.9683 13.3213 10.7871C13.0748 10.5721 12.7902 10.6975 12.6787 10.7871C12.4483 10.9683 12.1277 11.2866 11.6688 11.7454L11.6688 11.7454C11.2604 12.1538 10.8933 12.521 10.5576 12.7851C10.1934 13.0714 9.76589 13.3198 9.22428 13.3686C9.07506 13.382 8.92494 13.382 8.77572 13.3686C8.23411 13.3198 7.8066 13.0714 7.44245 12.7851C7.10664 12.521 6.73952 12.1538 6.33116 11.7454L3.29289 8.70711C2.90237 8.31658 2.90237 7.68342 3.29289 7.29289C3.68342 6.90237 4.31658 6.90237 4.70711 7.29289L7.70711 10.2929C8.16593 10.7517 8.44831 11.0317 8.67872 11.2129C8.78511 11.3073 9.06256 11.4394 9.32128 11.2129C9.55169 11.0317 9.87234 10.7134 10.3312 10.2546C10.7395 9.84619 11.1066 9.47902 11.4424 9.21495C11.8066 8.92859 12.2341 8.68019 12.7757 8.6314C12.9249 8.61796 13.0751 8.61796 13.2243 8.6314C13.7659 8.68019 14.1934 8.92859 14.5576 9.21495C14.8934 9.47901 15.2605 9.84617 15.6688 10.2546L19 13.5858V11C19 10.4477 19.4477 10 20 10C20.5523 10 21 10.4477 21 11L21 15.999C21 16.0024 21 16.0058 21 16.0092C20.9988 16.1364 20.9735 16.2634 20.9241 16.3828ZM20.9241 16.3828C20.9719 16.2676 20.9988 16.1415 21 16.0092L20.9241 16.3828Z" fill="#ED1C2B"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9241 16.3828C20.8753 16.5007 20.803 16.6112 20.7071 16.7071C20.524 16.8902 20.2877 16.9874 20.048 16.9989C20.032 16.9996 20.016 17 20 17H15C14.4477 17 14 16.5523 14 16C14 15.4477 14.4477 15 15 15H17.5858L14.2929 11.7071C13.8341 11.2483 13.5517 10.9683 13.3213 10.7871C13.0748 10.5721 12.7902 10.6975 12.6787 10.7871C12.4483 10.9683 12.1277 11.2866 11.6688 11.7454L11.6688 11.7454C11.2604 12.1538 10.8933 12.521 10.5576 12.7851C10.1934 13.0714 9.76589 13.3198 9.22428 13.3686C9.07506 13.382 8.92494 13.382 8.77572 13.3686C8.23411 13.3198 7.8066 13.0714 7.44245 12.7851C7.10664 12.521 6.73952 12.1538 6.33116 11.7454L3.29289 8.70711C2.90237 8.31658 2.90237 7.68342 3.29289 7.29289C3.68342 6.90237 4.31658 6.90237 4.70711 7.29289L7.70711 10.2929C8.16593 10.7517 8.44831 11.0317 8.67872 11.2129C8.78511 11.3073 9.06256 11.4394 9.32128 11.2129C9.55169 11.0317 9.87234 10.7134 10.3312 10.2546C10.7395 9.84619 11.1066 9.47902 11.4424 9.21495C11.8066 8.92859 12.2341 8.68019 12.7757 8.6314C12.9249 8.61796 13.0751 8.61796 13.2243 8.6314C13.7659 8.68019 14.1934 8.92859 14.5576 9.21495C14.8934 9.47901 15.2605 9.84617 15.6688 10.2546L19 13.5858V11C19 10.4477 19.4477 10 20 10C20.5523 10 21 10.4477 21 11L21 15.999C21 16.0024 21 16.0058 21 16.0092C20.9988 16.1364 20.9735 16.2634 20.9241 16.3828ZM20.9241 16.3828C20.9719 16.2676 20.9988 16.1415 21 16.0092L20.9241 16.3828Z" fill="#FFAF15"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.6439 9.25L21.3561 9.25C22.259 9.24997 23.0211 9.24994 23.6249 9.32042C24.255 9.39396 24.8707 9.55847 25.3706 10.0092C25.5958 10.2122 25.7873 10.449 25.9376 10.7111C26.2326 11.2257 26.2812 11.7733 26.2347 12.3161C27.2103 12.4287 28.0196 12.686 28.6521 13.3197C29.2538 13.9224 29.5125 14.6811 29.6335 15.5824C29.75 16.4507 29.75 17.5548 29.75 18.9249V24.043C29.75 25.4131 29.75 26.5172 29.6335 27.3855C29.5125 28.2867 29.2538 29.0455 28.6521 29.6482C28.0502 30.2511 27.2923 30.5105 26.3919 30.6318C25.5249 30.7486 24.4225 30.7486 23.055 30.7485L16.945 30.7485C15.5775 30.7486 14.4751 30.7486 13.6081 30.6318C12.7077 30.5105 11.9498 30.2511 11.3479 29.6482C10.7462 29.0455 10.4875 28.2867 10.3665 27.3855C10.25 26.5172 10.25 25.4131 10.25 24.0431L10.25 18.9248C10.25 17.5548 10.25 16.4507 10.3665 15.5824C10.4875 14.6811 10.7462 13.9224 11.3479 13.3197C11.9804 12.686 12.7898 12.4287 13.7653 12.3161C13.7188 11.7733 13.7674 11.2257 14.0624 10.7111C14.2127 10.449 14.4042 10.2122 14.6294 10.0092C15.1293 9.55847 15.745 9.39396 16.3751 9.32042C16.9789 9.24994 17.741 9.24997 18.6439 9.25ZM16.6007 11.2073C16.1364 11.2615 15.9925 11.3524 15.9258 11.4126C15.8493 11.4815 15.7853 11.5609 15.7357 11.6476C15.6964 11.7161 15.6403 11.8608 15.6975 12.3014C15.7567 12.758 15.9109 13.3608 16.1482 14.278C16.3373 15.0085 16.4625 15.4878 16.5955 15.8468C16.7217 16.1872 16.8265 16.3358 16.9287 16.4331C17.0098 16.5102 17.1002 16.5785 17.1982 16.6363C17.3246 16.7109 17.5056 16.7746 17.8839 16.8109C18.1378 16.8353 18.4406 16.8445 18.8301 16.8479L19.5514 14.684C19.726 14.1601 20.2924 13.8769 20.8163 14.0516C21.3402 14.2262 21.6234 14.7926 21.4488 15.3165L20.9378 16.8493C21.4424 16.8474 21.8138 16.8399 22.1161 16.8109C22.4944 16.7746 22.6754 16.7109 22.8018 16.6363C22.8998 16.5785 22.9902 16.5102 23.0713 16.4331C23.1735 16.3358 23.2783 16.1872 23.4045 15.8468C23.5375 15.4878 23.6628 15.0085 23.8518 14.278C24.0891 13.3608 24.2433 12.758 24.3025 12.3014C24.3597 11.8608 24.3036 11.7161 24.2643 11.6476C24.2147 11.5609 24.1507 11.4815 24.0742 11.4126C24.0075 11.3524 23.8636 11.2615 23.3993 11.2073C22.9218 11.1516 22.2747 11.15 21.2979 11.15L18.7021 11.15C17.7253 11.15 17.0782 11.1516 16.6007 11.2073ZM18 25C17.4477 25 17 25.4477 17 26C17 26.5523 17.4477 27 18 27H22C22.5523 27 23 26.5523 23 26C23 25.4477 22.5523 25 22 25L18 25Z" fill="#8F9AA3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="40" height="40" rx="10" fill="#FF47A9"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 9.25C14.9276 9.25 10.75 13.1683 10.75 18.0807C10.75 20.893 11.9428 23.0764 14.2071 24.965C15.6434 26.1629 17.4087 28.18 18.4598 29.8028C18.7996 30.3274 19.3185 30.75 19.9859 30.75C20.6456 30.75 21.1704 30.3361 21.5267 29.8228C22.6504 28.2044 24.3659 26.1551 25.7929 24.965C28.0572 23.0764 29.25 20.893 29.25 18.0807C29.25 13.1683 25.0724 9.25 20 9.25ZM19 15C19 14.4477 18.5523 14 18 14C17.4477 14 17 14.4477 17 15V21C17 21.5523 17.4477 22 18 22C18.5523 22 19 21.5523 19 21V19H21V21C21 21.5523 21.4477 22 22 22C22.5523 22 23 21.5523 23 21V15C23 14.4477 22.5523 14 22 14C21.4477 14 21 14.4477 21 15V17H19V15Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 833 B |
@ -0,0 +1,142 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_result.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/bf.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/bmi.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/bmr.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/calories.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/crabs.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/dduedate.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/ibw.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/ovulation.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class HealthCalculatorDetailedPage extends StatefulWidget {
|
||||||
|
HealthCalculatorsTypeEnum calculatorType;
|
||||||
|
|
||||||
|
HealthCalculatorDetailedPage({super.key, required this.calculatorType});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HealthCalculatorDetailedPage> createState() => _HealthCalculatorDetailedPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedPage> {
|
||||||
|
dynamic calculatedResult;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ChangeNotifierProvider(
|
||||||
|
create: (_) => HealthCalcualtorViewModel(),
|
||||||
|
child: Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return CollapsingListView(
|
||||||
|
title: widget.calculatorType.displayName,
|
||||||
|
bottomChild: Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h),
|
||||||
|
child: CustomButton(
|
||||||
|
text: "Calculate".needTranslation,
|
||||||
|
onPressed: () {
|
||||||
|
if (calculatedResult == null) return;
|
||||||
|
DialogService dialogService = getIt.get<DialogService>();
|
||||||
|
Navigator.of(context).push(CustomPageRoute(
|
||||||
|
page: HealthCalculatorResultPage(calculatorType: widget.calculatorType, calculatedResult: calculatedResult),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
icon: null,
|
||||||
|
fontSize: 16.f,
|
||||||
|
backgroundColor: calculatedResult == null ? AppColors.bgRedLightColor : AppColors.primaryRedColor,
|
||||||
|
borderColor: calculatedResult == null ? AppColors.bgRedLightColor : AppColors.primaryRedColor,
|
||||||
|
borderRadius: 12.r,
|
||||||
|
fontWeight: FontWeight.w500)),
|
||||||
|
child: getCalculatorWidget(
|
||||||
|
type: widget.calculatorType,
|
||||||
|
onCalculate: (result) {
|
||||||
|
// result may be directly provided by widget, but prefer provider maps if available
|
||||||
|
switch (widget.calculatorType) {
|
||||||
|
case HealthCalculatorsTypeEnum.bmi:
|
||||||
|
calculatedResult = provider.bmiResultMap ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.calories:
|
||||||
|
case HealthCalculatorsTypeEnum.bmr:
|
||||||
|
calculatedResult = provider.caloriesResultMap ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.idealBodyWeight:
|
||||||
|
calculatedResult = provider.ibwResultMap ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.bodyFat:
|
||||||
|
calculatedResult = provider.bodyFatResultMap ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.crabsProteinFat:
|
||||||
|
calculatedResult = provider.macrosResultMap ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.ovulation:
|
||||||
|
calculatedResult = provider.ovulationResult ?? result;
|
||||||
|
break;
|
||||||
|
case HealthCalculatorsTypeEnum.deliveryDueDate:
|
||||||
|
calculatedResult = provider.deliveryResult ?? result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
).paddingSymmetrical(20.w, 24.h),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getCalculatorWidget({required HealthCalculatorsTypeEnum type, required Function(dynamic result) onCalculate}) {
|
||||||
|
switch (widget.calculatorType) {
|
||||||
|
case HealthCalculatorsTypeEnum.bmi:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: BMIWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
|
||||||
|
case HealthCalculatorsTypeEnum.calories:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: CaloriesWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.bmr:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: BMRWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.idealBodyWeight:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: IdealBodyWeightWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.bodyFat:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: BodyFatWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.crabsProteinFat:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: CrabsWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.ovulation:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: OvulationWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
case HealthCalculatorsTypeEnum.deliveryDueDate:
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
|
||||||
|
child: DeliveryDueDWidget(onChange: onCalculate),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,657 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class HealthCalcualtorViewModel extends ChangeNotifier {
|
||||||
|
// BMI
|
||||||
|
double? bmiResult;
|
||||||
|
String? bmiCategory;
|
||||||
|
|
||||||
|
// BMR / Calories
|
||||||
|
double? bmrResult;
|
||||||
|
double? caloriesResult;
|
||||||
|
String? bmrRange;
|
||||||
|
String? calorieRange;
|
||||||
|
|
||||||
|
// IBW
|
||||||
|
double? ibwResult;
|
||||||
|
double? weightDifference;
|
||||||
|
String? weightStatus;
|
||||||
|
|
||||||
|
// Body Fat
|
||||||
|
double? bodyFatResult;
|
||||||
|
String? bodyFatCategory;
|
||||||
|
|
||||||
|
// Crabs/Protein/Fat
|
||||||
|
double? carbsCalories;
|
||||||
|
double? proteinCalories;
|
||||||
|
double? fatCalories;
|
||||||
|
double? carbsGrams;
|
||||||
|
double? proteinGrams;
|
||||||
|
double? fatGrams;
|
||||||
|
String? dietType;
|
||||||
|
|
||||||
|
// Ovulation
|
||||||
|
Map<String, dynamic>? ovulationResult;
|
||||||
|
|
||||||
|
// Delivery
|
||||||
|
Map<String, dynamic>? deliveryResult;
|
||||||
|
|
||||||
|
// Generic helpers
|
||||||
|
|
||||||
|
void calculateBMI({required String heightText, required String weightText, required String heightUnit, required String weightUnit}) {
|
||||||
|
if (heightText.trim().isEmpty || weightText.trim().isEmpty) {
|
||||||
|
bmiResult = null;
|
||||||
|
bmiCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double height = double.parse(heightText.trim());
|
||||||
|
double weight = double.parse(weightText.trim());
|
||||||
|
|
||||||
|
double heightInMeters = _convertHeightToMeters(height, heightUnit);
|
||||||
|
double weightInKg = _convertWeightToKg(weight, weightUnit);
|
||||||
|
|
||||||
|
if (heightInMeters <= 0 || weightInKg <= 0) {
|
||||||
|
bmiResult = null;
|
||||||
|
bmiCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bmi = weightInKg / (heightInMeters * heightInMeters);
|
||||||
|
String category = _getBMICategory(bmi);
|
||||||
|
|
||||||
|
bmiResult = bmi;
|
||||||
|
bmiCategory = category;
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
bmiResult = null;
|
||||||
|
bmiCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _convertHeightToMeters(double height, String unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'cm':
|
||||||
|
return height / 100;
|
||||||
|
case 'm':
|
||||||
|
return height;
|
||||||
|
case 'ft':
|
||||||
|
return height * 0.3048;
|
||||||
|
case 'in':
|
||||||
|
return height * 0.0254;
|
||||||
|
default:
|
||||||
|
return height / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _convertWeightToKg(double weight, String unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'kg':
|
||||||
|
return weight;
|
||||||
|
case 'lb':
|
||||||
|
return weight * 0.453592;
|
||||||
|
default:
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getBMICategory(double bmi) {
|
||||||
|
if (bmi < 18.5) {
|
||||||
|
return 'Underweight';
|
||||||
|
} else if (bmi >= 18.5 && bmi < 25) {
|
||||||
|
return 'Normal';
|
||||||
|
} else if (bmi >= 25 && bmi < 30) {
|
||||||
|
return 'Overweight';
|
||||||
|
} else {
|
||||||
|
return 'Obese';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BMR / Calories (shared logic)
|
||||||
|
void calculateBMRAndCalories({required String heightText, required String weightText, required String ageText, required String heightUnit, required String weightUnit, required String gender, required String activityLevel, bool forCaloriesOnly = false}) {
|
||||||
|
if (heightText.trim().isEmpty || weightText.trim().isEmpty || ageText.trim().isEmpty) {
|
||||||
|
bmrResult = null;
|
||||||
|
caloriesResult = null;
|
||||||
|
bmrRange = null;
|
||||||
|
calorieRange = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double height = double.parse(heightText.trim());
|
||||||
|
double weight = double.parse(weightText.trim());
|
||||||
|
int age = int.parse(ageText.trim());
|
||||||
|
|
||||||
|
if (age < 11 || age > 120) {
|
||||||
|
bmrResult = null;
|
||||||
|
caloriesResult = null;
|
||||||
|
bmrRange = null;
|
||||||
|
calorieRange = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double heightInCm = _convertHeightToCm(height, heightUnit);
|
||||||
|
double weightInKg = _convertWeightToKg(weight, weightUnit);
|
||||||
|
|
||||||
|
if (heightInCm <= 0 || weightInKg <= 0) {
|
||||||
|
bmrResult = null;
|
||||||
|
caloriesResult = null;
|
||||||
|
bmrRange = null;
|
||||||
|
calorieRange = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bmr = _calculateBMR(heightInCm, weightInKg, age, gender);
|
||||||
|
double activityMultiplier = _getActivityMultiplier(activityLevel);
|
||||||
|
double calories = bmr * activityMultiplier;
|
||||||
|
|
||||||
|
bmrResult = bmr;
|
||||||
|
caloriesResult = calories;
|
||||||
|
|
||||||
|
// ranges
|
||||||
|
if (bmr < 1200) {
|
||||||
|
bmrRange = 'Low';
|
||||||
|
} else if (bmr < 1600) {
|
||||||
|
bmrRange = 'Normal';
|
||||||
|
} else if (bmr < 2000) {
|
||||||
|
bmrRange = 'High';
|
||||||
|
} else {
|
||||||
|
bmrRange = 'Very High';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calories < 1800) {
|
||||||
|
calorieRange = 'Low';
|
||||||
|
} else if (calories < 2500) {
|
||||||
|
calorieRange = 'Normal';
|
||||||
|
} else if (calories < 3200) {
|
||||||
|
calorieRange = 'High';
|
||||||
|
} else {
|
||||||
|
calorieRange = 'High';
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
bmrResult = null;
|
||||||
|
caloriesResult = null;
|
||||||
|
bmrRange = null;
|
||||||
|
calorieRange = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _convertHeightToCm(double height, String unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'cm':
|
||||||
|
return height;
|
||||||
|
case 'm':
|
||||||
|
return height * 100;
|
||||||
|
case 'ft':
|
||||||
|
return height * 30.48;
|
||||||
|
case 'in':
|
||||||
|
return height * 2.54;
|
||||||
|
default:
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calculateBMR(double heightCm, double weightKg, int age, String gender) {
|
||||||
|
if (gender == "Male") {
|
||||||
|
return 88.362 + (13.397 * weightKg) + (4.799 * heightCm) - (5.677 * age);
|
||||||
|
} else {
|
||||||
|
return 447.593 + (9.247 * weightKg) + (3.098 * heightCm) - (4.330 * age);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getActivityMultiplier(String activityLevel) {
|
||||||
|
switch (activityLevel) {
|
||||||
|
case "Almost Inactive (no exercise)":
|
||||||
|
return 1.2;
|
||||||
|
case "Lightly active":
|
||||||
|
return 1.375;
|
||||||
|
case "Lightly active (1-3) days per week":
|
||||||
|
return 1.55;
|
||||||
|
case "Super active (very hard exercise)":
|
||||||
|
return 1.725;
|
||||||
|
default:
|
||||||
|
return 1.375;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IBW
|
||||||
|
void calculateIBW({required String heightText, required String weightText, required String heightUnit, required String weightUnit, required String bodyFrameSize}) {
|
||||||
|
if (heightText.trim().isEmpty || weightText.trim().isEmpty) {
|
||||||
|
ibwResult = null;
|
||||||
|
weightDifference = null;
|
||||||
|
weightStatus = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double height = double.parse(heightText.trim());
|
||||||
|
double weight = double.parse(weightText.trim());
|
||||||
|
|
||||||
|
double heightInCm = _convertHeightToCm(height, heightUnit);
|
||||||
|
double weightInKg = _convertWeightToKg(weight, weightUnit);
|
||||||
|
|
||||||
|
if (heightInCm <= 0 || weightInKg <= 0) {
|
||||||
|
ibwResult = null;
|
||||||
|
weightDifference = null;
|
||||||
|
weightStatus = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ibw = _calculateIBWValue(heightInCm);
|
||||||
|
double adjusted = _applyFrameSizeAdjustment(ibw, bodyFrameSize);
|
||||||
|
double difference = weightInKg - adjusted;
|
||||||
|
String status = _getWeightStatus(difference);
|
||||||
|
|
||||||
|
ibwResult = adjusted;
|
||||||
|
weightDifference = difference;
|
||||||
|
weightStatus = status;
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
ibwResult = null;
|
||||||
|
weightDifference = null;
|
||||||
|
weightStatus = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calculateIBWValue(double heightCm) {
|
||||||
|
double baseHeight = 152.4;
|
||||||
|
double baseWeight = 50.0;
|
||||||
|
double heightMultiplier = 2.3;
|
||||||
|
|
||||||
|
if (heightCm <= baseHeight) {
|
||||||
|
return baseWeight - ((baseHeight - heightCm) * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseWeight + ((heightCm - baseHeight) * heightMultiplier / 2.54);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _applyFrameSizeAdjustment(double ibw, String selectedBodyFrameSize) {
|
||||||
|
switch (selectedBodyFrameSize) {
|
||||||
|
case "Small (fingers overlaps)":
|
||||||
|
return ibw * 0.9;
|
||||||
|
case "Medium (fingers touch)":
|
||||||
|
return ibw;
|
||||||
|
case "Large (fingers don't touch)":
|
||||||
|
return ibw * 1.1;
|
||||||
|
default:
|
||||||
|
return ibw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getWeightStatus(double difference) {
|
||||||
|
if (difference.abs() < 2) {
|
||||||
|
return 'Normal';
|
||||||
|
} else if (difference > 0) {
|
||||||
|
return 'Overweight';
|
||||||
|
} else {
|
||||||
|
return 'Underweight';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body Fat
|
||||||
|
void calculateBodyFat({required String heightText, required String neckText, required String waistText, required String hipText, required String heightUnit, required String neckUnit, required String waistUnit, required String hipUnit, required String gender}) {
|
||||||
|
if (heightText.trim().isEmpty || neckText.trim().isEmpty || waistText.trim().isEmpty) {
|
||||||
|
bodyFatResult = null;
|
||||||
|
bodyFatCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gender == "Female" && hipText.trim().isEmpty) {
|
||||||
|
bodyFatResult = null;
|
||||||
|
bodyFatCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double height = double.parse(heightText.trim());
|
||||||
|
double neck = double.parse(neckText.trim());
|
||||||
|
double waist = double.parse(waistText.trim());
|
||||||
|
double hip = gender == "Female" ? double.parse(hipText.trim()) : 0;
|
||||||
|
|
||||||
|
double heightInCm = _convertHeightToCm(height, heightUnit);
|
||||||
|
double neckInCm = _convertLengthToCm(neck, neckUnit);
|
||||||
|
double waistInCm = _convertLengthToCm(waist, waistUnit);
|
||||||
|
double hipInCm = gender == "Female" ? _convertLengthToCm(hip, hipUnit) : 0;
|
||||||
|
|
||||||
|
if (heightInCm <= 0 || neckInCm <= 0 || waistInCm <= 0) {
|
||||||
|
bodyFatResult = null;
|
||||||
|
bodyFatCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bodyFat = _calculateBodyFatPercentage(heightInCm, neckInCm, waistInCm, hipInCm, gender == "Male");
|
||||||
|
String category = _getBodyFatCategory(bodyFat, gender == "Male");
|
||||||
|
|
||||||
|
bodyFatResult = bodyFat;
|
||||||
|
bodyFatCategory = category;
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
bodyFatResult = null;
|
||||||
|
bodyFatCategory = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _convertLengthToCm(double length, String unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'cm':
|
||||||
|
return length;
|
||||||
|
case 'm':
|
||||||
|
return length * 100;
|
||||||
|
case 'ft':
|
||||||
|
return length * 30.48;
|
||||||
|
case 'in':
|
||||||
|
return length * 2.54;
|
||||||
|
default:
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calculateBodyFatPercentage(double heightCm, double neckCm, double waistCm, double hipCm, bool isMale) {
|
||||||
|
if (isMale) {
|
||||||
|
double waistMinusNeck = waistCm - neckCm;
|
||||||
|
if (waistMinusNeck <= 0) return 0;
|
||||||
|
double log10WaistNeck = _log10(waistMinusNeck);
|
||||||
|
double log10Height = _log10(heightCm);
|
||||||
|
double bodyDensity = 1.0324 - (0.19077 * log10WaistNeck) + (0.15456 * log10Height);
|
||||||
|
double bodyFat = (495 / bodyDensity) - 450;
|
||||||
|
return bodyFat.clamp(0, 100);
|
||||||
|
} else {
|
||||||
|
double waistHipMinusNeck = waistCm + hipCm - neckCm;
|
||||||
|
if (waistHipMinusNeck <= 0) return 0;
|
||||||
|
double log10WaistHipNeck = _log10(waistHipMinusNeck);
|
||||||
|
double log10Height = _log10(heightCm);
|
||||||
|
double bodyDensity = 1.29579 - (0.35004 * log10WaistHipNeck) + (0.22100 * log10Height);
|
||||||
|
double bodyFat = (495 / bodyDensity) - 450;
|
||||||
|
return bodyFat.clamp(0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _log10(double x) {
|
||||||
|
return (x <= 0) ? 0 : (log(x) / log(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getBodyFatCategory(double bodyFat, bool isMale) {
|
||||||
|
if (isMale) {
|
||||||
|
if (bodyFat < 6) return 'Essential Fat';
|
||||||
|
if (bodyFat < 14) return 'Athletes';
|
||||||
|
if (bodyFat < 18) return 'Fitness';
|
||||||
|
if (bodyFat < 25) return 'Average';
|
||||||
|
return 'Obese';
|
||||||
|
} else {
|
||||||
|
if (bodyFat < 14) return 'Essential Fat';
|
||||||
|
if (bodyFat < 21) return 'Athletes';
|
||||||
|
if (bodyFat < 25) return 'Fitness';
|
||||||
|
if (bodyFat < 32) return 'Average';
|
||||||
|
return 'Obese';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crabs / Macros
|
||||||
|
void calculateMacros({required String caloriesText, required String selectedDietType}) {
|
||||||
|
if (caloriesText.trim().isEmpty) {
|
||||||
|
carbsCalories = null;
|
||||||
|
proteinCalories = null;
|
||||||
|
fatCalories = null;
|
||||||
|
carbsGrams = null;
|
||||||
|
proteinGrams = null;
|
||||||
|
fatGrams = null;
|
||||||
|
dietType = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double totalCalories = double.parse(caloriesText.trim());
|
||||||
|
if (totalCalories <= 0) {
|
||||||
|
carbsCalories = null;
|
||||||
|
proteinCalories = null;
|
||||||
|
fatCalories = null;
|
||||||
|
carbsGrams = null;
|
||||||
|
proteinGrams = null;
|
||||||
|
fatGrams = null;
|
||||||
|
dietType = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, double> macroPercentages = _getMacroPercentages(selectedDietType);
|
||||||
|
double carbsPercent = macroPercentages['carbs']!;
|
||||||
|
double proteinPercent = macroPercentages['protein']!;
|
||||||
|
double fatPercent = macroPercentages['fat']!;
|
||||||
|
|
||||||
|
double cCalories = totalCalories * (carbsPercent / 100);
|
||||||
|
double pCalories = totalCalories * (proteinPercent / 100);
|
||||||
|
double fCalories = totalCalories * (fatPercent / 100);
|
||||||
|
|
||||||
|
double cGrams = cCalories / 4;
|
||||||
|
double pGrams = pCalories / 4;
|
||||||
|
double fGrams = fCalories / 9;
|
||||||
|
|
||||||
|
carbsCalories = cCalories;
|
||||||
|
proteinCalories = pCalories;
|
||||||
|
fatCalories = fCalories;
|
||||||
|
carbsGrams = cGrams;
|
||||||
|
proteinGrams = pGrams;
|
||||||
|
fatGrams = fGrams;
|
||||||
|
dietType = selectedDietType;
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
carbsCalories = null;
|
||||||
|
proteinCalories = null;
|
||||||
|
fatCalories = null;
|
||||||
|
carbsGrams = null;
|
||||||
|
proteinGrams = null;
|
||||||
|
fatGrams = null;
|
||||||
|
dietType = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, double> _getMacroPercentages(String dietType) {
|
||||||
|
switch (dietType) {
|
||||||
|
case "Very Low Crabs":
|
||||||
|
return {'carbs': 5, 'protein': 30, 'fat': 65};
|
||||||
|
case "Low Crabs":
|
||||||
|
return {'carbs': 20, 'protein': 35, 'fat': 45};
|
||||||
|
case "Moderate Crabs":
|
||||||
|
return {'carbs': 40, 'protein': 30, 'fat': 30};
|
||||||
|
case "USDA Guidelines ":
|
||||||
|
return {'carbs': 55, 'protein': 15, 'fat': 30};
|
||||||
|
case "Zone Diet":
|
||||||
|
return {'carbs': 40, 'protein': 30, 'fat': 30};
|
||||||
|
default:
|
||||||
|
return {'carbs': 40, 'protein': 30, 'fat': 30};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ovulation
|
||||||
|
DateTime? _parseCustomDate(String dateString) {
|
||||||
|
try {
|
||||||
|
List<String> parts = dateString.split(' ');
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
int day = int.parse(parts[0]);
|
||||||
|
String month = parts[1].replaceAll(',', '');
|
||||||
|
int year = int.parse(parts[2]);
|
||||||
|
|
||||||
|
Map<String, int> months = {
|
||||||
|
'January': 1,
|
||||||
|
'February': 2,
|
||||||
|
'March': 3,
|
||||||
|
'April': 4,
|
||||||
|
'May': 5,
|
||||||
|
'June': 6,
|
||||||
|
'July': 7,
|
||||||
|
'August': 8,
|
||||||
|
'September': 9,
|
||||||
|
'October': 10,
|
||||||
|
'November': 11,
|
||||||
|
'December': 12,
|
||||||
|
'Jan': 1,
|
||||||
|
'Feb': 2,
|
||||||
|
'Mar': 3,
|
||||||
|
'Apr': 4,
|
||||||
|
'Jun': 6,
|
||||||
|
'Jul': 7,
|
||||||
|
'Aug': 8,
|
||||||
|
'Sep': 9,
|
||||||
|
'Oct': 10,
|
||||||
|
'Nov': 11,
|
||||||
|
'Dec': 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
int? monthNum = months[month];
|
||||||
|
if (monthNum != null) {
|
||||||
|
return DateTime(year, monthNum, day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? _tryParseDate(String dateString) {
|
||||||
|
try {
|
||||||
|
// try ISO like
|
||||||
|
return DateTime.tryParse(dateString) ?? _parseCustomDate(dateString);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime? date) {
|
||||||
|
if (date == null) return '';
|
||||||
|
String day = date.day.toString().padLeft(2, '0');
|
||||||
|
return '$day ${_getMonthName(date.month)}, ${date.year}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getMonthName(int month) {
|
||||||
|
const List<String> months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||||
|
return months[month - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
String _determineCyclePhase(int daysSincePeriod, int follicularPhase, int cycleLength) {
|
||||||
|
int normalizedDay = daysSincePeriod % cycleLength;
|
||||||
|
|
||||||
|
if (normalizedDay < follicularPhase - 1) {
|
||||||
|
return 'Follicular Phase';
|
||||||
|
} else if (normalizedDay < follicularPhase + 2) {
|
||||||
|
return 'Ovulation';
|
||||||
|
} else if (normalizedDay < cycleLength) {
|
||||||
|
return 'Luteal Phase';
|
||||||
|
} else {
|
||||||
|
return 'Menstruation';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateOvulation({required String dateText, required String cycleLengthText, required String lutealPhaseText}) {
|
||||||
|
if (dateText.trim().isEmpty || cycleLengthText.trim().isEmpty || lutealPhaseText.trim().isEmpty) {
|
||||||
|
ovulationResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? parsedDate = _tryParseDate(dateText);
|
||||||
|
if (parsedDate == null) {
|
||||||
|
ovulationResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int cycleLength = int.parse(cycleLengthText.trim());
|
||||||
|
int lutealPhase = int.parse(lutealPhaseText.trim());
|
||||||
|
|
||||||
|
if (cycleLength < 16 || cycleLength > 40 || lutealPhase < 8 || lutealPhase > 16) {
|
||||||
|
ovulationResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycleLength <= lutealPhase) {
|
||||||
|
ovulationResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int follicularPhase = cycleLength - lutealPhase;
|
||||||
|
DateTime ovulationDate = parsedDate.add(Duration(days: follicularPhase - 1));
|
||||||
|
DateTime fertileStart = ovulationDate.subtract(Duration(days: 5));
|
||||||
|
DateTime fertileEnd = ovulationDate.add(Duration(days: 1));
|
||||||
|
int daysSincePeriod = DateTime.now().difference(parsedDate).inDays;
|
||||||
|
String cyclePhase = _determineCyclePhase(daysSincePeriod, follicularPhase, cycleLength);
|
||||||
|
|
||||||
|
ovulationResult = {
|
||||||
|
'lastPeriodDate': _formatDate(parsedDate),
|
||||||
|
'ovulationDate': _formatDate(ovulationDate),
|
||||||
|
'fertileWindowStart': _formatDate(fertileStart),
|
||||||
|
'fertileWindowEnd': _formatDate(fertileEnd),
|
||||||
|
'cycleLength': cycleLength,
|
||||||
|
'lutealPhase': lutealPhase,
|
||||||
|
'follicularPhase': follicularPhase,
|
||||||
|
'cyclePhase': cyclePhase,
|
||||||
|
'nextPeriodDate': _formatDate(parsedDate.add(Duration(days: cycleLength))),
|
||||||
|
};
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
ovulationResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delivery due date
|
||||||
|
void calculateDueDate({required String isoDateString}) {
|
||||||
|
if (isoDateString.trim().isEmpty) {
|
||||||
|
deliveryResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? lmp;
|
||||||
|
try {
|
||||||
|
lmp = DateTime.tryParse(isoDateString) ?? _tryParseDate(isoDateString);
|
||||||
|
} catch (e) {
|
||||||
|
lmp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lmp == null) {
|
||||||
|
deliveryResult = null;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime dueDate = lmp.add(Duration(days: 280));
|
||||||
|
String dueDateStr = _formatDate(dueDate);
|
||||||
|
String lmpStr = _formatDate(lmp);
|
||||||
|
|
||||||
|
deliveryResult = {'lmpDate': lmpStr, 'dueDate': dueDateStr, 'lmpDateTime': lmp, 'dueDateTime': dueDate};
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// expose map-like results for widgets to forward to parent
|
||||||
|
Map<String, dynamic>? get bmiResultMap => bmiResult == null ? null : {'bmiResult': bmiResult, 'bmiCategory': bmiCategory};
|
||||||
|
Map<String, dynamic>? get caloriesResultMap => caloriesResult == null ? null : {'calories': caloriesResult, 'bmr': bmrResult, 'calorieRange': calorieRange, 'bmrRange': bmrRange};
|
||||||
|
Map<String, dynamic>? get ibwResultMap => ibwResult == null ? null : {'ibw': ibwResult, 'difference': weightDifference, 'status': weightStatus};
|
||||||
|
Map<String, dynamic>? get bodyFatResultMap => bodyFatResult == null ? null : {'fatPercentage': bodyFatResult, 'fatCategory': bodyFatCategory};
|
||||||
|
Map<String, dynamic>? get macrosResultMap => carbsCalories == null ? null : {'totalCalories': carbsCalories! + proteinCalories! + fatCalories!, 'carbsGrams': carbsGrams, 'carbsCalories': carbsCalories, 'proteinGrams': proteinGrams, 'proteinCalories': proteinCalories, 'fatGrams': fatGrams, 'fatCalories': fatCalories, 'dietType': dietType};
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_order_detail_page.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_selection_review_page.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_detailed_page.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/widgets/health_card.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/media_viewer/full_screen_image_viewer.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/radio_list_tile_widget.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
|
||||||
|
class HealthCalculatorsPage extends StatefulWidget {
|
||||||
|
const HealthCalculatorsPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HealthCalculatorsPage> createState() => _HealthCalculatorsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt.get<DialogService>();
|
||||||
|
return CollapsingListView(
|
||||||
|
title: "Health Calculators".needTranslation,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.general_health, height: 40.h, width: 40.w, fit: BoxFit.none),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
"General Health".needTranslation.toText16(weight: FontWeight.w600),
|
||||||
|
"Related To BMI, calories, body fat, etc to stay updated with your health.".needTranslation.toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 12.w,
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
|
||||||
|
],
|
||||||
|
).paddingAll(16.w))
|
||||||
|
.onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Calculator".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: showCalculatorsItems(type: HealthCalculatorEnum.general),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
SizedBox(height: 16.h),
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.women_health, height: 40.h, width: 40.w, fit: BoxFit.none),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
"Women's Health".needTranslation.toText16(weight: FontWeight.w600),
|
||||||
|
"Related To periods, ovulation, pregnancy, and other topics.".needTranslation.toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
|
||||||
|
],
|
||||||
|
).paddingAll(16.w))
|
||||||
|
.onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Calculator".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: showCalculatorsItems(type: HealthCalculatorEnum.women),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(20.w, 24.h));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget showCalculatorsItems({required HealthCalculatorEnum type}) {
|
||||||
|
return GridView.builder(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 3, // 4 icons per row
|
||||||
|
crossAxisSpacing: 16.w,
|
||||||
|
mainAxisSpacing: 16.w,
|
||||||
|
childAspectRatio: 0.80),
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: type == HealthCalculatorEnum.general ? generalHealthServices.length : womenHealthServices.length,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return HealthCard(
|
||||||
|
icon: type == HealthCalculatorEnum.general ? generalHealthServices[index].icon : womenHealthServices[index].icon,
|
||||||
|
labelText: type == HealthCalculatorEnum.general ? generalHealthServices[index].title : womenHealthServices[index].title,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
CustomPageRoute(
|
||||||
|
page: HealthCalculatorDetailedPage(
|
||||||
|
calculatorType: type == HealthCalculatorEnum.general ? generalHealthServices[index].type : womenHealthServices[index].type,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<HealthComponentModel> generalHealthServices = [
|
||||||
|
HealthComponentModel(title: "BMI\nCalculator".needTranslation, icon: AppAssets.bmi, type: HealthCalculatorsTypeEnum.bmi),
|
||||||
|
HealthComponentModel(title: "Calories\nCalculator".needTranslation, icon: AppAssets.calories, type: HealthCalculatorsTypeEnum.calories),
|
||||||
|
HealthComponentModel(title: "BMR\nCalculator".needTranslation, icon: AppAssets.bmr, type: HealthCalculatorsTypeEnum.bmr),
|
||||||
|
HealthComponentModel(title: "Ideal Body\nWeight".needTranslation, icon: AppAssets.ibw, type: HealthCalculatorsTypeEnum.idealBodyWeight),
|
||||||
|
HealthComponentModel(title: "Body Fat\nCalculator".needTranslation, icon: AppAssets.ibw, type: HealthCalculatorsTypeEnum.bodyFat),
|
||||||
|
HealthComponentModel(title: "Carbs\nProtein & Fat".needTranslation, icon: AppAssets.ibw, type: HealthCalculatorsTypeEnum.crabsProteinFat),
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<HealthComponentModel> womenHealthServices = [
|
||||||
|
HealthComponentModel(title: "Ovulation\nPeriod".needTranslation, icon: AppAssets.locate_me, type: HealthCalculatorsTypeEnum.ovulation),
|
||||||
|
HealthComponentModel(title: "Delivery\nDue Date".needTranslation, icon: AppAssets.activeCheck, type: HealthCalculatorsTypeEnum.deliveryDueDate),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
class HealthComponentModel {
|
||||||
|
String title;
|
||||||
|
String? subTitle;
|
||||||
|
String icon;
|
||||||
|
Color? iconColor;
|
||||||
|
Color? bgColor;
|
||||||
|
Color? textColor;
|
||||||
|
HealthCalculatorsTypeEnum type;
|
||||||
|
|
||||||
|
HealthComponentModel({required this.title, this.subTitle, required this.icon, this.iconColor, this.bgColor, this.textColor, required this.type});
|
||||||
|
}
|
||||||
@ -0,0 +1,642 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class BodyFatWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
BodyFatWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BodyFatWidgetState createState() => _BodyFatWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BodyFatWidgetState extends State<BodyFatWidget> {
|
||||||
|
final TextEditingController _heightController = TextEditingController();
|
||||||
|
final TextEditingController _neckController = TextEditingController();
|
||||||
|
final TextEditingController _waistController = TextEditingController();
|
||||||
|
final TextEditingController _hipController = TextEditingController();
|
||||||
|
final List<String> _heightUnits = ['cm', 'm', 'ft', 'in'];
|
||||||
|
String selectedHeightUnit = 'cm';
|
||||||
|
String selectedNeckUnit = 'in';
|
||||||
|
String selectedWaistUnit = 'in';
|
||||||
|
String selectedHipUnit = 'in';
|
||||||
|
String selectedGender = "Male";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_heightController.addListener(_onInputChanged);
|
||||||
|
_neckController.addListener(_onInputChanged);
|
||||||
|
_waistController.addListener(_onInputChanged);
|
||||||
|
_hipController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_heightController.removeListener(_onInputChanged);
|
||||||
|
_neckController.removeListener(_onInputChanged);
|
||||||
|
_waistController.removeListener(_onInputChanged);
|
||||||
|
_hipController.removeListener(_onInputChanged);
|
||||||
|
_heightController.dispose();
|
||||||
|
_neckController.dispose();
|
||||||
|
_waistController.dispose();
|
||||||
|
_hipController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateBodyFat(
|
||||||
|
heightText: _heightController.text,
|
||||||
|
neckText: _neckController.text,
|
||||||
|
waistText: _waistController.text,
|
||||||
|
hipText: _hipController.text,
|
||||||
|
heightUnit: selectedHeightUnit,
|
||||||
|
neckUnit: selectedNeckUnit,
|
||||||
|
waistUnit: selectedWaistUnit,
|
||||||
|
hipUnit: selectedHipUnit,
|
||||||
|
gender: selectedGender,
|
||||||
|
);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.bodyFatResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.gender, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Select Gender".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedGender.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _genders = ["Male", "Female"];
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Gender".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _genders.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _genders[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedGender,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedGender = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedGender = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.height, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _heightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '175',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Neck".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _neckController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '7',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedNeckUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedNeckUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedNeckUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedNeckUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Waist".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _waistController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '30',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedWaistUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedWaistUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedWaistUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedWaistUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Hip".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _hipController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '0',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHipUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHipUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHipUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Estimate the total body fat based on the size.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,309 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class BMIWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
BMIWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BMIWidgetState createState() => _BMIWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BMIWidgetState extends State<BMIWidget> {
|
||||||
|
final TextEditingController _heightController = TextEditingController();
|
||||||
|
final TextEditingController _weightController = TextEditingController();
|
||||||
|
final List<String> _heightUnits = ['cm', 'm', 'ft', 'in'];
|
||||||
|
final List<String> _weightUnits = ['kg', 'lb'];
|
||||||
|
String selectedHeightUnit = 'cm';
|
||||||
|
String selectedWeightUnit = 'kg';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_heightController.addListener(_onInputChanged);
|
||||||
|
_weightController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_heightController.removeListener(_onInputChanged);
|
||||||
|
_weightController.removeListener(_onInputChanged);
|
||||||
|
_heightController.dispose();
|
||||||
|
_weightController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateBMI(heightText: _heightController.text, weightText: _weightController.text, heightUnit: selectedHeightUnit, weightUnit: selectedWeightUnit);
|
||||||
|
// notify parent
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.bmiResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.height, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _heightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '175',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHeightUnit.toUpperCase().toText12(fontWeight: FontWeight.w600, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = value;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = unit;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _weightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '75',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedWeightUnit.toUpperCase().toText12(fontWeight: FontWeight.w600, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _weightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _weightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedWeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculate the BMI value and weight status to identify the healthy weight. NOt appropriate for children and women who are pregnant or breastfeeding."
|
||||||
|
.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,523 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class BMRWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
BMRWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BMRWidgetState createState() => _BMRWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BMRWidgetState extends State<BMRWidget> {
|
||||||
|
final TextEditingController _heightController = TextEditingController();
|
||||||
|
final TextEditingController _weightController = TextEditingController();
|
||||||
|
final TextEditingController _ageController = TextEditingController();
|
||||||
|
final List<String> _heightUnits = ['cm', 'm', 'ft', 'in'];
|
||||||
|
final List<String> _weightUnits = ['kg', 'lb'];
|
||||||
|
String selectedHeightUnit = 'cm';
|
||||||
|
String selectedWeightUnit = 'kg';
|
||||||
|
String selectedActivityLevel = 'Lightly active';
|
||||||
|
String selectedGender = "Male";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_heightController.addListener(_onInputChanged);
|
||||||
|
_weightController.addListener(_onInputChanged);
|
||||||
|
_ageController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_heightController.removeListener(_onInputChanged);
|
||||||
|
_weightController.removeListener(_onInputChanged);
|
||||||
|
_ageController.removeListener(_onInputChanged);
|
||||||
|
_heightController.dispose();
|
||||||
|
_weightController.dispose();
|
||||||
|
_ageController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateBMRAndCalories(
|
||||||
|
heightText: _heightController.text,
|
||||||
|
weightText: _weightController.text,
|
||||||
|
ageText: _ageController.text,
|
||||||
|
heightUnit: selectedHeightUnit,
|
||||||
|
weightUnit: selectedWeightUnit,
|
||||||
|
gender: selectedGender,
|
||||||
|
activityLevel: selectedActivityLevel,
|
||||||
|
);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.caloriesResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
AppState appState = getIt<AppState>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.gender, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Select Gender".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedGender.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _genders = ["Male", "Female"];
|
||||||
|
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Gender".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _genders.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _genders[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedGender,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
print(value);
|
||||||
|
setState(() {
|
||||||
|
selectedGender = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedGender = unit;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.age, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Age (11-120) yrs".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _ageController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '20',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.height, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _heightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '175',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _weightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '75',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _weightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _weightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedWeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.activity, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Activity Level".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedActivityLevel.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _activity = ["Almost Inactive (no exercise)", "Lightly active", "Lightly active (1-3) days per week", "Super active (very hard exercise)"];
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Activity Level".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _activity.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _activity[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedActivityLevel,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedActivityLevel = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedActivityLevel = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculates daily calorie intake based on server factors, like height, weight, age, gender and daily physical activity."
|
||||||
|
.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,524 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class CaloriesWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
CaloriesWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CaloriesWidgetState createState() => _CaloriesWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CaloriesWidgetState extends State<CaloriesWidget> {
|
||||||
|
final TextEditingController _heightController = TextEditingController();
|
||||||
|
final TextEditingController _weightController = TextEditingController();
|
||||||
|
final TextEditingController _ageController = TextEditingController();
|
||||||
|
final List<String> _heightUnits = ['cm', 'm', 'ft', 'in'];
|
||||||
|
final List<String> _weightUnits = ['kg', 'lb'];
|
||||||
|
String selectedHeightUnit = 'cm';
|
||||||
|
String selectedWeightUnit = 'kg';
|
||||||
|
String selectedActivityLevel = 'Lightly active';
|
||||||
|
String selectedGender = "Male";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_heightController.addListener(_onInputChanged);
|
||||||
|
_weightController.addListener(_onInputChanged);
|
||||||
|
_ageController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_heightController.removeListener(_onInputChanged);
|
||||||
|
_weightController.removeListener(_onInputChanged);
|
||||||
|
_ageController.removeListener(_onInputChanged);
|
||||||
|
_heightController.dispose();
|
||||||
|
_weightController.dispose();
|
||||||
|
_ageController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateBMRAndCalories(
|
||||||
|
heightText: _heightController.text,
|
||||||
|
weightText: _weightController.text,
|
||||||
|
ageText: _ageController.text,
|
||||||
|
heightUnit: selectedHeightUnit,
|
||||||
|
weightUnit: selectedWeightUnit,
|
||||||
|
gender: selectedGender,
|
||||||
|
activityLevel: selectedActivityLevel,
|
||||||
|
);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.caloriesResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
AppState appState = getIt<AppState>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.gender, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Select Gender".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedGender.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _genders = ["Male", "Female"];
|
||||||
|
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Gender".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _genders.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _genders[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedGender,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedGender = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedGender = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.age, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Age (11-120) yrs".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _ageController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '20',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.height, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _heightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '175',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _weightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '75',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _weightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _weightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedWeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.activity, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Activity Level".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedActivityLevel.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _activity = ["Almost Inactive (no exercise)", "Lightly active", "Lightly active (1-3) days per week", "Super active (very hard exercise)"];
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Activity Level".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _activity.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _activity[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedActivityLevel,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedActivityLevel = value;
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedActivityLevel = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculates daily calorie intake based on server factors, like height, weight, age, gender and daily physical activity."
|
||||||
|
.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_detailed_page.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class CrabsWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
CrabsWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CrabsWidgetState createState() => _CrabsWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CrabsWidgetState extends State<CrabsWidget> {
|
||||||
|
final TextEditingController _caloriesController = TextEditingController();
|
||||||
|
String selectedDietType = 'Very Low Crabs';
|
||||||
|
Function(String)? onUnitSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_caloriesController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_caloriesController.removeListener(_onInputChanged);
|
||||||
|
_caloriesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateMacros(caloriesText: _caloriesController.text, selectedDietType: selectedDietType);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.macrosResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.age, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Calories Per Day".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _caloriesController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '2000',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.ask_doctor_icon).onPress(() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.of(context).push(CustomPageRoute(page: HealthCalculatorDetailedPage(calculatorType: HealthCalculatorsTypeEnum.calories)));
|
||||||
|
}),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.activity, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Diet Type".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedDietType.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _activity = ["Very Low Crabs", "Low Crabs", "Moderate Crabs", "USDA Guidelines ", "Zone Diet"];
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Diet Type".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _activity.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _activity[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedDietType,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedDietType = value;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedDietType = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio."
|
||||||
|
.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class DeliveryDueDWidget extends StatefulWidget {
|
||||||
|
final Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
DeliveryDueDWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DeliveryDueDWidgetState createState() => _DeliveryDueDWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeliveryDueDWidgetState extends State<DeliveryDueDWidget> {
|
||||||
|
final TextEditingController _date = TextEditingController();
|
||||||
|
String selectedDate = '';
|
||||||
|
Function(String)? onUnitSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 0.h, bottom: 0.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextInputWidget(
|
||||||
|
labelText: "Date",
|
||||||
|
hintText: "11 July, 1994".needTranslation,
|
||||||
|
controller: _date,
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
isEnable: true,
|
||||||
|
prefix: null,
|
||||||
|
isAllowRadius: true,
|
||||||
|
isBorderAllowed: false,
|
||||||
|
isAllowLeadingIcon: true,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||||
|
leadingIcon: AppAssets.birthday_cake,
|
||||||
|
selectionType: SelectionTypeEnum.calendar,
|
||||||
|
isHideSwitcher: true,
|
||||||
|
onCalendarTypeChanged: (val) {},
|
||||||
|
onChange: (val) {
|
||||||
|
if (val == null) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
selectedDate = Utils.formatDateToDisplay(val);
|
||||||
|
_date.text = selectedDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
// provider expects ISO date string to calculate due date
|
||||||
|
provider.calculateDueDate(isoDateString: val);
|
||||||
|
|
||||||
|
if (widget.onChange != null) {
|
||||||
|
widget.onChange!(provider.deliveryResult);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).withVerticalPadding(4.h),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
|
||||||
|
class HealthCard extends StatelessWidget {
|
||||||
|
const HealthCard({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
required this.labelText,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String icon;
|
||||||
|
final String labelText;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 20.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(
|
||||||
|
icon: icon,
|
||||||
|
width: 40.w,
|
||||||
|
height: 40.h,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
).toShimmer2(isShow: false, radius: 12.r),
|
||||||
|
SizedBox(height: 14.h),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: labelText.toText12(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
maxLine: 2,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingAll(16.w).onPress(() {
|
||||||
|
onTap!();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,393 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class IdealBodyWeightWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
IdealBodyWeightWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_IdealBodyWeightWidgetState createState() => _IdealBodyWeightWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IdealBodyWeightWidgetState extends State<IdealBodyWeightWidget> {
|
||||||
|
final TextEditingController _heightController = TextEditingController();
|
||||||
|
final TextEditingController _weightController = TextEditingController();
|
||||||
|
final List<String> _heightUnits = ['cm', 'm', 'ft', 'in'];
|
||||||
|
final List<String> _weightUnits = ['kg', 'lb'];
|
||||||
|
String selectedHeightUnit = 'cm';
|
||||||
|
String selectedWeightUnit = 'kg';
|
||||||
|
String selectedBodyFrameSize = 'Medium (fingers touch)';
|
||||||
|
Function(String)? onUnitSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_heightController.addListener(_onInputChanged);
|
||||||
|
_weightController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_heightController.removeListener(_onInputChanged);
|
||||||
|
_weightController.removeListener(_onInputChanged);
|
||||||
|
_heightController.dispose();
|
||||||
|
_weightController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
provider.calculateIBW(
|
||||||
|
heightText: _heightController.text,
|
||||||
|
weightText: _weightController.text,
|
||||||
|
heightUnit: selectedHeightUnit,
|
||||||
|
weightUnit: selectedWeightUnit,
|
||||||
|
bodyFrameSize: selectedBodyFrameSize,
|
||||||
|
);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.ibwResultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DialogService dialogService = getIt<DialogService>();
|
||||||
|
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.height, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _heightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '175',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _heightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _heightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedHeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedHeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.weight, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _weightController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '75',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: 30.w,
|
||||||
|
color: Color(0xFFEAEAEB),
|
||||||
|
).paddingOnly(right: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Unit".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _weightUnits.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _weightUnits[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedWeightUnit,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedWeightUnit = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.activity, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Body Frame Size".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
selectedBodyFrameSize.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w).onPress(() {
|
||||||
|
List<String> _activity = ["Small (fingers overlaps)", "Medium (fingers touch)", "Large (fingers don't touch)"];
|
||||||
|
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||||
|
label: "Select Body Frame Size".needTranslation,
|
||||||
|
message: "",
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _activity.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final unit = _activity[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
child: Row(
|
||||||
|
spacing: 8.h,
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: unit,
|
||||||
|
groupValue: selectedBodyFrameSize,
|
||||||
|
activeColor: AppColors.errorColor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() {
|
||||||
|
selectedBodyFrameSize = value;
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
|
||||||
|
],
|
||||||
|
).onPress(() {
|
||||||
|
setState(() {
|
||||||
|
selectedBodyFrameSize = unit;
|
||||||
|
_onInputChanged();
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return Divider(height: 1, color: Color(0xFFEEEEEE));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onOkPressed: () {},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculates the ideal body weight based on height, weight and body size.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,186 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/health_calculators/health_calculator_view_model.dart';
|
||||||
|
|
||||||
|
class OvulationWidget extends StatefulWidget {
|
||||||
|
Function(dynamic result)? onChange;
|
||||||
|
|
||||||
|
OvulationWidget({super.key, required this.onChange});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_OvulationWidgetState createState() => _OvulationWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OvulationWidgetState extends State<OvulationWidget> {
|
||||||
|
final TextEditingController _ageController = TextEditingController();
|
||||||
|
final TextEditingController _cycleLengthController = TextEditingController();
|
||||||
|
final TextEditingController _lutealPhaseLengthController = TextEditingController();
|
||||||
|
Function(String)? onUnitSelected;
|
||||||
|
String selectedDate = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_ageController.addListener(_onInputChanged);
|
||||||
|
_cycleLengthController.addListener(_onInputChanged);
|
||||||
|
_lutealPhaseLengthController.addListener(_onInputChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_ageController.removeListener(_onInputChanged);
|
||||||
|
_cycleLengthController.removeListener(_onInputChanged);
|
||||||
|
_lutealPhaseLengthController.removeListener(_onInputChanged);
|
||||||
|
_ageController.dispose();
|
||||||
|
_cycleLengthController.dispose();
|
||||||
|
_lutealPhaseLengthController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onInputChanged() {
|
||||||
|
final provider = Provider.of<HealthCalcualtorViewModel>(context, listen: false);
|
||||||
|
final dateText = selectedDate.isEmpty ? _ageController.text.trim() : selectedDate;
|
||||||
|
provider.calculateOvulation(dateText: dateText, cycleLengthText: _cycleLengthController.text, lutealPhaseText: _lutealPhaseLengthController.text);
|
||||||
|
if (widget.onChange != null) widget.onChange!(provider.ovulationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<HealthCalcualtorViewModel>(builder: (context, provider, _) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextInputWidget(
|
||||||
|
labelText: "Date",
|
||||||
|
hintText: "11 July, 1994".needTranslation,
|
||||||
|
controller: _ageController,
|
||||||
|
isEnable: true,
|
||||||
|
prefix: null,
|
||||||
|
isAllowRadius: true,
|
||||||
|
isBorderAllowed: false,
|
||||||
|
isAllowLeadingIcon: true,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||||
|
leadingIcon: AppAssets.birthday_cake,
|
||||||
|
selectionType: SelectionTypeEnum.calendar,
|
||||||
|
isHideSwitcher: true,
|
||||||
|
onCalendarTypeChanged: (val) {},
|
||||||
|
onChange: (val) {
|
||||||
|
if (val == null) return;
|
||||||
|
setState(() {
|
||||||
|
_ageController.text = selectedDate = Utils.formatDateToDisplay(val!);
|
||||||
|
});
|
||||||
|
_onInputChanged();
|
||||||
|
},
|
||||||
|
).withVerticalPadding(8),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.age, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Average Cycle Length (Usually 28 days)".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _cycleLengthController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '15',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.age, width: 40.w, height: 40.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Average Luteal Phase Length(Usually 14 days)".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
Container(
|
||||||
|
height: 20.w,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: TextField(
|
||||||
|
controller: _lutealPhaseLengthController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
maxLines: 1,
|
||||||
|
cursorHeight: 14,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
isCollapsed: true,
|
||||||
|
hintText: '10',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, height: 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
if (provider.ovulationResult != null) ...[
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
// show result UI same as before; keeping unchanged design
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text('Last Period: ${provider.ovulationResult!['lastPeriodDate'] ?? ''}')),
|
||||||
|
Expanded(child: Text('Ovulation: ${provider.ovulationResult!['ovulationDate'] ?? ''}')),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: "Calculate ovulation and fertile window based on last period, cycle length and luteal phase.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(0.w, 16.w),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||