From 10c8a0a2d852613a90d1460e9fab0ec33701053a Mon Sep 17 00:00:00 2001 From: bossanyit Date: Mon, 12 Apr 2021 00:51:09 +0200 Subject: [PATCH] WT 1.1.11+3 Evaluation, null-safe fixes --- android/app/src/main/AndroidManifest.xml | 3 +- asset/image/kupa.png | Bin 0 -> 40846 bytes asset/menu/FG_1_test.jpg | Bin 0 -> 101976 bytes asset/menu/FG_1_training.jpg | Bin 0 -> 100972 bytes i18n/en.json | 29 +- i18n/hu.json | 29 +- ios/Podfile.lock | 28 +- ios/Runner.xcodeproj/project.pbxproj | 12 +- lib/bloc/account/account_bloc.dart | 2 +- .../customer_change/customer_change_bloc.dart | 52 +- .../customer_change_event.dart | 8 + .../exercise_execute_plan_add_bloc.dart | 24 +- lib/bloc/exercise_new/exercise_new_bloc.dart | 22 +- lib/bloc/login/login_bloc.dart | 39 +- lib/bloc/menu/menu_bloc.dart | 4 +- .../password_reset/password_reset_bloc.dart | 2 +- .../password_reset/password_reset_state.dart | 4 + lib/bloc/result/result_bloc.dart | 4 +- .../test_set_control_bloc.dart | 2 +- .../test_set_execute_bloc.dart | 13 +- lib/library/dropdown_search.dart | 541 ++++++++++++++++ lib/library/flurry.dart | 39 -- lib/library/popup_menu.dart | 589 ++++++++++++++++++ lib/library/select_dialog.dart | 489 +++++++++++++++ lib/main.dart | 9 +- lib/model/cache.dart | 12 +- lib/model/evaluation.dart | 28 + lib/model/evaluation_attribute.dart | 41 ++ lib/model/exercise_plan.dart | 6 + lib/model/exercise_plan_detail.dart | 19 +- lib/model/fitness_state.dart | 20 + lib/model/tracking.dart | 4 +- lib/repository/customer_repository.dart | 130 ++-- lib/repository/evaluation_repository.dart | 77 +++ .../exercise_device_repository.dart | 1 + lib/repository/exercise_plan_repository.dart | 59 +- lib/repository/exercise_repository.dart | 155 ++++- lib/repository/property_repository.dart | 2 +- lib/repository/workout_tree_repository.dart | 4 +- lib/service/api.dart | 3 + lib/service/exercise_plan_service.dart | 5 +- lib/service/firebase_api.dart | 15 +- lib/service/package_service.dart | 4 + lib/util/common.dart | 13 +- lib/util/enums.dart | 8 + lib/util/track.dart | 2 +- lib/util/trans.dart | 3 + lib/view/account.dart | 14 +- lib/view/customer_fitness_page.dart | 122 +++- lib/view/customer_goal_page.dart | 5 +- lib/view/customer_modify_page.dart | 150 +++-- .../{evaluation.dart => evaluation_page.dart} | 258 +++++++- lib/view/exercise_execute_plan_add_page.dart | 14 +- lib/view/exercise_log_page.dart | 14 +- lib/view/exercise_new_page.dart | 17 +- lib/view/login.dart | 6 +- lib/view/registration.dart | 2 +- lib/view/reset_password.dart | 2 +- lib/view/sales_page.dart | 2 +- lib/view/test_set_execute.dart | 106 +++- lib/view/test_set_new.dart | 6 +- lib/widgets/bmi_widget.dart | 2 +- lib/widgets/bmr_widget.dart | 4 +- .../bottom_bar_multiple_exercises.dart | 2 +- lib/widgets/bottom_nav.dart | 166 ++--- lib/widgets/dialog_common.dart | 2 +- lib/widgets/exercise_save.dart | 314 +++++----- lib/widgets/home.dart | 2 +- lib/widgets/menu_page_widget.dart | 14 +- lib/widgets/menu_search_bar.dart | 2 +- lib/widgets/number_picker.dart | 54 +- pubspec.lock | 75 ++- pubspec.yaml | 22 +- 73 files changed, 3256 insertions(+), 676 deletions(-) create mode 100644 asset/image/kupa.png create mode 100644 asset/menu/FG_1_test.jpg create mode 100644 asset/menu/FG_1_training.jpg create mode 100644 lib/library/dropdown_search.dart delete mode 100644 lib/library/flurry.dart create mode 100644 lib/library/popup_menu.dart create mode 100644 lib/library/select_dialog.dart create mode 100644 lib/model/evaluation.dart create mode 100644 lib/model/evaluation_attribute.dart create mode 100644 lib/repository/evaluation_repository.dart rename lib/view/{evaluation.dart => evaluation_page.dart} (70%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8ef85e5..2ef35ac 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ CV}c?A8Vg8QBBG#3 zlU@u}9f|@1QU*Z>q~p+sfp?vu#Q5Zin*00S>-*#5O3ZL(pS{;!>$mD&I|gd1t8%W~ zwh}=Q&Z9paI)xytXW-wn>)7GRKDM*%@Shb9Kb;{Vh=?Hi?=ts$O0Mu#ua&l*v!2=s zSpv~c#Ke?{HxqHUbAZtZBCq7`U_!7la~8mxSy=?=`+96N=;17LB-6{ z>L*VpGc8YbZGxu_0cW~HNl`%FT^1&=Gjlc(aJRFyC&{`i>|o9-3!l-)qB{hbL!50C zb{s$#6wp)C6i^{LnF&aVNDC7%l2QU#oCro*LK27DFCZ?4kr5S>5yeOdW29vz#bm|B z1r~mGz-msW=CY>_9a&flepA?C>Fn$vD=O;d<|g7MAwqPr5XIndI8iZiQE_o$7$HpZ zuy;0b7q%w}F3xbsj6`s5bXW7+wR2b+Y)@KXDKJ;m-NXR~ihVv%P3@nD+Sx6QCOMxn`*Ux9J26Sy z!@*4Slo^TW;zTfm;ev}ak(>{^!gdxI^9?T_c7=T{y>4Y|>q2sNGI1t4{rx1L4ft}4 z+L{)B|IQ*ioJAchKHKFXE4zOlKfg;DW@b+Uae)l~WW4G>zxwAo4xi!VFYJIXUMA1gvBI<#cM@nVSns;>9I}Wh7)Igk_{8and+(Q&UMZ>BaXC5eY7+ zrl9Z7+X7P}0p?g@pDYe9Esd2RV1=a!Cep&vCXyIo6O1WFSlZmgOcHM*jh8UNEzYLl zWCiBP#P;)9U0h%~sYBvoGB|NboW#K+hYn&eIE>^087Z9DAqk9>n2d_d;smG6>`kpu zU01O+A(0ezI5-iB=CUAqux@`cL>5&ig&m8l{PjC6XA>u97Y9uzqPdkVjM8)hmNDlC zz{~z*LejA!S>bKXG+fP`9MJC+b{sLWC7J!r5N#(Ddy<2RlbJoiLt%%ulgpoj)HMM^ zKpg}?WQK!JzFchlZlgu07gm#MbDocRUz6Eoz#e?K%B(q&yT98 zEKyB5rFu}{=wTIU3{F~FSX=~y+C#vwvz4>0*@AIBVC%f_$<_+hG+A2{dr)b2VN)}6 z6Bk?O9f}9sOq@(f0tBM1tr-DjtGp;`Lv5|#J?5wRDMUY;7(fKDI%MW#?mB6PCo_#f0%DfO2ywfUBg5nHfeLI5z)2z-kF-C{RrS z72oqd@!vyho&b>I{{k&h;Kuy6f3hNhCG}X~s+O7KpZsMSYFQH|7ZrApOn^J+*1&rI zEo&sqaAtUODHCB6ytKHm1VK_t7-wRN6&5!UClDlLaMC6O$;J2GEX}}$1Iy-31Xdd( zDJup4h%K6k`DI8%b7zpJ*#QgC*F~WHt(nlZvNt8Vk-$jIze%tJV3pTP& zzWe4KNtsGWVN9h=g{29wElC-ygs_P@79)%i13kvc;3UONOcodXVn<6xPx{~P=ySvO z?Rz2+?Lo<%|CAKyvIzt_W=^2F<|wvJ92{(|2qvg%h=LvqI1~T!$kpC-Axv1(-v5Gw z-wJmWQ;Qt@U!BoEU_x`zGcGs|++Q8X|I7UQ)4Tq!STmisE{kaSTq;W<6ZjwU`5R%N z^7@ALzN)R?<}v3P_kUY5OglYqibTJ(Qt&(K)YMGeQFo+egI%ai3=n@S>`$ z1^NQIB?uknJ*2?=OajcQm;u1TsQCax40XE9SD7x`1QLqBVFnsRs>+g>G|kVhYiZ>? zA1bKI+A;k+c=LtnotdGF<|z%8!&+Jk&zw-(EG{LrM1dL8AuK(>+qwYF=zz~u>VK@t zWah#Y-@K~C7mG?jK*OAQeoYDSe_T>U?aXn3Q>cAqzVpqiic8D~peVQJ_c|W|s>&`b zEB*Cl=P~u&D=#s1ab?NB?pW;WE32Y3fA7Nc`NPtNG17~SpWm?z=IiUCO84I`i-m~u zGx>^3$b5a>g8;wpU03o80ZY#32#W%SO2XIIT`(u#yR`V1m|kT4Jf|@dxPM++0WvK$ z1&o-Oz!?Rcn80!P_`M`xaEq@jpiE-%_jww=wtEFJpa9lBqaY&%1T4P%9s0GXPYdfy zL$LXo*rg=D!UA-C872e%-TIP?BrmML#E*sje?`lf`U;+^}1xtqPo00e>0b+}=m}i01S6scq zk__O(zpw;L?tPH~GK&PwuaCie4TfL`7i0kC?|(tT;(Ch&NH4m^1p>a#0VV;cE&5ls z5M2CUwLto>AQTt>CKNKw@xLNKd=YYs9Kb9-Sm1yR_A7dbsvkH+lm-7v1d@wU&jJO& z^3UBN2EPAiQ#4Ni`sW!1ag+q{MQi@edhn$fB>t)gV4D7!1g0L$Q}C~(fDu~^bQhQa zLC9a3@HH3!0v6+m;-?zLng7o;HmHDv$=P(!ugjDqC9 zq5y;YLLD(*+T^dsr?9Y^0+?g42Pg@PNd9JJ#J~!FHHwhw{zV(azmkxl3#*}joKcWQ zN%($)fWdsph9wQ1mxRn$ax(=fR1mN_kbr8#QqcX)Y*-4*7oZ67$mdJ|Pxa3>iHQV| z#R36~p6;91$KVzvw?F_!YLO=kU=;hBS%lDJK@80J;QIq4IKub>3g8bv=K{oR|I7uZ zRbq+(LWO^YgfD%=l7h^0LF#K1Fx3Gtu#m7WCBWYi7XHx^*ti^eiw;ZO4k+%Sn?MO1Yp01fbY+r zF_0bo1%j9_FbK^Q-=P9v#^z`HW)*dQi(IHV8rYPNuouW87ag}(3R!Qu;x z9FYDi0Z@JZXAYpUUpR9>{q*8=->jrd<%|UazHrlMVE@kqKmh&UtdIG!$^^yT&n19a z@Du-DN56OzJ)HjHAeuSiv&e2S{DUr7^y5elAN{!FD_NKy<9^!+oyzEs_HR2@F+cbnJ7v0z`APHW<^9YL z$ljyZ*qEP`ckW4>f0X#+;mz}(ZvOuNPxk*gp1rQ@2c#u6SZAJn)W5Ode$m|9H?O&l zgoZTS?ZSuH)EurT_cGTZ-?vyjlNZ|;H1)n-r_X>9+Am>u)~yb(QM)nK$RM%6XrNz< zd#l87N^ie~TzX1@#z4Quef5E40_E)#ZS~Dr=1>1dWl`&?wW~F;my@3bu40(o3T(*j zEBERQd&YsE{g8qg$`_;>xRsUT zKfS+G#fl)s4uReue~!nG@eeS%_a>F=XDS%cBf?*bG{(GKtslzIPpVJ6qt@7be|Bmx ze4?G3RxL+pcPEe|i6M-SqaRi1vr4I!bm#VAZu}^v>(Quy*-AA4pB3Ite`>0lX zqMeJzty_`b%RMl5IyKdOW+X}<6lZJ`y|zIosG*#vqSZ1!oX2)uosT5-Hs#9HqjUoQ zbWgm;ZKv#-)y_dXRwYIoil*V?YqzaWvU?inF=>L4)5yTcL`&49UgV((2kb-YPX@>^H)IL|qu=#4aAMQ(a^ zwNFN2s|9=R;cIO5>IOl{{yc#i-VYT*FXhzsL}{-Yq&&*4o{bS~JsJ<3uCNEy_wO3uLwNWfpb~g>Fc8dGq{u z=}aH*vXPvcGWofgCI@m%bl3_6vAo=5A(DD1d5yx4)LD9kzNxMH-hguMqII;&czkTL z$AzTnKS{it+@xolc{;h;&i8`V%w&eL^Q(dZd|!G+S^vg!jP@p=k$SFQRF3|JzyB2N^g^imvL7bS6eUmE^Gs`B{@BR9vfH^$qr zqU#$-BW0MivWX%ZuffWfh>nL1HUFq{KXW21DLEqSQg1j$kQ%~&$@_zbNKF;(;GhI+ z+uceQ>-&2TzbR(v%O8K`FE?-l#+|qJBx~?*qGhC%v%kkt-moBk?3cYi{C2#&_qb5S z%y6|wU}9l4HvK7)ALrg|mg6y7wg@{@?lUX*2dH=d=a0}qKAx*e?J<$yn6|7*O=)$> z(sNmvT*j@j>JaDYlv=07jWp~|E1VtE8_3i(+~8RuAwlrztqeC(+$p_-;f;dS@6HB# zWp&o0!dLAY_E`poJ3LkI*S@vxYf?4zFw@zvDD~^7P0Px=xMHs~aI5pqE|;um(5+}S zYiznCFS!T17D2>B@%%YQ>5;nKUi+x8U%uaY*G(;QGc9`Piue1{z>?_V#VxFM1I-Do zzV5_r(4>yRPj0By4^`MkyTE@r_^10FAn1p?$3q3*(#zsQLp0biQV+(X~I|@r5pe@;cM<*SUA4^g6h*yKKYSVA$-dbzurp>LN|{t zg`rMX!@fMeywQN0R1%Dzn{4g3pSywzsq^SMzwSWJ%AQxL)6XXGd!JO+d)%c>b`cDR zFHE|~nx8P-g&+ctU+SI{*2$U`Nni=utLMuISLMGM$!a_cUfr<%xln~yUBm-S$6>o7 zZ?e&>xO8=j;a-$4lE?Kg7TsqIBvEE|QEha8TD4sCW2Ht#N+Tzhtj;2q!YllrdiAW3 z&Ajd=6G29+88t}Tr$#!VaKhWsv~;`M@8!!7KfX=$^0tXf4Zld>LwIk8{f9j%o#<7< zL!eC7kKUQxiekTv=Zta}r~I+uKBF2nR@zlLAV}SOuKd+{U886e zmk))<10RiFpcp9#Qw!hzw4Pqd^;VUG&(+<2+ZOeSRxVN|wp4MX`3!AZFbDMu*fhU@>DKNvuHxao z{5vkqq4@YhYQzIakn$OQZvJ+W#~bw|Pj;Vs$KNI_#a{e!_z0M0+Vl|}Tg~=QZ}i$e zwcJr-a~W-l$52M9-9HVspLe2x>m9ztc*l3Xw3jSFs#D(N+)h4gcC(3pylM=az9^y+ zULQSImc$TU;jOgGv86|5ZytY71qlyc?rT@W27!c~>XdK7tDbY?IXJ}dr)zAZ`Avy4 zw4ceUC*HB?7~-h+!b*!o0*EvM!C&`XopEew5DERP*RjCTOS_2GuyR{#i#$I?v z{Npq53-#_Eq4K=6wOJKW;ybCNbV3M34$n@qHU^3Ek#auZL$y^= zYxER5VTC(d4lW_C`gWP&L@xq4>(O@7qwxUz`ZC>nWP9$gAs@Gy-0a!uR(YUJCY68c z_48EOo{m<@{A>>XOCEJq7~hG!aNk@(YV^QVBL`mTQ-87t<926b3D|+Ro{{0I8AW#( z2~yo-6$5s4_bc?RKTdYf)sNyB+ZQdn#o*=S*jY)PNV_*}+AcIu+riOy*#ZUc0+N4Z zr9_>}Gq5e_R={Mv-Aj$IS#w@&wo2tX{_)tY$+6cS8#aJ`>>THAd0G_gHL{jeCuz`K zmAEhE%GCAP(wVY`)Rf7W-nbmuBK0E~N(!}mWHn?IsXtNFc{&Jr6?29Z+4JhY_vmEh ztlc2H+gjH)asM7II5<0=6)tk9WausNtley3_B!w6Prn|T8Jf!7L>PM)jUUXlAy-El zt`3cz+fPl1tVoHi=J7_5tNX?|_dT89ESmE^LHT1FpZ9?Gv$?0Ml1N3{X=yLlTqAuwzh6(g4xzIx zYtgSRZJ8KKs=DFbT|Hirz2tPf&wx#;l6!}L{s|R99|gO#qsfmgiw-Ayf z)vcJT4po{xHP4UiR~(~>15c~eH94c>d0l6e+g2rqR7Wafd>v1dg|n_G<$2Ou&M8g$ zoFJ}tspwYZ=E<+lssh!oJE>ZxZR}D(X`xpNhFr>-u_5rgHklNhGIlAQQ&O^tJv8z$ z-l4V(dofc6s(WcpDDHp3% z&uh%$$<-&Uh_Ii)YiSxebZX%fgUq~ zjsyr9@{$@rEm(QJMn8PPMM9woh)P`a%>ID&{BD+8*1hFXcRRnFjQtfTIE zmHzlON_VZ;bW~^N+NQ=EeduNVys3@ak+vJ=W`$+;%B`rC2gz30PRH~wZ*b^1z$%&_ z=*y>+zgs88*PBd?xfzxi#_MEhYx%}dP7WeJH3wVzfrqDcs;W-9fy%cI#HSj)HLVMu zXbX!>lI@ID?T)N7qFdWX*mjkdc}05ji}gcD9olSsp?`Uf&un^jZQ;9s{vS_on)6Cf z3(Bd88Bxt@m!%%}&A1g-hjZ#mmQc9Efhc23U8s>Q6ibCkP-+)9#u%p2U8_H+T0uxk z-7cz0G0n%kBvhL??aeK40WyJtrrLb#=Xx$7vNjL7)XD0a*i^^7{xrQuW4FNIZM`i= z)EA`wU=L}#*pK+4Mx5+0Ii_c*IOYNgxRN|#aUsxO1JW5okN=fk%mOJnm{Py^H!A&oUpFk$2&}m0} zseZMO$HH8wXH#nG`2=t6QdQSrkN#ATz+TID7)+6u6Dr-_z4kXmn-x8}d_!KUX<>b1 zH<03k@xHXSAu9Fb_K_M|Xja9;2SOfKtcE6Jg`5!Q*d42S*4to53GobXOpygaycB=o zHHDkj6WR2$Bd-LvcP{)>q#4@On=Sy81UvZQk5AGB9K+ zR5|uEp#9=9ad)MCGF)r5RDrsHg(+9LBJ`nX4_{8jUidH*E&($JQS-lUN zS2&%B<)e+akVxxzDnKh6xQniCgKOzMCh6v7t@xKs7f(cmM_wH6qrmP{E1SZjuR9^R zs$dja_a?!k5m3smPp$p^KhER{hFtNO`T!ms@oP_SY)@S~Gd9+IbkYsm79K6ocx<1U z5aJ24NX$tv6l-%Pxw5ct)7$SeS%T*GqU*sh@)XY8Ya&LSS9jgOetFIy;Y46V0jEOe zh|)GsBla!opr2G7y&q7f6JMwo2L%O_Gpte}iVxS2j|uw^i0*zYV?R}B&C9B19g<`I zb&Ulga?5JzXezsUCcG!@<@6LTIK^EWAvrWv-aSS_ZmqYY z1ujAW)^;D5+S_ovT;D<4KIGk-=Hi(#|5o1BI=1q@?kAJ0XO1vmopx=O8ebo(5E1=L z&PaD&I@f!>gga}uu$DkMtiC?&r5W(2m^Jl|hpUe325Q-Xi>3K}k6U>WM0qUA`BeV| zn1TKu$wB9b0QV}_*ifJ*KG=DyQvGWD1Mc!MZl2fo6Dq8`Lra|x!S04DS_pg|ruJ*7 zH3bu>G~WN#yuz=YN$SlY3!%za^{cwt*Z=xv$Ud-P&;8#6Ab;C12I(zns7Z+I^y!FN z*_@Bo{qo-D4{o0cpkzCa2-6b0rni%p9OkBIiWGkQCo(C+JNy&C+*vqtq$ zmiGxf>DR?>B%FATQnr?twd4T0!O0PTkNgP4VgW~N^(@Gj8Y+}xA^|*G6anfVcRy>d zujkOL2&qy>Mnxd;*CK4LR=?UcQjyLjmT|W>_tOhK#_-)%sHrGVZ1?)9pEW`4rTRgv z{Ht2;JWyFa<}2l}(^vIEc@dpY1o4V}wqAfu9QJAGy8K#JTZKmt*2abKgheAg27r!) z%{D!3r?g`Gd%Z^vQ41zYdu{G_Cjc7)+J6|prcTq-uGgwbfPJaN`~jJo%?t;7oX)hZ z`?h!4U3m5Ah;0y9Z(-p!T*vl?6Kg8uKX|6})z8X}yVH=kgZK+AxXYcTTud#b4dygs zlw3d-0gXxt!1~mVN-&W_P#IM=^BzmW%qi}~LT(^70XEfLb0Fq?+I6ugS+-q>3REkQ zO&fXIKMn{@sVfL$IboE0G|@P^;}OZvJm>A4$mo^Hm}k3brJw!%iik-r2&)IE)rZ` z9nP@=8n|UY)b}sfewG~oQ&Zg8w|Jtwp2dI(UqduKd2EjnPFN1OT3*yzIt+&{e( zxgPDeH>cz0aE+SEDxgQrdFsQ+t~WRKG#{;Op!nL^d|VN)TG0#HNWxb8nyL`rwGq?B z7%^V34{B0RE6;+lXb^LVcNi%{w;dN)jl;?ZpDYI14mA$4)t#K1)2=d^GVlBmGUZcNa+{ zz&n@MM4scpgWWu5ATfO@Ir^;b`So-zq(B4S)9zl8-JDJ$nV!kL!(AzFWE)4H#(%hU z0Dxb}KPv_#Wz2j+H4o}8%!SljvxkV3Y+~>F3v|^TO+@I*=*axBv6x) zT9ri;Gw2_%`ApJ`sw>OGoznF+nW1l;O9cRR1^kA;0o!eQS?~MW^>}YJme~Um&zqYC z;OT%mieDf#hr-qn<)Ls{DXLVXX+oUmG&8k%w+X6o&v%mQOsD!7OY6)Q!NRG>R6_R~ zJIM0%TbL)5!&YL3gOb=|{E)f9F-}S>c5E3yi@DH5AuM$3oJh>fSx|}%KwOiAd1>!- zABrz!(Md|Ig;3m!h-tfIt2+D~&g_`$hPVmloAg15#M(P99WV^w#3Bf}Xs$pUnEP~^ z2SOanX~vfM75bve^+`^cK!M&P(G$s_p<1 zp=#*uojOfYiCym5cP@=f)66$&3oTWZDfaD*&R&p@7VlV5aiv~?$D>xsGlMyRvRYnH zDb?2IS|R`0^g9|p7@?D+<5@xNMwF|lLGA6CHbS>yNST#>6OPHjrGZzDMl9DH-drm< zJ6VD9`V1~+`8g&F`UAjO1J9Exc`ZPdkMfuCTu{Yt-oM6|6 zU*`vw8B2}@gJrsOcOs{``KTXN6OG~8>3H?n7zk>n`^<#mBJdYp?Rnzkd(L_bc;{bH zx2-Hy_G*?;QQli+lMrx})M5vMr=o>;Zfq}|jvu3+13a7|GP2_gjZHzG$q(jypzP4< z5FZnkKA4M%%$=KU8v;O8LEz&FxeoXTjJ-ZNL2-R~N9*`S)KX}AU*2z6%|7%3!bvyI zf*}@DFw6=GA2rzRo*V!G_pb%Z2Mhv~FdRE8>K}l1rX>vK$x%wvQkDmx@gjy9wCL8w zuI~E4NG*8R)AtfJDW~77?Kjlt{ZQ02saG)e=z+=WR-f}53&7y0vq+-+-RgM2rA8su zv;P%{)@xr7H}{+m*^($T>&1*V67RMYSpD%*FXbJjJQw2s<;o<$BA*LEg+lK2+Sey+ z^MM-ob{5P4f`|@xpHBz;K#uSFvp#la7r;axg$aY2fFc(~zV?~!s>2A9m-L-Wp~J+kz<+Oqg+T(_ODgqRc)ZS z8G&ZxpgBtJ6g6Cp@t==pZOYgtUcWRIx^sEf_5y^kbEA`8j`6dB73tSC53BiVW_(<` zlJ#^P$U)s!*}UrV={h5C;`A$yd>M@h7Noc+Tao&~_SJAaJtaq4ZkZpj$FWgr=J}lj zBL`Y1U&O}r6j9VmYCmI40J_h1{&GE)Kp1<>d8*|1JZc0;5_Vg`=nMnHWtZ=$?=i`vw9UMO4R-_VGU9jY z*ll^>_vy4(0T`oHcl!`q$YdYF5|X8Jv-=_Wsb+KU{+ajKNH8R0yANM0Q&v-Y^?q$u z*$Qmob`I@}z$_2PW#hgBq1W5pj3A9vD-y7X6N(|LLl!U<6Ll-vPv7z9=Lh2nnQg8) zR%wjU7eGu6c>Qfup)d$Z#%2fD4dd!;weB;0f!E`pJ@;;?Ne41h8)v1dql+ap+Vxlf zO=i<%c=Rggh^m0?h~KWhz9267n1~oKZ%wK1gu>-V+{u*I@JQB)zXIDy)%w1WWIOS7KlO93NnD^<$Zr9kz=;-AgQ}70%l0+-@J%5JOHU0IP&VYGqX)GCuW!INzL866u(u9D`_~0~Jc# z1I08Fc|B*5lIO@yn2GxPySY2;*~1r`S3G^4{#3%vhn-M%dIGm z$~IXc`KEZ)QzQMGk-J)KZiVK&D$l`fG1SQLWl%g+(Y&|P;nm^h6_vH4*i_jlP1bsV zNZgH1md6A)?orP5hwUMlNH_lg;jZ86x@gVXbrAYArKY}htZ0>N=H2t3dN5CMH%`4M z8(5W{K&hqJ=VcfBj-5hNUc|VkKc7TCT=9WgIi4PiJGCy62Rw_r;w+=|@7K<`2VYJH|7I-U{d> zrNnI8)uuHNFX`)SWxd+Z0}RHNj*75~C~0A=>Ql3@pqwgcG}uLq3;QQV(DKVjnd6SC zD_em!>MfvJSfnTB_SU3TB9&@dn?ej>WSn8AS~7Z${Iumm!h@BnD_O=M40J!)9TgcJ zw@tc9KpG)hPOgUx2C0A9ejds?*QVAguA@Cnav~vQxx<@_X{C{8iV78Vd<*5UYNz}A zc7vNm_6qZrLduss{&pWcjB+z`RWkdvndfkbnUG~GpQZAz4j3UTI z`EpZOp;eV$fyMQDv73(V9*%#I$+v-?fXh5r5z;ztKM0K*ur)7$*GZ4J(Y()regVgu z^8=->a+&$HgWnC`XOUSGKDhCmnp-%Udmtry+e^>-OzC+W1KLiHB=vP3XhoA27O{HZ z+=Y$YzeLBg`8uDmk9g?botx^fXAzB-(-Fz%2RO>~jZN)C9!|d93QC5a=89<)PG`by zI(LI}Kyvr-{jQG>)EmfAMc;n^E}YvaDv~wsJWNd5(@viL(2)A+g-?@bWRqsT$0O#n zqiHH@19K{yT6s^8j7fAUmZhfyPtmh-<3e`{o4EK;FfqUi;?~JsZ)WM;2@fu-JB|et zVW((#TZ9YdaZu7RR&3-k0lTq~5!qR{`*>(nZJ|DtutFxBG?`#3`$}VZy620~z5pRn-6!=7h$(dEJ%CW##wF z6R#KEjf(z>A552PKqd)9|t%}aV`rqf`Xy2+~5|T-?H+hm@}Ssg>Hg^z)et! znR-MzML>RpNXohFrf&I6*FX;LA+@v%jqMRl;gOE6w3lVQeymz0+SL#4mq+R`gQBab zJWD%S(u)`m=eYB4OBEmCsbK%05NRp|gv1?FJnu|?6;A?)YSj^9d_3*f_<6zR9#79s z5EO@%0T;M;le;s{`tXAoEEAi!ALm*pyS4~Ro%j&tp(DSW4msdaBrV7PKeam@pW)8C zCWpBF-j3Kj+C*cOqoTkSXzW(vhJz!Z7XKzWT-|+H4}?amgQ``}2x+JXpTy0P^yU4r z*4nOcSb+qn_On7=KKW^z2yox4uiss4{MyCz>xlNRV2ECJvW(uoohmz03Mi+|>0Hif zG(ug4_gkS{uXZJFziw3hk|b&_PfhGW*;J=m&gDUL_Z8%;gKYEp^;j1%uKar&r_j+*!)p~IC z`Oc7*xvv05Z8Tz7pCFoZjy~5YXSc}tz%(6VH)K^`6gdV1{Oe#aT75vYZ=%zevGqC= z%?lELttg%8cLd{+#2Bwp z(~@d0WeGoLznVq+kH(_)q)+dGF4~){^410GMUEEr#ZfC!2~m^NQ3Bjx-shbx;~%%C zPA5y07}c++sNnJr3IS6Lxj;~mcguMSPg0dM)PJQUEXw7pBlQpx6x}rUSj29p8ETkw z4)@-}`}AI7@=o4pqu#2J%cr~5_Or_}M%}ND-8l-N={*83tam&hXQPv>cwQfji|k}a zlfa{FJQE4CZLa)J7rH#zD;L zcR5HX+vhw9?2G`om+f8-wu$XeJegFrj|Zv{9;aEu&Sg6*n<{EzAdZXEg*uq^hf9J4 zMY(+-|0}#(Nq=lTX_+}Q3$9b_5I%Mc6r$mpDmCH3L#T>-Kp6#xc!t8+o78#@=eF}2 z$?E8N$iuU4ZQ*$Xx6oo+T(?)I_l=I8S0MV{t15U(J0%QOLMv|8AE73vI17RE!|Bez zkthjn?0sDm!@<^mn34BEHt?mK@;nc%XNkTnmM7 zHidy=;QezmuG*TdjIX#!^M6Txg6_wCxui>Fci*nl>;^f^m zN;}5i&4u3r7VMb`SYE5OMt(OnJsli)!vT(R_JK#lDkckrD>90n<-d4{axH;}5aVVD zW)n@KM=!bW=pW1@xV+c{{>R4u8m4ASXXDvbAur${4!LU4alz*zUj(pVnvS2HSe808 zn7nL@2!J7GR4F#78J8F74;UZ|hBE7CVl(WDO%qkb@_zZr3gzw?Znig7SYVFjUSL9K z;HrMBf%M$9a{IwhZlC(tRQ#xL9zO5J9%z*56HxQh(5%Zpxa=oCQ*@1S2#XATy+NYe zrt;(2RXtgt=-+dOmH$2c#?Mro%U>!d0U*D7lrcS@mg>%d{$Jk)b zcC?rUGTm~PBa6S4UXux`+g3;~Q+#_N4mk~_5z89{MIKUG{CdNBNT_1m9>=po7@f=G zext)Nwn|OQg(54rGMUnPHF~+9KiDSD_1y2HLmT+qH35Tt@^{?d-)B-rUZ8AlAUv95 zVoT3R1%ZzmX@YEm;M6jFcZSa}Lys|Z6Kwlnq9B2_rw6LParTNWKCFFeHVNg4`5In3 z#-F1H3d{YtA(gYt@SX^o8oh#Ya%#(`tQJ+ykgeCzu9J>?WrZK2`^tN-$C80Q==Y(W z>;AnQF%Ch=+oTcI<_7jg0d-=J(yYl`8e~)th7W=sZ0AX$RR`n#@Gjz(ve6-XEAr7h zXA7Z&fE-AvMRwndnzxY%eazmhx z#T|g{Z6YEav35$=o;J2)6t0bbszx!Fq6c{mTC`0SZFWk6=uH<`MW?l4lqPb(DwBnw zJ^0=Y^*b&loT<}qKLShaZ=2y`Mlw;8t;}`Uf9&B-g3FuRIaa;+Pz3@-4)V(l2sLEK zDnIU#2VjII(cpky%t2tCx)aTGdup`clnQ;P=fJIru@xtc4crXSSAyY{Wt}W@{B`)u zi3(C(ec4B&po2D3A3&cGq+s9p_VfK{=OhG39#9IQUB3u@IL1EE^5lN)FJ=O+8#acx$~HcE6b1;t_LPbEki)$e`8BW8q z4mZW8zI7{tL*J3x@%WOdY8NKCqGo7G{{jhmi5};eTVHw6C-0s^@kJ?41d6xZ*qs3e zU5DG?Mr1KM@VKJ zT=sJxDgzF9%#El1(B?+z|50@CK?LsuSWV^QgOANCWbs8$pxzk`YLRkQT=e92XTD_T z<%-HFpyy&5cWcpL&*eE(-FrKsH;_Hk+M-2C4(eG@mn?edE!bD7QP%2E(#q?S8XLcS z**28cbxrgxjGcgCJLoHPuz`OJsj8}j(@u1cTYS=A^Y>i#ewHVFso|CC{eTC-t3)ob z^s%U}V3GU<;4mdVydt|`yjo3*8#To%EZ$RlhS8iqV)u~$W7w(Q4jl?zoxFY!I(LfH zLO$+3;FmDM^CP0NW96gcqQMOw-S=S9wL`o-O$v||zhk~@`8MO-?AEU7A}u}dECXZV z0BX{v)Y2iMz~p+zc+mKu8=6$`y!;${l9F7+1p zLW}EBu|@WU9y%NKlyXod!huS!e$Zs>Ed2|Ai-Ob%JLkgau@e3xcc5nTjxiM*E}O5! zkCxGf4LB-XW}#!aS3;GuvF03cg2GShP=T=hDjY~C>mIt}(r`LecI-yISY~SHWlYgn zRa4K?fR7Vas;gL#+CyzcYeM>_&7iH!r!ngZ`@IxVs;c4!`ipl;EF1i+M|gygl8#|P zygHl+$BRIOO!enjQ&WR2+e(8>uJXp7lUGM4T>307vDmz?)H4{q0Mbr7r({ILjr=l6 z>7MQb1Z#4_30Ll3X#oh^(38z5sePg+qrKjhHX$ilK5o7-&#&jvyP#|2ru=x%@Ql3C z44&-vSWaHasIAS@h8zzuDy!c^RKfNGZc6M@Wj|HIZbnjsWa4YjdfpzKkuS--#)0_l zojlk0*taX!yJ9;*N?&MyeOsLb9uAvRiC%9mgd(e<2qEw+tZ*YOOt+hw7oyZ zn|Y-&lx7J7@oce?3K>cwz|>cifSE%-XRG^`^rp(vBUKn(6HiqX*R#Dba5EEH78ila zCaEcIhSpV(SMD`4l7Gl0&7S_#Z=x|W+De_pL{@p~Ij9v$77MEAm>%}e?8(+Mm?;Sl zy0ZNte^Hi!>lyM2E{OUxU4SpA4*7qo{aw%7b^_F%RP3K>GTXs%alFF2!~YIr1YE`C zT?&##N(c2rv))cLR5NtQH+5qu{8?N1d}OGDd279%tU(N;0N>iFj#IDhrOI}V0&i&G z!Pz~7r2?oGpl?X+^0jMTxeTqpSkda5cV}wE)8PJ-=}J{Rzf4)+I%M@_czq@AJr`z9 z5`$GWxU!XtrmA%LVBgK-WJ)O3>h+8T6gMA6PH<#*=F+{-mG)+L1?)X35jFYi?kA7O zADJRlbcSP>IIDo)W6#!>d6}CV-jzLL@@qKvjDYl;Rac_2_`Cf%F7Ai;SNqKc5(ClI zw{QztGgF;K2yrm7)3h(dQ7uBMtClGnOmTAi9Sb~YgQo{W`U@2aERX#b+A5*y7K$&$ zTO|yt!Rf$PGp;C4PIREv!&J;s>iI^`yHUW9LV$K4VeNKJ$pUz17G!1#Zar@kwW`wclQ+xRMxY z7a)I3L#}Id_Y;xa-Jr(>aB8l6uZ+Ft`GeHABdnP1M=>&JGw8>iCoP3D>yI;Tj@0Ck z>RNt+TCl$>qf?nBq}rb@J_*PokpI7hy)_Gs`jfc%~dBeW(IHRd4GBW za7piX!pQyl=3uB}5<5Sp^DIY~k!D-?q}gwi+;`B?BRHF>|VSi#=WF?|(in8i+F|J0v_T zDi{x8dCBUCI71E6@9fq|M{1~#WCGEmDjF@0(eGR=oQy5%s(yz?oh9##*4mbh4JV#V zoqqQ#Cd^1t@U*vD_&R#}kPjB+iW+c5xGcb`w_eY#NQG>{Yl6{Lm&ZsCpR<=54lNo! zdK#kf6tKdKlCIFTGhNZwSGMO24qH4-uf8_K!FK};LIOBVO`Fso7>q}$(?hIIb@f%3 z#$AftinxA#6Zz&!W<1+6q-Uq^boYg`j&0{B-=)5F$@*Pcs=?#x6Pob;wHsK(~4FnyoXW-Q8Iob$56Tqpw_HB0;X6}8}-JI7`yNW~K^5+yX#`D3`h-t^Ti&Lit zINCSrk-CG7G>w;CeGdK21MY%B&n{upp$k?gYet%S7n)cbPIg~<)}YHwWoTW*>x4qj z2jC59FOvv`7InMRVj`m>FFqtKHp5@uPd&2nY>O>d(s7ZaoFIbL@r8xqg8BYZy1Uda zPud}l0gyxcCO zfY~LmI;=V?<>`6f{vi1#4&_YnqLvv=+1~Vgy=c#4=+UCz!?TJzby{qBNaa_U)YCd42^-8a zf(+<`J8>1H&`!39n*22yjWVHoah(P&>)k6rZjAKn-cJ1)3heTgC)J-DI$ zARU5j-;xwM$n5#)F}N;N(du-ZZ8M?RsOj4c&h4 zAQ|%e!HsM|@R_4C`%$J_%o+hsIXi)JQ&8pNb(cBKeT|Y8-zZuSC-BfVKqNTk{Qg>M zQFhN=V8B)Mx}m{3p6crL0_TW4w3LbaY+$XdJ6lP3KHr1TxRiKF$Cvrzy4vrV4Z6F1 za%|-2*;$)&$2Hs;)~VA~DFhK|MPXo^CI&DTFWwHlzO93dl_2CFzzeL7ZwfbT^Lg#G08|e?Z%Hb30Q)2XYR9b1w9jvO z>Pdy}C_P5a98=H%&~l%9&VDQXXZnx^ItJ1_Kj&DSEOn~88?9I7-iNU3qS6L5+3p5* zc>+U}?=}^WXJIq)hWOyF*oU34+1#$;3!T$*zXd^1r5p{2ieq8-sh?uVmF9zi0i`Og zmHGI4cDvHRzUHGy9GVtt{*e02+sQf^PL9K`@4APwjQVP#h<`rg@$}m#$?D5X}ZM z-N^h#w@`?CHpHB-7@L4gO;BNB0+??dPj5BXUH%zOvXN0Nb4;1Z#j>&StSCSD>w5mi zUufDf7>!SF#Q+=h7`<=9q1P70SAF$!p~LMmA5ln|FIw~jSDsq>_WRFwX1e=SG2xkWepu!D6BI5+Unl%OMfqEM7t zC-Di?OZgTY5|H}rHt8Xn24KQ0q@7RL2Jta?kKx zK9Eto^|=`C?Ts!OcW$si$h`YM&noz;s#uZLs1>v1J{xARImy5^xH+Yk1$GXXsi@^c zfqm`O+mJ+7P@`du)YQ4^snoYaMQGP*@k}_}$2c;;(l<-j>VktrXhT8rm2F3BWE6BR zZoh?g#8AsK#<3~VZ+AbonHzcx#f#z@&=W*-kMyk39x+3CpIFq*tw+Hr0m`pJq=~G)o&8L# zFP5_Z(gR?-v0;B^7RxFG<&ui}9X6ymzs0R6J7$J@2J8cR=z_%AY&;9Wg5mvQNF2b- zD!YJ;{T`nmIoS7NA8Ld(x5K&}_rU-9nMgdT^_-&HOAuDooZ=s69El{>Z>xWFj&t};6x>L0Z!yc?kaurcNWP2OU=_-38k&h zb>L77q_Ar>g%&UJK+`rkX?KQVAu+ko^(^@owtkskztSkV$)d)EZWYD(Vz=? z`{B)EMUY?*SEtGjwT241OHm`q&nMIkme>M16k>E{OdyxW3opbivX&<**`GYI#g^qo%ZcmiaA}FJ~ z%7UYy8Bb@ZS(2Jzg5`Mv-Uxm`qhVea5 zLPz9nINmXOd8_IldejO@R2S z)x0HpR;D|uUIucmO6Xf2kU6_9F;~WO816l~jA9;`Ab#5-p#^jExg-8#eSKhA^H(ok ze1$XpUMOvOPe42I{W9Kn!S0Ch8tBj>(I%i0ThRh$k$ijcA{MxE7f7tu$d&iXQ9^P$ z5PzWuwA<)BLu&eM+=45B;^hhY-ak@IXuAJie6B~6a zyrB1}Sd*D9!8qi{AfB_KO5hFig{V8ZyCJv=IAUZ(E*RGVn?{C47)1H-YWcI47uS|X ziTwcL5F^Ie&-oPE(R+#|impMh9#@E7a1DXm7zu BY;2bt?E{!ArSY!KGgaPvD4Z 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%0A3F~noA%2}Yswu7^CY5Vm5StGT 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 literal 0 HcmV?d00001 diff --git a/asset/menu/FG_1_test.jpg b/asset/menu/FG_1_test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..528f4e64e5c8e3629a5bacd24b8d595fc351f478 GIT binary patch literal 101976 zcmeFa2UHYUw>Df&&RJ9xZ4i`TV^?*m#z01LXb@2`R997#gH1Awf`|%6L`2010*W~S zqGQ4wMgb8M<{VIGR2)&^-_@w&ysyLSfA9U)y6gLFm))mM)k*v8@a(;-ijFHCcT7?| zCPm8-#NQvWLl8s*(P1HoGL%@VVerN3EGsIM6lJEkhNae7k0`-4MxV>jkk##T8LncP zA*xJ2@V}JR>uY%n%jTQ5DANOC1^@T3Fh$t}%2(z@BP#IAw0#agRxmH-pVr4r1Q}rP z<=2Hul+x^=sKk`SEhOJZnQf8K{_^7dzKhIt5+x4;Jf31%-<=gcsvA*=d z%2N8;x6#wj{dvJJvqr^l$CZv2L`Siv@oxSItK$Wtr#M2WOy;_d>qs`DqoJXpsiC8( zsbi?ErES2i3GceK9(=%bd^h`RN z82(5H9fuJ^4P-R3PMKwiC>gSp4OtyGV3ArK$5>7dqNJ*>rlG2=!-JEC$|@E{sH#gaVQe{Z2Bv1RxOk^)KdWw% zBT^n;o>lLrZk-?U>dx)m6`M+DMF%wYlFQ{#RNv!Wqd5jRI z(09L1;h$$P;IAokTtjq}6=NDAPRJ?N(oFja3yO+s%xPzC6w59G`@NQ_+m0Rhp;qzTqWqUsIIKb&65WgR|?^?9}=APx} z=B;uYHO4Z13_WXW%bT#RYe%&23K^4tR~~qFwW`;sPu2^OVKWyjelz-E&C+1E4#eZh)JrdNb1z<>7Zy83HJpDRt=f5I zd;EpiaU%ZJF+Kekw00ot-Y&hc53zeOHg(-WOQ&+$aCGRT1A%L`1NS$n3@z_F=2@2h zi<%9OYYeOQJllQk&ib6bZ=H(v>u;Slpgi*4{sBf!qw=raN*KD;a%^kb%hN{>C0_ib zd23t;(h%BqSwF00o$j^)%@|>+5 zYxeNOq?852j~w3GXI}omXE%oB>+HA4^IoI1q-XD>_BR=PSNkfanj z?$+=RzC2HRi=I|{#pjR(UY1TzL-3j!e5vV7|ElUn*_?@bS9*|guJ$qM(bIC9wZ*O=Z7G#gqs=xaA zl#qir(wppdRlHf~a&p?~F?LaR3?xP2m-6}tt`LrVGohV7Qvg*RQ zQ45|84Qt!7U+GN6`KLWD-FvGxwqNYC8zJ(?I}hGyU%IkyO-xGnl$%S#KUl{-ofeko zQNwKn_Q0s1gmBPbId{zCv^VWbVwXJR-aS&(^Y;&T zBl_GNe7oZ0ymdT>4ou23KmY6F0wFx8e(F_jn(#H<#A3&g#D$-kz^nSChDNyLvF@ zJI|%}lH2QBs48dV=sNF3=d;R>j@>dLIpVi_ug#m4hiq)ynlc+X@mLg&H0sZd)SnyQ z>s|MjsN97oM;K3x;#{i>eE4MJrw*je?y3r+ygb6YxyRrcm{Y25c<@jZ8oCS6|+yML-g4*%+4h>o=;g6`{qT{Yp3`5P9=2_Y$LVePb=TJ9oVE+HS>k? zYWH^^TT1)IT1P26N&U8q|ADnz$_dwsT!l)1D90&SFQ6U7I5>4_p3D?iT6Yftwp(PvlaIHrT;Km*drEou(Kg3bJ?^%Aa5h`p-A1tB!Efwi zEt4HoOWiBC4$cq{zP0AEw^2W8y~)VuQOTpVr@g>V%y=`cWN3?LW%0JD;lytSbg6m3 z!Iw8i{MOpz24`}c#a2sp!pmWO_my8CUfT3B?T_@Sl`pr`=8Gz$2RiS3M5pBr5tJ(L zU39Z;Tw}|2OS)<5quAREyk1=rZ5)=zNzNSdWJ?D!;JugL#8jD!u1eHz?8&~|dlKRn>n*%iHs*b=)9D7@4o;#B8{1`CS8JB2*1w!s zXD)~_n^bZo^L@grUiV7pc+K(rZFAt$XUbEKnpa#qyXvB{=iId?9Y$RwTC*ZX3nurA z(sq0Lsspk4wQ%ay1^I48DhBqGbXzNuF4y!`+O|+2BL*!ZZ?=vi_R;5a9UMV`Xb)qoM>_E z+2Qrv7gHavez*)*Qr3CVdZcYY^}~!2p_b*o)fEYQOU1*n=UL>y! zOr_7IXI{Ch+0ydyR_W4$Lmdq#r`qcfTaSqr^eX*i-rRp%O;U1hzHf?}-kMwfV5AQ{ ztjKp8t(#z$FVj$8o7m^pgYww;^m-vbdW%~?effszj|LV=r2|tlijz_oRF5=$?5Cd^ zIq~}bz;j_eE{ycK8Sb(y?9YkHujx5AK9%x3&Mnvw>$iIJRQ=@YY2By)G3oU6mVra` zQq@oIcgqhRraQa4^B}#X=mpn(sp%~X+@{74xYn|JW2{Slz@aPcl6mur>QWGmN!B+T zblTG&Wn9|WJFC{M?zwS899SKh{FvEAz36kazz0zV$u+#gVYSa8A z3Qu=R9g~tgW-#gcOk~9h+v<6L^t2NjaNKa6*|Qf}(+)LB?PHau7Z`3Cyra)$@s=Cy zF{{rX-NLH5reS>oS)0^)AS^xj#ICw?Q`hw^y;GXYiEH`PvhnimBH8ni{1>rfw>axs zH)Q1BdB1u1)0TvUu!v>OPu@qS7zpBg&7IDS3Y=8A@>8UN)sTJe@-h1ZZ;ou=&fnd- zyXf}`LwW2yYJ2sQ5^h|-(o~UjvT4|%{G9r;ak{%cIL8THRLt4YFWQ`%JhyE+|BS^M zR_D8C0hu=Y#76zFs@y$-`N*;O*7iiF!1O;tO!rSzX5%g#B&*-u%Nexi8sn?tSY(c& z{ySm7oVzV+ueouOa|~|m%N(UX{%Lz+-(P37O6oV?to56@Baggc_F?*xx}w_J`ll_3 zx&EU18*dhuzPcDAUlIQ1n!A!W#_B)>hu#b;>NV)sOM`u9^m(T=_Vx9a#EKej#mgw& zoeiHn?+r+AXcn1HUu<%(`E_+;Qp@)JLBCYByi>8?rN6S{;_>N+Z+PFd^h&#GT%Fc| zOg*%zy!RiUwx#c0?!YtqaA3^_K$X*wUdnEFt?(=rgW-QUZNmjofn3)F5n7?3TgiHRsJTy?YJ+Bd|KLUQ#&QfA*M<%cf?>-0VOOe7r^`Hwza< zaq`BL4t~A1@9LfIy|fQE4wh(KLy-8kNH7u{EoJG_E7uMd9^KI3Fl1SD<+feI{fRI8 z@eH?ovYtO}>caXx2@Tp~TDBZYyOsE)Pu8cU$^(_G&MkBEn*C=>yKBby=;mHJeZ69p z`X#S=SpBiroSg=)8%0-V6SFs6IFz>GnzPE@gikkiKYwQ--qvgrtKG8qL-^H+6P@-p zEtKo;JF&T!@|^sn!#hFgLe`0Tycil0cd2=%z!++_KVIN#-bo}ay@lPVA_s_7~8lQFHQEd6GKhMOb ze;N^eGpJ8X$Yb-`>qD(Y5wh#oZG{WP0crtc)oVijdAzY50W=FFsuej(m-y_nM3@ zze~7UY-^a(fpjz;n;JWyanL(m?d7*_Ec)%^%kzO_=EYudP7WyVm!CeET2%I?1G!zd zrOlx|`AOgPpMpD(;h*egPTu~cJ-zql{1OzmM-UCN6Vr^((`j`z$o z8+2xTia&Vb#kD!@hRsRS7hSylsqce~ryi#ATV;puwat0bf%NM@Jo~@VytE^(?nO$? z$vG~@+ZH;3e9nxRHnvi8&w^`MO5FLYv18sGn#Nk|(SfK>)1UJwqc)?|W5fIE45v?v z9y*EMulNA9U?$e1ec|FNX*`g~UL+SqlkyS2ooP0851`PRql z)0fqyzh3LK;p3uPL4AMQy=Ls2Lq+Y^#(C>4t~&F4>-oWJEE)!6>}k1PGh?mWyAI^B zefX!?jbRt7g|&hE33iLx@am}%mp>fZw)1KG^g}s+PHb0iH*WtkchSciYcEXeccK2? z>qD2{A0IgM?3;-hGh-KZOG!KQXk^I7qnqMC5IbJg`RSLn=^lL4KK)kt%J#|kO{eY% zzkDv{QD#Q|g|hwf!%i-T&Zo6Golm;(wgag)KD6lkz3Te*3!h%U*E;;+QNW<>`kxw( z-WxxBePYw<>!T+gnSAld;l*bs?QUN7@lnP6spH*NlPeASAV!`GVq;Sey%}lqa$QDI z+xX)h$eX%nfX>QJK9sK-_Tl4!%7e?wokqp(Eaml{5$0y>r}mPIcQ5r?VeUC%-s#;O?Wn zKv@IL6AH# zQ5Hg{q$UMQV@NpJ9SK6@$ZJH35J&Wx4KoXhk|MkF4p5XS^K|;!32Y%AHi*TWD;XR4nLM4eLnOmp-C#C~*s6qLv#G>XsdM6OW`>H-aCySN)w#y~`*p5zDs)6b zif2Db9JAwJKdx~U`+N10WW z7=D>)BYw23vzvbzHWcE}-y1fBjO(lql9SG(m>v`#15%!tG%|{Y%>}-VPKf+MK0W4y z1m@(Ln39qhAD5UA`MHVCmov;1zNy#!QZI>)1hfBF1MM#-U~XTl83|Hu>6}An{27F< zGajv^kgooUb9$c#g&(KP&oZg2sAHtHkK)Rne?L}!tvJ?He8U$@!KDzjXFgh=MF$vG zF@v{?S}$afOa^D-;2R?A@L_$fDPB?ZwFXnvkB09r*9a6f4CjBYQS|vI93w~)f^>ZM zqx_gT{oPX?|DAIRS}Wif~V$>L98JI7{F_$SJO03Sa%)FVIGrTrMab81F^I3|O zoZ(XV|G8}+ru0o;X3U{7qrW=W&KxSFiOJg_JwOMgDl`oR`X?Rq(0I@|iSV5OI*5cb z;}u$oM52&rMco&rrs(JE*Joj6j+uTH()#rzQ~2hBul1i7z#J!}#xXjSA%vPVF*Sio z{ueDkrl4?6K_W(UzVatj{tJI2u3zP#a~hJbY_3?cT5?=8NhgQJ`7wgZ`rA0FigQqH z1pn0)2j1@AjHMnaPfShvTa{X(JUTM^s}A-EVR{c#)IlG*L~2Una5{mO6Dc$W6-emh zq%Vd=tMg)}hB@OMAIXNF|I_PNPL;>~%{(cH|MwdI`V3EFV~hvx+TUX%D*A5VQ2rY!@r(@CP+YH0z+%fl*A-3{>k*W`cEqk6y?9w>PQue z_225XVT+^w^>T%D+AsqlI`Ch~>6N0~Db*&5GQ+~1Qp{X|7&B7I2**134?2r=8$mW6 z1_pczvdQ|H>-(AO`-(AO`-(AO`-(AO`-(AO`@bXCr-+^NztrJ{33C^OZAEZK@nQeuZ%D(>NDhx-Sy-4f+A zTi_t%kSJfk7Kt5DA&)N>4`iNNzz1>hK`4)d3LW`eM+{?s{#e3ja!TeXargW@790(= z{Jg02^mK=Go%uM6HVnh>oMr*Ju3AqNwMqPQbZ?8xQX zb20etywo2!iGqaW*NagOm(SsfL%%%9^0$ia$TmM|%Y z=xE9jrDznxaFT=50*b@OaGWC*$aowP#+C61R7mkqnPOhh-aU~_Wyry_?__)`k%T_J zlGKspqtG3Ja)dmZ&*6#qT#i^Kg5G3ODNfQ{-Z#Btu|Z+m5CMJZ;53P_oh%i#$K z5r**hrI%{@AjP6pQVz&%lcnhul(;!_JwIWuSrgnrKA&b+9eXU^T*hD&_7K-nMnH& zlg9)YUo4_I0udkO@Wn!kBc{a^N5H4VC>N9R#A3mh#s43g{AbdVQA9!{4QhUn<$rxS z6otc_F&fxX%!e-#20Sk1aVQxoB?(j@;7fsv{$(#fhEWcRGBS+ej+nrn3(u#4!2g%G z?N~5h>OZdN$FRb`%hPw!%m1+E8^nOyP9r<$U+r(_v3sX_@?;cVata_u$M0F%AC)ksJ)oce zfQK-YO9(L%44zED<)BhNpMz7F04$-9OG*h+ChnA1rnk;JPyQQth>?x2YWgc!a{<7| z0FmU+_S=<+{A$UEaRnmWUC0-EcnDlDPY=`+A?-OfM9rFkA>u2qwk39FZ8XqF9QGIW&(D;4&0gi27lAp<-Od z6N{waHQ*?S93Q+IS^#TADH+L?5n>AeVV*$6Qn5@ZlmXO(m7yr~3ZzFlGF(JRQ65fl z1^AEB3&SyiNFd{KAf$kENP-WRoCNQVz@$<>KocHC{&-&`%>yhT<&b=ExcDLfHiQtD za(F_i2o+E=*k;)e&r3#7Kw*+YQ3Onh$Cq*dmSY@&5C}#|F-(B|FzqoJiPAzT#i3D( zV(2B}NX0bHp(&YwCZ$r~3DJ+vi^s)LsZfY>2$?{{k%<_3iNq*}pm-vYm?HU7g8##P z(R?8)1F{Q5D8F+b2^jz=QV0?Obf-nQ^hf&wCloY03KZu{6?{)HEDt^a7nAXD5)%=! zA6_F!us}@6WE_kFPM`@s!I4tnWAa2OCB#AK#Xqhm_~6Zp2};O81vKMth$x^JvlTR` z8%!+X!T$c}8bMc4Qn3`{fEE<7zNxe7IAS15b${%9K)eQDJ2lmLOv?y{;k%EA)hz0F^BUJC6yZVvwvK zo)=E=;6oC?@-!phjQ+wgaAe?+CV7}x%Ku>AC_&B&yxy-5K|+C5Gn$V3i}97I!6iu!RPTYp;-FEYb1p@3M2tl51Rl$ z0B8@&kp`8Gp;8fr<9zN9>s4?xflCRT!v$Ocni=o}A%cAr(Wroj60{hC1wTrAk|Ig2 z2vj}ItdY+J3r+y3I3jTV0D1{XO7_FDP2;3UB$5HK854wo9pMn97=m>|K&BMXpXC4W z8fk(jpn;1x7?>PzOc^{O#2`k7b7@LQqFmmO(~Bl)XaTH6k-+z$3n}1xoaRVLu}CPQ zNdYPn{3yL>O27q32z)O96GRH3FM@);AgD>g{A6O$kM||x0yJV~!@~$6N5lsmO>hD1 zpfZ$zsRAAo{xILmFr1HbaRIo5;0XX`g1#uQNE9iegi@Z65>Y?Q_cEG<3;<|Aupz(+ zQbtcuz;0rc0I@{*A_4s!dZnf^Ayypt*#qo4AIIH2P}J3x>x!d7Hy4qshX50PM^$|Z z&T_e^5XV3X&|Dg<1D{a>JTQJV0b9jbAK;{a-=wdtQ5usGLIGpn05@V_ox~_b02tze zKg8up3F&u8`L#91b@OmRT`^aT3v!C$80I4O;E4n%&jY?(x)_%S){x5;b70>%?j z6tMPuAuR^~iH7ORq@Wt4;16^7LQ0IIBn5uQcVPGzs=@Ap4Q8VL1kU3?44IjQnB>r6 zF60?-2(${mBPf5hmf}K^1U-b(5UV9AaO$`yAaA~i;KJx4p0umhIK{*GpwDQU3z2GY zk%4Nk1~7ZT>jX}M0Q~cgp@4E_;III!KxLi#jSInzL`mRxnGEM*_;=vp*O^lynM??V zk)i>=0Vou500RS(Ljk4oc_ay8zb?j>2}OXg0Z!6@Mffr)XgnUcK0+`bVlgEZ;w1Uc ztuYkvHYiMpD2fyYo(Betgn$JffRz-yVy>_YUIS9|0bG-e1?Dm`#^*>u+j2-5DFyZi z<&J;H41ZZS#-oKi3iLjYW@2Rk*CDtGU|-C`c;J)@MPL)Un7LTS$Ay4;X&L|=OaNds zMv~z70h49zM9hbXE4Y5&b6UR49IUSZu(}vb z4NQQ~c;F z(=Y;P6i`pFNf06dWdp561Q<9Gj~0E$vG_uB0ccbl5E^h8_~cTYu|(k0L$VlX$Mg%P z|DPokWP{P$;LixajpPf#01|v2j4g#bBOrbR#-a*02{y> zKyU*rft27uHVNy3=0ZLM|7gHDd`N_XO9J~%@Bz4kP9+G4lELD;pqkKw?<&Myg(%nz z6!idoIA=<~o#XLTM_Ab^;3BenH zNE{_#QcVCCzz%~^A_*B-F$j|Kh%PXT2*MB`zo3;s8h~PquP*{%N^?c9TVfy~-Gvnb zV2lI4hPxzSmM{SjGSKb-BTyhCKtw(;!avU(B9J^W4i*q6Wq>uLLNUx7^fI*L3IKi( zLUC8Eg&^G!K?8gW=oVUl3nGHZ7=Q<<7^7r3+#S)y*g^urm5^;DP;h|=943w9V1i`; zWTY}aL^Zix@EI<`!Ic1+lo8MxOg&gYCKd7Vx-Ze8FLG89~K#;pgV0C6Y+VlAi)BLJIA1q8nf3+Mt;?di!Cy1RP{Jrv%I zi#ratsJQV&ojiQJUO&K6 zDfro4teXb92_fFb2T9%5?{(51m}3gDgvh$k{O zNCqAiAKC+i=%O`vUpySZKPYT26T&5c`hez9(A`2{T^Z=7E?SEK{(%%QDNDemC`cFq zp;rh(LdX{4M>N$Hw@3*bj2kEeP+Wk*F$U$3ObOgj@FgfQm)C_R1pJOuVhnr%P+?49 z11tdJSctfofRJ$^rYh*d`aqlvgX|XASsG%_d{PV%5oRGg0G0rG2$A@E>9Q}{h~xtN zL;=+b8CvmSdqJOoQV~&*y%2-T4LaeUsRq~rfDB`OWPnCNpTN4o&xY)g5YqlU8FTwa z7p(>GszHFY;I08k;{&Dw%Lh6U+(t2wTmXUAE_jUuB}9OW2Y0wYoB>c3NGK#hVeh~* z!eHfCS5g9z1tH`S;l4eBiRLI0gHjlq(V!GS9f<9A;k}WN`+#LbtPWNHCKIqB4pBr< zjNo_*;C2TYuL~~*To{6e#3S4(NC9^<_lUthL83&kPa1duhL7&s>bGXQr6F%mH=kp;{bF4Uv_~PD9AuaG4O#%0+fQ6A-G;}rzhYA2nRwE3j<{#5Moj; zh2jL!#n_Mx<;h4PKm{oQ+KeU{Ow5GIAs7wLG17Q-74L}F5Zk`xy zHWyq1Uk$sT4384Idc@3i!SMykiiYfRI0g z-vAu}JmWKdh?Geof-Z*Sf((U_`9HUYSS1JvxL)99Le`7nR4Hg-FengW5O4t~lix|x ze$i_(kqmGa+-;6S8i51Qi1GD7SwQR<0#4wJ@W?JC6jG&t4k17xf&?cl88}r8sd)%9 z2_WQ5p)#;VM1Ui~n*gf`-a4!S^13_-#e*3Yc47TNClCVWmIa}R05;gVB2dYbUU zKX_*zK_(~phj=n?BSqk8BsTL{5>x*Cf#)n+n>ydlcah^2#Abgv@q@pHWRe`-r^yO} z>me1FOhP#u%5mu_NlZQS0yGn8EK^ouUVUaFhY3Qt2UCvhEDuy%(^+<8u9Y0-2#lpCRJ6_*+x4Q1wqjwbOm0q^xwVP39gl|qwI zQ078eM;Buqw-&NVZw5Tk#4XD+ zmVebHmcVNv;pLgid4JVOS0G6BEO__hwZH1D*1_Yp*$7f~;hXiCGW_z*6Qt2J>A(>5 z>*qgS@b$`nE_^ed12f*&$K}}Woo@qVGh9u=+x=4I>|}Tdmu7SRm5Kk$4ZoSzH}e<> zH`mj08g7GUhXI$t+g4$_6R2qB0Vj5J!rvb1`=*EgVzY00V2Yi54F%)UcEn`717dK& z1W|4|ho~59BgzqL;0){QzWHf}!E@0FGP3WZPQHgSoL7AR?MrD994aM8E8Z}PxJg3U zB>YjL&P!C7A60nesyniIG>j~=*R-2Ntl8%yzlDSfUr9n!#l8chBQmE2crAVcTN>h}wl@=;3S1M83 zqEw}HMCq*3C8gU+&y?Q6-(%BOHdVGz<|yOJZpwkmqm^aKa^>mDbCnB~OO&@M?^8ah zd{Ozf@^j_)DrzdmDr^;w%3u{Ql@ThW%0!jvD)Uu}R5q$qsT@p zdsRnOZ`E+sNY!-J*{X%A<*Jpc$5k(>K2U8{Q&Tfl8=xjq^HLk77Na&*ZNA!Swe4z$ z)#}ymsl8QKSMRBAuRc^gP@PauQJ<~8LVb(+A@v6J2kL)nXlwMcW9r|zN7tK$4JLc$4w_fCsk*m&IX;sI#+dG>+0(E z*LBt%t(&5|P`5(&sP0YOKlP0D9Q3^PD7_55m3mcr4SFy1HTA9aUG&H4Pth;b->rW^ z|G9ytL4N}`1Hxds!AgS~gUbeQ4UG*^!$8A$!}*4r3{M+AHc~h0Z{%S_8D$%-H#%x` z&sf>GpRt=UX*|=o)cCmZ0~0lq0VduiF(&g&wwjzbdDYFR8^2pvw@KYrc017RwyCmd ze^YPMIMYR@yG)x*+q#=~cj-=d&+WdgdqekkJ$m(U?m_pM+hco=OFi0pvU_^;jP1F& zXLZjTW-4ZOX2E9ZW^2rjn?3Jk(ra)pve(>RyLw><>ADx1{&f-s^jx>;2ZekNI%( zB=Z&KN6nx2G412rC#Fw9pM!m#^fm4~tZ#JRg1(3PK4o`fyRhTf%h|`+uPl07cv+-c z6kD9P_+UA}a)f1;<#x*({j~bw{bc$DC=d`C#~Q0 zAJBhf|2h4u`#&DgV}SR7DFZeSXtvR{ak80cQ)1I-t7ePa#@ep7t+P|I6WB%Bt+cyf zuVgQ@kG5ZJUq4WFpm<=yz>I{N7RMnBut0@#SF7;Ml>N20tD$U)7h9JYmn|+Y;3h%2Yo+Tu zH%GT=Zin5K-G{r+bwBT6$11GH|bsB z{mMt=GtK9iua0ksZ;|g^KRZ9U-#&jOe;@xP{>=f_0SN)sfk>ct;F7=_K?8y&2JH`4 z3l0ii8T?R!N`8@?3^5KFAF?T=HPkh9QRwv%HX~9;)Q0JYMTAv^y&LI1GJoXl@Im3z z!q1K}8x=ij?`ZYWVWZ1Oza8T-X6cv*5rT->5lv$UjGZ+0^fcB>RhT#>a5f|X~WW1ru~^7mcDzE;iQB~=O)`t&Yk>diszKF zsVY;+skOgY{4(R0JJVdI6;EePmrk$E=$Db5@gQ?}=EfOXGh%1_mc`A=&w4X+BwRyJQZ29cVa|X{@o1>BwopU~ym%AdjeJ(Zk%slSA!g(L(lk?9kKo=}u z(6Laq@cbg-qBVJHc?o$_NT~}^j)q7R$s&}iY)eUQ$*KAvBzINW)wsq0#t`rY1t}5wY zQn+4iecJkmrD3Hf%kZ*_aE9X|RswP*xs*b9@y?4~! z`Wl~_+I@rfRql7#zu{MlUsoRJc3{y#t%KPII}S}b^!9Ml;paypkKC&rUwi#%_|e8= z!N<-Y_dR~-NE31zf6c8qsv+a>V7^SEwtGuf|>d{aV_!57#rBRh#GD zFt|~8v-i!CTei2h-xl6JaL4`5xw|2Euiqo@J-eTL|Ko$~hq@07AN74y@fdsj>l4o> zbx+4Ueef*l*@x$|TMS!Py|8()=cV(@bFac*J@{Szd&eL1U-x{y;SK*y?c1QYH(TRd zKfIg!XU{)3zQ^C6Y#Z73=tKHP^^eOx*?!vB?%Uqn5f868|K6VvHPX{FH8Abhty{l= zmc1+o4(JV^0hZmFkL8d21yGiWsi}ESbITq*EdLvSLR1;vC;l&gLNx5>pAh{Y{S%_c zE_rq1R2&`QYHFhyqAOwfso2^aHGb~F@=^(%DNU;~oEH-B?Takdn$TbFGhwA%8mj57 zDe{@&H6Xj-a3yDBUV%!%VGW69u9oT(i$R+2)=kMx^!9#N8t3c~+$;&1aHa5%%~?5G zKHfFBytg$sKmSFr^>BH#w{Lz?Xb8JBKR?q>GIx}fk-yF86+Y5X7oT9sAIoeDvSOB- zmS;;!^20Mrm0Y|mXJz^rm)TTM$7)k@TpN$d6GB_dD(xkiA+a>J#3!tLjPY~7-WzJV z9hsq}>R02O7ZlRt(h%FIf|AM^F}YO9nLJJFEwKUiVZt)Rsx(fzWTl|{!E z6W5}!LY3MXHnWCD+1eV~s<&g5#?<{GWu8mJYo`82$eZ=1iuio4v;uVS(7Olzg3vvU^6 z<16m4%Uzz1DRWk3Hx``I^qM8C6vxrzKu@lMXB3D#PhnhaLM;xvNnT#mTsxLI2ZMJm^`wv7XNj@U3L*uky2@P0u_sKhou zguRB)S!#YgQh0@zBu9(`>#HP?x_VKG?!YQde5);$?E*X0?G` zWR1u9An3v5T+hHwIr1@!wW$2r_p7bSqZ4N7o}RZ@GWN8OfqdHOszQ0;1E|Q`W8G~0 z!nwJ$cb+8aey(ftV!3VA)Fm%mn%BxrOZVqD8&q3Gtd8*ovC=8qq~%{Tq*!UK+=sS( zXX7KuTJgefT(NDg$G~|P)oo^RDmKwIw!zm*d)X8x-S<48FLkymIQ~@ikL4;HC349+LBOig%<|?EYfA&43HRsa?FF&Fzd?g4z3k)VZ3h- zD;Zw1R#Yi! zrexZ@j4N)7w$77eA9h`?VY{&L&L(14xmo^V(=#qPM{-STEi|esDk~am<>96_wHCIS zE`DyInSq*?*_l?M%J~l71D6<-^j;I>B^_PhS|pYDqh9?TZC-eLTc9yZQr)lHG#I#B zx%OMBrD|<4u%LF&w2GM$U!z8>Zjt7yWAT3W^}Q`vT2t>>4ScvRZ^6N!Gg{$MYeKb` zWaalXDtw?6;^RZt2=8YGxY|W@>u&O7;YQnSuQxfLv40k%n_X}s)jROwi+rDqj{5S( zr|jA>OFib7x!1VYxSV%w%%@RTJ@;sz#^b*QWlKwxye5>Y%_Vk~RT)+GySA#*+p8?b z`QgS|`vQsNjw3b0+xLw0{;LvKEl;2LGak;_1$%)5`>zhkTk4)YBhbcVhlPv5(<0+& zKibP=X6R9gmgi#6YbCCB9=5rXq#z4xyO1+klU%}nb6?6{8xuMksmQL{kR7^{TM|(U zi=u7I2FE7v^-Qo|tkmkUUUkU&DJr(RyIr_5IJ?!qvJ4+xnX*hAZf+WUdl@|ukM_Bq z?ml#B?3AHf?$t_sc6&bQ`T|~-6=M5%}6ameM-{( zppe=bn$@6Imo4pIqfv0AE`O!bx`cWRRMQqznZ(a#mD4ykd5_n%{$IfYa}7vZ7RAM<@ojq zsdmo{Ff8S%qP8l>t#;TZ1Zw7Z=oH=ec2+Z~wkq9U5NhpZVzSPpHhY7&FUp1XOVAO`ko$@zlnOkY*wb%PX8Ujh~`}9@K6nl zh-=voLAUK%6_(TVctfSN;Kt}aSHkXS-Zd)Sq3UIVg(P^`J~X)bW`^tZjU3r!AGV|2 zxxD%2C8=eLLo_5gdbt+zJTF_TLKQ0|d7g`}Yf!n8+`tE$V{?3?M}_4jJM#u>%*N-W z>wpTaeUV~qNroh}_Z#HR{nCOKY=efb+&*G%zU$2Sw#zJ5OMI}$!Of))&n#@L(8=`I z@^;nmyj{X~w{YKS&CzsC*Cc`T)I}?IGnF)QsGljw1Slk!Gp^u_$pB3YE1RL4CTy^j_?TB5mSo?y_98WFCj~oN zl}n85kP>e99^R8?*kAVE?NJ=)>+oRglRq;vy~dU(S>?Wh;dgh= zh&4bW4mAa=i%vKZhWvr|IiPml{&wtl28eFO!DeGl^YSiSh7K_YO ze|f}zY&b@Czt%FOWli&-zKiQ`o;OW?k}A`B)4lQXf-ukYfY?fw%fQrKzc2iF)6e!y z!=Z9 zJLM5pBYaI7%Cxvy1wOfU?lyUOlB~sdc&pcz4~+85fyp1_&h0*@ga&)sI*|SLA2_2DwvJfy?5OZs@CxbX9ob>ra@)O^H|DP>F&PIq z-7Iv~V`n`J%llis(=D7rmzczd&Ci}`a(JnYPou9!u!X6Kw#1d&_{hW3Lig0@t;;RE zaP|VD<{ba3>)W+FHjWVYk_*eNE9MbiHSPz4DpkvH`NI3ROA4vty%lGoG;OtuiVe0D zW?y&pann-WbpF_hJ>C5MD_p|NBWz4d%2~ty3>Z@~BgV)7fLG5E6SBjM<}Rtt)r?u^ z?d?-Vn@h&cEldtN8@lR^XsGp|+8I76ei8XSS(*=Y97D6fq~!Y$R%lYeb!&e|ANSZ` z!TO~=yuF6<4`5p|wF(;xnyzZ9t*_aHYFf{Ti3+jCyuzba732ilT1zFG1w~pGejhBH zJmL$Cy2b5lK4RxGe{qb;xUK0!#~^h^>y{G&sJnxlL_kQ&+ zj#YvPRSn$|qp(eTVwOakZ+g(aA(QS}={M$x?c{ zt4)yBI;#7$2;@V^P-n{-j+NKnX5C%edf??tjjfLZG@d_xp@n%TlCv!;Vzi&{&L2^< zJW{fCclloXfk%dE&gT5S%*11OT~>+ffe{rVjS?NH5i(nRKYL$!{3fAeh#K(RyEqev z+S>9fS7t3*eQ|wIb6H@m7Vso$zRofQ-N!4jHR>HrV#L{{ENLRO&W|!%5Ts1(ehG~9~0K}tWZI9 zvmfj#T|;*hj5}sCD5b>#Ft9Z3Er^O7>& zQ~McVm$;2$iJt$SQkwv?(nmID=#9H|3+9FJOH%_>eX>8K_!*yaZFDoZ60jO|F9 zS(cA0wH00(d(KYD)i!aZiQA=Vryp2V_juwyYsA9r_{X*@H1rcT?zYqO)^Rh^k!YG5 zNCxb5S&$tWlf7V5gHP5BtC;n+qnG4JG%U`zoEqIz>>{=;>m~UBZ)LGnskVqAz`L7o zi`q-h#~8-!x;@*~y+W-qEUTa&S7oao>Ask4m9@5`@w7v*D(a_IzSF{ns=znG{xoc` z(A=0ecbSiI<|-AF@`|kGf+6g(vjy67LEXm44QtNyTHO1Rd5&$aHNU_=H_qG7wVy;c zBvukUT1(n6-wXtDzQ7MP+^Xe{%ndT#XsC5gXc#u6X4Y@dl9nEh2z3K^;oB!^t!FEULo7<*U?;gK7 zXR-ElSjP zGRqX(#0Vt8{`nE&u?U{q7`#=3AmZ}s)#M|Fq31w#D>;;$NL&B~9@E+hC{ zz58=`K(OV5DFjydmprhH9l(xMFqy2O_`#+bsxcPq6=!tEJ+fM*iCS_V#$%Xf%8t6{ zVq&$V3{UN~<)H7zpBAK7?Ma8=(8VZihSYwRGTSIzuqXCw(#C30 z%o7YpVs;+0biWvg*U0pih)_*VioDEDTOgEVdzpj-t`gGTJN`S7r{x(EEBz1U6#+=p zSv-S|&L8*pVaGwxG)*=^i32OT^oa(xWz0O?{cpiU>B9!;dq){^x)RCr5*CVK`3$-> zx%i5k^5qcriiso7h(9-&0Ko7uXc*N6ju2>~@IgubgJmxRSF+OX<|J-C7;^^E zx%Z#@{w*7wC%Z;YgiyL_e+m2LPAEVA4DE=y{d|qqgxKI{P6U;Aqn}I?4G$5&Ivj}( zL@F=$dVP9dNBMtU$}LyTi)LVnPHZhN0l}yA_YaoFm;pg?2{)Pi&GhBF<=4m@$Z`HG$A83oHpaO->HPJh^Eb zM}$?t86;yOGe%J1G04uyCsqTLg$6iAqbxH{5!t8<3Sijmq_Qb23@yEPs2~+clH&68 z2vV8>PA2zb5wykMn9>>Pd|eUx_C$jVPoQ8a^%;z9IE93N_P?p2iomvB|JF!NNcg~1 zUD;sYQL@vNhun%nNGc6$bVc%vG}!gNQ=bZLW=Tq}=g_B8 zZUw)rsUW2iun3%RK_xR9$zd=8BrX)`2MJ(6DXOkx3gt-*Ek`k-6WN}#4C4YG?Sd`X zw&83wyiam=wDa)n+<`0Jh!T=+V+qHf5$0NLZDD4}MW^JAL zm0Xl{YYW@g=kM0Sl?^a25CQ8KwXV^1_C{?pQ~^bG(1xGsHR!{=(uyDZ5T~s8)V7d^=wKr=aKdv;p36%N9-7-^-oJ zH!lYr?H9~Id~+k=Ql(65HA9d{U`N^-LJoBMB!`Xct=J%_Abd8e-s_ac=(%SAshMoQ z)8f!WyL&xskM+|<@00Z`i|D{w_}!Fr$cEPaSBl)4 zQ429N(!Et!*I=G4I>tngwICwq6VYSU;6x{$2`Lt5DcH1Llc&ipUzW(=L?-S3&tz+j zC&Ud}sL$jm+xT?WJ|al>n?KGIIFvtIUWT)1a-E_!hC63<*-F(Han1XANJV5hS|Wx4 z`tEcSg1zRHywB!nR{`p* z#vBqJ?e$4gEzOxS%+zcX*O;t8e_#~!hdb^4R!^%NnEA;uU^xEz=d*uv$bi=G@-LqY zOwDmq$Mfq_}z`k(xT45u|cHpnu8P;(qdUEbl|4bvHrT-VwRsm&_wZ<^w9n< z`J?^~eM~Ww7!l`#$iJ)n17-@2YFsitN-t+lG0@gih$Rnax_EeMlU$k_1R-)EzGfAT6?DlFy+APPykrty&1AdQ0q;xj3yq((K;paxTyNCg>4kU3!Z z7laelD^*EJt*P4Jj2xJUQo2Y8FyVZ83mq@HE@&An7+Kb2Iw`Q2DOEQ>@J}yapL%wzY-4xTq8+^-r zHWwFr+IGKhIV_5NvdMF=#Z6uUwkdJ9NP=+7N_iM}TI%&V{I$4e?!)iV6(L`Lt>NjH z?8B_TpqMJ3^CtDX^*C)-e4k%e`1XCqj!@yU_UoTzJURHi$_n=6iKQZ;x{2~NHS+Sb zv0`C|eeIszC-T}*n$kEGKf{Z<=PvHuYXKwTrc2%PD;1HwnKqW&h%uStML2r%KMoCd zhx8oxXYVE>&Q&rO)5aDmrAKq}uZ%3QIIGYGf$lW~3 zN_qc}u_$3r8a6gIDzJ5F{f&zg(ewCM#kSi{>gxTDq?YOHlGdvjwC1?iOh$d#NMh($ z0m98Qk-TlvyKAm%tXtz(*?Y=)Ch=QaE@*TVmP;8W!l;{D_qPUqA6YYZ)I*vduGs&| zbZEa<*y1bs15)U~ls{7(D?e-Jsl{(~5{{`}>ohz=L_PeJgiL~cVqox|hLi;AY~Ax} z6&}N9aE*h3D4sW=Q}aQlLBLP(G}*|f^dNL4`hmR|2`p;Nl-3Pyg4WHG-Gdb@uB{Xe zqIt784K5Qr8BBO*naCzVMjmR`Ye|8THErxyIGxmNV7lWD1tmDIIp(d_9%;(z27>HG zcnpSsf)P1VeD78QkqWAYvf#84VhQ08vVgaoFT4&T{n zFp17Od1Jrnmw!pcuGyE3Uk^7gB^)n$>UHBkJ%YSP{p5cWe&6rV)2=S{?xSmiU3t=e zjB>7%Ghdq-Annt5pNx5FL1gTvaF)z1xR!Wd)kK9}wVq<1KJD@p3D_s6ht|P$tc!0c zDHl`>Y;}Th@eZbFN`-;uyBZ#?&aR5<0~`iCD2$Y%(C5m>1VpJNr!yRQ=@#cV8IH#j zyfx;_ibXgfNfkYETWUug1bm4uFMFX0=N2)*NULz#)1=SAwKh*44Ug`#-NXhlsbEvr z>U~cTc=|l+@nPXqw({_o9R=f$=x3WG9gRYHlv5k=5LTw&J>hE*0 zM}>o~#QSA6XRR-eULP)bZ_c|io3(EXp!ePO^L@^hhm3)zy+A?EVril`H&KQ|_c0oL;W2wjt>1E1p2r}`Q?EX} zUu_O0t!vs#KI_u^`K(jRn)tpjr)m9N`C+)z{*L>$?53%ifl=4qi0^Sv+Dcq6uo*^Q zh4rq-K8=J6Pi(}0eATyK5|@)v_oMCjx;e-8H=k1yf{r8>EYEia?J=L!%%$(Uez)l2 z+YLqdUD7aadO$Q2I8rwlJjEwQJ?DFd8uC(LM1*W>Q+bRy1*gK;>cg$=T^OuMYt$ju zQRdj40g7o^H*hq8h~jB?QAd=paK)@@wpWnB6$gz*RZ+|0|{|!l5zIPgaE7YjgX!UF9D}G&l<%0At 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 K>wj$G!T$q<%(l<~ literal 0 HcmV?d00001 diff --git a/i18n/en.json b/i18n/en.json index 616d0b7..1af39c4 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -32,7 +32,9 @@ "Exception: Customer does not exist or the password is wrong": "The email does not exist or the password is wrong", "Exception: Facebook signup was not successful. Please try another method": "Facebook signup was not successful. Please try another method", "Exception: Customer exists": "The email address has been registered already", + "Exception: Exception: Facebook login was not successful": "Facebook signin was not successful", "Exception: The email address has been registered already":"The email address has been registered already", + "Exception: Exception: The email address has been registered already":"The email address has been registered already", "Exception: Please type an email address": "Please type an email address", "Exception: Password too short": "Password too short (at least 9 characters)", "Exception: Google Sign In failed":"Google Sign In failed", @@ -83,6 +85,7 @@ "No": "No", "with": "with", "Do you save this exercise with these parameters?":"Do you save this exercise with these parameters?", + "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?":"Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?", "The number of the exercise": "The number of the exercise", "The number of the exercise done with": "The number of the exercise done with", "Please repeat with": "Please repeat with", @@ -226,6 +229,7 @@ "Almost!": "Almost done!", "You have only 1-2 exercise left to finish!": "You have only 1-2 exercise left to finish!", "exercise!": "exercise!", + "exercise": "exercise", "Chest": "Chest", "Back": "Back", "Thigh": "Thigh", @@ -409,6 +413,29 @@ "Compact Test":"Compact Test", "Custom Test":"Custom Test", "Set": "Set", - "Add this exercise to execute it paralell":"Add this exercise to execute it paralell" + "Add this exercise to execute it paralell":"Add this exercise to execute it paralell", + + "very_poor": "Very Poor", + "poor": "Poor", + "below_average": "Below Average", + "average": "Average", + "above_verage": "Above Average", + "good": "Good", + "excellent": "Excellent", + "elite": "Elite", + + "Training":"Training", + "Growth":"Growth", + "Compared with...":"Compared with...", + "your best":"your best", + "your last":"your last", + "best":"best", + "last":"last", + "volumen":"volumen", + "Get the Fastlane to your":"Get the Fastlane to your", + "Development":"Development", + "How can serve you this result?":"How can serve you this result?", + "Go":"Go", + "Result":"Result" } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index becdf9e..7e00b04 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -33,6 +33,7 @@ "Customer does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.", "The email does not exist or the password is wrong": "A felhasználó nem létezik vagy a jelszó rossz.", "Exception: Facebook login was not successful": "Facebook bejelentkezés sikertelen", + "Exception: Exception: Facebook login was not successful": "Facebook bejelentkezés sikertelen", "Exception: Facebook signup was not successful. Please try another method": "Facebook regisztráció sikertelen. Kérlek próbálj mással regisztrálni", "Exception: Please type an email address": "Kérlek írj be egy email címet", "Exception: Password too short": "A jelszó min. 9 karakterből álljon", @@ -40,6 +41,7 @@ "Exception: Facebook login cancelled":"Facebook bejelentkezés megszakítva ", "Exception: Facebook login failed":"Facebook bejelentkezés sikertelen", "Exception: The email address has been registered already":"Ezzel email címmel már regisztráltak", + "Exception: Exception: The email address has been registered already":"Ezzel email címmel már regisztráltak", "Exception: Google Sign In failed":"Google bejelentkezés sikertelen", "Exception: Apple Sign-In failed":"Apple bejelentkezés sikertelen", "Exception: Apple Sign-In cancelled":"Apple bejelentkezés megszakítva", @@ -94,6 +96,7 @@ "No": "Nem", "with": "", "Do you save this exercise with these parameters?":"Elmented a gyakorlatot?", + "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?":"Mindent beleadtál, ez a MAXIMUM, amit végre tudtál hajtani? Biztos vagy benne, hogy CSAK 12 ismétléssel mentsük el a gyakorlatot?", "repeat": "ismétlés", @@ -224,6 +227,7 @@ "Almost!": "Majdnem a végén vagy!", "You have only 1-2 exercise left to finish!": "Már csak 1-2 gyakorlat van a hátra!", "exercise!": "gyakorlattal!", + "exercise": "gyakorlat", "Chest": "mell", "Back": "hát", "Thigh": "comb", @@ -270,7 +274,7 @@ "Available Equipments":"Elérhető eszközök", "select your places by tapping":"kattints az edzéshelyszínre", "Available Training Places":"Elérhető edzéshelyszínek", - "Please take a relative bigger weight and repeat 12-20 times":"Válassz egy relatív nagyobb súlyt, amivel maximum 12-20 közötti ismétlésre vagy képes", + "Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!":"Válassz egy relatív nagyobb súlyt, amivel 12-20 közötti ismétlésre vagy képes, és adj bele mindent! MAXOLD KI!", "Please take a medium weight and repeat 20-30 times": "Válassz egy közepes súlyt, amivel maximum 20-30 közötti ismétlésre vagy képes", "Equipment Filter":"Eszköz szűrő", "Live-Server":"Live-Server", @@ -405,7 +409,28 @@ "Compact Test":"Kompakt teszt", "Custom Test":"Egyedi teszt", "Set": "Széria", - "Add this exercise to execute it paralell":"Adj hozzá egy gyakorlatot a párhuzamos végrehajtáshoz" + "Add this exercise to execute it paralell":"Adj hozzá egy gyakorlatot a párhuzamos végrehajtáshoz", + "very_poor": "Nagyon gyenge", + "poor": "Gyenge", + "below_average": "Átlag alatti", + "average": "Átlagos", + "above_verage": "Átlag feletti", + "good": "Jó", + "excellent": "Kitűnő", + "elite": "Elit kategória", + "Training":"Tréning", + "Growth":"Fejlődésem", + "Compared with...":"Összehasonlítva...", + "your best":"a legjobb", + "your last":"az utolsó", + "best":"a legjobb", + "last":"az utolsó", + "volumen":"volumennel", + "How can serve you this result?":"Hogyan segít ez az eredmény téged?", + "Get the Fastlane to your":"Kapd el a gyorsítósávot a optimális", + "Development":"Fejlődésedhez", + "Go":"Érdekel", + "Result":"Értékelés" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 42b988b..f0fd441 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -31,18 +31,18 @@ PODS: - Firebase/Messaging (7.3.0): - Firebase/CoreOnly - FirebaseMessaging (~> 7.3.0) - - firebase_analytics (8.0.0-dev.0): + - firebase_analytics (8.0.0-dev.2): - Firebase/Analytics (= 7.3.0) - firebase_core - Flutter - - firebase_auth (1.0.1): + - firebase_auth (1.0.3): - Firebase/Auth (= 7.3.0) - firebase_core - Flutter - - firebase_core (1.0.2): + - firebase_core (1.0.3): - Firebase/CoreOnly (= 7.3.0) - Flutter - - firebase_messaging (9.1.0): + - firebase_messaging (9.1.1): - Firebase/Messaging (= 7.3.0) - firebase_core - Flutter @@ -86,6 +86,10 @@ PODS: - GoogleUtilities/Environment (~> 7.0) - GoogleUtilities/Reachability (~> 7.0) - GoogleUtilities/UserDefaults (~> 7.0) + - flurry (0.0.4): + - Flurry-iOS-SDK/FlurrySDK + - Flutter + - Flurry-iOS-SDK/FlurrySDK (11.2.0) - Flutter (1.0.0) - flutter_facebook_auth (2.0.0): - FBSDKCoreKit (~> 9.1.0) @@ -181,6 +185,7 @@ DEPENDENCIES: - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) + - flurry (from `.symlinks/plugins/flurry/ios`) - Flutter (from `Flutter`) - flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) @@ -210,6 +215,7 @@ SPEC REPOS: - FirebaseInstallations - FirebaseInstanceID - FirebaseMessaging + - Flurry-iOS-SDK - FMDB - GoogleAppMeasurement - GoogleDataTransport @@ -236,6 +242,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_core/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" + flurry: + :path: ".symlinks/plugins/flurry/ios" Flutter: :path: Flutter flutter_facebook_auth: @@ -274,10 +282,10 @@ SPEC CHECKSUMS: FBSDKCoreKit: a00fe2efd780c195a5e09201bf51c56106245b40 FBSDKLoginKit: d98498c598ec09de657385a9349a1f21119b7f86 Firebase: 26223c695fe322633274198cb19dca8cb7e54416 - firebase_analytics: c9b8ddc8e864e45cd70761c5d972bd11c83574ab - firebase_auth: 9f6491ea8e44570323361ae713a2ae3175b3f21a - firebase_core: e6cbb0d1f7091edfcae31559e58224bfc1e455dc - firebase_messaging: 9c746d6c52bb05764e73bbe745d0d698e5afb695 + firebase_analytics: 4c032e04324c47ee6dd7a28bfff831e0ac0d09b6 + firebase_auth: c2c27e1081671b02f90981e70dad54722198491f + firebase_core: b5d81dfd4fb2d6f700e67de34d9a633ae325c4e9 + firebase_messaging: 7547c99f31466371f9cfcb733d5a1bf32a819872 FirebaseAnalytics: 2580c2d62535ae7b644143d48941fcc239ea897a FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 @@ -285,6 +293,8 @@ SPEC CHECKSUMS: FirebaseInstallations: 5e777e6640fa060405cc7632447b6c5ca5af4742 FirebaseInstanceID: 53140c03b9f6136f890d7901399f85a4c90ab2d0 FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232 + flurry: 15b01f664ab1367c62b50291541ea7f78ca85aad + Flurry-iOS-SDK: 6636d30c30f12010e7c7c71d84b443416a168efc Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c flutter_facebook_auth: 4b170c07b7fce791497093fcc3f134fb215f3f07 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 @@ -307,7 +317,7 @@ SPEC CHECKSUMS: PurchasesCoreSwift: 31c2a3d7394432abbe64d46f0933835de0b33033 PurchasesHybridCommon: 013c8072b73e752a206779747e88c068fbf999ec shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d - smartlook: bda0b1561935a02ef0fea5448258d5ac75027859 + smartlook: bbc5c73a85c752a31dabf100c8930838c646342e sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e wakelock: b0843b2479edbf6504d8d262c2959446f35373aa diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8ffd538..26f1ac7 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -405,7 +405,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.10; + MARKETING_VERSION = 1.1.11; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -531,7 +531,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -548,7 +548,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.10; + MARKETING_VERSION = 1.1.11; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -566,7 +566,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = SFJJBDCU6Z; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -583,7 +583,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.1.10; + MARKETING_VERSION = 1.1.11; PRODUCT_BUNDLE_IDENTIFIER = com.aitrainer.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/bloc/account/account_bloc.dart b/lib/bloc/account/account_bloc.dart index b4d8920..9b4f89b 100644 --- a/lib/bloc/account/account_bloc.dart +++ b/lib/bloc/account/account_bloc.dart @@ -74,7 +74,7 @@ class AccountBloc extends Bloc { yield AccountLoggedIn(); } else if (event is AccountLogout) { await Cache().logout(); - //customerRepository.customer = null; + customerRepository.customer = null; customerRepository.emptyTrainees(); loggedIn = false; yield AccountLoggedOut(); diff --git a/lib/bloc/customer_change/customer_change_bloc.dart b/lib/bloc/customer_change/customer_change_bloc.dart index f9b73d6..99f0963 100644 --- a/lib/bloc/customer_change/customer_change_bloc.dart +++ b/lib/bloc/customer_change/customer_change_bloc.dart @@ -9,6 +9,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; +import '../../model/fitness_state.dart'; + part 'customer_change_event.dart'; part 'customer_change_state.dart'; @@ -19,14 +21,40 @@ class CustomerChangeBloc extends Bloc double weight = 60; double height = 170; CustomerChangeBloc({required this.customerRepository}) : super(CustomerChangeInitial()) { - year = this.customerRepository.customer.birthYear; + if (this.customerRepository.customer == null) { + this.customerRepository.createNew(); + } + year = this.customerRepository.customer!.birthYear; if (year == null || year == 0) { year = 1990; } weight = this.customerRepository.getWeight() == 0 ? 60 : this.customerRepository.getWeight(); height = this.customerRepository.getHeight() == 0 ? 170 : this.customerRepository.getHeight(); + + print("fitnesslevel " + customerRepository.customer!.fitnessLevel.toString()); + if (customerRepository.customer!.fitnessLevel != null) { + if (!FitnessItem().elements.contains(customerRepository.customer!.fitnessLevel)) { + Sport.values.forEach((element) { + print(" .. ${element.toStr()}"); + if (element.equalsStringTo(customerRepository.customer!.fitnessLevel!)) { + selectedSport = element; + selectedFitnessItem = FitnessState.professional; + } + }); + if (selectedSport == null) { + selectedFitnessItem = customerRepository.customer!.fitnessLevel; + } + } else { + selectedFitnessItem = customerRepository.customer!.fitnessLevel; + } + } + + print("selected: $selectedFitnessItem sport: $selectedSport " + customerRepository.customer!.fitnessLevel.toString()); } + Sport? selectedSport; + String? selectedFitnessItem; + @override Stream mapEventToState( CustomerChangeEvent event, @@ -42,7 +70,8 @@ class CustomerChangeBloc extends Bloc visiblePassword = !visiblePassword; yield CustomerDataChanged(); } else if (event is CustomerFitnessChange) { - customerRepository.setFitnessLevel(event.fitness); + //customerRepository.setFitnessLevel(event.fitness); + selectedFitnessItem = event.fitness; yield CustomerDataChanged(); } else if (event is CustomerBirthYearChange) { yield CustomerChangeLoading(); @@ -77,9 +106,25 @@ class CustomerChangeBloc extends Bloc } else if (event is CustomerGenderChange) { customerRepository.setSex(event.gender == 0 ? "m" : "w"); yield CustomerDataChanged(); + } else if (event is CustomerSportChange) { + yield CustomerChangeLoading(); + selectedSport = event.sport; + //customerRepository.setFitnessLevel(event.sport.toStr()); + yield CustomerDataChanged(); } else if (event is CustomerSave) { yield CustomerSaving(); if (validation()) { + if (selectedFitnessItem != null) { + if (selectedFitnessItem == FitnessState.professional) { + if (selectedSport != null) { + customerRepository.setFitnessLevel(selectedSport!.toStr()); + } else { + customerRepository.setFitnessLevel(FitnessState.professional); + } + } else { + customerRepository.setFitnessLevel(selectedFitnessItem!); + } + } await customerRepository.saveCustomer(); Cache().initBadges(); yield CustomerSaveSuccess(); @@ -123,4 +168,7 @@ class CustomerChangeBloc extends Bloc } return null; } + + Sport? get getSelectedSport => this.selectedSport; + set setSelectedSport(Sport? selectedSport) => this.selectedSport = selectedSport; } diff --git a/lib/bloc/customer_change/customer_change_event.dart b/lib/bloc/customer_change/customer_change_event.dart index 0a0a2a2..da7da66 100644 --- a/lib/bloc/customer_change/customer_change_event.dart +++ b/lib/bloc/customer_change/customer_change_event.dart @@ -95,6 +95,14 @@ class CustomerPasswordChange extends CustomerChangeEvent { List get props => [password]; } +class CustomerSportChange extends CustomerChangeEvent { + final Sport sport; + const CustomerSportChange({required this.sport}); + + @override + List get props => [sport]; +} + class CustomerChangePasswordObscure extends CustomerChangeEvent { const CustomerChangePasswordObscure(); } diff --git a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart b/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart index cf48547..4e04269 100644 --- a/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart +++ b/lib/bloc/exercise_execute_plan_add/exercise_execute_plan_add_bloc.dart @@ -26,8 +26,8 @@ class ExerciseExecutePlanAddBloc extends Bloc with Logg exerciseRepository.start = DateTime.now(); if (Cache().userLoggedIn != null) { customerRepository.customer = Cache().userLoggedIn!; - weight = customerRepository.customer.getProperty("Weight"); - height = customerRepository.customer.getProperty("Height"); - birthYear = customerRepository.customer.birthYear!; - fitnessLevel = customerRepository.customer.fitnessLevel!; + weight = customerRepository.customer!.getProperty("Weight"); + height = customerRepository.customer!.getProperty("Height"); + birthYear = customerRepository.customer!.birthYear!; + fitnessLevel = customerRepository.customer!.fitnessLevel!; } if (exerciseType.unit == "second") { stopWatchTimer.rawTime.listen((value) => {timerValue = value, this.setQuantity((value / 1000).toDouble())}); @@ -183,22 +183,22 @@ class ExerciseNewBloc extends Bloc with Logg int year = int.parse(DateFormat(DateFormat.YEAR).format(date)); - if (customerRepository.customer.sex == "m") { + if (customerRepository.customer!.sex == "m") { //66.47 + ( 13.75 × tömeg kg-ban ) + ( 5.003 × magasság cm-ben ) − ( 6.755 × életkor évben kifejezve ) - bmr = 66.47 + (13.75 * weight) + (5.003 * height) - (6.755 * (year - customerRepository.customer.birthYear!)); + bmr = 66.47 + (13.75 * weight) + (5.003 * height) - (6.755 * (year - customerRepository.customer!.birthYear!)); } else { //BMR = 655.1 + ( 9.563 × ömeg kg-ban ) + ( 1.85 × magasság cm-ben) − ( 4.676 × életkor évben kifejezve ) - bmr = 655.1 + (9.563 * weight) + (1.85 * height) - (4.676 * (year - customerRepository.customer.birthYear!)); + bmr = 655.1 + (9.563 * weight) + (1.85 * height) - (4.676 * (year - customerRepository.customer!.birthYear!)); } bmrEnergy = bmr; - if (customerRepository.customer.fitnessLevel == FitnessState.beginner) { + if (customerRepository.customer!.fitnessLevel == FitnessState.beginner) { bmr *= 1.2; - } else if (customerRepository.customer.fitnessLevel == FitnessState.intermediate) { + } else if (customerRepository.customer!.fitnessLevel == FitnessState.intermediate) { bmr *= 1.375; - } else if (customerRepository.customer.fitnessLevel == FitnessState.advanced) { + } else if (customerRepository.customer!.fitnessLevel == FitnessState.advanced) { bmr *= 1.55; - } else if (customerRepository.customer.fitnessLevel == FitnessState.professional) { + } else if (customerRepository.customer!.fitnessLevel == FitnessState.professional) { bmr *= 1.9; } return bmr; diff --git a/lib/bloc/login/login_bloc.dart b/lib/bloc/login/login_bloc.dart index a40ea5d..4a93a77 100644 --- a/lib/bloc/login/login_bloc.dart +++ b/lib/bloc/login/login_bloc.dart @@ -75,12 +75,17 @@ class LoginBloc extends Bloc with Trans { if (!this.dataPolicyAllowed) { throw Exception("Please accept our data policy"); } - await userRepository.addUser(); - accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); - await saveCustomer(); - Track().track(TrackingEvent.registration, eventValue: "email"); - Cache().setLoginType(LoginType.email); - yield LoginSuccess(); + final String? validationError = validate(); + if (validationError != null) { + yield LoginError(message: validationError); + } else { + await userRepository.addUser(); + accountBloc.add(AccountLogInFinished(customer: Cache().userLoggedIn!)); + await saveCustomer(); + Track().track(TrackingEvent.registration, eventValue: "email"); + Cache().setLoginType(LoginType.email); + yield LoginSuccess(); + } } else if (event is RegistrationFB) { yield LoginLoading(); if (!this.dataPolicyAllowed) { @@ -131,20 +136,36 @@ class LoginBloc extends Bloc with Trans { Future saveCustomer() async { customerRepository.customer = Cache().userLoggedIn!; - customerRepository.customer.dataPolicyAllowed = 1; + customerRepository.customer!.dataPolicyAllowed = 1; await customerRepository.saveCustomer(); } - String? emailValidation(String email) { + String? emailValidation(String? email) { String? message = Common.emailValidation(email); return message; } - String? passwordValidation(String value) { + String? passwordValidation(String? value) { String? message = Common.passwordValidation(value); if (message != null) { message = t(message); } return message; } + + String? validate() { + String? error; + + error = emailValidation(userRepository.user.email); + if (error != null) { + return error; + } + + error = passwordValidation(userRepository.user.password); + if (error != null) { + return error; + } + + return error; + } } diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart index 8af724f..f82e7fb 100644 --- a/lib/bloc/menu/menu_bloc.dart +++ b/lib/bloc/menu/menu_bloc.dart @@ -98,7 +98,9 @@ class MenuBloc extends Bloc with Trans, Logging { //await menuTreeRepository.createTree(); //menuTreeRepository.getBranch(this.parent); //setMenuInfo(); - exerciseDeviceRepository.setDevices(Cache().getDevices()!); + if (Cache().getDevices() != null) { + exerciseDeviceRepository.setDevices(Cache().getDevices()!); + } yield MenuReady(); } else if (event is MenuRecreateTree) { yield MenuLoading(); diff --git a/lib/bloc/password_reset/password_reset_bloc.dart b/lib/bloc/password_reset/password_reset_bloc.dart index c0659ee..3af2af6 100644 --- a/lib/bloc/password_reset/password_reset_bloc.dart +++ b/lib/bloc/password_reset/password_reset_bloc.dart @@ -25,7 +25,7 @@ class PasswordResetBloc extends Bloc { } else if (event is PasswordResetSubmit) { yield PasswordResetLoading(); await userRepository.resetPassword(); - yield PasswordResetReady(); + yield PasswordResetFinished(); } } on Exception catch (e) { yield PasswordResetError(message: e.toString()); diff --git a/lib/bloc/password_reset/password_reset_state.dart b/lib/bloc/password_reset/password_reset_state.dart index f323b23..9d3ce24 100644 --- a/lib/bloc/password_reset/password_reset_state.dart +++ b/lib/bloc/password_reset/password_reset_state.dart @@ -26,3 +26,7 @@ class PasswordResetError extends PasswordResetState { class PasswordResetReady extends PasswordResetState { const PasswordResetReady(); } + +class PasswordResetFinished extends PasswordResetState { + const PasswordResetFinished(); +} diff --git a/lib/bloc/result/result_bloc.dart b/lib/bloc/result/result_bloc.dart index 5dd1837..4d46856 100644 --- a/lib/bloc/result/result_bloc.dart +++ b/lib/bloc/result/result_bloc.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:aitrainer_app/model/result.dart'; +import 'package:aitrainer_app/repository/evaluation_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/exercise_result_repository.dart'; import 'package:aitrainer_app/service/logging.dart'; @@ -17,6 +18,7 @@ part 'result_state.dart'; class ResultBloc extends Bloc with Logging, Trans { final ExerciseResultRepository resultRepository; final ExerciseRepository exerciseRepository; + final EvaluationRepository evaluationRepository = EvaluationRepository(); final BuildContext context; //List _healthDataList = []; DateTime? startTime; @@ -53,7 +55,7 @@ class ResultBloc extends Bloc with Logging, Trans { //await _fetchHealthData(); _matchExerciseData(); - await resultRepository.saveExerciseResults(); + //await resultRepository.saveExerciseResults(); yield ResultReady(); } } on Exception catch (ex) { diff --git a/lib/bloc/test_set_control/test_set_control_bloc.dart b/lib/bloc/test_set_control/test_set_control_bloc.dart index 3cc8a2b..c888bdf 100644 --- a/lib/bloc/test_set_control/test_set_control_bloc.dart +++ b/lib/bloc/test_set_control/test_set_control_bloc.dart @@ -31,7 +31,7 @@ class TestSetControlBloc extends Bloc } void initBloc() { - oneRepMax = executeBloc.calculate1RM(exercisePlanDetail.exercises!.last.unitQuantity, exercisePlanDetail.exercises!.last.quantity); + oneRepMax = executeBloc.calculate1RM(exercisePlanDetail.exercises!.last.unitQuantity!, exercisePlanDetail.exercises!.last.quantity!); initQuantity = 12; quantity = initQuantity; initUnitQuantity = oneRepMax * 0.75; diff --git a/lib/bloc/test_set_execute/test_set_execute_bloc.dart b/lib/bloc/test_set_execute/test_set_execute_bloc.dart index a6960e7..a1b68f9 100644 --- a/lib/bloc/test_set_execute/test_set_execute_bloc.dart +++ b/lib/bloc/test_set_execute/test_set_execute_bloc.dart @@ -136,7 +136,9 @@ class TestSetExecuteBloc extends Bloc if (!this.existsInPlanDetails(event.exerciseTypeId)) { ExercisePlanDetail exercisePlanDetail = ExercisePlanDetail(event.exerciseTypeId); - exercisePlanDetail.exercisePlanId = exercisePlan!.exercisePlanId!; + if (exercisePlan!.exercisePlanId != null) { + exercisePlanDetail.exercisePlanId = exercisePlan!.exercisePlanId!; + } final ExerciseType exerciseType = Cache().getExerciseTypeById(event.exerciseTypeId)!; exercisePlanDetail.serie = exerciseType.unitQuantityUnit == null ? 1 : 4; @@ -316,8 +318,9 @@ class TestSetExecuteBloc extends Bloc ExercisePlanDetail? getNext() { ExercisePlanDetail? nextExercisePlanDetail; + int minStep = 99; - if (this.exercisePlanDetails == null) { + if (this.exercisePlanDetails != null) { for (final detail in this.exercisePlanDetails!) { if (!detail.state.equalsTo(ExercisePlanDetailState.finished)) { if (detail.exercises == null) { @@ -369,9 +372,9 @@ class TestSetExecuteBloc extends Bloc if (!hasBegun() || exercisePlanDetail.exercises!.length < 2) { return text; } - final double unitQuantity = exercisePlanDetail.exercises!.last.unitQuantity; - final double quantity = exercisePlanDetail.exercises!.last.quantity; - double oneRepMax = this.calculate1RM(quantity, unitQuantity); + final double? unitQuantity = exercisePlanDetail.exercises!.last.unitQuantity!; + final double? quantity = exercisePlanDetail.exercises!.last.quantity!; + double oneRepMax = this.calculate1RM(quantity!, unitQuantity!); text = (oneRepMax * 0.75).round().toStringAsFixed(0) + " " + exercisePlanDetail.exerciseType!.unitQuantityUnit!; return text; } diff --git a/lib/library/dropdown_search.dart b/lib/library/dropdown_search.dart new file mode 100644 index 0000000..9c5f4bc --- /dev/null +++ b/lib/library/dropdown_search.dart @@ -0,0 +1,541 @@ +library dropdown_search; + +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import 'popup_menu.dart'; +import 'select_dialog.dart'; + +typedef Future> DropdownSearchOnFind(String text); +typedef String DropdownSearchItemAsString(T item); +typedef bool DropdownSearchFilterFn(T item, String filter); +typedef bool DropdownSearchCompareFn(T item, T selectedItem); +typedef Widget DropdownSearchBuilder(BuildContext context, T selectedItem, String itemAsString); +typedef Widget DropdownSearchPopupItemBuilder( + BuildContext context, + T item, + bool isSelected, +); +typedef bool DropdownSearchPopupItemEnabled(T item); +typedef Widget ErrorBuilder(BuildContext context, String? searchEntry, dynamic exception); +typedef Widget EmptyBuilder(BuildContext context, String? searchEntry); +typedef Widget LoadingBuilder(BuildContext context, String? searchEntry); +typedef Widget IconButtonBuilder(BuildContext context); +typedef Future BeforeChange(T prevItem, T nextItem); + +typedef Widget FavoriteItemsBuilder(BuildContext context, T item); + +///[items] are the original item from [items] or/and [onFind] +typedef List FavoriteItems(List items); + +enum Mode { DIALOG, BOTTOM_SHEET, MENU } + +class DropdownSearch extends StatefulWidget { + ///DropDownSearch label + final String? label; + + ///DropDownSearch hint + final String? hint; + + ///show/hide the search box + final bool showSearchBox; + + ///true if the filter on items is applied onlie (via API) + final bool isFilteredOnline; + + ///show/hide clear selected item + final bool showClearButton; + + ///offline items list + final List? items; + + ///selected item + final T? selectedItem; + + ///function that returns item from API + final DropdownSearchOnFind? onFind; + + ///called when a new item is selected + final ValueChanged? onChanged; + + ///to customize list of items UI + final DropdownSearchBuilder? dropdownBuilder; + + ///to customize selected item + final DropdownSearchPopupItemBuilder? popupItemBuilder; + + ///decoration for search box + final InputDecoration? searchBoxDecoration; + + ///the title for dialog/menu/bottomSheet + final Color? popupBackgroundColor; + + ///custom widget for the popup title + final Widget? popupTitle; + + ///customize the fields the be shown + final DropdownSearchItemAsString? itemAsString; + + /// custom filter function + final DropdownSearchFilterFn? filterFn; + + ///enable/disable dropdownSearch + final bool enabled; + + ///MENU / DIALOG/ BOTTOM_SHEET + final Mode mode; + + ///the max height for dialog/bottomSheet/Menu + final double? maxHeight; + + ///the max width for the dialog + final double? dialogMaxWidth; + + ///select the selected item in the menu/dialog/bottomSheet of items + final bool showSelectedItem; + + ///function that compares two object with the same type to detected if it's the selected item or not + final DropdownSearchCompareFn? compareFn; + + ///dropdownSearch input decoration + final InputDecoration? dropdownSearchDecoration; + + ///custom layout for empty results + final EmptyBuilder? emptyBuilder; + + ///custom layout for loading items + final LoadingBuilder? loadingBuilder; + + ///custom layout for error + final ErrorBuilder? errorBuilder; + + ///the search box will be focused if true + final bool autoFocusSearchBox; + + ///custom shape for the popup + final ShapeBorder? popupShape; + + final AutovalidateMode autoValidateMode; + + /// An optional method to call with the final value when the form is saved via + final FormFieldSetter? onSaved; + + /// An optional method that validates an input. Returns an error string to + /// display if the input is invalid, or null otherwise. + final FormFieldValidator? validator; + + ///custom dropdown clear button icon widget + final Widget? clearButton; + + ///custom clear button widget builder + final IconButtonBuilder? clearButtonBuilder; + + ///custom dropdown icon button widget + final Widget? dropDownButton; + + ///custom dropdown button widget builder + final IconButtonBuilder? dropdownButtonBuilder; + + ///whether to manage the clear and dropdown icons via InputDecoration suffixIcon + final bool showAsSuffixIcons; + + ///If true, the dropdownBuilder will continue the uses of material behavior + ///This will be useful if you want to handle a custom UI only if the item !=null + final bool dropdownBuilderSupportsNullItem; + + ///defines if an item of the popup is enabled or not, if the item is disabled, + ///it cannot be clicked + final DropdownSearchPopupItemEnabled? popupItemDisabled; + + ///set a custom color for the popup barrier + final Color? popupBarrierColor; + + ///text controller to set default search word for example + final TextEditingController? searchBoxController; + + ///called when popup is dismissed + final VoidCallback? onPopupDismissed; + + /// callback executed before applying value change + ///delay before searching, change it to Duration(milliseconds: 0) + ///if you do not use online search + final Duration? searchDelay; + + /// callback executed before applying value change + final BeforeChange? onBeforeChange; + + ///show or hide favorites items + final bool showFavoriteItems; + + ///to customize favorites chips + final FavoriteItemsBuilder? favoriteItemBuilder; + + ///favorites items list + final FavoriteItems? favoriteItems; + + ///favorite items alignment + final MainAxisAlignment? favoriteItemsAlignment; + + DropdownSearch({ + Key? key, + this.onSaved, + this.validator, + this.autoValidateMode = AutovalidateMode.disabled, + this.onChanged, + this.mode = Mode.DIALOG, + this.label, + this.hint, + this.isFilteredOnline = false, + this.popupTitle, + this.items, + this.selectedItem, + this.onFind, + this.dropdownBuilder, + this.popupItemBuilder, + this.showSearchBox = false, + this.showClearButton = false, + this.searchBoxDecoration, + this.popupBackgroundColor, + this.enabled = true, + this.maxHeight, + this.filterFn, + this.itemAsString, + this.showSelectedItem = false, + this.compareFn, + this.dropdownSearchDecoration, + this.emptyBuilder, + this.loadingBuilder, + this.errorBuilder, + this.autoFocusSearchBox = false, + this.dialogMaxWidth, + this.clearButton, + this.clearButtonBuilder, + this.dropDownButton, + this.dropdownButtonBuilder, + this.showAsSuffixIcons = false, + this.dropdownBuilderSupportsNullItem = false, + this.popupShape, + this.popupItemDisabled, + this.popupBarrierColor, + this.onPopupDismissed, + this.searchBoxController, + this.searchDelay, + this.onBeforeChange, + this.favoriteItemBuilder, + this.favoriteItems, + this.showFavoriteItems = false, + this.favoriteItemsAlignment = MainAxisAlignment.start, + }) : assert(!showSelectedItem || T == String || compareFn != null), + super(key: key); + + @override + DropdownSearchState createState() => DropdownSearchState(); +} + +class DropdownSearchState extends State> { + final ValueNotifier _selectedItemNotifier = ValueNotifier(null); + final ValueNotifier _isFocused = ValueNotifier(false); + + @override + void initState() { + super.initState(); + _selectedItemNotifier.value = widget.selectedItem; + } + + @override + void didUpdateWidget(DropdownSearch oldWidget) { + final oldSelectedItem = oldWidget.selectedItem; + final newSelectedItem = widget.selectedItem; + if (oldSelectedItem != newSelectedItem) { + _selectedItemNotifier.value = newSelectedItem; + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _selectedItemNotifier, + builder: (context, T? data, wt) { + return IgnorePointer( + ignoring: !widget.enabled, + child: GestureDetector( + onTap: () => _selectSearchMode(data), + child: _formField(data), + ), + ); + }, + ); + } + + Widget _defaultSelectItemWidget(T? data) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: widget.dropdownBuilder != null + ? widget.dropdownBuilder!( + context, + data, + _selectedItemAsString(data), + ) + : Text(_selectedItemAsString(data), style: Theme.of(context).textTheme.subtitle1), + ), + if (!widget.showAsSuffixIcons) _manageTrailingIcons(data), + ], + ); + } + + Widget _formField(T? value) { + return FormField( + enabled: widget.enabled, + onSaved: widget.onSaved, + validator: widget.validator, + autovalidateMode: widget.autoValidateMode, + initialValue: widget.selectedItem, + builder: (FormFieldState state) { + if (state.value != value) { + WidgetsBinding.instance!.addPostFrameCallback((_) { + state.didChange(value); + }); + } + return ValueListenableBuilder( + valueListenable: _isFocused, + builder: (context, bool isFocused, w) { + return InputDecorator( + isEmpty: value == null && (widget.dropdownBuilder == null || widget.dropdownBuilderSupportsNullItem), + isFocused: isFocused, + decoration: _manageDropdownDecoration(state, value), + child: _defaultSelectItemWidget(value), + ); + }); + }, + ); + } + + ///manage dropdownSearch field decoration + InputDecoration _manageDropdownDecoration(FormFieldState state, T? data) { + return (widget.dropdownSearchDecoration ?? + InputDecoration(contentPadding: EdgeInsets.fromLTRB(12, 12, 0, 0), border: OutlineInputBorder())) + .applyDefaults(Theme.of(state.context).inputDecorationTheme) + .copyWith( + enabled: widget.enabled, + labelText: widget.label, + hintText: widget.hint, + suffixIcon: widget.showAsSuffixIcons ? _manageTrailingIcons(data) : null, + errorText: state.errorText); + } + + ///function that return the String value of an object + String _selectedItemAsString(T? data) { + if (data == null) { + return ""; + } else if (widget.itemAsString != null) { + return widget.itemAsString!(data); + } else { + return data.toString(); + } + } + + ///function that manage Trailing icons(close, dropDown) + Widget _manageTrailingIcons(T? data) { + final clearButtonPressed = () => _handleOnChangeSelectedItem(null); + final dropdownButtonPressed = () => _selectSearchMode(data); + + return Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (data != null && widget.showClearButton == true) + widget.clearButtonBuilder != null + ? GestureDetector( + onTap: clearButtonPressed, + child: widget.clearButtonBuilder!(context), + ) + : IconButton( + icon: widget.clearButton ?? const Icon(Icons.clear, size: 24), + onPressed: clearButtonPressed, + ), + widget.dropdownButtonBuilder != null + ? GestureDetector( + onTap: dropdownButtonPressed, + child: widget.dropdownButtonBuilder!(context), + ) + : IconButton( + icon: widget.dropDownButton ?? const Icon(Icons.arrow_drop_down, size: 24), + onPressed: dropdownButtonPressed, + ), + ], + ); + } + + ///open dialog + Future _openSelectDialog(T? data) { + return showGeneralDialog( + barrierDismissible: true, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, + transitionDuration: const Duration(milliseconds: 400), + barrierColor: widget.popupBarrierColor ?? const Color(0x80000000), + context: context, + pageBuilder: (context, animation, secondaryAnimation) { + return AlertDialog( + contentPadding: EdgeInsets.all(0), + shape: widget.popupShape, + backgroundColor: widget.popupBackgroundColor, + content: _selectDialogInstance(data), + ); + }, + ); + } + + ///open BottomSheet (Dialog mode) + Future _openBottomSheet(T? data) { + return showModalBottomSheet( + barrierColor: widget.popupBarrierColor, + backgroundColor: widget.popupBackgroundColor, + isScrollControlled: true, + shape: widget.popupShape, + context: context, + builder: (ctx) { + return _selectDialogInstance(data, defaultHeight: 350); + }); + } + + ///openMenu + Future _openMenu(T? data) { + // Here we get the render object of our physical button, later to get its size & position + final RenderBox popupButtonObject = context.findRenderObject() as RenderBox; + // Get the render object of the overlay used in `Navigator` / `MaterialApp`, i.e. screen size reference + final RenderBox overlay = Overlay.of(context)!.context.findRenderObject() as RenderBox; + // Calculate the show-up area for the dropdown using button's size & position based on the `overlay` used as the coordinate space. + final RelativeRect position = RelativeRect.fromSize( + Rect.fromPoints( + popupButtonObject.localToGlobal(popupButtonObject.size.bottomLeft(Offset.zero), ancestor: overlay), + popupButtonObject.localToGlobal(popupButtonObject.size.bottomRight(Offset.zero), ancestor: overlay), + ), + Size(overlay.size.width, overlay.size.height), + ); + return customShowMenu( + barrierColor: widget.popupBarrierColor, + shape: widget.popupShape, + color: widget.popupBackgroundColor, + context: context, + position: position, + elevation: 8, + items: [ + CustomPopupMenuItem( + enabled: false, + child: Container( + width: popupButtonObject.size.width, + child: _selectDialogInstance(data, defaultHeight: 224), + ), + ), + ]); + } + + SelectDialog _selectDialogInstance(T? data, {double? defaultHeight}) { + return SelectDialog( + popupTitle: widget.popupTitle, + maxHeight: widget.maxHeight ?? defaultHeight, + isFilteredOnline: widget.isFilteredOnline, + itemAsString: widget.itemAsString, + filterFn: widget.filterFn, + items: widget.items, + onFind: widget.onFind, + showSearchBox: widget.showSearchBox, + itemBuilder: widget.popupItemBuilder, + selectedValue: data, + searchBoxDecoration: widget.searchBoxDecoration, + onChanged: _handleOnChangeSelectedItem, + showSelectedItem: widget.showSelectedItem, + compareFn: widget.compareFn, + emptyBuilder: widget.emptyBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + autoFocusSearchBox: widget.autoFocusSearchBox, + dialogMaxWidth: widget.dialogMaxWidth, + itemDisabled: widget.popupItemDisabled, + searchBoxController: widget.searchBoxController ?? TextEditingController(), + searchDelay: widget.searchDelay, + showFavoriteItems: widget.showFavoriteItems, + favoriteItems: widget.favoriteItems, + favoriteItemBuilder: widget.favoriteItemBuilder, + favoriteItemsAlignment: widget.favoriteItemsAlignment, + ); + } + + ///Function that manage focus listener + ///set true only if the widget already not focused to prevent unnecessary build + ///same thing for clear focus, + void _handleFocus(bool isFocused) { + if (isFocused && !_isFocused.value) { + FocusScope.of(context).unfocus(); + _isFocused.value = true; + } else if (!isFocused && _isFocused.value) _isFocused.value = false; + } + + ///handle on change value , if the validation is active , we validate the new selected item + void _handleOnChangeSelectedItem(T? selectedItem) { + final changeItem = () { + _selectedItemNotifier.value = selectedItem; + if (widget.onChanged != null) widget.onChanged!(selectedItem); + }; + + if (widget.onBeforeChange != null) { + widget.onBeforeChange!(_selectedItemNotifier.value, selectedItem).then((value) { + if (value == true) { + changeItem(); + } + }); + } else { + changeItem(); + } + + _handleFocus(false); + } + + ///Function that return then UI based on searchMode + ///[data] selected item to be passed to the UI + ///If we close the popup , or maybe we just selected + ///another widget we should clear the focus + Future _selectSearchMode(T? data) async { + _handleFocus(true); + T? selectedItem; + if (widget.mode == Mode.MENU) { + selectedItem = await _openMenu(data); + } else if (widget.mode == Mode.BOTTOM_SHEET) { + selectedItem = await _openBottomSheet(data); + } else { + selectedItem = await _openSelectDialog(data); + } + _handleFocus(false); + widget.onPopupDismissed?.call(); + + return selectedItem; + } + + ///Public Function that return then UI based on searchMode + ///[data] selected item to be passed to the UI + ///If we close the popup , or maybe we just selected + ///another widget we should clear the focus + ///THIS USED FOR OPEN DROPDOWN_SEARCH PROGRAMMATICALLY, + ///otherwise you can you [_selectSearchMode] + Future openDropDownSearch() => _selectSearchMode(_selectedItemNotifier.value); + + ///Change selected Value; this function is public USED to change the selected + ///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem] + void changeSelectedItem(T selectedItem) => _handleOnChangeSelectedItem(selectedItem); + + ///Change selected Value; this function is public USED to clear selected + ///value PROGRAMMATICALLY, Otherwise you can use [_handleOnChangeSelectedItem] + void clear() => _handleOnChangeSelectedItem(null); + + ///get selected value programmatically + T? get getSelectedItem => _selectedItemNotifier.value; + + ///check if the dropdownSearch is focused + bool get isFocused => _isFocused.value; +} diff --git a/lib/library/flurry.dart b/lib/library/flurry.dart deleted file mode 100644 index 4d9f17b..0000000 --- a/lib/library/flurry.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/services.dart'; - -class Flurry { - static const MethodChannel _channel = const MethodChannel('flurry'); - - static Future get platformVersion async { - final String version = await _channel.invokeMethod('getPlatformVersion'); - return version; - } - - static Future initialize({String androidKey = "", String iosKey = "", bool enableLog = true}) async { - Map args = {}; - args.putIfAbsent("api_key_android", () => androidKey); - args.putIfAbsent("api_key_ios", () => iosKey); - args.putIfAbsent("is_log_enabled", () => enableLog); - - await _channel.invokeMethod('initialize', args); - - return null; - } - - static Future logEvent(String message) async { - Map args = {}; - args.putIfAbsent("message", () => message); - - await _channel.invokeMethod('logEvent', args); - return null; - } - - static Future setUserId(String userId) async { - Map args = {}; - args.putIfAbsent("userId", () => userId); - - await _channel.invokeMethod('userId', args); - return null; - } -} diff --git a/lib/library/popup_menu.dart b/lib/library/popup_menu.dart new file mode 100644 index 0000000..f36c046 --- /dev/null +++ b/lib/library/popup_menu.dart @@ -0,0 +1,589 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +const Duration _kMenuDuration = Duration(milliseconds: 300); +const double _kMenuCloseIntervalEnd = 2.0 / 3.0; +const double _kMenuHorizontalPadding = 0.0; +const double _kMenuMinWidth = 2.0 * _kMenuWidthStep; +const double _kMenuVerticalPadding = 0.0; +const double _kMenuWidthStep = 1.0; +const double _kMenuScreenPadding = 0.0; + +// This widget only exists to enable _PopupMenuRoute to save the sizes of +// each menu item. The sizes are used by _PopupMenuRouteLayout to compute the +// y coordinate of the menu's origin so that the center of selected menu +// item lines up with the center of its PopupMenuButton. +class _MenuItem extends SingleChildRenderObjectWidget { + const _MenuItem({ + Key? key, + required this.onLayout, + Widget? child, + }) : super(key: key, child: child); + + final ValueChanged onLayout; + + @override + RenderObject createRenderObject(BuildContext context) { + return _RenderMenuItem(onLayout); + } + + @override + void updateRenderObject(BuildContext context, covariant _RenderMenuItem renderObject) { + renderObject.onLayout = onLayout; + } +} + +class _RenderMenuItem extends RenderShiftedBox { + _RenderMenuItem(this.onLayout, [RenderBox? child]) : super(child); + + ValueChanged onLayout; + + @override + void performLayout() { + if (child == null) { + size = Size.zero; + } else { + child!.layout(constraints, parentUsesSize: true); + size = constraints.constrain(child!.size); + } + final BoxParentData childParentData = child!.parentData as BoxParentData; + childParentData.offset = Offset.zero; + onLayout(size); + } +} + +/// An item in a material design popup menu. +/// +/// To show a popup menu, use the [customShowMenu] function. To create a button that +/// shows a popup menu, consider using [PopupMenuButton]. +/// +/// To show a checkmark next to a popup menu item, consider using +/// [CheckedPopupMenuItem]. +/// +/// Typically the [child] of a [CustomPopupMenuItem] is a [Text] widget. More +/// elaborate menus with icons can use a [ListTile]. By default, a +/// [CustomPopupMenuItem] is kMinInteractiveDimension pixels high. If you use a widget +/// with a different height, it must be specified in the [height] property. +/// +/// {@tool sample} +/// +/// Here, a [Text] widget is used with a popup menu item. The `WhyFarther` type +/// is an enum, not shown here. +/// +/// ```dart +/// const CustomPopupMenuItem( +/// value: WhyFarther.harder, +/// child: Text('Working a lot harder'), +/// ) +/// ``` +/// {@end-tool} +/// +/// See the example at [PopupMenuButton] for how this example could be used in a +/// complete menu, and see the example at [CheckedPopupMenuItem] for one way to +/// keep the text of [CustomPopupMenuItem]s that use [Text] widgets in their [child] +/// slot aligned with the text of [CheckedPopupMenuItem]s or of [CustomPopupMenuItem] +/// that use a [ListTile] in their [child] slot. +/// +/// See also: +/// +/// * [PopupMenuDivider], which can be used to divide items from each other. +/// * [CheckedPopupMenuItem], a variant of [CustomPopupMenuItem] with a checkmark. +/// * [customShowMenu], a method to dynamically show a popup menu at a given location. +/// * [PopupMenuButton], an [IconButton] that automatically shows a menu when +/// it is tapped. +class CustomPopupMenuItem extends PopupMenuEntry { + /// Creates an item for a popup menu. + /// + /// By default, the item is [enabled]. + /// + /// The `enabled` and `height` arguments must not be null. + const CustomPopupMenuItem({ + Key? key, + this.value, + this.enabled = true, + this.height = kMinInteractiveDimension, + this.textStyle, + required this.child, + }) : super(key: key); + + /// The value that will be returned by [customShowMenu] if this entry is selected. + final T? value; + + /// Whether the user is permitted to select this item. + /// + /// Defaults to true. If this is false, then the item will not react to + /// touches. + final bool enabled; + + /// The minimum height height of the menu item. + /// + /// Defaults to [kMinInteractiveDimension] pixels. + @override + final double height; + + /// The text style of the popup menu item. + /// + /// If this property is null, then [PopupMenuThemeData.textStyle] is used. + /// If [PopupMenuThemeData.textStyle] is also null, then [ThemeData.textTheme.subhead] is used. + final TextStyle? textStyle; + + /// The widget below this widget in the tree. + /// + /// Typically a single-line [ListTile] (for menus with icons) or a [Text]. An + /// appropriate [DefaultTextStyle] is put in scope for the child. In either + /// case, the text should be short enough that it won't wrap. + final Widget child; + + @override + bool represents(T? value) => value == this.value; + + @override + PopupMenuItemState> createState() => PopupMenuItemState>(); +} + +/// The [State] for [CustomPopupMenuItem] subclasses. +/// +/// By default this implements the basic styling and layout of Material Design +/// popup menu items. +/// +/// The [buildChild] method can be overridden to adjust exactly what gets placed +/// in the menu. By default it returns [CustomPopupMenuItem.child]. +/// +/// The [handleTap] method can be overridden to adjust exactly what happens when +/// the item is tapped. By default, it uses [Navigator.pop] to return the +/// [CustomPopupMenuItem.value] from the menu route. +/// +/// This class takes two type arguments. The second, `W`, is the exact type of +/// the [Widget] that is using this [State]. It must be a subclass of +/// [CustomPopupMenuItem]. The first, `T`, must match the type argument of that widget +/// class, and is the type of values returned from this menu. +class PopupMenuItemState> extends State { + /// The menu item contents. + /// + /// Used by the [build] method. + /// + /// By default, this returns [CustomPopupMenuItem.child]. Override this to put + /// something else in the menu entry. + @protected + Widget buildChild() => widget.child; + + /// The handler for when the user selects the menu item. + /// + /// Used by the [InkWell] inserted by the [build] method. + /// + /// By default, uses [Navigator.pop] to return the [CustomPopupMenuItem.value] from + /// the menu route. + @protected + void handleTap() { + Navigator.pop(context, widget.value); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); + TextStyle? style = widget.textStyle ?? popupMenuTheme.textStyle ?? theme.textTheme.subtitle1; + + if (!widget.enabled) style = style!.copyWith(color: theme.disabledColor); + + Widget item = AnimatedDefaultTextStyle( + style: style!, + duration: kThemeChangeDuration, + child: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: widget.height), + padding: const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding), + child: buildChild(), + ), + ); + + if (!widget.enabled) { + final bool isDark = theme.brightness == Brightness.dark; + item = IconTheme.merge( + data: IconThemeData(opacity: isDark ? 0.5 : 0.38), + child: item, + ); + } + + return InkWell( + onTap: widget.enabled ? handleTap : null, + canRequestFocus: widget.enabled, + child: item, + ); + } +} + +class _PopupMenu extends StatelessWidget { + const _PopupMenu({ + Key? key, + this.route, + this.semanticLabel, + }) : super(key: key); + + final _PopupMenuRoute? route; + final String? semanticLabel; + + @override + Widget build(BuildContext context) { + final double unit = 1.0 / (route!.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade. + final List children = []; + final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); + + for (int i = 0; i < route!.items.length; i += 1) { + final double start = (i + 1) * unit; + final double end = (start + 1.5 * unit).clamp(0.0, 1.0); + final CurvedAnimation opacity = CurvedAnimation( + parent: route!.animation!, + curve: Interval(start, end), + ); + Widget item = route!.items[i]; + if (route!.initialValue != null && route!.items[i].represents(route!.initialValue)) { + item = Container( + color: Theme.of(context).highlightColor, + child: item, + ); + } + children.add( + _MenuItem( + onLayout: (Size size) { + route!.itemSizes[i] = size; + }, + child: FadeTransition( + opacity: opacity, + child: item, + ), + ), + ); + } + + final CurveTween opacity = CurveTween(curve: const Interval(0.0, 1.0 / 3.0)); + final CurveTween width = CurveTween(curve: Interval(0.0, unit)); + final CurveTween height = CurveTween(curve: Interval(0.0, unit * route!.items.length)); + + final Widget child = ConstrainedBox( + constraints: const BoxConstraints(minWidth: _kMenuMinWidth), + child: IntrinsicWidth( + stepWidth: _kMenuWidthStep, + child: Semantics( + scopesRoute: true, + namesRoute: true, + explicitChildNodes: true, + label: semanticLabel, + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: _kMenuVerticalPadding), + child: ListBody(children: children), + ), + ), + ), + ); + + return AnimatedBuilder( + animation: route!.animation!, + builder: (BuildContext context, Widget? child) { + return Opacity( + opacity: opacity.evaluate(route!.animation!), + child: Material( + shape: route!.shape ?? popupMenuTheme.shape, + color: route!.color ?? popupMenuTheme.color, + type: MaterialType.card, + elevation: route!.elevation ?? popupMenuTheme.elevation ?? 8.0, + child: Align( + alignment: AlignmentDirectional.topEnd, + widthFactor: width.evaluate(route!.animation!), + heightFactor: height.evaluate(route!.animation!), + child: child, + ), + ), + ); + }, + child: child, + ); + } +} + +// Positioning of the menu on the screen. +class _PopupMenuRouteLayout extends SingleChildLayoutDelegate { + _PopupMenuRouteLayout(this.position, this.itemSizes, this.selectedItemIndex, this.textDirection); + + // Rectangle of underlying button, relative to the overlay's dimensions. + final RelativeRect? position; + + // The sizes of each item are computed when the menu is laid out, and before + // the route is laid out. + List itemSizes; + + // The index of the selected item, or null if PopupMenuButton.initialValue + // was not specified. + final int? selectedItemIndex; + + // Whether to prefer going to the left or to the right. + final TextDirection textDirection; + + // We put the child wherever position specifies, so long as it will fit within + // the specified parent size padded (inset) by 8. If necessary, we adjust the + // child's position so that it fits. + + @override + BoxConstraints getConstraintsForChild(BoxConstraints constraints) { + // The menu can be at most the size of the overlay minus 8.0 pixels in each + // direction. + return BoxConstraints.loose(constraints.biggest - const Offset(_kMenuScreenPadding * 2.0, _kMenuScreenPadding * 2.0) as Size); + } + + @override + Offset getPositionForChild(Size size, Size childSize) { + // size: The size of the overlay. + // childSize: The size of the menu, when fully open, as determined by + // getConstraintsForChild. + + // Find the ideal vertical position. + double y = position!.top; + if (selectedItemIndex != null) { + double selectedItemOffset = _kMenuVerticalPadding; + for (int index = 0; index < selectedItemIndex!; index += 1) selectedItemOffset += itemSizes[index]!.height; + selectedItemOffset += itemSizes[selectedItemIndex!]!.height / 2; + y = position!.top + (size.height - position!.top - position!.bottom) / 2.0 - selectedItemOffset; + } + + // Find the ideal horizontal position. + late double x; + if (position!.left > position!.right) { + // Menu button is closer to the right edge, so grow to the left, aligned to the right edge. + x = size.width - position!.right - childSize.width; + } else if (position!.left < position!.right) { + // Menu button is closer to the left edge, so grow to the right, aligned to the left edge. + x = position!.left; + } else { + // Menu button is equidistant from both edges, so grow in reading direction. + switch (textDirection) { + case TextDirection.rtl: + x = size.width - position!.right - childSize.width; + break; + case TextDirection.ltr: + x = position!.left; + break; + } + } + + // Avoid going outside an area defined as the rectangle 8.0 pixels from the + // edge of the screen in every direction. + if (x < _kMenuScreenPadding) + x = _kMenuScreenPadding; + else if (x + childSize.width > size.width - _kMenuScreenPadding) x = size.width - childSize.width - _kMenuScreenPadding; + if (y < _kMenuScreenPadding) + y = _kMenuScreenPadding; + else if (y + childSize.height > size.height - _kMenuScreenPadding) y = size.height - childSize.height - _kMenuScreenPadding; + return Offset(x, y); + } + + @override + bool shouldRelayout(_PopupMenuRouteLayout oldDelegate) { + // If called when the old and new itemSizes have been initialized then + // we expect them to have the same length because there's no practical + // way to change length of the items list once the menu has been shown. + assert(itemSizes.length == oldDelegate.itemSizes.length); + + return position != oldDelegate.position || + selectedItemIndex != oldDelegate.selectedItemIndex || + textDirection != oldDelegate.textDirection || + !listEquals(itemSizes, oldDelegate.itemSizes); + } +} + +class _PopupMenuRoute extends PopupRoute { + _PopupMenuRoute({ + this.position, + required this.items, + this.initialValue, + this.elevation, + this.theme, + this.popupMenuTheme, + this.barrierLabel, + this.semanticLabel, + this.shape, + this.color, + this.showMenuContext, + this.captureInheritedThemes, + this.barrierColor, + }) : itemSizes = List.filled(items.length, null, growable: false); + + final RelativeRect? position; + final List> items; + final List itemSizes; + final dynamic initialValue; + final double? elevation; + final ThemeData? theme; + final String? semanticLabel; + final ShapeBorder? shape; + final Color? color; + final PopupMenuThemeData? popupMenuTheme; + final BuildContext? showMenuContext; + final bool? captureInheritedThemes; + final Color? barrierColor; + + @override + Animation createAnimation() { + return CurvedAnimation( + parent: super.createAnimation(), + curve: Curves.linear, + reverseCurve: const Interval(0.0, _kMenuCloseIntervalEnd), + ); + } + + @override + Duration get transitionDuration => _kMenuDuration; + + @override + bool get barrierDismissible => true; + + @override + final String? barrierLabel; + + @override + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { + int? selectedItemIndex; + if (initialValue != null) { + for (int index = 0; selectedItemIndex == null && index < items.length; index += 1) { + if (items[index].represents(initialValue)) selectedItemIndex = index; + } + } + + Widget menu = _PopupMenu(route: this, semanticLabel: semanticLabel); + if (captureInheritedThemes!) { + menu = InheritedTheme.captureAll(showMenuContext!, menu); + } else { + // For the sake of backwards compatibility. An (unlikely) app that relied + // on having menus only inherit from the material Theme could set + // captureInheritedThemes to false and get the original behavior. + if (theme != null) menu = Theme(data: theme!, child: menu); + } + + return MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + removeLeft: true, + removeRight: true, + child: Builder( + builder: (BuildContext context) { + return CustomSingleChildLayout( + delegate: _PopupMenuRouteLayout( + position, + itemSizes, + selectedItemIndex, + Directionality.of(context), + ), + child: menu, + ); + }, + ), + ); + } +} + +/// Show a popup menu that contains the `items` at `position`. +/// +/// `items` should be non-null and not empty. +/// +/// If `initialValue` is specified then the first item with a matching value +/// will be highlighted and the value of `position` gives the rectangle whose +/// vertical center will be aligned with the vertical center of the highlighted +/// item (when possible). +/// +/// If `initialValue` is not specified then the top of the menu will be aligned +/// with the top of the `position` rectangle. +/// +/// In both cases, the menu position will be adjusted if necessary to fit on the +/// screen. +/// +/// Horizontally, the menu is positioned so that it grows in the direction that +/// has the most room. For example, if the `position` describes a rectangle on +/// the left edge of the screen, then the left edge of the menu is aligned with +/// the left edge of the `position`, and the menu grows to the right. If both +/// edges of the `position` are equidistant from the opposite edge of the +/// screen, then the ambient [Directionality] is used as a tie-breaker, +/// preferring to grow in the reading direction. +/// +/// The positioning of the `initialValue` at the `position` is implemented by +/// iterating over the `items` to find the first whose +/// [CustomPopupMenuEntry.represents] method returns true for `initialValue`, and then +/// summing the values of [CustomPopupMenuEntry.height] for all the preceding widgets +/// in the list. +/// +/// The `elevation` argument specifies the z-coordinate at which to place the +/// menu. The elevation defaults to 8, the appropriate elevation for popup +/// menus. +/// +/// The `context` argument is used to look up the [Navigator] and [Theme] for +/// the menu. It is only used when the method is called. Its corresponding +/// widget can be safely removed from the tree before the popup menu is closed. +/// +/// The `useRootNavigator` argument is used to determine whether to push the +/// menu to the [Navigator] furthest from or nearest to the given `context`. It +/// is `false` by default. +/// +/// The `semanticLabel` argument is used by accessibility frameworks to +/// announce screen transitions when the menu is opened and closed. If this +/// label is not provided, it will default to +/// [MaterialLocalizations.popupMenuLabel]. +/// +/// See also: +/// +/// * [CustomPopupMenuItem], a popup menu entry for a single value. +/// * [PopupMenuDivider], a popup menu entry that is just a horizontal line. +/// * [CheckedPopupMenuItem], a popup menu item with a checkmark. +/// * [PopupMenuButton], which provides an [IconButton] that shows a menu by +/// calling this method automatically. +/// * [SemanticsConfiguration.namesRoute], for a description of edge triggered +/// semantics. +Future customShowMenu({ + required BuildContext context, + required RelativeRect position, + required List> items, + T? initialValue, + double? elevation, + String? semanticLabel, + Color? barrierColor, + ShapeBorder? shape, + Color? color, + bool captureInheritedThemes = true, + bool useRootNavigator = false, +}) { + assert(items.isNotEmpty); + assert(debugCheckHasMaterialLocalizations(context)); + + String? label = semanticLabel; + switch (Theme.of(context).platform) { + case TargetPlatform.iOS: + case TargetPlatform.macOS: + label = semanticLabel; + break; + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + label = semanticLabel ?? MaterialLocalizations.of(context).popupMenuLabel; + } + + return Navigator.of(context, rootNavigator: useRootNavigator).push( + _PopupMenuRoute( + position: position, + items: items, + initialValue: initialValue, + elevation: elevation, + semanticLabel: label, + theme: Theme.of(context), + popupMenuTheme: PopupMenuTheme.of(context), + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, + barrierColor: barrierColor, + shape: shape, + color: color, + showMenuContext: context, + captureInheritedThemes: captureInheritedThemes, + ), + ); +} diff --git a/lib/library/select_dialog.dart b/lib/library/select_dialog.dart new file mode 100644 index 0000000..45ccf36 --- /dev/null +++ b/lib/library/select_dialog.dart @@ -0,0 +1,489 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'dropdown_search.dart'; + +class SelectDialog extends StatefulWidget { + final T? selectedValue; + final List? items; + final bool showSearchBox; + final bool isFilteredOnline; + final ValueChanged? onChanged; + final DropdownSearchOnFind? onFind; + final DropdownSearchPopupItemBuilder? itemBuilder; + final InputDecoration? searchBoxDecoration; + final DropdownSearchItemAsString? itemAsString; + final DropdownSearchFilterFn? filterFn; + final String? hintText; + final double? maxHeight; + final double? dialogMaxWidth; + final Widget? popupTitle; + final bool showSelectedItem; + final DropdownSearchCompareFn? compareFn; + final DropdownSearchPopupItemEnabled? itemDisabled; + + ///custom layout for empty results + final EmptyBuilder? emptyBuilder; + + ///custom layout for loading items + final LoadingBuilder? loadingBuilder; + + ///custom layout for error + final ErrorBuilder? errorBuilder; + + ///the search box will be focused if true + final bool autoFocusSearchBox; + + ///text controller to set default search word for example + final TextEditingController? searchBoxController; + + ///delay before searching + final Duration? searchDelay; + + ///show or hide favorites items + final bool showFavoriteItems; + + ///build favorites chips + final FavoriteItemsBuilder? favoriteItemBuilder; + + ///favorite items alignment + final MainAxisAlignment? favoriteItemsAlignment; + + ///favorites item + final FavoriteItems? favoriteItems; + + const SelectDialog({ + Key? key, + this.popupTitle, + this.items, + this.maxHeight, + this.showSearchBox = false, + this.isFilteredOnline = false, + this.onChanged, + this.selectedValue, + this.onFind, + this.itemBuilder, + this.searchBoxDecoration, + this.hintText, + this.itemAsString, + this.filterFn, + this.showSelectedItem = false, + this.compareFn, + this.emptyBuilder, + this.loadingBuilder, + this.errorBuilder, + this.autoFocusSearchBox = false, + this.dialogMaxWidth, + this.itemDisabled, + this.searchBoxController, + this.searchDelay, + this.favoriteItemBuilder, + this.favoriteItems, + this.showFavoriteItems = false, + this.favoriteItemsAlignment = MainAxisAlignment.start, + }) : super(key: key); + + @override + _SelectDialogState createState() => _SelectDialogState(); +} + +class _SelectDialogState extends State> { + final FocusNode focusNode = new FocusNode(); + final StreamController> _itemsStream = StreamController>.broadcast(); + final ValueNotifier _loadingNotifier = ValueNotifier(false); + final List _items = []; + late Debouncer _debouncer; + + @override + void initState() { + super.initState(); + _debouncer = Debouncer(delay: widget.searchDelay); + + Future.delayed( + Duration.zero, + () => manageItemsByFilter(widget.searchBoxController?.text ?? '', isFistLoad: true), + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (widget.autoFocusSearchBox) FocusScope.of(context).requestFocus(focusNode); + } + + @override + void dispose() { + _itemsStream.close(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Size deviceSize = MediaQuery.of(context).size; + bool isTablet = deviceSize.width > deviceSize.height; + double maxHeight = deviceSize.height * (isTablet ? .8 : .6); + double maxWidth = deviceSize.width * (isTablet ? .7 : .9); + + return Container( + width: widget.dialogMaxWidth ?? maxWidth, + constraints: BoxConstraints(maxHeight: widget.maxHeight ?? maxHeight), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + _searchField(), + if (widget.showFavoriteItems == true) _favoriteItemsWidget(), + Expanded( + child: Stack( + children: [ + StreamBuilder>( + stream: _itemsStream.stream, + builder: (context, snapshot) { + if (snapshot.hasError) { + return _errorWidget(snapshot.error); + } else if (!snapshot.hasData) { + return _loadingWidget(); + } else if (snapshot.data!.isEmpty) { + if (widget.emptyBuilder != null) + return widget.emptyBuilder!(context, widget.searchBoxController?.text); + else + return const Center( + child: const Text("No data found"), + ); + } + return ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.symmetric(vertical: 0), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var item = snapshot.data![index]; + return _itemWidget(item); + }, + ); + }, + ), + _loadingWidget() + ], + ), + ), + ], + ), + ); + } + + void _showErrorDialog(dynamic error) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Error while getting online items"), + content: _errorWidget(error), + actions: [ + TextButton( + child: new Text("OK"), + onPressed: () { + Navigator.of(context).pop(false); + }, + ) + ], + ); + }, + ); + } + + Widget _errorWidget(dynamic error) { + if (widget.errorBuilder != null) + return widget.errorBuilder!(context, widget.searchBoxController?.text, error); + else + return Padding( + padding: EdgeInsets.all(8), + child: Text( + error?.toString() ?? 'Error', + ), + ); + } + + Widget _loadingWidget() { + return ValueListenableBuilder( + valueListenable: _loadingNotifier, + builder: (context, bool isLoading, wid) { + if (isLoading) { + if (widget.loadingBuilder != null) + return widget.loadingBuilder!(context, widget.searchBoxController?.text); + else + return Padding( + padding: const EdgeInsets.all(24.0), + child: const Center( + child: const CircularProgressIndicator(), + ), + ); + } + return Container(); + }); + } + + void _onTextChanged(String filter) async { + manageItemsByFilter(filter); + } + + ///Function that filter item (online and offline) base on user filter + ///[filter] is the filter keyword + ///[isFirstLoad] true if it's the first time we load data from online, false other wises + void manageItemsByFilter(String filter, {bool isFistLoad = false}) async { + _loadingNotifier.value = true; + + String encoded(String item) { + String encodedItem = ""; + for (int i = 0; i < item.length; i++) { + var char = item[i]; + switch (char) { + case 'Á': + case 'á': + case 'ą': + case 'ä': + char = 'a'; + break; + case 'é': + case 'É': + char = 'e'; + break; + case 'ú': + case 'ű': + case 'ü': + case 'Ú': + case 'Ű': + case 'Ü': + char = 'u'; + break; + case 'ö': + case 'ő': + case 'ó': + case 'Ö': + case 'Ő': + case 'Ó': + char = 'o'; + break; + case 'í': + case 'Í': + char = 'i'; + break; + } + encodedItem += char; + } + return encodedItem; + } + + List applyFilter(String filter) { + return _items.where((i) { + if (widget.filterFn != null) + return (widget.filterFn!(i, filter)); + else if (i.toString().toLowerCase().contains(filter.toLowerCase()) || + encoded(i.toString()).toLowerCase().contains(encoded(filter.toLowerCase()))) { + return true; + } else if (widget.itemAsString != null) { + bool found = (widget.itemAsString!(i)).toLowerCase().contains(filter.toLowerCase()); + if (!found) { + found = (encoded(widget.itemAsString!(i))).toLowerCase().contains(encoded(filter.toLowerCase())); + } + return found; + } + return false; + }).toList(); + } + + //load offline data for the first time + if (isFistLoad && widget.items != null) _items.addAll(widget.items!); + + //manage offline items + if (widget.onFind != null && (widget.isFilteredOnline || isFistLoad)) { + try { + final List onlineItems = []; + onlineItems.addAll(await widget.onFind!(filter)); + + //Remove all old data + _items.clear(); + //add offline items + if (widget.items != null) { + _items.addAll(widget.items!); + //if filter online we filter only local list based on entered keyword (filter) + if (widget.isFilteredOnline == true) { + var filteredLocalList = applyFilter(filter); + _items.clear(); + _items.addAll(filteredLocalList); + } + } + //add new online items to list + _items.addAll(onlineItems); + + //don't filter data , they are already filtred online and local data are already filtered + if (widget.isFilteredOnline == true) + _addDataToStream(_items); + else + _addDataToStream(applyFilter(filter)); + } catch (e) { + _addErrorToStream(e); + //if offline items count > 0 , the error will be not visible for the user + //As solution we show it in dialog + if (widget.items != null && widget.items!.isNotEmpty) { + _showErrorDialog(e); + _addDataToStream(applyFilter(filter)); + } + } + } else { + _addDataToStream(applyFilter(filter)); + } + + _loadingNotifier.value = false; + } + + void _addDataToStream(List data) { + if (_itemsStream.isClosed) return; + _itemsStream.add(data); + } + + void _addErrorToStream(Object error, [StackTrace? stackTrace]) { + if (_itemsStream.isClosed) return; + _itemsStream.addError(error, stackTrace); + } + + Widget _itemWidget(T? item) { + if (widget.itemBuilder != null) + return InkWell( + child: widget.itemBuilder!( + context, + item, + _manageSelectedItemVisibility(item), + ), + onTap: widget.itemDisabled != null && (widget.itemDisabled!(item)) == true ? null : () => _handleSelectItem(item), + ); + else + return ListTile( + title: Text(_selectedItemAsString(item)), + selected: _manageSelectedItemVisibility(item), + onTap: widget.itemDisabled != null && (widget.itemDisabled!(item)) == true ? null : () => _handleSelectItem(item), + ); + } + + /// selected item will be highlighted only when [widget.showSelectedItem] is true, + /// if our object is String [widget.compareFn] is not required , other wises it's required + bool _manageSelectedItemVisibility(T? item) { + if (!widget.showSelectedItem) return false; + + if (item is String?) { + return item == widget.selectedValue; + } else { + return widget.compareFn!(item, widget.selectedValue); + } + } + + Widget _searchField() { + return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ + widget.popupTitle ?? const SizedBox.shrink(), + if (widget.showSearchBox) + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: widget.searchBoxController, + focusNode: focusNode, + onChanged: (f) => _debouncer(() { + _onTextChanged(f); + }), + decoration: widget.searchBoxDecoration ?? + InputDecoration( + hintText: widget.hintText, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + ), + ), + ) + ]); + } + + Widget _favoriteItemsWidget() { + return StreamBuilder>( + stream: _itemsStream.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + return _buildFavoriteItems(widget.favoriteItems!(snapshot.data!)); + } else { + return Container(); + } + }); + } + + Widget _buildFavoriteItems(List? favoriteItems) { + if (favoriteItems != null) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 8), + child: LayoutBuilder(builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: constraints.maxWidth), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: widget.favoriteItemsAlignment ?? MainAxisAlignment.start, + children: favoriteItems + .map( + (f) => GestureDetector( + onTap: () => _handleSelectItem(f), + child: Container( + margin: EdgeInsets.only(right: 4), + child: widget.favoriteItemBuilder != null + ? widget.favoriteItemBuilder!(context, f) + : _favoriteItemDefaultWidget(f), + ), + ), + ) + .toList()), + ), + ); + }), + ); + } else { + return Container(); + } + } + + void _handleSelectItem(T? selectedItem) { + Navigator.pop(context, selectedItem); + if (widget.onChanged != null) widget.onChanged!(selectedItem); + } + + Widget _favoriteItemDefaultWidget(T? item) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Theme.of(context).primaryColorLight), + child: Text( + _selectedItemAsString(item), + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.subtitle1, + ), + ); + } + + ///function that return the String value of an object + String _selectedItemAsString(T? data) { + if (data == null) { + return ""; + } else if (widget.itemAsString != null) { + return widget.itemAsString!(data); + } else { + return data.toString(); + } + } +} + +class Debouncer { + final Duration? delay; + Timer? _timer; + + Debouncer({this.delay}); + + call(Function action) { + _timer?.cancel(); + _timer = Timer(delay ?? const Duration(milliseconds: 500), action as void Function()); + } +} diff --git a/lib/main.dart b/lib/main.dart index 46a13b1..d68b962 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,7 @@ import 'package:aitrainer_app/view/customer_fitness_page.dart'; import 'package:aitrainer_app/view/customer_goal_page.dart'; import 'package:aitrainer_app/view/customer_modify_page.dart'; import 'package:aitrainer_app/view/customer_welcome_page.dart'; -import 'package:aitrainer_app/view/evaluation.dart'; +import 'package:aitrainer_app/view/evaluation_page.dart'; import 'package:aitrainer_app/view/exercise_control_page.dart'; import 'package:aitrainer_app/view/exercise_execute_page.dart'; import 'package:aitrainer_app/view/exercise_execute_plan_add_page.dart'; @@ -37,7 +37,7 @@ import 'package:aitrainer_app/view/test_set_new.dart'; import 'package:aitrainer_app/widgets/home.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; -import 'package:aitrainer_app/library/flurry.dart'; +import 'package:flurry/flurry.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -118,6 +118,11 @@ Future main() async { // - https://api.dartlang.org/stable/1.24.2/dart-async/Zone-class.html // - https://www.dartlang.org/articles/libraries/zones runZonedGuarded>(() async { + await Sentry.init( + (options) { + options.dsn = dsn; + }, + ); final WorkoutTreeRepository menuTreeRepository = WorkoutTreeRepository(); WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/model/cache.dart b/lib/model/cache.dart index a9a90fa..1d9f775 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -1,6 +1,7 @@ import 'dart:collection'; import 'dart:convert'; import 'package:aitrainer_app/model/customer.dart'; +import 'package:aitrainer_app/model/evaluation.dart'; import 'package:aitrainer_app/model/exercise_plan.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_plan_template.dart'; @@ -21,7 +22,7 @@ import 'package:aitrainer_app/main.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/env.dart'; import 'package:aitrainer_app/util/track.dart'; -import 'package:aitrainer_app/library/flurry.dart'; +import 'package:flurry/flurry.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; import 'package:package_info/package_info.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -84,8 +85,8 @@ class Cache with Logging { AccessToken? accessTokenFacebook; Customer? userLoggedIn; String? firebaseUid; - late LoginType loginType; - late PackageInfo packageInfo; + LoginType? loginType; + PackageInfo? packageInfo; bool hasPurchased = false; @@ -93,6 +94,7 @@ class Cache with Logging { List? _exerciseTypes; List? _exerciseTree; + List? _evaluations; List? _exercises; ExercisePlan? _myExercisePlan; @@ -204,7 +206,6 @@ class Cache with Logging { String? detailsJson = sharedPreferences.getString(Cache.activeExercisePlanDetailsKey); if (detailsJson != null) { - print("Details $detailsJson"); Iterable json = jsonDecode(detailsJson); this.activeExercisePlanDetails = json.map((details) => ExercisePlanDetail.fromJsonWithExerciseList(details)).toList(); } @@ -641,4 +642,7 @@ class Cache with Logging { } return isMuscleDevelopmentSeen!; } + + List? get evaluations => this._evaluations; + set evaluations(List? value) => this._evaluations = value; } diff --git a/lib/model/evaluation.dart b/lib/model/evaluation.dart new file mode 100644 index 0000000..b4e4bc9 --- /dev/null +++ b/lib/model/evaluation.dart @@ -0,0 +1,28 @@ +import 'package:aitrainer_app/model/evaluation_attribute.dart'; + +class Evaluation { + late int evaluationId; + late String name; + late int exerciseTypeId; + late String unit; + late List attributes; + + Evaluation.fromJson(Map json) { + evaluationId = json['evaluationId']; + name = json['name']; + exerciseTypeId = json['exerciseTypeId']; + unit = json['unit']; + this.attributes = json['attributes'].map((attr) => EvaluationAttribute.fromJson(attr)).toList(); + } + + @override + String toString() { + Map json = { + 'evaluationId': this.evaluationId, + 'name': this.name, + 'exerciseTypeId': this.exerciseTypeId, + 'unit': this.unit + }; + return json.toString(); + } +} diff --git a/lib/model/evaluation_attribute.dart b/lib/model/evaluation_attribute.dart new file mode 100644 index 0000000..31250aa --- /dev/null +++ b/lib/model/evaluation_attribute.dart @@ -0,0 +1,41 @@ +class EvaluationAttribute { + late int evaluationAttrId; + late int evaluationId; + late String name; + late String sex; + late int ageMin; + late int ageMax; + late double valueMin; + late double valueMax; + late String evaluationText; + String? suggestion; + + EvaluationAttribute.fromJson(Map json) { + evaluationAttrId = json['evaluationAttrId']; + evaluationId = json['evaluationId']; + name = json['name']; + sex = json['sex']; + ageMin = json['ageMin']; + ageMax = json['ageMax']; + valueMin = json['valueMin']; + valueMax = json['valueMax']; + evaluationText = json['evaluation_text']; + suggestion = json['suggestion']; + } + + @override + String toString() { + Map json = { + 'evaluationAttrId': this.evaluationAttrId, + 'evaluationId': this.evaluationId, + 'name': this.name, + 'sex': this.sex, + 'ageMin': this.ageMin, + 'ageMax': this.ageMax, + 'valueMin': this.valueMin, + 'valueMax': this.valueMax, + 'evaluation_text': this.evaluationText, + }; + return json.toString(); + } +} diff --git a/lib/model/exercise_plan.dart b/lib/model/exercise_plan.dart index f74dd23..1763c18 100644 --- a/lib/model/exercise_plan.dart +++ b/lib/model/exercise_plan.dart @@ -61,4 +61,10 @@ class ExercisePlan { }; } } + + @override + String toString() { + Map json = toJson(); + return json.toString(); + } } diff --git a/lib/model/exercise_plan_detail.dart b/lib/model/exercise_plan_detail.dart index e502098..6f208d0 100644 --- a/lib/model/exercise_plan_detail.dart +++ b/lib/model/exercise_plan_detail.dart @@ -12,13 +12,16 @@ extension ExericisePlanDetailStateExt on ExercisePlanDetailState { class ExercisePlanDetail { int? exercisePlanDetailId; - late int exercisePlanId; + int? exercisePlanId; late int exerciseTypeId; int? serie; int? repeats; String? weightEquation; - List? exercises; + /// List + List? exercises; + + /// bool finished bool? finished; ExercisePlanDetailState state = ExercisePlanDetailState.start; @@ -53,7 +56,7 @@ class ExercisePlanDetail { jsonExercises = jsonExercises.replaceAll(r'\"null\"', 'null'); - print("Exercises $jsonExercises"); + //print("Exercises $jsonExercises"); Iterable iterable = jsonDecode(jsonExercises); this.exercises = iterable.map((exercise) => Exercise.fromJson(exercise)).toList(); } on Exception catch (e) { @@ -62,7 +65,7 @@ class ExercisePlanDetail { } Map toJson() => { - "exercisePlanId": exercisePlanId, + "exercisePlanId": exercisePlanId == null ? 0 : exercisePlanId, "exerciseTypeId": exerciseTypeId, "serie": serie, "repeats": repeats, @@ -76,6 +79,12 @@ class ExercisePlanDetail { "serie": serie, "repeats": repeats, "weightEquation": weightEquation, - 'exercises': exercises!.map((exercise) => exercise.toJson()).toList().toString(), + 'exercises': exercises == null ? [].toString() : exercises!.map((exercise) => exercise.toJson()).toList().toString(), }; + + @override + String toString() { + Map json = toJsonWithExerciseList(); + return json.toString(); + } } diff --git a/lib/model/fitness_state.dart b/lib/model/fitness_state.dart index e24c109..798b987 100644 --- a/lib/model/fitness_state.dart +++ b/lib/model/fitness_state.dart @@ -1,3 +1,23 @@ +enum Sport { football, fitness, footgolf } + +extension SportExt on Sport { + String toStr() => this.toString().split(".").last; + bool equalsTo(Sport sport) => this.toString() == sport.toString(); + bool equalsStringTo(String sport) => this.toStr() == sport; + + String description(Sport sport) { + if (Sport.football.equalsTo(sport)) { + return "Football"; + } else if (Sport.fitness.equalsTo(sport)) { + return "Fitness / Body Building"; + } else if (Sport.footgolf.equalsTo(sport)) { + return "Footgolf"; + } else { + return "Sport"; + } + } +} + class FitnessState { late final String value; late final String stateText; diff --git a/lib/model/tracking.dart b/lib/model/tracking.dart index 5fe9d23..377453a 100644 --- a/lib/model/tracking.dart +++ b/lib/model/tracking.dart @@ -7,7 +7,7 @@ class Tracking { late int customerId; late DateTime dateAdd; late String event; - late String eventValue; + String? eventValue; late String area; late String platform; late String version; @@ -19,6 +19,6 @@ class Tracking { "eventValue": eventValue, "area": Platform.localeName, "platform": Platform.isAndroid ? "Android" : "iOS", - "version": Cache().packageInfo.version + "+" + Cache().packageInfo.buildNumber + "version": Cache().packageInfo != null ? Cache().packageInfo!.version + "+" + Cache().packageInfo!.buildNumber : "" }; } diff --git a/lib/repository/customer_repository.dart b/lib/repository/customer_repository.dart index 33affc6..219690a 100644 --- a/lib/repository/customer_repository.dart +++ b/lib/repository/customer_repository.dart @@ -21,8 +21,8 @@ class GenderItem { } class CustomerRepository with Logging { - late Customer customer; - late Customer? _trainee; + Customer? customer; + Customer? _trainee; List? _trainees; List? _allProperties; final PropertyRepository propertyRepository = PropertyRepository(); @@ -68,51 +68,61 @@ class CustomerRepository with Logging { } String? get name { - return this.customer.name != null ? this.customer.name : ""; + return this.customer != null && this.customer!.name != null ? this.customer!.name : ""; } String? get firstName { - return this.customer.firstname != null ? this.customer.firstname : ""; + return this.customer != null && this.customer!.firstname != null ? this.customer!.firstname : ""; } String get sex { - return this.customer.sex == "m" ? "Man" : "Woman"; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.sex == "m" ? "Man" : "Woman"; } int? get birthYear { - return this.customer.birthYear; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.birthYear; } String? get goal { - return this.customer.goal; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.goal; } String? get fitnessLevel { - return this.customer.fitnessLevel; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.fitnessLevel; } String? get bodyType { - return this.customer.bodyType; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.bodyType; } setName(String name) { - this.customer.name = name; + if (this.customer == null) return; + this.customer!.name = name; } setFirstName(String firstName) { - this.customer.firstname = firstName; + if (this.customer == null) return; + this.customer!.firstname = firstName; } setPassword(String password) { - this.customer.password = password; + if (this.customer == null) return; + this.customer!.password = password; } setEmail(String email) { - this.customer.email = email; + if (this.customer == null) return; + this.customer!.email = email; } setSex(String sex) { - this.customer.sex = sex; + if (this.customer == null) return; + this.customer!.sex = sex; } setWeight(int weight) { @@ -126,19 +136,20 @@ class CustomerRepository with Logging { } setCustomerProperty(String propertyName, double value, {id = 0}) { - if (this.customer.properties[propertyName] == null) { - this.customer.properties[propertyName] = CustomerProperty( + if (this.customer == null) throw Exception("Initialize the customer object"); + if (this.customer!.properties[propertyName] == null) { + this.customer!.properties[propertyName] = CustomerProperty( propertyId: propertyRepository.getPropertyByName("Height")!.propertyId, - customerId: this.customer.customerId!, + customerId: this.customer!.customerId!, propertyValue: value, dateAdd: DateTime.now()); } else { - this.customer.properties[propertyName]!.propertyValue = value; + this.customer!.properties[propertyName]!.propertyValue = value; } - this.customer.properties[propertyName]!.dateAdd = DateTime.now(); - this.customer.properties[propertyName]!.newData = true; + this.customer!.properties[propertyName]!.dateAdd = DateTime.now(); + this.customer!.properties[propertyName]!.newData = true; if (id > 0) { - this.customer.properties[propertyName]!.customerPropertyId = id; + this.customer!.properties[propertyName]!.customerPropertyId = id; } } @@ -151,31 +162,36 @@ class CustomerRepository with Logging { } double getCustomerPropertyValue(String propertyName) { - if (this.customer.properties[propertyName] == null) { + if (this.customer == null || this.customer!.properties[propertyName] == null) { return 0.0; } else { - return this.customer.properties[propertyName]!.propertyValue; + return this.customer!.properties[propertyName]!.propertyValue; } } CustomerProperty? getCustomerProperty(String propertyName) { - return this.customer.properties[propertyName]; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!.properties[propertyName]; } setBirthYear(int birthYear) { - this.customer.birthYear = birthYear; + if (this.customer == null) throw Exception("Initialize the customer object"); + this.customer!.birthYear = birthYear; } setFitnessLevel(String level) { - this.customer.fitnessLevel = level; + if (this.customer == null) throw Exception("Initialize the customer object"); + this.customer!.fitnessLevel = level; } setGoal(String goal) { - this.customer.goal = goal; + if (this.customer == null) throw Exception("Initialize the customer object"); + this.customer!.goal = goal; } setBodyType(String bodyType) { - this.customer.bodyType = bodyType; + if (this.customer == null) throw Exception("Initialize the customer object"); + this.customer!.bodyType = bodyType; } createNew() { @@ -183,7 +199,8 @@ class CustomerRepository with Logging { } Customer getCustomer() { - return this.customer; + if (this.customer == null) throw Exception("Initialize the customer object"); + return this.customer!; } void setCustomer(Customer customer) { @@ -191,12 +208,14 @@ class CustomerRepository with Logging { } Future addCustomer() async { - final Customer modelCustomer = customer; + if (this.customer == null) throw Exception("Initialize the customer object"); + final Customer modelCustomer = customer!; await CustomerApi().addCustomer(modelCustomer); } Future saveCustomer() async { - final Customer modelCustomer = customer; + if (this.customer == null) throw Exception("Initialize the customer object"); + final Customer modelCustomer = customer!; await CustomerApi().saveCustomer(modelCustomer); await this.saveProperties(modelCustomer.properties); } @@ -317,6 +336,7 @@ class CustomerRepository with Logging { } void addSizes(String sex) { + if (this.customer == null) throw Exception("Initialize the customer object"); List? properties = Cache().getProperties(); if (properties == null) { return; @@ -328,62 +348,62 @@ class CustomerRepository with Logging { if (element.propertyName == "Shoulder") { element.top = (122 * distortionHeight).toInt(); element.left = (130 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Shoulder"); + element.value = this.customer!.getProperty("Shoulder"); manSizes.add(element); } else if (element.propertyName == "Neck") { element.top = (68 * distortionHeight).toInt(); element.left = (130 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Neck"); + element.value = this.customer!.getProperty("Neck"); manSizes.add(element); } else if (element.propertyName == "Biceps") { element.top = (178 * distortionHeight).toInt(); element.left = (208 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Biceps"); + element.value = this.customer!.getProperty("Biceps"); manSizes.add(element); } else if (element.propertyName == "Chest") { element.top = (154 * distortionHeight).toInt(); element.left = (130 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Chest"); + element.value = this.customer!.getProperty("Chest"); manSizes.add(element); } else if (element.propertyName == "Belly") { element.top = (244 * distortionHeight).toInt(); element.left = (130 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Belly"); + element.value = this.customer!.getProperty("Belly"); manSizes.add(element); } else if (element.propertyName == "Hip") { element.top = (308 * distortionHeight).toInt(); element.left = (130 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Hip"); + element.value = this.customer!.getProperty("Hip"); manSizes.add(element); } else if (element.propertyName == "Thigh Top") { element.top = (332 * distortionHeight).toInt(); element.left = (165 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Thigh Top"); + element.value = this.customer!.getProperty("Thigh Top"); manSizes.add(element); } else if (element.propertyName == "Thigh Middle") { element.top = (382 * distortionHeight).toInt(); element.left = (100 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Thigh Middle"); + element.value = this.customer!.getProperty("Thigh Middle"); manSizes.add(element); } else if (element.propertyName == "Knee") { element.top = (464 * distortionHeight).toInt(); element.left = (97 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Knee"); + element.value = this.customer!.getProperty("Knee"); manSizes.add(element); } else if (element.propertyName == "Calf") { element.top = (520 * distortionHeight).toInt(); element.left = (97 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Calf"); + element.value = this.customer!.getProperty("Calf"); manSizes.add(element); } else if (element.propertyName == "Ankle") { element.top = (620 * distortionHeight).toInt(); element.left = (150 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Ankle"); + element.value = this.customer!.getProperty("Ankle"); manSizes.add(element); } else if (element.propertyName == "Weight") { element.top = (402 * distortionHeight).toInt(); element.left = (240 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Weight"); + element.value = this.customer!.getProperty("Weight"); manSizes.add(element); } }); @@ -392,62 +412,62 @@ class CustomerRepository with Logging { if (element.propertyName == "Shoulder") { element.top = (122 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Shoulder"); + element.value = this.customer!.getProperty("Shoulder"); manSizes.add(element); } else if (element.propertyName == "Neck") { element.top = (78 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Neck"); + element.value = this.customer!.getProperty("Neck"); manSizes.add(element); } else if (element.propertyName == "Biceps") { element.top = (178 * distortionHeight).toInt(); element.left = (212 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Biceps"); + element.value = this.customer!.getProperty("Biceps"); manSizes.add(element); } else if (element.propertyName == "Chest") { element.top = (154 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Chest"); + element.value = this.customer!.getProperty("Chest"); manSizes.add(element); } else if (element.propertyName == "Belly") { element.top = (230 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Belly"); + element.value = this.customer!.getProperty("Belly"); manSizes.add(element); } else if (element.propertyName == "Hip") { element.top = (294 * distortionHeight).toInt(); element.left = (151 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Hip"); + element.value = this.customer!.getProperty("Hip"); manSizes.add(element); } else if (element.propertyName == "Thigh Top") { element.top = (335 * distortionHeight).toInt(); element.left = (185 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Thigh Top"); + element.value = this.customer!.getProperty("Thigh Top"); manSizes.add(element); } else if (element.propertyName == "Thigh Middle") { element.top = (377 * distortionHeight).toInt(); element.left = (125 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Thigh Middle"); + element.value = this.customer!.getProperty("Thigh Middle"); manSizes.add(element); } else if (element.propertyName == "Knee") { element.top = (468 * distortionHeight).toInt(); element.left = (129 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Knee"); + element.value = this.customer!.getProperty("Knee"); manSizes.add(element); } else if (element.propertyName == "Calf") { element.top = (525 * distortionHeight).toInt(); element.left = (129 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Calf"); + element.value = this.customer!.getProperty("Calf"); manSizes.add(element); } else if (element.propertyName == "Ankle") { element.top = (620 * distortionHeight).toInt(); element.left = (162 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Ankle"); + element.value = this.customer!.getProperty("Ankle"); manSizes.add(element); } else if (element.propertyName == "Weight") { element.top = (402 * distortionHeight).toInt(); element.left = (240 * distortionWidth).toInt(); - element.value = this.customer.getProperty("Weight"); + element.value = this.customer!.getProperty("Weight"); manSizes.add(element); } }); diff --git a/lib/repository/evaluation_repository.dart b/lib/repository/evaluation_repository.dart new file mode 100644 index 0000000..2c85301 --- /dev/null +++ b/lib/repository/evaluation_repository.dart @@ -0,0 +1,77 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/model/customer.dart'; +import 'package:aitrainer_app/model/evaluation.dart'; +import 'package:aitrainer_app/model/evaluation_attribute.dart'; +import 'package:aitrainer_app/util/enums.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class EvaluationRepository { + List? evaluations; + + EvaluationRepository() { + evaluations = Cache().evaluations; + } + + String getEvaluationTextByExerciseType(int exerciseTypeId, double value) { + String? eval; + + Evaluation? evaluation = this.getEvaluationByExerciseTypeId(exerciseTypeId); + Customer? customer = Cache().userLoggedIn; + + if (evaluation != null && customer != null) { + int? age = customer.age; + if (age != null) { + for (var attribute in evaluation.attributes) { + EvaluationAttribute attr = attribute as EvaluationAttribute; + if (age >= attr.ageMin && age <= attr.ageMax && value >= attr.valueMin && value <= attr.valueMax) { + eval = attr.evaluationText; + break; + } + } + } + } + + if (eval == null) { + eval = EvaluationText.fair.toStr(); + } + return eval; + } + + Evaluation? getEvaluationByExerciseTypeId(int exerciseTypeId) { + Evaluation? eval; + if (evaluations != null) { + for (var evaluation in evaluations!) { + if (evaluation.exerciseTypeId == exerciseTypeId) { + eval = evaluation; + break; + } + } + } + return eval; + } + + Color getEvaluationColor(String eval) { + Color color = Colors.yellow[100]!; + + if (EvaluationText.very_poor.equalsStringTo(eval)) { + return Colors.red[800]!; + } else if (EvaluationText.poor.equalsStringTo(eval)) { + return Colors.red[400]!; + } else if (EvaluationText.below_average.equalsStringTo(eval)) { + return Colors.orange[600]!; + } else if (EvaluationText.average.equalsStringTo(eval)) { + return Colors.yellow[600]!; + } else if (EvaluationText.above_average.equalsStringTo(eval)) { + return Colors.greenAccent; + } else if (EvaluationText.good.equalsStringTo(eval)) { + return Colors.green[400]!; + } else if (EvaluationText.excellent.equalsStringTo(eval)) { + return Colors.green[600]!; + } else if (EvaluationText.elite.equalsStringTo(eval)) { + return Colors.green[800]!; + } + + return color; + } +} diff --git a/lib/repository/exercise_device_repository.dart b/lib/repository/exercise_device_repository.dart index e14fa5b..a82d400 100644 --- a/lib/repository/exercise_device_repository.dart +++ b/lib/repository/exercise_device_repository.dart @@ -37,6 +37,7 @@ class ExerciseDeviceRepository { } List getGymDevices() { + if (Cache().getDevices() == null) return []; final List gymDevices = []; if (_devices.isEmpty) { _devices = Cache().getDevices()!; diff --git a/lib/repository/exercise_plan_repository.dart b/lib/repository/exercise_plan_repository.dart index dd3d05e..b0d4092 100644 --- a/lib/repository/exercise_plan_repository.dart +++ b/lib/repository/exercise_plan_repository.dart @@ -27,8 +27,13 @@ class ExercisePlanRepository { ExercisePlan? getExercisePlan() => exercisePlan; void addDetailToPlan() { + if (exercisePlan == null) { + this.createNewPlan(); + } if (exercisePlan != null && actualPlanDetail != null) { - actualPlanDetail!.exercisePlanId = exercisePlan!.exercisePlanId!; + if (exercisePlan!.exercisePlanId != null) { + actualPlanDetail!.exercisePlanId = exercisePlan!.exercisePlanId!; + } exercisePlanDetails[actualPlanDetail!.exerciseTypeId] = actualPlanDetail!; Cache().addToMyExercisePlanDetails(actualPlanDetail!); } @@ -85,24 +90,14 @@ class ExercisePlanRepository { } void removeExerciseTypeFromPlanByExerciseTypeId(int exerciseTypeId) { + if (exercisePlanDetails[exerciseTypeId] == null) return; exercisePlanDetails[exerciseTypeId]!.change = ModelChange.delete; Cache().deleteMyExercisePlanDetailByExerciseTypeId(exerciseTypeId); } Future saveExercisePlan() async { if (exercisePlan == null) { - if (Cache().userLoggedIn == null) { - throw Exception("please log in"); - } - - String exercisePlanName; - if (this.customerId == Cache().userLoggedIn!.customerId) { - exercisePlanName = Cache().userLoggedIn!.name! + " private"; - } else { - exercisePlanName = Cache().getTrainee()!.name! + " " + Cache().getTrainee()!.firstname! + " private"; - } - - exercisePlan = ExercisePlan(exercisePlanName, this.customerId); + this.createNewPlan(); } if (newPlan) { exercisePlan!.dateAdd = DateTime.now(); @@ -139,6 +134,22 @@ class ExercisePlanRepository { } } + void createNewPlan() { + if (Cache().userLoggedIn == null) { + throw Exception("please log in"); + } + + String exercisePlanName; + if (this.customerId == Cache().userLoggedIn!.customerId) { + exercisePlanName = Cache().userLoggedIn!.name! + " private"; + } else { + exercisePlanName = Cache().getTrainee()!.name! + " " + Cache().getTrainee()!.firstname! + " private"; + } + + exercisePlan = ExercisePlan(exercisePlanName, this.customerId); + newPlan = true; + } + Future getLastExercisePlan() async { if (customerId == 0) { return null; @@ -150,7 +161,12 @@ class ExercisePlanRepository { } exercisePlan = await ExercisePlanApi().getLastExercisePlan(customerId); + newPlan = (exercisePlan == null); + if (exercisePlan == null) { + this.createNewPlan(); + } + ; Cache().setMyExercisePlan(exercisePlan!); return exercisePlan; } @@ -170,16 +186,19 @@ class ExercisePlanRepository { exercisePlanDetails = listCache; return; } else { - list = await ExercisePlanApi().getExercisePlanDetail(exercisePlan!.exercisePlanId!); + if (exercisePlan!.exercisePlanId != null) { + list = await ExercisePlanApi().getExercisePlanDetail(exercisePlan!.exercisePlanId!); + } } exercisePlanDetails = LinkedHashMap(); - - list.forEach((element) { - newPlan = false; - ExercisePlanDetail detail = element; - exercisePlanDetails[detail.exerciseTypeId] = detail; - }); + if (list.isNotEmpty) { + list.forEach((element) { + newPlan = false; + ExercisePlanDetail detail = element; + exercisePlanDetails[detail.exerciseTypeId] = detail; + }); + } Cache().setMyExercisePlanDetails(exercisePlanDetails); return; diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 09af0d0..e066cfd 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -270,16 +270,167 @@ class ExerciseRepository { void getSameExercise(int exerciseTypeId, String day) { this.actualExerciseList = []; if (exerciseList != null) { + int index = 0; for (int i = 0; i < this.exerciseList!.length; i++) { Exercise exercise = exerciseList![i]; - String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); - if (exerciseTypeId == exercise.exerciseTypeId && exerciseDate == day) { + final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + if (exerciseTypeId == exercise.exerciseTypeId && exerciseDate == day && index < 4) { this.actualExerciseList!.add(exercise); + index++; } } } } + double calculate1RM(Exercise exercise) { + double weight = exercise.unitQuantity!; + double repeat = exercise.quantity!; + if (weight == 0 || repeat == 0) { + return 0; + } + + double rmWendler = weight * repeat * 0.0333 + weight; + double rmOconner = weight * (1 + repeat / 40); + double average = (rmWendler + rmOconner) / 2; + + return average; + } + + double getBest1RMPercent(Exercise exercise) { + double result = 0; + if (this.exerciseList == null || this.exerciseList!.isEmpty) { + this.exerciseList = Cache().getExercises(); + } + + final int exerciseTypeId = exercise.exerciseTypeId!; + double toCompare = this.calculate1RM(exercise); + + final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now()); + List oldExercises = []; + this.exerciseList!.forEach((exercise) { + final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) { + oldExercises.add(exercise); + } + }); + + if (oldExercises.isNotEmpty) { + oldExercises.sort((a, b) { + double sumA = 0; + double sumB = 0; + if (a.unitQuantity != null && b.unitQuantity != null) { + sumA = a.quantity! * a.unitQuantity!; + sumB = b.quantity! * b.unitQuantity!; + } else { + sumA = a.quantity!; + sumB = b.quantity!; + } + return sumA >= sumB ? 1 : -1; + }); + + double withCompare = this.calculate1RM(oldExercises.last); + + result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100; + } + return result; + } + + double getLast1RMPercent(Exercise exercise) { + double result = 0; + if (this.exerciseList == null || this.exerciseList!.isEmpty) { + this.exerciseList = Cache().getExercises(); + } + + final int exerciseTypeId = exercise.exerciseTypeId!; + double toCompare = this.calculate1RM(exercise); + + final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + List oldExercises = []; + this.exerciseList!.forEach((exercise) { + final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) { + oldExercises.add(exercise); + } + }); + + if (oldExercises.isNotEmpty) { + double withCompare = this.calculate1RM(oldExercises.first); + result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100; + } + return result; + } + + double getBestExercisePercent(Exercise exercise) { + double result = 0; + if (this.exerciseList == null || this.exerciseList!.isEmpty) { + this.exerciseList = Cache().getExercises(); + } + + final int exerciseTypeId = exercise.exerciseTypeId!; + double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!; + + final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now()); + List oldExercises = []; + this.exerciseList!.forEach((exercise) { + final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) { + oldExercises.add(exercise); + } + }); + + if (oldExercises.isNotEmpty) { + oldExercises.sort((a, b) { + double sumA = 0; + double sumB = 0; + if (a.unitQuantity != null && b.unitQuantity != null) { + sumA = a.quantity! * a.unitQuantity!; + sumB = b.quantity! * b.unitQuantity!; + } else { + sumA = a.quantity!; + sumB = b.quantity!; + } + return sumA >= sumB ? 1 : -1; + }); + + double withCompare = oldExercises.last.unitQuantity != null + ? oldExercises.last.quantity! * oldExercises.last.unitQuantity! + : oldExercises.last.quantity!; + + result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100; + print("Last Best: ${oldExercises.last} - result: $result"); + } + return result; + } + + double getLastExercisePercent(Exercise exercise) { + double result = 0; + if (this.exerciseList == null || this.exerciseList!.isEmpty) { + this.exerciseList = Cache().getExercises(); + } + + final int exerciseTypeId = exercise.exerciseTypeId!; + double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!; + + final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + List oldExercises = []; + this.exerciseList!.forEach((exercise) { + final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!); + if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) { + oldExercises.add(exercise); + } + }); + + if (oldExercises.isNotEmpty) { + double withCompare = oldExercises.first.unitQuantity != null + ? oldExercises.first.quantity! * oldExercises.first.unitQuantity! + : oldExercises.first.quantity!; + + result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100; + print("Last Last: ${oldExercises.first} vs. $exercise - - result: $result"); + } + return result; + } + void sortByDate() { if (exerciseList == null || exerciseList!.isEmpty) { return; diff --git a/lib/repository/property_repository.dart b/lib/repository/property_repository.dart index 2c23594..e5f23fb 100644 --- a/lib/repository/property_repository.dart +++ b/lib/repository/property_repository.dart @@ -3,7 +3,7 @@ import 'package:aitrainer_app/model/property.dart'; import 'package:aitrainer_app/service/property_service.dart'; class PropertyRepository { - late List? _properties; + List? _properties; Future?> getDBProperties() async { this._properties = await PropertyApi().getProperties(); diff --git a/lib/repository/workout_tree_repository.dart b/lib/repository/workout_tree_repository.dart index a7cbab5..a0d2a41 100644 --- a/lib/repository/workout_tree_repository.dart +++ b/lib/repository/workout_tree_repository.dart @@ -112,7 +112,9 @@ class WorkoutTreeRepository with Logging { parent != null ? parent.nameEnglish : "", 0); this.tree[exerciseType.name] = menuItem; - menuAsExercise.add(menuItem); + if (isRunning || is1RM) { + menuAsExercise.add(menuItem); + } //log("ExerciseType in Menu item ${exerciseType.toJson()} is1RM: $is1RM"); }); } else { diff --git a/lib/service/api.dart b/lib/service/api.dart index d94b765..7a949ec 100644 --- a/lib/service/api.dart +++ b/lib/service/api.dart @@ -8,6 +8,7 @@ import 'package:aitrainer_app/model/cache.dart'; class APIClient with Common, Logging { Future get(String endPoint, String param) async { final url = Cache.getBaseUrl() + endPoint + param; + trace("-------- API get " + url); String authToken = Cache().getAuthToken(); if (authToken.length == 0) { @@ -57,6 +58,7 @@ class APIClient with Common, Logging { final response = await http.post(uri, headers: {'Authorization': '1', 'Content-Type': 'application/json'}, body: body); final responseCode = response.statusCode; if (responseCode != 200) { + trace("authentication response: $responseCode"); return { "error": "Authentication error, total failure", }; @@ -65,6 +67,7 @@ class APIClient with Common, Logging { final responseJson = json.decode(response.body); return responseJson; } catch (exception) { + print(exception.toString()); return {"error": "Network error, try again later " + exception.toString()}; } } diff --git a/lib/service/exercise_plan_service.dart b/lib/service/exercise_plan_service.dart index 7b9d678..181cfac 100644 --- a/lib/service/exercise_plan_service.dart +++ b/lib/service/exercise_plan_service.dart @@ -91,9 +91,10 @@ class ExercisePlanApi with Logging { final String responseBody = await _client.get("exercise_plan/last/" + customerId.toString(), body); exercisePlan = ExercisePlan.fromJson(jsonDecode(responseBody)); } on Exception catch (e) { + print(e.toString()); if (e is NotFoundException) { log("ExercisePlan not found for " + customerId.toString()); - return exercisePlan; + return null; } else { throw new Exception(e.toString()); } @@ -104,7 +105,7 @@ class ExercisePlanApi with Logging { Future> getExercisePlanDetail(int exercisePlanId) async { String body = ""; log(" ===== get exercisePlanDetail $exercisePlanId"); - List listExercisePlanDetail; + List listExercisePlanDetail = []; try { final String responseBody = await _client.get("exercise_plan_detail/" + exercisePlanId.toString(), body); log("response body:" + responseBody); diff --git a/lib/service/firebase_api.dart b/lib/service/firebase_api.dart index 3e3e225..b224a3d 100644 --- a/lib/service/firebase_api.dart +++ b/lib/service/firebase_api.dart @@ -109,7 +109,7 @@ class FirebaseApi with logging.Logging { // Sign in the user with Firebase. If the nonce we generated earlier does // not match the nonce in `appleCredential.identityToken`, sign in will fail. UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential); - + Cache().firebaseUid = userCredential.user!.uid; log("userCredential: " + userCredential.toString()); log("Apple Credentials: " + @@ -181,7 +181,8 @@ class FirebaseApi with logging.Logging { idToken: googleAuth.idToken, ); - await FirebaseAuth.instance.signInWithCredential(credential); + UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential); + Cache().firebaseUid = userCredential.user!.uid; log("GoogleUser: " + googleUser.toString()); userData['email'] = googleUser.email; @@ -230,8 +231,9 @@ class FirebaseApi with logging.Logging { Map userData; // by default the login method has the next permissions ['email','public_profile'] - AccessToken? accessToken = await FacebookAuth.instance.accessToken; - if (accessToken != null) { + final LoginResult result = await FacebookAuth.instance.login(); + if (result.status == LoginStatus.success) { + final AccessToken accessToken = result.accessToken!; log(accessToken.toJson().toString()); Cache().accessTokenFacebook = accessToken; // get the user data @@ -249,8 +251,9 @@ class FirebaseApi with logging.Logging { Map userData; // by default the login method has the next permissions ['email','public_profile'] - AccessToken? accessToken = await FacebookAuth.instance.accessToken; - if (accessToken != null) { + final LoginResult result = await FacebookAuth.instance.login(); + if (result.status == LoginStatus.success) { + final AccessToken accessToken = result.accessToken!; Cache().accessTokenFacebook = accessToken; // get the user data userData = await FacebookAuth.instance.getUserData(); diff --git a/lib/service/package_service.dart b/lib/service/package_service.dart index 3864fb8..07fefe1 100644 --- a/lib/service/package_service.dart +++ b/lib/service/package_service.dart @@ -4,6 +4,7 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/model/customer_exercise_device.dart'; import 'package:aitrainer_app/model/customer_property.dart'; +import 'package:aitrainer_app/model/evaluation.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_device.dart'; import 'package:aitrainer_app/model/exercise_plan_template.dart'; @@ -59,6 +60,9 @@ class PackageApi { Cache().setExercisePlanTemplates(exercisePlanTemplates); } else if (headRecord[0] == "ExerciseTreeParents") { exerciseTreeParents = json.map((exerciseTreeParent) => ExerciseTreeParents.fromJson(exerciseTreeParent)).toList(); + } else if (headRecord[0] == "Evaluation") { + final List evaluations = json.map((evaluation) => Evaluation.fromJson(evaluation)).toList(); + Cache().evaluations = evaluations; } }); diff --git a/lib/util/common.dart b/lib/util/common.dart index 8b3d17b..ef08ff6 100644 --- a/lib/util/common.dart +++ b/lib/util/common.dart @@ -98,17 +98,22 @@ mixin Common { return datePart; } - static String? emailValidation(String email) { + static String? emailValidation(String? email) { + final String error = "Please type an email address"; + if (email == null) { + return error; + } bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email); - return emailValid ? null : "Please type an email address"; + return emailValid ? null : error; } static String? passwordValidation(String? value) { + final String error = "Password too short"; if (value == null || value.length == 0) { - return null; + return error; } bool valid = 8 < value.length; - return valid ? null : "Password too short"; + return valid ? null : error; } static Widget badgedIcon(Color color, IconData icon, String badgeKey) { diff --git a/lib/util/enums.dart b/lib/util/enums.dart index f3d16bc..e7576ba 100644 --- a/lib/util/enums.dart +++ b/lib/util/enums.dart @@ -73,3 +73,11 @@ extension SizesExt on SizesEnum { bool equalsTo(SizesEnum event) => this.toString() == event.toString(); bool equalsStringTo(String event) => this.toString() == event; } + +enum EvaluationText { very_poor, poor, fair, below_average, average, above_average, good, excellent, elite } + +extension EvaluationTextExt on EvaluationText { + String toStr() => this.toString().split(".").last; + bool equalsTo(EvaluationText eval) => this.toString() == eval.toString(); + bool equalsStringTo(String eval) => this.toStr() == eval; +} diff --git a/lib/util/track.dart b/lib/util/track.dart index bf40084..3bfae7b 100644 --- a/lib/util/track.dart +++ b/lib/util/track.dart @@ -3,8 +3,8 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/service/tracking_service.dart'; import 'package:aitrainer_app/util/enums.dart'; -import 'package:aitrainer_app/library/flurry.dart'; import 'package:aitrainer_app/model/tracking.dart' as model; +import 'package:flurry/flurry.dart'; import 'package:smartlook/smartlook.dart'; class Track with Logging { diff --git a/lib/util/trans.dart b/lib/util/trans.dart index ef4e07d..618c473 100644 --- a/lib/util/trans.dart +++ b/lib/util/trans.dart @@ -8,6 +8,9 @@ mixin Trans { } String t(String text) { + if (context == null) { + throw Exception("Translation: initialize the context"); + } return AppLocalizations.of(context)!.translate(text); } } diff --git a/lib/view/account.dart b/lib/view/account.dart index 2f0660c..da7a2b0 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -52,9 +52,11 @@ class AccountPage extends StatelessWidget with Trans { customerName = accountBloc.customerRepository.firstName! + " " + accountBloc.customerRepository.name!; customerName = customerName.length < 3 ? t("Personal data") : customerName; - goal = accountBloc.customerRepository.customer.goal != null ? t(accountBloc.customerRepository.customer.goal!) : goal; - fitnessLevel = accountBloc.customerRepository.customer.fitnessLevel != null - ? t(capitalize(accountBloc.customerRepository.customer.fitnessLevel!)) + goal = accountBloc.customerRepository.customer != null && accountBloc.customerRepository.customer!.goal != null + ? t(accountBloc.customerRepository.customer!.goal!) + : goal; + fitnessLevel = accountBloc.customerRepository.customer != null && accountBloc.customerRepository.customer!.fitnessLevel != null + ? t(capitalize(accountBloc.customerRepository.customer!.fitnessLevel!)) : fitnessLevel; bodyType = accountBloc.getAccurateBodyType(); @@ -166,8 +168,6 @@ class AccountPage extends StatelessWidget with Trans { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text(t("Available Devices"), style: TextStyle(color: Colors.orange)), Icon(Icons.arrow_forward_ios)]), - //textColor: Colors.orange, - //color: Colors.white, onPressed: () => { if (Cache().userLoggedIn != null) { @@ -185,7 +185,7 @@ class AccountPage extends StatelessWidget with Trans { String text = "Logout"; Color buttonColor = Colors.orange; - if (accountBloc.customerRepository.customer.email == null) { + if (accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer!.email == null) { text = "Login"; buttonColor = Colors.blue; } @@ -220,7 +220,7 @@ class AccountPage extends StatelessWidget with Trans { } Widget getMyTrainees(BuildContext context, AccountBloc accountBloc) { - if (accountBloc.customerRepository.customer.trainer == 0) { + if (accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer!.trainer == 0) { return ListTile( title: Container(), ); diff --git a/lib/view/customer_fitness_page.dart b/lib/view/customer_fitness_page.dart index 5dc05a9..7725c60 100644 --- a/lib/view/customer_fitness_page.dart +++ b/lib/view/customer_fitness_page.dart @@ -11,6 +11,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../bloc/customer_change/customer_change_bloc.dart'; +import '../library/dropdown_search.dart'; // ignore: must_be_immutable class CustomerFitnessPage extends StatefulWidget { @@ -22,10 +26,8 @@ class CustomerFitnessPage extends StatefulWidget { } } -// dropbox for professional sport - class _CustomerFitnessPageState extends State with Trans { - late String selected; + String? selected; bool fulldata = false; @override @@ -41,8 +43,6 @@ class _CustomerFitnessPageState extends State with Trans { customerRepository = ModalRoute.of(context)!.settings.arguments as CustomerRepository; } - selected = customerRepository.customer.fitnessLevel!; - PreferredSizeWidget _bar = AppBarMin( back: true, ); @@ -56,6 +56,10 @@ class _CustomerFitnessPageState extends State with Trans { child: Builder(builder: (context) { // ignore: close_sinks CustomerChangeBloc changeBloc = BlocProvider.of(context); + selected = changeBloc.selectedFitnessItem; + if (selected == null) { + selected = FitnessState.beginner; + } return SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( @@ -85,7 +89,7 @@ class _CustomerFitnessPageState extends State with Trans { TextButton( style: TextButton.styleFrom( padding: EdgeInsets.all(10.0), - shape: getShape(customerRepository, FitnessState.beginner), + shape: getShape(changeBloc, FitnessState.beginner), ), child: Container( width: cWidth, @@ -103,14 +107,14 @@ class _CustomerFitnessPageState extends State with Trans { onPressed: () => { setState(() { selected = FitnessState.beginner; - changeBloc.add(CustomerFitnessChange(fitness: selected)); + changeBloc.add(CustomerFitnessChange(fitness: selected!)); }), }), Divider(), TextButton( style: TextButton.styleFrom( padding: EdgeInsets.all(10.0), - shape: getShape(customerRepository, FitnessState.intermediate), + shape: getShape(changeBloc, FitnessState.intermediate), ), child: Container( width: cWidth, @@ -136,7 +140,7 @@ class _CustomerFitnessPageState extends State with Trans { onPressed: () => { setState(() { selected = FitnessState.intermediate; - changeBloc.add(CustomerFitnessChange(fitness: selected)); + changeBloc.add(CustomerFitnessChange(fitness: selected!)); print(selected); }), }), @@ -144,7 +148,7 @@ class _CustomerFitnessPageState extends State with Trans { TextButton( style: TextButton.styleFrom( padding: EdgeInsets.all(10.0), - shape: getShape(customerRepository, FitnessState.advanced), + shape: getShape(changeBloc, FitnessState.advanced), ), child: Container( width: cWidth, @@ -170,7 +174,7 @@ class _CustomerFitnessPageState extends State with Trans { onPressed: () => { setState(() { selected = FitnessState.advanced; - changeBloc.add(CustomerFitnessChange(fitness: selected)); + changeBloc.add(CustomerFitnessChange(fitness: selected!)); print(selected); }), }), @@ -178,7 +182,7 @@ class _CustomerFitnessPageState extends State with Trans { TextButton( style: TextButton.styleFrom( padding: EdgeInsets.all(10.0), - shape: getShape(customerRepository, FitnessState.professional), + shape: getShape(changeBloc, FitnessState.professional), ), child: Container( width: cWidth, @@ -204,11 +208,13 @@ class _CustomerFitnessPageState extends State with Trans { onPressed: () => { setState(() { selected = FitnessState.professional; - changeBloc.add(CustomerFitnessChange(fitness: selected)); + changeBloc.add(CustomerFitnessChange(fitness: selected!)); print(selected); }), }), Divider(), + selected == FitnessState.professional ? getSport(changeBloc) : Offstage(), + Divider(), ElevatedButton( style: ElevatedButton.styleFrom( onPrimary: Colors.white, @@ -228,8 +234,8 @@ class _CustomerFitnessPageState extends State with Trans { }))); } - dynamic getShape(CustomerRepository customerRepository, String fitnessLevel) { - String selected = customerRepository.fitnessLevel!; + dynamic getShape(CustomerChangeBloc changeBloc, String fitnessLevel) { + String? selected = changeBloc.selectedFitnessItem; dynamic returnCode = (selected == fitnessLevel) ? RoundedRectangleBorder( side: BorderSide(width: 4, color: Colors.orange), @@ -240,4 +246,90 @@ class _CustomerFitnessPageState extends State with Trans { //return return returnCode; } + + Widget getSport(CustomerChangeBloc bloc) { + Sport? selected = bloc.getSelectedSport; + return Container( + padding: EdgeInsets.only(left: 65, right: 65), + child: DropdownSearch( + dropdownSearchDecoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 15, top: 5, bottom: 5), + labelText: t("Sport"), + labelStyle: GoogleFonts.inter(fontSize: 16, color: Colors.indigo), + //fillColor: Colors.black38, + filled: false, + border: OutlineInputBorder( + gapPadding: 2.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.blue, width: 0.4), + ), + ), + mode: Mode.MENU, + compareFn: (Sport i, Sport s) => i.equalsTo(s), + showSelectedItem: true, + selectedItem: selected, + itemAsString: (data) => t(data.toStr()), + onChanged: (data) { + bloc.add(CustomerSportChange(sport: data)); + }, + dropdownBuilder: _customDropDownItem, + popupItemBuilder: _customMenuBuilder, + popupBarrierColor: Colors.white10, + //popupBackgroundColor: Colors.yellow, + items: Sport.values, + dropDownButton: Icon( + Icons.arrow_drop_down, + color: Colors.indigo, + ), + )); + //items: FitnessItem().toList())); + } + + Widget _customMenuBuilder(BuildContext context, Sport sport, bool isSelected) { + //bool selected = bloc.getSelectedSport; + return Container( + decoration: !isSelected + ? BoxDecoration(color: Colors.grey[300]) + : BoxDecoration( + border: Border.all(color: Colors.blue), + borderRadius: BorderRadius.circular(12), + color: Colors.grey[100], + ), + child: ListTile( + selected: isSelected, + title: Text( + t(sport.toStr()), + style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), + ), + subtitle: Text( + t(sport.description(sport)), + style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), + ), + ), + ); + } + + Widget _customDropDownItem(BuildContext context, Sport? item, String itemDesignation) { + return Container( + child: (item == null) + ? ListTile( + contentPadding: EdgeInsets.all(0), + title: Text( + t("No item selected"), + style: GoogleFonts.inter(fontSize: 14, color: Colors.blue[600]), + ), + ) + : ListTile( + contentPadding: EdgeInsets.all(0), + title: Text( + t(item.toStr()), + style: GoogleFonts.archivoBlack(fontSize: 20, color: Colors.blue[600]), + ), + subtitle: Text( + t(item.description(item)), + style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[600]), + ), + ), + ); + } } diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart index b6d50b0..245ac94 100644 --- a/lib/view/customer_goal_page.dart +++ b/lib/view/customer_goal_page.dart @@ -9,6 +9,7 @@ import 'package:aitrainer_app/widgets/app_bar_progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; class GoalsItem { static String muscle = "gain_muscle"; @@ -70,7 +71,8 @@ class _CustomerGoalPage extends State with Trans { InkWell( child: Text( AppLocalizations.of(context)!.translate("Set Your Goals"), - style: TextStyle(color: Colors.orange, fontSize: 50, fontFamily: 'Arial', fontWeight: FontWeight.w900), + style: + GoogleFonts.archivoBlack(color: Colors.orange, fontSize: 30, fontWeight: FontWeight.w900), ), highlightColor: Colors.white, ), @@ -146,6 +148,7 @@ class _CustomerGoalPage extends State with Trans { } dynamic getShape(CustomerChangeBloc customerBloc, String goal) { + if (customerBloc.customerRepository.goal == null) return null; String selectedGoal = customerBloc.customerRepository.goal!; dynamic returnCode = (selectedGoal == goal) ? RoundedRectangleBorder( diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index dd43ea1..93efdb3 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -26,12 +26,12 @@ class CustomerModifyPage extends StatelessWidget with Trans { @override Widget build(BuildContext context) { + setContext(context); dynamic arguments = ModalRoute.of(context)!.settings.arguments; if (arguments is HashMap && arguments['personal_data'] != null) { fulldata = arguments['personal_data']; } - setContext(context); // ignore: close_sinks final accountBloc = BlocProvider.of(context); @@ -88,7 +88,6 @@ class CustomerModifyPage extends StatelessWidget with Trans { Widget loadForm(CustomerChangeBloc customerBloc) { return Form( key: _scaffoldKey, - autovalidateMode: AutovalidateMode.always, child: SingleChildScrollView( scrollDirection: Axis.vertical, padding: EdgeInsets.only(top: 40, left: 25, right: 25, bottom: 250), @@ -96,7 +95,7 @@ class CustomerModifyPage extends StatelessWidget with Trans { alignment: Alignment.center, child: Column( children: [ - Text(t("Edit Profile"), style: GoogleFonts.inter(color: Colors.indigo, fontSize: 16), textAlign: TextAlign.center), + Text(t("Edit Profile"), style: GoogleFonts.archivoBlack(color: Colors.indigo, fontSize: 20), textAlign: TextAlign.center), Divider( color: Colors.transparent, ), @@ -114,7 +113,7 @@ class CustomerModifyPage extends StatelessWidget with Trans { borderSide: BorderSide(color: Colors.green[50]!, width: 0.4), ), ), - initialValue: customerBloc.customerRepository.customer.email, + initialValue: customerBloc.customerRepository.customer!.email, autovalidateMode: AutovalidateMode.always, validator: (val) { String? validator = customerBloc.emailValidation(val!); @@ -147,8 +146,8 @@ class CustomerModifyPage extends StatelessWidget with Trans { borderSide: BorderSide(color: Colors.green[50]!, width: 0.4), ), ), - initialValue: customerBloc.customerRepository.customer.password, - autovalidateMode: AutovalidateMode.always, + initialValue: customerBloc.customerRepository.customer!.password, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { String? validator = customerBloc.passwordValidation(val!); return validator == null ? null : t(validator); @@ -173,8 +172,8 @@ class CustomerModifyPage extends StatelessWidget with Trans { borderSide: BorderSide(color: Colors.green[50]!, width: 0.4), ), ), - initialValue: customerBloc.customerRepository.customer.name, - autovalidateMode: AutovalidateMode.always, + initialValue: customerBloc.customerRepository.customer!.name, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { return customerBloc.nameValidation(val); }, @@ -198,8 +197,8 @@ class CustomerModifyPage extends StatelessWidget with Trans { borderSide: BorderSide(color: Colors.green[50]!, width: 0.4), ), ), - initialValue: customerBloc.customerRepository.customer.firstname, - autovalidateMode: AutovalidateMode.always, + initialValue: customerBloc.customerRepository.customer!.firstname, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { return customerBloc.nameValidation(val); }, @@ -210,65 +209,92 @@ class CustomerModifyPage extends StatelessWidget with Trans { Divider( color: Colors.transparent, ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - flex: 4, - child: Text(t("Birth Year"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)), - ), - NumberPickerWidget( - minValue: 1930, - maxValue: 2100, - initalValue: customerBloc.year!.toInt(), - unit: " ", - color: Colors.indigo, - onChange: (value) => {customerBloc.add(CustomerBirthYearChange(year: value.toInt()))}), - SizedBox(width: 80), - ], + Container( + padding: EdgeInsets.only(left: 15), + decoration: BoxDecoration( + color: Colors.white24, + border: Border.all(color: Colors.black, width: 0.4), + borderRadius: BorderRadius.all(Radius.circular(12))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 2, + child: Text(t("Birth Year"), + style: GoogleFonts.inter( + fontWeight: FontWeight.normal, + fontSize: 18, + ))), + NumberPickerWidget( + minValue: 1930, + maxValue: DateTime.now().year, + initalValue: customerBloc.year!.toInt(), + unit: " ", + color: Colors.indigo, + onChange: (value) => {customerBloc.add(CustomerBirthYearChange(year: value.toInt()))}), + SizedBox(width: 30), + ], + )), + Divider( + color: Colors.transparent, ), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - flex: 4, - child: Text(t("Weight"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)), - ), - NumberPickerWidget( - minValue: 0, - maxValue: 200, - initalValue: customerBloc.weight.toInt(), - unit: " ", - color: Colors.indigo, - onChange: (value) => {customerBloc.add(CustomerWeightChange(weight: value.toInt()))}), - SizedBox(width: 80), - ], + Container( + padding: EdgeInsets.only(left: 15), + decoration: BoxDecoration( + color: Colors.white24, + border: Border.all(color: Colors.black, width: 0.4), + borderRadius: BorderRadius.all(Radius.circular(12))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 4, + child: Text(t("Weight"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)), + ), + NumberPickerWidget( + minValue: 0, + maxValue: 200, + initalValue: customerBloc.weight.toInt(), + unit: " ", + color: Colors.indigo, + onChange: (value) => {customerBloc.add(CustomerWeightChange(weight: value.toInt()))}), + SizedBox(width: 30), + ], + )), + Divider( + color: Colors.transparent, ), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - flex: 4, - child: Text(t("Height"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)), - ), - NumberPickerWidget( - minValue: 0, - maxValue: 230, - initalValue: customerBloc.height.toInt(), - unit: " ", - color: Colors.indigo[300]!, - onChange: (value) => {customerBloc.add(CustomerHeightChange(height: value.toInt()))}), - SizedBox(width: 80), - ], + Container( + padding: EdgeInsets.only(left: 15), + decoration: BoxDecoration( + color: Colors.white24, + border: Border.all(color: Colors.black, width: 0.4), + borderRadius: BorderRadius.all(Radius.circular(12))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 4, + child: Text(t("Height"), style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18)), + ), + NumberPickerWidget( + minValue: 0, + maxValue: 230, + initalValue: customerBloc.height.toInt(), + unit: " ", + color: Colors.indigo[300]!, + onChange: (value) => {customerBloc.add(CustomerHeightChange(height: value.toInt()))}), + SizedBox(width: 30), + ], + )), + Divider( + color: Colors.transparent, ), - Divider(), ToggleSwitch( minWidth: 80.0, minHeight: 50.0, fontSize: 14.0, - initialLabelIndex: customerBloc.customerRepository.customer.sex == "m" ? 0 : 1, + initialLabelIndex: customerBloc.customerRepository.customer!.sex == "m" ? 0 : 1, activeBgColor: Colors.indigo, activeFgColor: Colors.white, inactiveBgColor: Colors.white30, diff --git a/lib/view/evaluation.dart b/lib/view/evaluation_page.dart similarity index 70% rename from lib/view/evaluation.dart rename to lib/view/evaluation_page.dart index c799307..f498b36 100644 --- a/lib/view/evaluation.dart +++ b/lib/view/evaluation_page.dart @@ -1,5 +1,6 @@ import 'dart:collection'; import 'dart:ui'; +import 'package:aitrainer_app/util/enums.dart'; import 'package:intl/intl.dart'; import 'package:aitrainer_app/bloc/result/result_bloc.dart'; import 'package:aitrainer_app/util/app_language.dart'; @@ -281,6 +282,7 @@ class EvaluationPage extends StatelessWidget with Trans { } Widget getSuggestionWidget(ResultBloc resultBloc, String title, String picture, String repeats, double percent, String restTime) { + double _opacity = 1; String unitQuantityUnit = resultBloc.exerciseRepository.exerciseType!.unitQuantityUnit == null ? "" : resultBloc.exerciseRepository.exerciseType!.unitQuantityUnit!; @@ -325,7 +327,7 @@ class EvaluationPage extends StatelessWidget with Trans { softWrap: true, style: GoogleFonts.archivoBlack( fontSize: 18, - color: Colors.orange, + color: Colors.orange.withOpacity(_opacity), shadows: [ Shadow( offset: Offset(5.0, 5.0), @@ -398,7 +400,7 @@ class EvaluationPage extends StatelessWidget with Trans { Widget getSummary(ResultBloc bloc) { int index = 0; - List resultList = []; + List resultList = []; bloc.exerciseRepository.actualExerciseList!.forEach((actual) { //final String unit = t(bloc.exerciseRepository.exerciseType.unit); @@ -421,36 +423,248 @@ class EvaluationPage extends StatelessWidget with Trans { exerciseElement = t("3rd Control") + ": "; } index++; - resultList.add( - Text(exerciseElement + exerciseRepeats + exerciseUnitQuantity + " " + t(unit), - textAlign: TextAlign.center, - maxLines: 2, - softWrap: true, + resultList.add(RichText( + text: TextSpan( style: GoogleFonts.inter( - fontSize: 18, + fontSize: 20, color: Colors.white, - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, + ), + children: [ + TextSpan(text: exerciseElement), + TextSpan( + text: exerciseRepeats + exerciseUnitQuantity + " " + t(unit), + style: GoogleFonts.archivoBlack( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.yellow[300]!, ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - )), - ); + ), + ]), + )); }); + + return Column(children: resultList); + } + + Widget getEvaluationWidget(ResultBloc bloc) { + List resultList = []; + print("Act ${bloc.exerciseRepository.actualExerciseList}"); + if (bloc.exerciseRepository.actualExerciseList == null || bloc.exerciseRepository.actualExerciseList!.isEmpty) { + return Offstage(); + } + final int exerciseTypeId = bloc.exerciseRepository.actualExerciseList![0].exerciseTypeId!; + final double quantity = bloc.exerciseRepository.actualExerciseList![0].quantity!; + String eval = bloc.evaluationRepository.getEvaluationTextByExerciseType(exerciseTypeId, quantity); + Color color = bloc.evaluationRepository.getEvaluationColor(eval); + double compareBest = bloc.exerciseRepository.getBestExercisePercent(bloc.exerciseRepository.actualExerciseList![0]); + double compareLast = bloc.exerciseRepository.getLastExercisePercent(bloc.exerciseRepository.actualExerciseList![0]); + bool has1RM = bloc.exerciseRepository.actualExerciseList![0].unitQuantity != null; + double? bestCompared1RM; + double? lastCompared1RM; + if (has1RM) { + lastCompared1RM = bloc.exerciseRepository.getLast1RMPercent(bloc.exerciseRepository.actualExerciseList![0]); + bestCompared1RM = bloc.exerciseRepository.getBest1RMPercent(bloc.exerciseRepository.actualExerciseList![0]); + } + + if (!EvaluationText.fair.equalsStringTo(eval)) { + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + children: [ + TextSpan(text: t("Your result is: ")), + TextSpan( + text: eval, + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + color: color, + ), + ), + ]), + )); + + resultList.add(Divider(color: Colors.transparent)); + } + + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + color: Colors.white, + ), + children: [ + TextSpan(text: t("Compared with...")), + ]), + )); + + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + color: Colors.white, + ), + children: [ + TextSpan(text: t("your best")), + TextSpan(text: " "), + TextSpan(text: has1RM ? t("volumen") : t("exercise")), + TextSpan(text: ": "), + TextSpan( + text: compareBest.toStringAsFixed(1) + "%", + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + color: compareBest >= 0 ? Colors.green : Colors.red[600], + ), + ), + ]), + )); + + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + color: Colors.white, + ), + children: [ + TextSpan(text: t("your last")), + TextSpan(text: " "), + TextSpan(text: has1RM ? t("volumen") : t("exercise")), + TextSpan(text: ": "), + TextSpan( + text: compareLast.toStringAsFixed(1) + "%", + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + color: compareLast >= 0 ? Colors.green : Colors.red[600], + ), + ), + ]), + )); + resultList.add(Divider(color: Colors.transparent)); + + if (has1RM) { + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + color: Colors.white, + ), + children: [ + TextSpan(text: t("best")), + TextSpan(text: " "), + TextSpan(text: t("1RM")), + TextSpan(text: ": "), + TextSpan( + text: bestCompared1RM!.toStringAsFixed(1) + "%", + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + color: bestCompared1RM >= 0 ? Colors.green : Colors.red[600], + ), + ), + ]), + )); + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + color: Colors.white, + ), + children: [ + TextSpan(text: t("last")), + TextSpan(text: " "), + TextSpan(text: t("1RM")), + TextSpan(text: ": "), + TextSpan( + text: lastCompared1RM!.toStringAsFixed(1) + "%", + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + color: lastCompared1RM >= 0 ? Colors.green : Colors.red[600], + ), + ), + ]), + )); + } + + if (!Cache().hasPurchased) { + resultList.add(Divider()); + resultList.add(Divider()); + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.normal, + color: Colors.white, + ), + children: [ + TextSpan( + text: t('How can serve you this result?'), + style: GoogleFonts.inter( + color: Colors.white, + ), + ), + ]), + )); + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.normal, + color: Colors.white, + ), + children: [ + TextSpan( + text: t('Get the Fastlane to your'), + style: GoogleFonts.inter( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ]), + )); + resultList.add(RichText( + text: TextSpan( + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.normal, + color: Colors.white, + ), + children: [ + TextSpan( + text: t('Development'), + style: GoogleFonts.inter( + fontWeight: FontWeight.bold, + color: Colors.yellow[300], + ), + ) + ]))); + + resultList.add(TextButton( + onPressed: () => {Navigator.of(context).pushNamed("salesPage")}, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset('asset/icon/gomb_orange_a.png', width: 140, height: 60), + Text( + t("Go"), + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + )); + } + return Column(children: resultList); } Widget getResultSummary(ResultBloc resultBloc) { return SliverList( delegate: SliverChildListDelegate( - [Divider(color: Colors.transparent), getSummary(resultBloc)], + [Divider(color: Colors.transparent), getSummary(resultBloc), Divider(color: Colors.transparent), getEvaluationWidget(resultBloc)], ), ); } diff --git a/lib/view/exercise_execute_plan_add_page.dart b/lib/view/exercise_execute_plan_add_page.dart index fe2d46e..70bdfe3 100644 --- a/lib/view/exercise_execute_plan_add_page.dart +++ b/lib/view/exercise_execute_plan_add_page.dart @@ -45,7 +45,8 @@ class _ExerciseExecuteAddPage extends State with Tra exercisePlanRepository: planBloc.exercisePlanRepository, customerId: customerId, workoutTree: workoutTree, - planBloc: planBloc), + planBloc: planBloc) + ..add(ExerciseExecutePlanAddLoad()), child: BlocConsumer(listener: (context, state) { if (state is ExerciseExecutePlanAddError) { ScaffoldMessenger.of(context).showSnackBar( @@ -54,7 +55,7 @@ class _ExerciseExecuteAddPage extends State with Tra }, builder: (context, state) { // ignore: close_sinks final exerciseBloc = BlocProvider.of(context); - if (state is ExerciseExecutePlanAddReady) { + if (state is ExerciseExecutePlanAddReady && _controller.hasClients) { _controller.animateTo(exerciseBloc.scrollOffset, duration: Duration(milliseconds: 300), curve: Curves.easeIn); } return ModalProgressHUD( @@ -67,7 +68,10 @@ class _ExerciseExecuteAddPage extends State with Tra })); } - Form getControlForm(ExerciseExecutePlanAddBloc exerciseBloc) { + Widget getControlForm(ExerciseExecutePlanAddBloc exerciseBloc) { + if (exerciseBloc.exerciseRepository.exerciseType == null || exerciseBloc.quantity == null) { + return Offstage(); + } String exerciseName = AppLanguage().appLocal == Locale("en") ? exerciseBloc.exerciseRepository.exerciseType!.name : exerciseBloc.exerciseRepository.exerciseType!.nameTranslation; @@ -186,7 +190,7 @@ class _ExerciseExecuteAddPage extends State with Tra minValue: 0, maxValue: 1000, fontSize: 16, - initalValue: exerciseBloc.unitQuantity.toInt(), + initalValue: exerciseBloc.unitQuantity!.toInt(), unit: t(exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit!), color: Colors.yellow[50]!, onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeUnitQuantity(quantity: value.toDouble()))}), @@ -194,7 +198,7 @@ class _ExerciseExecuteAddPage extends State with Tra minValue: 0, maxValue: 200, fontSize: 16, - initalValue: exerciseBloc.quantity.toInt(), + initalValue: exerciseBloc.quantity!.toInt(), unit: t(exerciseBloc.exerciseRepository.exerciseType!.unit), //t("repeat"), color: Colors.yellow[50]!, onChange: (value) => {exerciseBloc.add(ExerciseExecutePlanAddChangeQuantity(quantity: value.toDouble()))}), diff --git a/lib/view/exercise_log_page.dart b/lib/view/exercise_log_page.dart index 288fede..dae5e7b 100644 --- a/lib/view/exercise_log_page.dart +++ b/lib/view/exercise_log_page.dart @@ -1,6 +1,5 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_log/exercise_log_bloc.dart'; -import 'package:aitrainer_app/library/custom_icon_icons.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/dialog_premium.dart'; @@ -196,13 +195,12 @@ class _ExerciseLogPage extends State with Trans, Common { exercise.summary == null ? "" : exercise.summary!, style: TextStyle(fontSize: 12, color: Colors.blue[800]), )), - IconButton( - iconSize: 36, - icon: Icon(CustomIcon.heart_1, color: Colors.orange[800]), - onPressed: () { - evaluation(exerciseRepository, exercise); - }, - ), + GestureDetector( + onTap: () => evaluation(exerciseRepository, exercise), + child: Image.asset( + "asset/image/kupa.png", + width: 35, + )), IconButton( icon: Icon(Icons.delete, color: Colors.black12), onPressed: () { diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index e142322..cafb2d5 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -107,7 +107,9 @@ class _ExerciseNewPageState extends State with Trans, Logging { child: ExerciseSave( exerciseName: exerciseBloc.exerciseRepository.exerciseType!.nameTranslation, exerciseDescription: exerciseBloc.exerciseRepository.exerciseType!.descriptionTranslation, - exerciseTask: t("Please take a relative bigger weight and repeat 12-20 times"), + exerciseTask: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null + ? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!") + : t("Please repeat as much times as you can! MAXIMIZE it!"), unit: exerciseBloc.exerciseRepository.exerciseType!.unit, unitQuantityUnit: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit, hasUnitQuantity: exerciseBloc.exerciseRepository.exerciseType!.unitQuantityUnit != null, @@ -121,10 +123,13 @@ class _ExerciseNewPageState extends State with Trans, Logging { floatingActionButton: FloatingActionButton.extended( onPressed: () => save(exerciseBloc, menuBloc), backgroundColor: Colors.orange[800], - icon: Icon(CustomIcon.save), + icon: Icon( + CustomIcon.save, + size: 20, + ), label: Text( t("Save"), - style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 16), + style: GoogleFonts.inter(fontWeight: FontWeight.bold, fontSize: 12), ), ), bottomNavigationBar: BottomBarMultipleExercises( @@ -164,11 +169,15 @@ class _ExerciseNewPageState extends State with Trans, Logging { // ignore: close_sinks final TestSetExecuteBloc executeBloc = BlocProvider.of(context); + final question = bloc.exerciseRepository.exercise!.quantity! == 12.0 + ? "Did you try the MAXIMUM what you can do? Are you sure we save the exercise with ONLY 12 repeats?" + : "Do you save this exercise with these parameters?"; + showCupertinoDialog( useRootNavigator: true, context: context, builder: (_) => CupertinoAlertDialog( - title: Text(t("Do you save this exercise with these parameters?")), + title: Text(t(question)), content: Column(children: [ Divider(), Text( diff --git a/lib/view/login.dart b/lib/view/login.dart index 50dd671..608e509 100644 --- a/lib/view/login.dart +++ b/lib/view/login.dart @@ -120,7 +120,7 @@ class LoginPage extends StatelessWidget with Trans { initialValue: loginBloc.userRepository.user.email, autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { - String? validationText = loginBloc.emailValidation(val!); + String? validationText = loginBloc.emailValidation(val); return validationText == null ? null : t(validationText); }, onChanged: (value) => loginBloc.add(LoginEmailChange(email: value)), @@ -150,9 +150,9 @@ class LoginPage extends StatelessWidget with Trans { ), ), initialValue: loginBloc.userRepository.user.password, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { - String? validationText = loginBloc.passwordValidation(val!); + String? validationText = loginBloc.passwordValidation(val); return validationText == null ? null : t(validationText); }, onChanged: (value) => loginBloc.add(LoginPasswordChange(password: value)), diff --git a/lib/view/registration.dart b/lib/view/registration.dart index 60bf2f3..24590f9 100644 --- a/lib/view/registration.dart +++ b/lib/view/registration.dart @@ -163,7 +163,7 @@ class RegistrationPage extends StatelessWidget with Trans { ), ), initialValue: loginBloc.userRepository.user.password, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (val) { final String? validator = loginBloc.passwordValidation(val!); return validator == null ? null : t(validator); diff --git a/lib/view/reset_password.dart b/lib/view/reset_password.dart index 440ce9b..114565b 100644 --- a/lib/view/reset_password.dart +++ b/lib/view/reset_password.dart @@ -31,7 +31,7 @@ class ResetPasswordPage extends StatelessWidget with Trans { if (state is PasswordResetError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: Colors.orange, content: Text(t(state.message), style: TextStyle(color: Colors.white)))); - } else if (state is PasswordResetReady) { + } else if (state is PasswordResetFinished) { Navigator.of(context).pop(); } }, diff --git a/lib/view/sales_page.dart b/lib/view/sales_page.dart index 5814c1c..65c4680 100644 --- a/lib/view/sales_page.dart +++ b/lib/view/sales_page.dart @@ -150,7 +150,7 @@ class SalesPage extends StatelessWidget with Trans, Logging { bloc.product2Display.forEach((element) { final String title = element.sort == 3 ? t("Montly") : t("Annual"); final String desc4 = element.sort == 1 ? "" : t("Predictions with Artificial Intelligence"); - late String badge; + String? badge; if (element.sort == 2) { badge = t("14% discount"); } else if (element.sort == 1) { diff --git a/lib/view/test_set_execute.dart b/lib/view/test_set_execute.dart index b900866..a137812 100644 --- a/lib/view/test_set_execute.dart +++ b/lib/view/test_set_execute.dart @@ -3,12 +3,16 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/test_set_execute/test_set_execute_bloc.dart'; import 'package:aitrainer_app/library/custom_icon_icons.dart'; +import 'package:aitrainer_app/library/fade_in.dart'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/dialog_common.dart'; import 'package:aitrainer_app/widgets/menu_image.dart'; import 'package:aitrainer_app/widgets/victory_widget.dart'; +import 'package:ezanimation/ezanimation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -64,7 +68,9 @@ class TestSetExecute extends StatelessWidget with Trans { }), ), floatingActionButton: FloatingActionButton.extended( - onPressed: () => executeExercise(executeBloc, executeBloc.getNext()!, context), + onPressed: () => executeBloc.getNext() != null + ? executeExercise(executeBloc, executeBloc.getNext()!, context) + : Navigator.of(context).pushNamed('home'), backgroundColor: Colors.orange[800], icon: Icon(CustomIcon.weight_hanging), label: Text( @@ -86,11 +92,6 @@ class TestSetExecute extends StatelessWidget with Trans { tiles.add(getStartTile(bloc)); tiles.addAll(getExerciseTiles(bloc, context)); tiles.add(getEndTile()); - /* if (bloc.isDone100Percent()) { - tiles.add(Victory( - victory: true, - )); - } */ return tiles; } @@ -254,7 +255,9 @@ class TestSetExecute extends StatelessWidget with Trans { //if (element != null && element.exerciseTypeId != null) { tiles.add(GestureDetector( onDoubleTap: () => print("Execute ${element.exerciseType!.nameTranslation}"), - onTap: () => executeExercise(bloc, element, context), + onTap: () => element.state.equalsTo(ExercisePlanDetailState.finished) + ? evaluation(bloc, element) + : executeExercise(bloc, element, context), child: ExerciseTile( bloc: bloc, exercisePlanDetail: element, @@ -266,12 +269,27 @@ class TestSetExecute extends StatelessWidget with Trans { return tiles; } + void evaluation(TestSetExecuteBloc bloc, ExercisePlanDetail planDetail) { + if (planDetail.exercises != null && planDetail.exercises!.isNotEmpty) { + ExerciseRepository exerciseRepository = ExerciseRepository(); + exerciseRepository.start = planDetail.exercises![0].dateAdd; + exerciseRepository.exerciseList = Cache().getExercises(); + exerciseRepository.actualExerciseList = planDetail.exercises!; + print("actualEx ${exerciseRepository.actualExerciseList}"); + LinkedHashMap args = LinkedHashMap(); + args['exerciseRepository'] = exerciseRepository; + args['exercise'] = planDetail.exercises![0]; + args['past'] = true; + Navigator.of(context).pushNamed('evaluationPage', arguments: args); + } + } + void executeExercise(TestSetExecuteBloc bloc, ExercisePlanDetail exercisePlanDetail, BuildContext context) { ExercisePlanDetail? next = bloc.getNext(); if (next != null) { final HashMap args = HashMap(); - args['exerciseType'] = exercisePlanDetail.exerciseType; + args['exerciseType'] = next.exerciseType; args['exercisePlanDetailId'] = exercisePlanDetail.exercisePlanDetailId; args['testSetExecuteBloc'] = bloc; String title = ""; @@ -314,17 +332,43 @@ class TestSetExecute extends StatelessWidget with Trans { } // ignore: must_be_immutable -class ExerciseTile extends StatelessWidget with Trans { +class ExerciseTile extends StatefulWidget { final TestSetExecuteBloc bloc; final ExercisePlanDetail exercisePlanDetail; ExerciseTile({required this.bloc, required this.exercisePlanDetail}); + @override + _ExerciseTileState createState() => _ExerciseTileState(); +} + +class _ExerciseTileState extends State with Trans { + final EzAnimation animation = EzAnimation(1.0, 30.0, Duration(seconds: 3), reverseCurve: Curves.easeIn); + + @override + void initState() { + animation.start(); + animation.addStatusListener((status) { + if (status == AnimationStatus.completed) { + setState(() {}); + } + }); + + super.initState(); + } + + @override + bool didUpdateWidget(ExerciseTile oldWidget) { + super.didUpdateWidget(oldWidget); + animation.start(); + return true; + } + Widget getIndicator(ExercisePlanDetailState state) { - ExercisePlanDetail? next = bloc.getNext(); + ExercisePlanDetail? next = widget.bloc.getNext(); bool actual = false; if (next != null) { - if (next.exerciseTypeId == exercisePlanDetail.exerciseTypeId) { + if (next.exerciseTypeId == widget.exercisePlanDetail.exerciseTypeId) { actual = true; } } @@ -357,10 +401,11 @@ class ExerciseTile extends StatelessWidget with Trans { @override Widget build(BuildContext context) { - final ExercisePlanDetailState state = exercisePlanDetail.state; + final ExercisePlanDetailState state = widget.exercisePlanDetail.state; final bool done = state.equalsTo(ExercisePlanDetailState.finished); - final String countSerie = exercisePlanDetail.exercises == null ? "1" : (exercisePlanDetail.exercises!.length).toString(); - final String serie = exercisePlanDetail.exerciseType!.unitQuantityUnit == null ? "/1" : "/4"; + final String countSerie = widget.exercisePlanDetail.exercises == null ? "1" : (widget.exercisePlanDetail.exercises!.length).toString(); + final String serie = widget.exercisePlanDetail.exerciseType!.unitQuantityUnit == null ? "/1" : "/4"; + setContext(context); return Container( color: Colors.transparent, @@ -387,8 +432,8 @@ class ExerciseTile extends StatelessWidget with Trans { width: 120, height: 80, child: MenuImage( - imageName: bloc.getActualImageName(exercisePlanDetail.exerciseType!.exerciseTypeId), - workoutTreeId: bloc.getActualWorkoutTreeId(exercisePlanDetail.exerciseType!.exerciseTypeId)!, + imageName: widget.bloc.getActualImageName(widget.exercisePlanDetail.exerciseType!.exerciseTypeId), + workoutTreeId: widget.bloc.getActualWorkoutTreeId(widget.exercisePlanDetail.exerciseType!.exerciseTypeId)!, )), SizedBox( width: 10, @@ -403,7 +448,7 @@ class ExerciseTile extends StatelessWidget with Trans { ), children: [ TextSpan( - text: exercisePlanDetail.exerciseType!.nameTranslation, + text: widget.exercisePlanDetail.exerciseType!.nameTranslation, style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.bold, @@ -421,20 +466,20 @@ class ExerciseTile extends StatelessWidget with Trans { ), ], )), - exercisePlanDetail.exerciseType!.unitQuantityUnit != null + widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null ? TextSpan( text: "\n", ) : TextSpan(), - exercisePlanDetail.exerciseType!.unitQuantityUnit != null + widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null ? TextSpan( - text: t(exercisePlanDetail.exerciseType!.unitQuantityUnit!) + ": ", + text: t(widget.exercisePlanDetail.exerciseType!.unitQuantityUnit!) + ": ", style: GoogleFonts.inter( fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)) : TextSpan(), - exercisePlanDetail.exerciseType!.unitQuantityUnit != null + widget.exercisePlanDetail.exerciseType!.unitQuantityUnit != null ? TextSpan( - text: t(bloc.getExerciseWeight(exercisePlanDetail)), + text: t(widget.bloc.getExerciseWeight(widget.exercisePlanDetail)), style: GoogleFonts.inter( fontSize: 12, )) @@ -443,11 +488,11 @@ class ExerciseTile extends StatelessWidget with Trans { text: "\n", ), TextSpan( - text: t(exercisePlanDetail.exerciseType!.unit) + ": ", + text: t(widget.exercisePlanDetail.exerciseType!.unit) + ": ", style: GoogleFonts.inter( fontSize: 12, color: done ? Colors.grey[100] : Colors.yellow[400], fontWeight: FontWeight.bold)), TextSpan( - text: bloc.repeatTimesText(exercisePlanDetail), + text: widget.bloc.repeatTimesText(widget.exercisePlanDetail), style: GoogleFonts.inter( fontSize: 12, )), @@ -465,6 +510,19 @@ class ExerciseTile extends StatelessWidget with Trans { )), ]), )), + done + ? AnimatedBuilder( + animation: animation, + builder: (context, snapshot) { + return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + Image.asset( + "asset/image/kupa.png", + width: animation.value, + ), + Text("Result", style: GoogleFonts.inter(fontSize: 10, color: Colors.white)), + ]); + }) + : Offstage() ]), ), ), diff --git a/lib/view/test_set_new.dart b/lib/view/test_set_new.dart index 5c47114..01a5ea1 100644 --- a/lib/view/test_set_new.dart +++ b/lib/view/test_set_new.dart @@ -92,9 +92,11 @@ class TestSetNew extends StatelessWidget with Trans { return ExerciseSave( exerciseName: bloc.exerciseType.nameTranslation, exerciseDescription: bloc.exerciseType.descriptionTranslation, - exerciseTask: t("Please take a relative bigger weight and repeat 12-20 times"), + exerciseTask: bloc.exerciseType.unitQuantityUnit != null + ? t("Please take a relative bigger weight and repeat 12-20 times and do your best! MAXIMIZE it!") + : t("Please repeat as much times as you can! MAXIMIZE it!"), unit: bloc.exerciseType.unit, - unitQuantityUnit: bloc.exerciseType.unitQuantityUnit!, + unitQuantityUnit: bloc.exerciseType.unitQuantityUnit, hasUnitQuantity: bloc.exerciseType.unitQuantityUnit != null, onQuantityChanged: (value) { bloc.add(TestSetNewChangeQuantity(quantity: double.parse(value))); diff --git a/lib/widgets/bmi_widget.dart b/lib/widgets/bmi_widget.dart index 533e321..7d5c68f 100644 --- a/lib/widgets/bmi_widget.dart +++ b/lib/widgets/bmi_widget.dart @@ -241,7 +241,7 @@ class _BMIState extends State with Trans { } Widget getHeightInput() { - if (widget.exerciseBloc.customerRepository.customer.birthYear! < 2003) { + if (widget.exerciseBloc.customerRepository.customer!.birthYear! < 2003) { return Flexible( child: TextFormField( focusNode: _nodeText2, diff --git a/lib/widgets/bmr_widget.dart b/lib/widgets/bmr_widget.dart index b5bdc16..be6ca0b 100644 --- a/lib/widgets/bmr_widget.dart +++ b/lib/widgets/bmr_widget.dart @@ -3,7 +3,7 @@ import 'package:aitrainer_app/bloc/exercise_new/exercise_new_bloc.dart'; import 'package:aitrainer_app/util/app_localization.dart'; import 'package:aitrainer_app/model/fitness_state.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:dropdown_search/dropdown_search.dart'; +import 'package:aitrainer_app/library/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -196,7 +196,7 @@ class _BMRState extends State with Trans { } Widget getHeightInput() { - if (widget.exerciseBloc.customerRepository.customer.birthYear! < 2003) { + if (widget.exerciseBloc.customerRepository.customer!.birthYear! < 2003) { return Flexible( child: TextFormField( focusNode: _nodeText2, diff --git a/lib/widgets/bottom_bar_multiple_exercises.dart b/lib/widgets/bottom_bar_multiple_exercises.dart index 4b4188e..7195845 100644 --- a/lib/widgets/bottom_bar_multiple_exercises.dart +++ b/lib/widgets/bottom_bar_multiple_exercises.dart @@ -71,7 +71,7 @@ class _BottomBarMultipleExercisesState extends State ], ), ), - height: 90, + height: 70, child: BlocConsumer(listener: (context, state) { if (state is TestSetExecuteError) { ScaffoldMessenger.of(context) diff --git a/lib/widgets/bottom_nav.dart b/lib/widgets/bottom_nav.dart index 2618466..a639439 100644 --- a/lib/widgets/bottom_nav.dart +++ b/lib/widgets/bottom_nav.dart @@ -1,10 +1,12 @@ -import 'package:aitrainer_app/library/gradient_bottom_navigation_bar.dart'; +import 'dart:ui'; + import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:aitrainer_app/util/track.dart'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:convex_bottom_bar/convex_bottom_bar.dart'; import 'package:flutter/material.dart'; // ignore: must_be_immutable @@ -34,91 +36,93 @@ class _NawDrawerWidget extends State with Trans, Logging { Widget build(BuildContext context) { final Color bgrColor = Color(0xffb4f500); final Color bgrColorEnd = Colors.blue; - final Color active = Colors.black; - final Color inactive = Colors.black26; + final Color active = Colors.white; + final Color inactive = Colors.black38; setContext(context); - return GradientBottomNavigationBar( - currentIndex: widget.bottomNavIndex, // this will be set when a new tab is tapped - backgroundColorStart: bgrColorEnd, - backgroundColorEnd: bgrColor, - fixedColor: active, - items: [ - BottomNavigationBarItem( - backgroundColor: bgrColor, - icon: Common.badgedIcon(inactive, Icons.home, "home"), - activeIcon: Common.badgedIcon(active, Icons.home, "home"), - title: Text(t("Home"), style: TextStyle(fontSize: 12))), - BottomNavigationBarItem( - backgroundColor: bgrColor, - icon: Common.badgedIcon(inactive, Icons.trending_up, "development"), - activeIcon: Common.badgedIcon(active, Icons.trending_up, "development"), - title: Text( - t("My Development"), - style: TextStyle(fontSize: 12), - ), - ), - BottomNavigationBarItem( - backgroundColor: bgrColor, - icon: Icon(Icons.featured_play_list, color: inactive), - activeIcon: Icon( - Icons.featured_play_list, - color: active, - ), - title: Text( - t("My Training Plan"), - style: TextStyle(fontSize: 12), - ), - ), - BottomNavigationBarItem( - backgroundColor: bgrColor, - icon: Common.badgedIcon(inactive, Icons.person, "account"), - activeIcon: Common.badgedIcon(active, Icons.person, "account"), - title: Text( - t("Account"), - style: TextStyle(fontSize: 12), - )), - BottomNavigationBarItem( - backgroundColor: bgrColor, - icon: Icon(Icons.settings, color: inactive), - activeIcon: Icon(Icons.settings, color: active), - title: Text(t("Settings"), style: TextStyle(fontSize: 12))), - ], - onTap: (index) { - setState(() { - widget.bottomNavIndex = index; - switch (index) { - case 0: - Navigator.of(context).pop(); - Track().track(TrackingEvent.home); - Navigator.of(context).pushNamed('home'); + return StyleProvider( + style: Style(), + child: ConvexAppBar( + initialActiveIndex: widget.bottomNavIndex, + curve: Curves.easeIn, + style: TabStyle.react, + color: inactive, + height: 55, + gradient: LinearGradient(colors: [bgrColorEnd, bgrColor], stops: [0.1, .75]), + items: [ + TabItem( + isIconBlend: false, + title: t("Home"), + icon: Common.badgedIcon(inactive, Icons.home, "home"), + activeIcon: Common.badgedIcon(active, Icons.home, "home"), + ), + TabItem( + title: t("Growth"), + icon: Common.badgedIcon(inactive, Icons.trending_up, "development"), + activeIcon: Common.badgedIcon(active, Icons.trending_up, "development"), + ), + TabItem( + title: t("Training"), + icon: Icon(Icons.featured_play_list, color: inactive), + activeIcon: Icon(Icons.featured_play_list, color: active)), + TabItem( + title: t("Account"), + icon: Common.badgedIcon(inactive, Icons.person, "account"), + activeIcon: Common.badgedIcon(active, Icons.person, "account"), + ), + TabItem(title: t("Settings"), icon: Icon(Icons.settings, color: inactive), activeIcon: Icon(Icons.settings, color: active)), + ], + onTap: (index) { + setState(() { + widget.bottomNavIndex = index; + switch (index) { + case 0: + Navigator.of(context).pop(); + Track().track(TrackingEvent.home); + Navigator.of(context).pushNamed('home'); - break; - case 1: - Navigator.of(context).pop(); - Track().track(TrackingEvent.my_development); - Navigator.of(context).pushNamed('myDevelopment'); - break; - case 2: - Navigator.of(context).pop(); - Track().track(TrackingEvent.my_exerciseplan); - Navigator.of(context).pushNamed('myExercisePlan'); + break; + case 1: + Navigator.of(context).pop(); + Track().track(TrackingEvent.my_development); + Navigator.of(context).pushNamed('myDevelopment'); + break; + case 2: + Navigator.of(context).pop(); + Track().track(TrackingEvent.my_exerciseplan); + Navigator.of(context).pushNamed('myExercisePlan'); - break; - case 3: - Navigator.of(context).pop(); - Track().track(TrackingEvent.account); - Navigator.of(context).pushNamed('account'); + break; + case 3: + Navigator.of(context).pop(); + Track().track(TrackingEvent.account); + Navigator.of(context).pushNamed('account'); - break; - case 4: - Navigator.of(context).pop(); - Track().track(TrackingEvent.settings); - Navigator.of(context).pushNamed('settings'); + break; + case 4: + Navigator.of(context).pop(); + Track().track(TrackingEvent.settings); + Navigator.of(context).pushNamed('settings'); - break; - } - }); - }); + break; + } + }); + })); + } +} + +class Style extends StyleHook { + @override + double get activeIconSize => 50; + + @override + double get activeIconMargin => 10; + + @override + double get iconSize => 20; + + @override + TextStyle textStyle(Color color) { + return TextStyle(fontSize: 12, color: color); } } diff --git a/lib/widgets/dialog_common.dart b/lib/widgets/dialog_common.dart index 11aee68..4d3423f 100644 --- a/lib/widgets/dialog_common.dart +++ b/lib/widgets/dialog_common.dart @@ -42,7 +42,7 @@ class _DialogPremiumState extends State with Trans { ), elevation: 0, backgroundColor: Colors.transparent, - child: contentBox(context), + child: SingleChildScrollView(padding: EdgeInsets.only(bottom: 30), child: contentBox(context)), ); } diff --git a/lib/widgets/exercise_save.dart b/lib/widgets/exercise_save.dart index 1a357fb..707a371 100644 --- a/lib/widgets/exercise_save.dart +++ b/lib/widgets/exercise_save.dart @@ -129,109 +129,105 @@ class _ExerciseSaveState extends State with Trans { return KeyboardActions( config: _buildConfig(context), child: Container( - padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - widget.exerciseName, - style: GoogleFonts.archivoBlack( - fontWeight: FontWeight.bold, - fontSize: 24, - color: Colors.white, - shadows: [ - Shadow( - offset: Offset(5.0, 5.0), - blurRadius: 12.0, - color: Colors.black54, - ), - Shadow( - offset: Offset(-3.0, 3.0), - blurRadius: 12.0, - color: Colors.black54, - ), - ], - ), - overflow: TextOverflow.fade, - maxLines: 4, - softWrap: true, - textAlign: TextAlign.center, + scrollDirection: Axis.vertical, + child: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ + Text( + widget.exerciseName, + style: GoogleFonts.archivoBlack( + fontWeight: FontWeight.bold, + fontSize: 24, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(5.0, 5.0), + blurRadius: 12.0, + color: Colors.black54, + ), + Shadow( + offset: Offset(-3.0, 3.0), + blurRadius: 12.0, + color: Colors.black54, + ), + ], + ), + overflow: TextOverflow.fade, + maxLines: 4, + softWrap: true, + textAlign: TextAlign.center, + ), + SizedBox( + height: 15, + ), + Padding( + padding: const EdgeInsets.only(top: 10, left: 55, right: 55), + child: Text( + widget.exerciseDescription, + style: GoogleFonts.inter(fontSize: 12, color: Colors.yellow[300]), + maxLines: 1, + overflow: TextOverflow.fade, + softWrap: true, + )), + InkWell( + child: Text( + t("More »"), + style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[200]), + ), + onTap: () => { + showDialog( + context: context, + builder: (BuildContext context) { + return DialogHTML(title: widget.exerciseName, htmlData: '

' + widget.exerciseDescription + '

'); + }) + }, + ), + Divider( + color: Colors.transparent, + ), + Text( + t(widget.exerciseTask), + style: GoogleFonts.inter( + fontSize: 14, + color: Colors.orange, + fontWeight: FontWeight.bold, + ), + maxLines: 3, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: true, + ), + Divider( + color: Colors.transparent, + ), + columnQuantityUnit(), + Divider( + color: Colors.transparent, + ), + columnQuantity(), + Divider( + color: Colors.transparent, + ), + widget.hasUnitQuantity + ? Text( + t("Step") + ": " + "1/4", + style: GoogleFonts.inter( + fontSize: 22, + color: Colors.white, + fontWeight: FontWeight.bold, ), - SizedBox( - height: 15, - ), - Text( - widget.exerciseDescription, - style: GoogleFonts.inter(fontSize: 12, color: Colors.yellow[300]), - maxLines: 1, - overflow: TextOverflow.fade, - softWrap: true, - ), - InkWell( - child: Text( - t("More »"), - style: GoogleFonts.inter(fontSize: 12, color: Colors.blue[200]), - ), - onTap: () => { - showDialog( - context: context, - builder: (BuildContext context) { - return DialogHTML(title: widget.exerciseName, htmlData: '

' + widget.exerciseDescription + '

'); - }) - }, - ), - Divider( - color: Colors.transparent, - ), - widget.hasUnitQuantity - ? Text( - t(widget.exerciseTask), - style: GoogleFonts.inter( - fontSize: 14, - color: Colors.orange, - fontWeight: FontWeight.bold, - ), - maxLines: 3, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: true, - ) - : Offstage(), - Divider( - color: Colors.transparent, - ), - columnQuantityUnit(), - Divider( - color: Colors.transparent, - ), - columnQuantity(), - Divider( - color: Colors.transparent, - ), - widget.hasUnitQuantity - ? Text( - t("Step") + ": " + "1/4", - style: GoogleFonts.inter( - fontSize: 22, - color: Colors.white, - fontWeight: FontWeight.bold, - ), - maxLines: 3, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: true, - ) - : Offstage(), - Divider( - color: Colors.transparent, - ), - Divider( - color: Colors.transparent, - ), - /* TextButton( + maxLines: 3, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: true, + ) + : Offstage(), + Divider( + color: Colors.transparent, + ), + Divider( + color: Colors.transparent, + ), + /* TextButton( onPressed: () { widget.onSubmit(); /* showDialog( @@ -252,43 +248,46 @@ class _ExerciseSaveState extends State with Trans { ), ], )), */ - ]), - ))); + ]), + ))); } - Column columnQuantityUnit() { - Column row = Column(); + Widget columnQuantityUnit() { + Widget row = Padding(padding: const EdgeInsets.only(top: 10, left: 55, right: 55), child: Column()); if (widget.hasUnitQuantity) { - row = Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - TextFormField( - focusNode: _nodeText1, - controller: _controller1, - decoration: InputDecoration( - contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), - labelText: t(widget.unitQuantityUnit!), - labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]), - fillColor: Colors.black38, - filled: true, - border: OutlineInputBorder( - gapPadding: 8.0, - borderRadius: BorderRadius.circular(12.0), - borderSide: BorderSide(color: Colors.white12, width: 0.4), - ), - ), - keyboardType: TextInputType.numberWithOptions(decimal: true), - textInputAction: TextInputAction.done, - style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]), - onChanged: (value) => widget.onUnitQuantityChanged!(value)), - ]); + row = Padding( + padding: const EdgeInsets.only(top: 10, left: 55, right: 55), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + TextFormField( + focusNode: _nodeText1, + controller: _controller1, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), + labelText: t(widget.unitQuantityUnit!), + labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.yellow[50]), + fillColor: Colors.black38, + filled: true, + border: OutlineInputBorder( + gapPadding: 8.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.white12, width: 0.4), + ), + ), + keyboardType: TextInputType.numberWithOptions(decimal: true), + textInputAction: TextInputAction.done, + style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.yellow[300]), + onChanged: (value) => widget.onUnitQuantityChanged!(value)), + ])); } return row; } - Column columnQuantity() { + Widget columnQuantity() { if (widget.unit == "second") { return Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Padding( - padding: const EdgeInsets.only(bottom: 0), + padding: const EdgeInsets.only(top: 10, left: 55, right: 55), + //padding: const EdgeInsets.only(bottom: 0), child: StreamBuilder( stream: stopWatchTimer.rawTime, initialData: stopWatchTimer.rawTime.valueWrapper?.value, @@ -300,13 +299,14 @@ class _ExerciseSaveState extends State with Trans { padding: const EdgeInsets.all(8), child: Text( displayTime, - style: const TextStyle(fontSize: 40, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white), + style: const TextStyle(fontSize: 35, fontFamily: 'Helvetica', fontWeight: FontWeight.bold, color: Colors.white), ), ), ]); })), Padding( - padding: const EdgeInsets.all(2), + //padding: const EdgeInsets.all(2), + padding: const EdgeInsets.only(top: 10, left: 25, right: 25), child: Column( children: [ Padding( @@ -315,7 +315,7 @@ class _ExerciseSaveState extends State with Trans { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), color: Colors.white70, @@ -328,7 +328,7 @@ class _ExerciseSaveState extends State with Trans { ), ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), iconSize: 40, @@ -341,7 +341,7 @@ class _ExerciseSaveState extends State with Trans { ), ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 5), child: IconButton( padding: const EdgeInsets.all(2), iconSize: 40, @@ -364,30 +364,32 @@ class _ExerciseSaveState extends State with Trans { TimePickerWidget(onChange: (value) => widget.onQuantityChanged((value).toString())) ]); } - Column row = Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - TextFormField( - focusNode: _nodeText2, - controller: _controller2, - decoration: InputDecoration( - contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), - labelText: t(widget.unit), - labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.orange[50], decorationColor: Colors.black12), - fillColor: Colors.black38, - filled: true, - border: OutlineInputBorder( - gapPadding: 8.0, - borderRadius: BorderRadius.circular(12.0), - borderSide: BorderSide(color: Colors.black26, width: 0.4), + Widget row = Container( + padding: const EdgeInsets.only(top: 10, left: 55, right: 55), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + TextFormField( + focusNode: _nodeText2, + controller: _controller2, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 25, top: 5, bottom: 5), + labelText: t(widget.unit), + labelStyle: GoogleFonts.inter(fontSize: 20, color: Colors.orange[50], decorationColor: Colors.black12), + fillColor: Colors.black38, + filled: true, + border: OutlineInputBorder( + gapPadding: 8.0, + borderRadius: BorderRadius.circular(12.0), + borderSide: BorderSide(color: Colors.black26, width: 0.4), + ), + ), + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]), + onChanged: (value) { + widget.onQuantityChanged(value); + }, ), - ), - keyboardType: TextInputType.number, - textInputAction: TextInputAction.next, - style: GoogleFonts.archivoBlack(fontSize: 80, color: Colors.orange[200]), - onChanged: (value) { - widget.onQuantityChanged(value); - }, - ), - ]); + ])); return row; } diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index d0d47ca..22dce82 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -36,7 +36,7 @@ class _HomePageState extends State with Logging { } Future runDelayedEvent() async { - await Future.delayed(Duration(seconds: 2), () async { + await Future.delayed(Duration(milliseconds: 500), () async { // ignore: close_sinks SessionBloc sessionBloc = BlocProvider.of(context); if (sessionBloc.state != SessionReady()) { diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart index df23b78..8829ffd 100644 --- a/lib/widgets/menu_page_widget.dart +++ b/lib/widgets/menu_page_widget.dart @@ -68,7 +68,7 @@ class _MenuPageWidgetState extends State with Trans, Logging { @override bool didUpdateWidget(MenuPageWidget oldWidget) { super.didUpdateWidget(oldWidget); - scrollController.animateTo(40, duration: Duration(milliseconds: 300), curve: Curves.easeIn); + scrollController.animateTo(5, duration: Duration(milliseconds: 300), curve: Curves.easeIn); return true; } @@ -234,17 +234,18 @@ class _MenuPageWidgetState extends State with Trans, Logging { SliverGrid getFilterElements(MenuBloc menuBloc) { List list = []; + int index = 0; menuBloc.exerciseDeviceRepository.getGymDevices().forEach((element) { String deviceName = AppLanguage().appLocal == Locale('en') ? element.name : element.nameTranslation; ChoiceChip chip = ChoiceChip( - labelPadding: EdgeInsets.only(right: 5), + labelPadding: EdgeInsets.only(right: 3), avatar: Icon( Icons.remove_circle_outline, color: Colors.orange, - size: 18, + size: 10, ), label: Text(deviceName), - labelStyle: TextStyle(fontSize: 9, color: Colors.black), + labelStyle: TextStyle(fontSize: 9, color: Colors.indigo), selectedColor: Colors.white, selected: menuBloc.selectedDevice(element.exerciseDeviceId), backgroundColor: Colors.blue[100], @@ -252,6 +253,11 @@ class _MenuPageWidgetState extends State with Trans, Logging { onSelected: (value) => menuBloc.add(MenuFilterExerciseType(deviceId: element.exerciseDeviceId)), ); list.add(chip); + if (index == 4) { + list.add(Divider()); + } + + index++; }); SliverGrid sliverList = SliverGrid( diff --git a/lib/widgets/menu_search_bar.dart b/lib/widgets/menu_search_bar.dart index 5e7efb2..fe98635 100644 --- a/lib/widgets/menu_search_bar.dart +++ b/lib/widgets/menu_search_bar.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:dropdown_search/dropdown_search.dart'; +import 'package:aitrainer_app/library/dropdown_search.dart'; //import 'package:aitrainer_app/library/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/lib/widgets/number_picker.dart b/lib/widgets/number_picker.dart index 7487775..12e1840 100644 --- a/lib/widgets/number_picker.dart +++ b/lib/widgets/number_picker.dart @@ -35,19 +35,23 @@ class _NumberPickerWidgetState extends State with Trans { void initState() { super.initState(); _scrollController = FixedExtentScrollController(initialItem: widget.initalValue); + _scrollController.animateToItem(widget.initalValue, duration: Duration(milliseconds: 100), curve: Curves.easeIn); } @override void didUpdateWidget(NumberPickerWidget oldWidget) { super.didUpdateWidget(oldWidget); - _scrollController.animateToItem(widget.initalValue, duration: Duration(milliseconds: 100), curve: Curves.easeIn); } Widget durationPicker({bool inSeconds = false, bool inHundredths = false}) { double value = 0; return CupertinoPicker( scrollController: _scrollController, + useMagnifier: true, + magnification: 1.2, + diameterRatio: 0.85, backgroundColor: Colors.transparent, + selectionOverlay: Container(), onSelectedItemChanged: (x) { currentData = x.toDouble(); value = x.toDouble(); @@ -57,7 +61,7 @@ class _NumberPickerWidgetState extends State with Trans { }, children: List.generate( widget.maxValue, (index) => Text('$index ' + widget.unit, style: TextStyle(color: widget.color, fontSize: widget.fontSize))), - itemExtent: 40, + itemExtent: 30, ); } @@ -66,32 +70,30 @@ class _NumberPickerWidgetState extends State with Trans { Widget build(BuildContext context) { setContext(context); return Container( - //color: Colors.white24, + color: Colors.transparent, width: MediaQuery.of(context).size.width * .40, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 5.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.transparent, - width: MediaQuery.of(context).size.width * .35, - child: Center( - child: Container( - color: Colors.transparent, - width: MediaQuery.of(context).size.width * 0.95, - height: MediaQuery.of(context).size.height * 0.25, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(child: durationPicker()), - ], - )), - ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.transparent, + width: MediaQuery.of(context).size.width * .35, + child: Center( + child: Container( + color: Colors.transparent, + width: MediaQuery.of(context).size.width * 0.95, + //height: MediaQuery.of(context).size.height * 0.25, + height: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded(child: durationPicker()), + ], + )), ), - ], - ), + ), + ], ), ); } diff --git a/pubspec.lock b/pubspec.lock index d0704ae..63995c4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -77,14 +77,14 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.6.3" + version: "2.0.0" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.5" + version: "0.4.7" build_daemon: dependency: transitive description: @@ -98,21 +98,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.5.4" + version: "2.0.0" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.11.5" + version: "1.12.2" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.10" + version: "6.1.12" built_collection: dependency: transitive description: @@ -154,7 +154,7 @@ packages: name: checked_yaml url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" chewie: dependency: transitive description: @@ -211,6 +211,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + convex_bottom_bar: + dependency: "direct main" + description: + name: convex_bottom_bar + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" coverage: dependency: transitive description: @@ -252,7 +259,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.14" + version: "2.0.0" devicelocale: dependency: "direct main" description: @@ -260,13 +267,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.1" - dropdown_search: - dependency: "direct main" - description: - name: dropdown_search - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.0" equatable: dependency: "direct main" description: @@ -315,7 +315,7 @@ packages: name: firebase_analytics url: "https://pub.dartlang.org" source: hosted - version: "8.0.0-dev.0" + version: "8.0.0-dev.2" firebase_analytics_platform_interface: dependency: transitive description: @@ -336,28 +336,28 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.3" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.2" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.5" firebase_core: dependency: "direct main" description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" firebase_core_platform_interface: dependency: transitive description: @@ -378,21 +378,21 @@ packages: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "9.1.0" + version: "9.1.1" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.4" fixnum: dependency: transitive description: @@ -407,6 +407,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.30.0" + flurry: + dependency: "direct main" + description: + name: flurry + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.7" flutter: dependency: "direct main" description: flutter @@ -425,21 +432,21 @@ packages: name: flutter_facebook_auth url: "https://pub.dartlang.org" source: hosted - version: "3.3.1+1" + version: "3.3.2+2" flutter_facebook_auth_platform_interface: dependency: transitive description: name: flutter_facebook_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" flutter_facebook_auth_web: dependency: transitive description: name: flutter_facebook_auth_web url: "https://pub.dartlang.org" source: hosted - version: "2.4.0+2" + version: "2.4.1+1" flutter_html: dependency: "direct main" description: @@ -545,7 +552,7 @@ packages: name: graphs url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "1.0.0" html: dependency: transitive description: @@ -615,7 +622,7 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "3.1.1" + version: "4.0.1" keyboard_actions: dependency: "direct main" description: @@ -636,7 +643,7 @@ packages: name: logging url: "https://pub.dartlang.org" source: hosted - version: "0.11.4" + version: "1.0.1" matcher: dependency: transitive description: @@ -832,7 +839,7 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "0.1.8" + version: "1.0.0" purchases_flutter: dependency: "direct main" description: @@ -881,7 +888,7 @@ packages: name: sentry url: "https://pub.dartlang.org" source: hosted - version: "4.1.0-nullsafety.1" + version: "5.0.0" shared_preferences: dependency: "direct dev" description: @@ -963,14 +970,14 @@ packages: name: smartlook url: "https://pub.dartlang.org" source: hosted - version: "1.0.6" + version: "1.0.7" source_gen: dependency: transitive description: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "0.9.10+3" + version: "1.0.0" source_map_stack_trace: dependency: transitive description: @@ -1040,7 +1047,7 @@ packages: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" string_scanner: dependency: transitive description: @@ -1208,7 +1215,7 @@ packages: name: webkit_inspection_protocol url: "https://pub.dartlang.org" source: hosted - version: "0.7.4" + version: "1.0.0" webview_flutter: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index de05cb5..c8f0a9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: cupertino_icons: ^1.0.0 google_fonts: ^2.0.0 devicelocale: ^0.4.1 - sentry: ^4.1.0-nullsafety.1 + sentry: ^5.0.0 flutter_bloc: ^7.0.0 equatable: ^2.0.0 @@ -54,25 +54,28 @@ dependencies: confetti: ^0.6.0-nullsafety crypto: ^3.0.0 carousel_slider: ^4.0.0-nullsafety.0 - dropdown_search: ^0.5.0 + #dropdown_search: ^0.5.0 + convex_bottom_bar: ^3.0.0 - firebase_core: ^1.0.2 - firebase_analytics: ^8.0.0-dev.0 - firebase_messaging: ^9.1.0 + firebase_core: ^1.0.3 + firebase_analytics: ^8.0.0-dev.2 + firebase_messaging: ^9.1.1 flutter_local_notifications: ^5.0.0 - firebase_auth: ^1.0.1 - flutter_facebook_auth: ^3.3.1+1 + firebase_auth: ^1.0.3 + flutter_facebook_auth: ^3.3.2 google_sign_in: ^5.0.1 apple_sign_in: ^0.1.0 - smartlook: ^1.0.6 + smartlook: ^1.0.7 + flurry: ^0.0.4 #animated_widgets: ^1.0.6 mockito: ^5.0.3 sqflite: ^2.0.0+3 flutter_secure_storage: ^4.1.0 + #social_share: ^2.1.1 flutter_localizations: sdk: flutter @@ -167,6 +170,7 @@ flutter: - asset/image/BMI_graph_C.png - asset/image/BMI_mutato.png - asset/image/haken.png + - asset/image/kupa.png - asset/image/pict_calorie.png - asset/image/pict_development_by_bodypart_percent.png @@ -226,6 +230,8 @@ flutter: - asset/menu/3.bcs1.jpg - asset/menu/300m.jpg - asset/menu/400m.jpg + - asset/menu/FG_1_test.jpg + - asset/menu/FG_1_training.jpg - asset/menu/alternate_dumbbell_presses.jpg - asset/menu/alternate_standing_shoulder_press.jpg - asset/menu/arnold_press.jpg