From 89f6df8c7789b66db7de27f7850634612d44068d Mon Sep 17 00:00:00 2001 From: tall3at <91608104+tall3at@users.noreply.github.com> Date: Wed, 15 Dec 2021 21:14:46 +0500 Subject: [PATCH] basic structure --- assets/icons/ic_face_id.png | Bin 0 -> 6282 bytes assets/icons/ic_fingerprint.png | Bin 0 -> 12319 bytes assets/icons/ic_sms.png | Bin 0 -> 6433 bytes assets/icons/ic_whatsapp.png | Bin 0 -> 7235 bytes assets/images/bn_map.png | Bin 0 -> 43970 bytes ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + lib/config/app_provider.dart | 19 ++ lib/config/background_loader.dart | 34 ++ lib/config/constants.dart | 8 + lib/config/dependencies.dart | 38 +++ lib/config/localization.dart | 18 + lib/config/routes.dart | 46 +++ lib/generated/codegen_loader.g.dart | 94 ++++++ lib/main.dart | 139 +++----- lib/models/account.dart | 43 +++ lib/models/config_model.dart | 12 + lib/models/parent_list.dart | 26 ++ lib/models/response_models.dart | 34 ++ lib/models/user.dart | 9 + lib/pages/a.dart | 0 lib/pages/user/splash_page.dart | 48 +++ lib/provider/counter.dart | 20 ++ lib/repo/account_repository.dart | 49 +++ lib/services/backend_service.dart | 127 +++++++ lib/services/firebase_service.dart | 180 ++++++++++ lib/services/http_service.dart | 36 ++ lib/services/media_service.dart | 26 ++ lib/services/network_service.dart | 25 ++ lib/services/secure_storage.dart | 33 ++ lib/services/shared_preferences.dart | 119 +++++++ lib/theme/app_theme.dart | 29 ++ lib/theme/colors.dart | 13 + lib/utils/AppPermissionHandler.dart | 36 ++ lib/utils/dialogs.dart | 35 ++ lib/utils/navigator.dart | 9 + lib/utils/utils.dart | 311 ++++++++++++++++++ lib/widgets/app_bar.dart | 28 ++ lib/widgets/blurry_container.dart | 62 ++++ lib/widgets/button/show_circular_button.dart | 23 ++ lib/widgets/button/show_image_button.dart | 43 +++ lib/widgets/dialog/dialogs.dart | 16 + lib/widgets/dialog/message_dialog.dart | 38 +++ lib/widgets/dialog/otp_dialog.dart | 74 +++++ lib/widgets/dragable_sheet.dart | 26 ++ lib/widgets/dropdown/dropdow_field.dart | 78 +++++ lib/widgets/dropdown/dropdown_text.dart | 33 ++ lib/widgets/extensions/extensions_widget.dart | 235 +++++++++++++ lib/widgets/gradient_app_bar.dart | 63 ++++ lib/widgets/images/circular_image.dart | 30 ++ lib/widgets/show_card_buttton.dart | 32 ++ lib/widgets/show_fill_button.dart | 41 +++ lib/widgets/txt.dart | 170 ++++++++++ lib/widgets/txt_field.dart | 155 +++++++++ lib/widgets/user_image.dart | 26 ++ pubspec.lock | 307 ++++++++++++++++- pubspec.yaml | 14 + resources/langs/en-US.json | 38 +++ resources/langs/en.json | 38 +++ resources/s.dart | 0 test/widget_test.dart | 2 +- 61 files changed, 3086 insertions(+), 104 deletions(-) create mode 100644 assets/icons/ic_face_id.png create mode 100644 assets/icons/ic_fingerprint.png create mode 100644 assets/icons/ic_sms.png create mode 100644 assets/icons/ic_whatsapp.png create mode 100644 assets/images/bn_map.png create mode 100644 lib/config/app_provider.dart create mode 100644 lib/config/background_loader.dart create mode 100644 lib/config/constants.dart create mode 100644 lib/config/dependencies.dart create mode 100644 lib/config/localization.dart create mode 100644 lib/config/routes.dart create mode 100644 lib/generated/codegen_loader.g.dart create mode 100644 lib/models/account.dart create mode 100644 lib/models/config_model.dart create mode 100644 lib/models/parent_list.dart create mode 100644 lib/models/response_models.dart create mode 100644 lib/models/user.dart create mode 100644 lib/pages/a.dart create mode 100644 lib/pages/user/splash_page.dart create mode 100644 lib/provider/counter.dart create mode 100644 lib/repo/account_repository.dart create mode 100644 lib/services/backend_service.dart create mode 100644 lib/services/firebase_service.dart create mode 100644 lib/services/http_service.dart create mode 100644 lib/services/media_service.dart create mode 100644 lib/services/network_service.dart create mode 100644 lib/services/secure_storage.dart create mode 100644 lib/services/shared_preferences.dart create mode 100644 lib/theme/app_theme.dart create mode 100644 lib/theme/colors.dart create mode 100644 lib/utils/AppPermissionHandler.dart create mode 100644 lib/utils/dialogs.dart create mode 100644 lib/utils/navigator.dart create mode 100644 lib/utils/utils.dart create mode 100644 lib/widgets/app_bar.dart create mode 100644 lib/widgets/blurry_container.dart create mode 100644 lib/widgets/button/show_circular_button.dart create mode 100644 lib/widgets/button/show_image_button.dart create mode 100644 lib/widgets/dialog/dialogs.dart create mode 100644 lib/widgets/dialog/message_dialog.dart create mode 100644 lib/widgets/dialog/otp_dialog.dart create mode 100644 lib/widgets/dragable_sheet.dart create mode 100644 lib/widgets/dropdown/dropdow_field.dart create mode 100644 lib/widgets/dropdown/dropdown_text.dart create mode 100644 lib/widgets/extensions/extensions_widget.dart create mode 100644 lib/widgets/gradient_app_bar.dart create mode 100644 lib/widgets/images/circular_image.dart create mode 100644 lib/widgets/show_card_buttton.dart create mode 100644 lib/widgets/show_fill_button.dart create mode 100644 lib/widgets/txt.dart create mode 100644 lib/widgets/txt_field.dart create mode 100644 lib/widgets/user_image.dart create mode 100644 resources/langs/en-US.json create mode 100644 resources/langs/en.json create mode 100644 resources/s.dart diff --git a/assets/icons/ic_face_id.png b/assets/icons/ic_face_id.png new file mode 100644 index 0000000000000000000000000000000000000000..913e850d43c2df6b5a2109c2898167ebcffc2855 GIT binary patch literal 6282 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 W2txEMD5U;301S0ab(*vhkN*c^tNOD5 literal 0 HcmV?d00001 diff --git a/assets/icons/ic_fingerprint.png b/assets/icons/ic_fingerprint.png new file mode 100644 index 0000000000000000000000000000000000000000..bf731975d839e1023097654b284f9f991b42f844 GIT binary patch literal 12319 zcmWk!2Q*yI7hYEHT||`VeU(J-(TV82ETSbVL|d`S5~B0dyC8@XEH)(SYLVzIEP@p^ ztS*Zv`}cq6yfZWJ%*?$r^UZhfytxS`#=6uL_bC7X0JXlJmKgv*aF-{{JPA56xe?`^XlkZ4+qj>k$Zb@plJ6p->6$ zS1$wHT>RW6eEmHO50vi%0Nen5Ee(s1qWxlMo}=YYyqZj==5ED*01Z7aGltM?xSqLz zx&}Rcn1(hHGvXl&cX~OvpH)jM#FU_hg-Cdsp%(r~*o-j);7wRU6e`b>=UmRgxe`Pc1s`CXAD0jTbMj|fZoAifK0PakR^62Cq9Wo5pQUa%jTz3E^~I2&(85u@tVALdw$ zM8X3Q3+8+o?6Lr3;*yGMkbZp@D_j^YCdweJ@HPtM#%}GCg^#WNIi0xA_JEcOdz`Ax zBa9&TQ$_rDqITVM4Yeh7&~f0=8+eLZsUf8Sbd!XAx{0(VtVcI3cA%PZ_*RM~hrR1_ zf;>;~b`R}fC%LYP!u&GHHSi0O(o-){U+Brx2Y^ryHH<4b$sbw)JS1qbS$}OUc6#9} zw~XvMe0RM1>f4?xX9D}lDrX|FUB-mOiNKiTKm@9LV(F9*wjBVJERxeLK}R$avWtx z;gDDypnK9AGJ5JOg%58m={<0%U@zxVKb&=;MeCqVe`apjSG&q1I3=6S*`mT9$06tU zgeckxy*Xo?Ji9*L;E1DK4Iu{Zqsv+ zcqQ)@+?0ii<<9-=qT*Qg=fAw+qJe{Qw?H6H+mC)Ba-tF#yLmx;=w(U*Av6ZNQN z^fyO7GxJBToY8GA?1|p(omOVv-$pojT|7!tm)rNmx;YYhMA&UVyeaa*HT zL9xIDleadxWvyH^6b+6>B<+oBh4OI%1>6iV5HWPcFCMNWDcUaAH%X5rI<<3GvOm#N z`t*^AA*hEV_9J}<9Ac6V`n9cp|FvEBk*5hzkdSL5@N^~QNeBD}(0!AK)J9TOrV%+L z*C)OA#h=<5i6LjV|D@ziXih1;erdGwX%_f|Ad{)k==lL&ZOPCKpo(FV8dXH zLi6oS4scb_!!5O9AfnTmk$Yd7{Xzjjx;gU1gyKG;w2F&h<_AtLoLV0FD3`fLXAXF) z6->M1fZ@SbPFx{XMT2`ux)+|GE3HQDFKF?+N(HPuyoqaKJ`GnRw71eq4uw<&@oq5` z;SO&({S-IVY7#bzsH2qh@dQPrMNa}#h=RdSmz3h%G3(aT0REzFkDpH~1jE=2tzI)$M!MXW9Q-XVIlnR16m)hE%z+hU z4NW12(AmJr;p5n&?MTVZ#4Gl0lOc;mf5d@=q|l+Hi}=GiZkD#O(s9h zikHs`TKmn?9AguFbEZX`0&K~Zquz*=MC<$K?zUAA6;*Ck28u&nq-fJcJY=R{?|Kh9 zq0IPheo-YUeW5KZ$lY^4&v8eG-o&A^RQ7BVG|-2c?}n!#5V|CbhjHL^KYgx@A^5)G z?6%DJEr-smO-D>9M!xx`?^Qw#jo|*F|L8MbGwq^x;h!^=?}@^F&D(c+dt;3g0%Dp{jHWSH7*}%#Ck7 zR9KVU)V5PcPp-Q9yZ%sGI@7L(88F6MN+q)t3&0*&L_z=6g=H3WR;{}*;-`Ukf9mG6 z!TvJW48(}X_(a+i!gu~D?Q%74WrH~{MEmPP{Iu|&jV_}>&8iuUU&bKJ=;JV9d+iTK zzbDx1H#4Y6JPQu4Tw<4&ajXPsQ;x9`BV7$Mf`T=!-Wwu{FU}YD#C#gn;ExFmQ}WI+ zdZXbX91wMp*oi04u7Rg3VIpP4w5|*JtyI_NElnHsjr>?$K5g0Flb>;^*z?S50!FUVIy6H42XBZ^qHDiM7X;gF4FJ|3<6{^zUJP_#FMamktSAB z9eL7Pq=QixBimIq$YggRKjN6v%a5Cc*U-n})`DC$XB;H+5$x!9U0?L`;)tn4Ce&?D1T&OD-&8x0|s+@ss7jHNB^vp~gc$E#`GXwNFTH*;~mnD{0DTS>AFgIx34JmR~%V6zIkA{h=Wg&%?Ry zl#H~Z6B63Gh@vwi}Z6I{2W!y>+mUvpiN_FIC;dWB>fi| z5DsQfXIe;HnfE;(Ni<@lR)6{d*&?F&-+$nS6e+d125dtgVJnYF%mWkLkBoU&t*sg zn3omGiZ4-eh^UGzs&6FyPbCirnrxB>$VR)ML|YfLT&D+bAJ?M9(N6kJql213AKf~z zjjkZgn(847SR~P>!pS-FJdxR=isD2I5F?q=?>*)HPQn1Wu>y^}j^p#F9#_zd91Sh; zy!%I=(KX)=vUDUr&?jvf?gn48RAvSwS=>`fSLVVbq2gq)Zc-fw&y5bjL4CJXrYpb$ zf^Jes@p2_l7R~?qN&>7(X1nr1kGPriN3nT>>sXfJ)BA{>mR0abqJPg(S2i1ZKJ=U_ zf)Y_ZSkZKI=(6yRhh9}v`lf3TmCkndG5{l3@e$^ndLklgzaX@^N`fUW$t?AKYI6at zc$cpwt#U~Ur#8br18(I;sQu(8yK@c)BC`VJO6G~H_;==C{N^@k!9KA8=r!?C^B#N$ zh>7@wM&DW(yrExOcqaPRgbVvgEf57Ep)sxjrBk?A_f?izx&1*+m#X1@3+4}7y~rmh zQV)H*<4HEpW1}H`oTEqFM7DMOnbm6;0oeP~m(pWwr zv%zGIBu=bNmD~7Z{RL`DE8+8`+_n?CKb>%Pmu`xO;dzGH2@yCQ@i&AtDB0v-@{2Z* z%Y8y$0k24*Rx6eClk0;dg!=>xoduP8sSJn%zx<=|iJp+d?wx8YVNai{WDY+)nO?)i#V9->0-nM-_*EK*)a@#~q zY5Hg0voPVJv=#2lZ@)~iSppR|uz2C#+Qa~I7#-)s{G%=P(I~+Y)>|pD#Upit%+crE zTX|?|(A4fVGi9@800GECpbk}sxg#Oud!(5vW37k}Mv;%!7aCku$#{4poL z%4Ok|`r4wFiM9FMal(Lbi@v4iR`$7Q`hOdEQ_SVLnqjj8ZYbk+UeWasDuC6)@QcKwr9zxuQAmSU~!V|dfkPe{&d0kLKTL#7FY_L}Pt zO-wF*$=P#x7X@DDK9c|RVvo(w3U6o-wqV%Xs3qVcwpMJ2&}lkQMuPQ&r0vmQ#p%*@ z=F!syshCSA2Ntwbg%LKYTj6Jneni|6L53%v1lWB16!GE93pXE(Ctp&RP!20Otz>~Z z8XTwvhNUyi#MhvO zW||VZk$DlE=sA|WVWSMv%cn2QrNclO?=$#*ocdX*!y+jlqGmieEwvL^%pG^sPi5!@ zO4Q$-QCN6*viks-A-+?78!ej%lp+R1u+PCVY4xf`z#4Oln(<=5oFbk-3>a*c?Jojr zKBL@n>s3D|x`oBwqNUv_A?B~?T3r-A-AoW}aduYUOl;-}u8g1)^vucseG%O9mX|$R z1@+>5OIYyBl445f8CuV21lrrRAfg#kyIBn>lD5^5?_E5&laWb3wP=? zH@*~QX@#*@UABq-89ato(FX`n?C&Y}8T$q6LVdgWN{jX0g#oTYh2@Ta!-LAiI zSA@`{eWnou3}YGVJn>(&>9+gMLi&u@Q#9C*Xc9EIrv&%w&FLUq+^!Lm*%|#KO3;M^seu_GUtF~J`Uo#3izN}#W+;ug z;nF^beKCT3fY)EPQTK@ba~qc$BkhPpFEP?&Exuu=<$!6Rxcc57xj)y?V883@@ zQhxJxvWub+5e6)Sz`NNU_1#&1n7#ASKO8ALJ~Mdk-<^H;>1*R77$v z393(a;wnu#=M6|IeE?7lBCz4)}71Q&u-Ujn7SJBAI{_CGpa!QKb zwAkFLV;=Eu-Dum&jf3ywDD4X9qf4sGloqa30nU^8lfahXour(9e)BYQ3Pn|uma6TMHkg<<#& z@J~k@Z~Kd0UfapKN}%-n(ztlJ-r9LK^hHw7kgKi5pP}v{Uz)Bv>jzDoV?h6EN<~PM81R)8w|Lcy| ztGQrskr=Dr@NErXxV;5&N4Df8dW1h>^VF8GTeeYl#=^Mz%-lZ%1mk@+)5FOb&u!GZ zgJ;>NEI#7IpUKr_W**~@CgNKU86hAIsDo*-equ{9?_hA}r?leKFe>vC1;VREW4;d7 zS8{#+k5+U;SAuSqu9~>kTo&io4`J~KS{3YBG6C9DuASgEcrm#sB&l0;MKg4I=B6q4 zYRKll^ligVBKudxab05G`r-RKke&bqS(lE#d3Ao<@=2bPa(gt4|5TJ*dU*G#)EPI> znuz)hT8PfbPwTj^?X|SmKQ!5wn}DtK**bnxOL_1YUMwZ4*p!m{SdI*Lt%E;gjcoE3 zh|est>Y-N+db7IiYv60OFVZ)MZ^@w{K6qp7`8LtgU`F6Mj24p$4_2w9KJ7Q@uT?r- zfVW0+pyxhvw6d0q?=cZ9`V!-mrU?dLZ$WwwG@_Sfk@0U)Tj*kW&j0!nm`&z3trT*Vwaf zq@F#k>?$N&wB171>xDI@;I(#Drn3zj72d7kn^@P>U_Rfv4xrDx%LSAmwV=& zmR>}htcyaHZyqe`mUA6tOzKeD@l0@$wKlS1BPM#Ov+6xGguCvjTYX+%8wcyVMddYS(sSNh5E6?By0AN!veXmR%g{J%)1+wj;* zFAeQB`F$42gqK14pF+yB>l_4~7`@E@=6kKR8aTx{5_deK49@&sHZSIOgh!bZcf4o9 zuJE8aq68kit_8DWM5FghNRUgC6DY5h#713$vA2hsy$#a5 z=Kk8>jxc~MYLqisF!N#03{l50gOsN?1KpVRVZ~EFq-tZ|^Wj6L+mzfr$+Yj$N4~OysXAT1xMwYM zWYfrlk z+~4mVA)qts1(R+aM4>MK_AhA`JT0see10Ci%uysuy6%jhp#P^x^Hj^?__?7bhhN8J zqwJ$Y75AB`mu;qqOV+Z+ZL;8O>dlPwdGCaBcwLBH3iGZpbZ&_XpX*gHdFy%P`}=Xa zu^oh#*hqV!16%tf>3)&O?D8*LU-GTseHY%c8@br_my|(g3LA8hy6X+Aqu1oZj z_1-JG=+?$kEwbDD=CTsZjUR}mgbDMXkq2e3u3TLS0Z~et%Y3yu`RVfn>iGm>esgvG zL{ch3+r5dXjim9=n`6;KBtuTQm~ao%?m;TPV#~ev6$?Vu)lmJvLyP>)+n-LB+sX`S#|c-!_-;ukDO{k}Lr>t(=#M=>=>VoNaL)XdI*SZo zu&SBpBtje-w8)XVN~&oK6rD$o+CF0g{H7|UJW&LMT>XrW1em;npEQSZ<%mIA!JL_# zSs;XUnayRbNQ>ejpp;6)_Utox2Pw0m6Dd4@JXA*VnGEg9chw^`vA-8wESr=Y%kifN z!a}mWhv*9Q*^1y1-s88~-28*W~DGw+5*Mjjoq3itF#1Fn--ijyUJ``QC^?*IpHbW?M%c24z_w6<;R zknmzpT46P8sxi{&DzsnuES+^XXb-?J0K~``fU$B zeM8Pl^p7{l^%g}`xw;k)vl2He*qWs7WvKuTFI)YQd4qsbY@cXLw*LfRmbZf~Pwb}5 zhGc_&AHOevQJAvQ|8WX1*-)6iq;Cna$tF-&kREXbm;{e@fiRN2RsIy^RDhMMf}aef zdZI`3I5Vk@H{}1xuBdY#k=}EsM-tr6gbr#tFhmq1~5_X)OYqzj;Oari;0ElGB13KToxk&5HB2d4q4ee2P0;wO- z0>m$z)!j)DdewDz!Zn1(0qvzZByg$m&;a7X#Mx{pNvYS+n-7$|AiX9sd}`Nk$7*a; z7L25HOYW1+3wMAGdt`(xf9pyew>_%S9>YbR#(P06?o13bi`_y{f z)y=!(u8Pl{K$OGv5PbL!M^-DFiOru(Z~&%`;Xm)%XcM3EkA5jn0HUrg(^^*iuAMFU zA;Co^^BqL@5+dEEl+2?bvMd^f1VS+O6%BbBfaSZ4#F4$`8Zw8FN|d^x(OR~Id5nZ7 zWsscT+)E91o924}lp3Xphb;5cdMbcDWl&dN@txd?cPBJ}q~m9G*<-_UG3_e^V48w$ z55`+wV|Tz2jo51v6YaCX_sVDD*NVQj)BvY4L;lW3b!_nS0ZsZ1vEM3>O;cUh0h0-K z_W;2tF3IvQ4@4KdyMQRkYiH!AJ3vx_??s%uZhmQwhyI|;1&~+A5`fE1&fjY~HbSf@ zZ!?MAYsxnyOmYBD9V>s-V#WY>Qv$vw&{$zZfnTgIS0^M{kid0|{_3nn5>&09Ynqb) z_`8280d1o3gXWJev%_O~Y>RY4Na2b|OjBaz;$22eG$qao^V-w#aGpyn0Fmnx+rF}C z0KCoevUS%o!?Kv(sig|{Z~t#Zi__bDABe(T5KFut>d`r)5h^MBwBbd_1ZT8d9t331>Oq|IF8}pQK`v%As~ z6Xd1x8bMkLileUN7!Hg`!3$16D%0+Y#OzJAi8ULbQwGk0Oc~_qAqnnp22S)l_`F9k zy@cvg&MoLv*I`8ipR?U+EGPl<(wz5DFn@+05BnuuCZbzHxOP5EdW$wX9_D%r;ap)5 z)qP|>zxLPQXA3eKN)rl8CaLF%Z`FM_7l>{e?$rj7_}z=J{kU);_Y*g8Xr;`7e-vze zoe#?j#MrKJNK*2d2jUKZRD(SrnHgK(Pc2T`=Ji07XH2^s<~@tZ489|#{fZ*!=ikW1 z*)=YJ$J+aE(AZ}BonoIc|2rnF3b9DHSO*{^5NRf>nM?KGA8I;o>iMXZz5412U|&ni zF;q;kk|pdq7?&X~T#spI)_+U`xP!oz8b{BE@epu;=dy>`+w{2fi~j6wy6&1B76Yc| z)Bm@(Asm@^TY9pSLp0+BJ75AI*G+@bngg!@cx zEF?CKZ2z{;`}A$}SkK1*w)9W(H2ZszS>UVnyR*@wveBAoAAXB-HyT*WWG<02-9K1@ zSBKyn>*C*b=Xn>2Va~$Dcj^@bRO(m%dQw*n=|+p^UoO?P16w7W_mLgf+%2BpgPORD>2eN^^CGZO$tcb;!4KO`mVzaJOAt^|d_umvK<$)iQpyNPWE zpR4vBcwM^;_OM{*)@VQY@PyN^gFc?2K-;rA!Wl;79Nq##i%1GkDufwqCb=4vq@vVW$&qrIm zW=`ENPJ!_7vh2!1&glJ9;*P`$wB#uJ_)C~!FHR-eQ9EH_L{;;uhZQ@{ggq#}5aFqL z{dkcwv{88n$2LdM0RdG3)z) z-nWn*Jz^xa96XL_DQOb-uz?=3%7^BNR_m*W=e#|Vi(Zx?>JW)!{nwN|;}JP3HT)79 zrkKm1pD3q^B}b-=s}uz%Y3H+~YQprHFPlCPb=+0|>(wHzEv8i|c=&Bxdn(34^LtNj zUyzm=Si1WzqQ%QPU#G3~&Fzgkq6-lDoY!WjejZ00y`13szDxJNhYuc}ZEE%&G(|i5 z{=3loM%7zB5ZZ3bnjqVz%Xss}7_m%>zuyYDZUnQgkJ?E=e%>kXXvaY*QL~3av{nT6>@ZWO3c^K_If-%~?dhwyyCrsbOukJK zuIPy9hy_E5+hw2d4Lyj*F)V`?d(03jq+_~xv)c_j16H@K~lk4B}+NZX8JZLA%5NkxemgXyAv=8BZ;wAli?^cP!R2L(Lo)rgEqBehk z_%lc?-s8Q?!`)fC*dhyFPLAIeN~#fo)L0;*H9u$6^YeuicvQvE#n0SHr?n9u2HGY` z0NP9eK9GRXm-Y%)L^I?^i&mQ1KSN?c2{*=x%~;b;b#{aUiLEka`hycjWUjO66|wkk zBb>!lo?ycyzx(1^I-1JJd5z&fJPgpV2zoO2e$Hd>ml&Ui|?k{qW7(k%7Da^ zLsw+(7Z8h)`7fw9Mt~*&lxD(Olg9+fbk!+k43r(RE3f^TGJ? zU!*a%=XZ-FjgOnDzJz~He@U~2sq*<&*(ES}5U&*GfM6Gvft9IXh>@QaymT#y1AT6X zh?gcls9bE5wK~&7(gt$P!q^H3r;W;b3@|Jg1G+4kDc81qK$2g0 z5NaQ21gHt0SOSj38vglQFN7CTBpZf@ywSNKPRidAKRM!?@~B7_B3GqE8y~T_vP0v2 z!ZeVPcbnzm;`F9XZ!mi>({>qXG4&=z^SzolI@}0h{~XjlPPvfG;>9E^)3G*@V=lIx zez3vG&DguI)xqAG1vMbOqU%1nQ^^s-BdLYR7( zxLB6tH6ZK4$l}Pq#9da{o{X~qn%n%571y4mP1mx+#Q%oGqnGuvkHni{7pQBBb~{0w z#Vmo&g1t%Q@Z6KrFeO7oz5HyTnD1{j7qkVUgYapeiW#v`=o^_PR=c&_=o`XN^{G@Z z-Q3!6?Y|$%d$_k+U3-*RwZEM4_cmlGNtqFc@GdPb?ulTNTfd@{w{j@$q?2W@^>urd z53?3Ven#)_5qCIdb5Z&TYjQ@?VRdTZs^}^wU)pyf-pi~PZT+u?%JstE7CQ+!n_7M{ zTP|Du=?!&z6dB(zEZP?&6x~3XU>or=b$Mrjao_u_27eAi%v;*VaM%kA2@g}ClLrh{ z>sZ5!AR?~8vyH7_+?il0*}|mAfnrBP ztv1|ucFF5(wW^~WBJcDNZRzQnF5*hqN`L1_=s2Y-J`!#qtdiXedPQJgSodmVxXPBJ zV6`u-3~zATy)r!0_gI*%*t2|4Pm2ft9#pRO-9U@eOOTZ~&~&_C>uOtmpGIRwP7XW5yd{1_GliaDmPLE$0$ZgGd)j7?}FSdM`J zBY=#9l1u79)_Iz!H@#>AA6Gal?Ble}8E`pqu`kVyAD7T$b$A}6-5y0~L);i2HFjp_ zg~UexE?iAchzT*N7|1FoI!;zsibxKPpl4&KlZ>m4u$)w zpcL~ViC|~}SkCN2Q3}rQ&PX<(Dyo^wp8c&O^MiY>ijGdfagUv&e?<65T(k3^V{EQh zGR0#!h;?7YgdYi(i{%VW@;5~Cw;uBpD7{o^{ZkosGo<7bX@$K?Q@mc!P@wqd)0PwR;u(X}AC+?;}J8MH&j%;4Z76Hq=Q;2t# zk!9r8vZ+|r%ehVy*26TXX+Gi8JMdKHfiUidwfOeNxUa`?E<&8Ml^H9dp__hxt#Ai~ zFWxz*e4$i__}mgL694;Yd09JSB(&^!s4Cf9b1AJ2Yx; zQ~pQHaG60_9ar5~PFG_W#Gq`x0^;@wp1>yXdfRw6!S$%Ox#`2ySpeyW$QxS5rE0+| zI1k)#K(^BCT} zh5d&&gW`j(9Lf(waXlYFP%ey5@0^w=S1$wfERgS>{~?d%INvqxy7`XtrufwQ=Fiy; zzt|JGV`*L=|pR?it6hYJpVGIB{*qhb7UI}Sm8VFY*Nzw~H& zAlNGP-+AP5sHk)Bq7$X~*t|g)xdF* zpX^JZydah){Xp*9pssZPrW1Un-}{QsEVw}nCQO-k)u0(~wf3Iq%3Nznjs8lF!ROt^ znDK*ztn3d{&bNiYMbqw5Vr=rSy|WgYf`D1+zH)38g-;O!SdBqgm~m3Jjl!m==Uln_ zyBD(MjIbc@yLT0H#g_s??iC-7j4^a;YMvcpZ;DV+D+S-;@iQ_DwZJ`XJLC?O(ag6<$4u-VYD}u zoovfXzD)mS@ut_Dk#8rp4SsqJmK?Pk)pawZ>s2Wy;-*JYO#YU$*93Kw%J&45aK4mZ z-j&?bK6Hx1f6J)W;rXgTo@Gmny*K$yUz2_C1lW74zJys01qpRU@^^XZFf3b3NPet)n zkqv$i?cu`5@FP#=I5M9LB}-+Q6@o|J-0-IB(Hae$yLOLV&AfuK#ZP<=;eWjjx(n!r z&Rn(QOJjVD4k}X!?Icb481xf@5a5yqr(qV%0Mnuupkc2ng)rxy_D|7xEgs=1+L?P{ z4+o79L^0qg2S+mAiVlc)GvP9V#Yng};GL}%vNYvQI7)!DxODG0; z6WJ<+OpDBjrb1Hmo!+1C`}6xFe&73e+{f#6?>+atUgvq9=iYNpE(&>45C{dbv9Sp{ z*xR7l*w|T9b~XSfYlyh#>Bbs3T1NISIe7&|C1sVP zFjX~m4NWa=onyLs`UZwZ$Bj))&CD%MSX#lYZEWpM+B+Z|ot%*_C|5Mb&E4aarxDON77>bXmE0~ZyK|RVUQt<9O{yW+*3~yO-fOzw+|t_C{-EPwXIFR6qu##$$4>?Z zsYAmfPe=bA8-F%2`Fv`6W_Iqy%UAOYi?5fKSKh4B-oAVPVeR8Oow4z0bL%tn%hzw) zJGjD>r;gPE!y4m%ST=|(?Y zVubp6(#tE`pi2lN4XhLu?+tP-MslDHW^) zg-$IP6(He&Fbj(-WR<|=I6{O&QG7dLvz(B5ek{n(KUA`%S?3Pw&@Dy3C|Kp&bOY=R zIyAK1Dfl_kwi`5kN!+ngXJfQHzd%ZFSNdHm{0AEQ2OwSs*Dpr4U?^U zi9Rfu#}^DsG&g^JqKX?~o5%N)zEGw_C>}j}o}RnB-w7a;<@lYMr#no(1Hr^5`YA^* zBcn3Vx6C&6&3_n*leMv@EzIZlErSW^YQkHUyooNhRfLWe3<+jU(`MSklTZz%)`Fd4zZU=-d%^1AytF^eE@8Rx*o3)SDUoHxDX_z#Td>)lm zyKPTaoBH(7+PYKJm;YuItm>J^P^3yz&KZ~Phs+56{HDU?)$svGibZQ^wa+`?ae;{j zT<+~oJ*`a!W9ctnuiJ&_ak+ku>@hJ?0e*nb-HyW*FH2d)H5c4}sZ3h3_d9v&VLIR@ zZ{E7XcmUiupe<{2wfeAhGOSjCV zVpySO=zis-{T?mew8L;nZfGd7^46vYWVeJmH|AtH>M(EJ#QEav+{rnU8<`@6+=CJAD2^iwnJvY@r73x>KPEYJ8HkGr(*0`G}*6hogz$k)6ub|}zvWk#EIBqj>|qP~d5;_N)>P6f2sj-naS%u;1S{NQjD0eBM>a+4pn%+oha2$B2rtnvZG zBo_}pFDeo*dL9N=fk!Bi69LxWV9uF#+s6>E6a;)7>7M#EhlH1M8ckJmf6ee9Qbca; zqZZk!xXD*;032`ds&=H!av=giwIk>md}>MB*X%5RRdpoUp?fFeP7!Ii>#*+0kpM{p+C%|w;@{d|VvCQ{*vz}7MtU9O)uF>ZCs`4)j|M4eg*vKGv+7#5ZJCN#>!z`H|(eYJ^Tjj1lxJ1aX^tf;EWv!5#+kgQNh40 zn%dbvQ_fpNz5p5qHJ~y@Pb7K zaw(PJxr);yUNf?(L}J$2&kEqRX|F%KHCu#H2wsyp|tcq?@Rv5nE>Yq8YG6 z1?#5OB?F^YXNz$?!aOjXe*`DR6&IX7>)C)T6=}Hxdw;S@lG{fhd7Ok55zn59k&OTa6f^} zp7vl#T|JJZh}8q;q=7DH74UpIm6~M1DB2ZxaiWHM0xhGH&I}|JtMO<408jRbWIu7L z)dA_Ln(-(Kq12KZEqc*)x=%lmsZ|%*I_DE}`}FR)dC6^8j#T}u2Ic4j^==?(sgQFg zIwHp}**ww1tLY`V8t~^VIA?RGv6OPnhG1pxEJ2iw^5G^HRa3lnHj$*2bc)z&F1%Q> zWu~gmyqrRMlfZpeZ?0@d*wXP+&{_lyy50206r&)90w;HqsDPV*-_oV?8Y$gNZDlrWzjNw?4^;3O??rY~j6(s^cS> z9M~lQDRx#aRb%CupQ!$7`h_!p#X#}VouImfNa1_@~vD^{J!R;G2c;#q=~Ouj>FS~LMQ5pGNPG*QW- z5Y^vU+Y4CCLNB>%lNWmr?@xu*C=v%bVXac}=p-pM7Ie~O@4r=NWTCuRdn;N0@`JEA zP{(4}vAjVT;q)Qg-EGIEXSa90e5J|JeI_s=hbS=v0)8#mJ8>#otIz}w#>AW{J@#PGB zy9q91_X2w>h-lKcW&itb*0ZWvXPq?_;1Aa)9>4>U95<7RG*;+IuKaO8p(GBGeoPoM z%Epw(Q$%vgaZpZP77)FytS!k`Osfiw^dkM`({jNxpq%ULIPL%HwVNBKF!TkyUHk>o ztibUKYzg!0bKHN-x){hJ9%uEf8TwHOt_&{BW`u4KJhS-u()atvyRKf;DM8{5Xn#QZ zv-#Mv@-L^Qx?E#a@!!unr62^^hSo59yvaGoZN4tG$zPB!U9{NYBnP8d0Z1!gInS*x zaV5RfH$dLKZ)2jJ(DUT>7Pu{TzzDoHkbSN9{hma#nQqwlTY^mRX_D_I!CpskPUv+PrX;ZGhy0RPoq68_|h4ptfO3o|Sjqya9@Uh7Zslezh?cW6=j zxM-dHtK%dc@!_qSIqsFy{uJTeP{qn~H(5UNvMS~l3S1xWH`KZJ=Qr*_MfzLxCVl1V z+iz3E>DKiKt@K*EpE-W|AeXoT(>~4jZl$JByuh)aTsbt@*mWa`cK$fDXK4ptp^()k|-VJK%`ZS=jR*)?gqd{g+TbWyzX zZpl3ZE7FJA@ynXk*V?c+b+KRPImrXDbx|ZHU%rlk&UI;{@U>sUC71P6o}GBqQRkl# zD(F*DBFcZ}?M{X==k!&8rn(W$UBvbO;S^(!J zkhM~p1b=-iNfho^>^pZ`IEL{N9jFFhk(wK==WkX)Q&$A#?;q_Lp*|XWRCjJ$E%Sl* z)B09rpv432nvwHjaL<}zXtyepZj5!d1Ke?LqA@kG>K(H1QK+q^dcM8+$%4Vxpz8gF z`g09h0*U$Fq23Bg%eZUa2~@}|7JJ|)EDxh|b*yB&3bxf1gRU1A6*~N>B|KwHJ3QuQ#qT%KwT$;|OTC8GWN za3IMw)waO+ms&LR*j>N@W zQs)|uAnXn_+1;r-#~|~-INfEJtc1UoUxlOQOizXbmvWMAs>(Ot5K&_{Da6{NZ)z3n zS1Royx)@zoqN9F?3LuoX`NCbkNw|8^=e>fDbQ%*f5j#@$Xe?x?%WNpK6=@c$e#Q__6j zT8Au685!_A?#9Z#Mnp#!k0%Ui@-bZ|{EA-QCCB(YQmBI-Ae*dUFT`(fUZ>X+-`}>dT<8<`ySOmpyKE9=VGbQX}ZGq&aYeC{|Tpl~uEDM!4 zDQ|#BpK^qmY$}br?S69O^8Kc`{rcrg*b%*0^Kqv&k~=7Q6FGCH+r?&3b*BTP!MQ6Q zB8IT~sImX|tyF^t0iSz_bROATgDEF`nALu`t-^dV zxBqwZH%Z*CGpPuLeOq#5N*GUfUUffxz4VdfKfSCf+(Ga+1bx$_a4BlO*vSFSZQczILysVkTf*3 z=Sx}|0R6e}Z@P&-7xZC9=4b!_Ftcd0aK2>;HnR_-p)tAq@1!;Ge*LeGAO?C9g9`G( zM7V}}(nLf=DEbEahq=23dnyKndY9~66QZG^`wh25L;wE$d#?ZgF604nV11AtZeKYoENybc`gEZ`S=9{g@i>!#l$5pNlIP5A}s@w1mPVce)D$l z-O%v+kq@I|;}ah#pC+fM)1PN%=f2D@EG{jttgd}s-`L#R{<6kGI zXTSf9V5FRBX!swSn;4=61n2tL?x7FMG!}L}9&rtF&?P;SpjD>hGs;PjlavG^fL!;J zTHVZtMOn5LZ^$l%jwAgU@iPa3dHgTU{sAl{Wj0p|1RgCeNtXV8mINrAy zigbA=m#R2keLdC5{=fjn6L-7mH)(1GKhEqK<==Mdu353j!5btKbmPrC(Xschts+TThUn-1Mk2)=!wOrYs_P$GddNv@l`~6j zDQ5difoZ{a>UhVPk4R*)_qQL0eNWZXZW9~t{CM+c!+)4WdXp_c)p>SD{uTd>`m>+C z+{>i4f}SH{*%zv9y`)O*DrF+K?d;ii*j-Y;l>hUb?~`i<3? z9=<|J@^^e$=0Kxu5*a7JC#^&LsW-Xk@-HdbwzVed0SZ=l!vj_*+mTqyvu#vBO(0aI zUy_uODKd;RG$VV6x=VQ$`9$j;P|e0ow9yklXIYp!p>Z<<1Qz@clzZ~AfEq|Wx^bS-16PiQNf;<)`GTJ8Sp z5lkzIt)DP#-9ar)dw5+R=6%JTOWoaKefy@iQGnmoGKze*sQ_82r7Cxm2&i2b(H`D? z0NEGAbiK383j2MbMtG^I;03Ece6rFK??%G|JP5cFehnWq8FO!!|3Tv7vjY5f)Lrqm# zcZ4Lz)P%$RRJcu4MJYsqj5M_uN!nROfMQGL@SzFrs)^x-P5hIfJv=5aUu|tD#~=~SA8uP!bQ4DTUYd+iZNIh?rrS_hV)BS~oJEAF%z&`MYf#>&6xMfVqKao$Ab!jR@<~ z%`mY!-73HwtZHH%PM0Yn*RlHQDAXh`Y)9|In=c)#u9MF)ibwVa(0G@B<2smcNSTv3zAR>mFpu|cbtW&23!~Q#eXiC;+`?C<3o-0m=xSed8+;Dr7 zPHMjv9mf$-Ue(6GIJL@yH{A2MfM;f1;5*c!GInFY~VulvoG7;&GS zf`ioYqJSQ%6k5e4KnGt1`QZT8f^hv7ykD)+9|XdGgHVPIbC})l0}rSA2F<`F+_VQE zk!}|cHoPn3hbnl+W%~}UBM{Kzif->UxTQ;u6F61Hzk=Moh(AoDIhEb~xZuQzABO;A z@|WTl-#M|ym}2LBz*@m_&+-X_bjHgK>`5}uk@ksuBp`xP*I)JtE!|HI#2G~%DU!)@ zTyYnPO)0{60)TIuAeeAnvPh(Q>5vMx;!xmzGlx(K>Uk|AvvPvh zY3_QhM!(XlM{dfuZ{lQ~u*~t5ntx+hV;BPK(n1LiN+I(bbcJdbnu1 zBjmB(hrm0Fj_>swJS2_eYp5wq`JuSPy+MmwsV)cWRZDrv*9N#ywz!AZ2wwV8uoa#@=y{sixC71FL zmuUwp2U80zT&ZERqvuI6E2C4NjvM03idPN}62T9l zO-i|eayiDzT+-xNt9{N{l1NS4RFawMLrLbBz;4$`Nw7w5cMiqUv}VI9CtdeE=o;s) z;6x#N5n#L{mn%~Cs4{_|VCi7x378dLsXTZNGRbcTGJ*(c_kE`(QTIF&W0c_E=_Ieq zL%FTn5=HeQGl!CcmHseXW#h8n4hZI14G8Abdr>r0ps@I5Q;9{yET$-S?Qyp4GKbx*&IYJiZHpET$X0ZjpS)FfY!|mzmr*6Pe zUqo}3<%RQ>odbe7^#l!TccKLZ5H=(Hc9-0!(+ihhxzG#WBP9=&Kn}zyvVNHW?&}Dw z6cDlZ?y5R?z{P_;T<;Z6PL%?bmJ|7UK*I+4OOuu977$r~e9Huj#_578eVFH8SJWLq zGM?%im9?B+;`vcGNOr@osZ(AhiWI4m@Lp3FP4R9h|hU4_Fq6>yjZ3Q-LVP(aZ z$8uV=j;A1o0RRq9HBe@FwTZG>MF^ZsYfwVoC+m0UhZN3hk)j)TX@zZC*=-PKta=Vs z9`7%GZBM@j0#|>ur|36i<9t zS9m`LRGz%+g(LyrbQHjYY=n_)YghPqwd_;#pLmT z*X<3AXFr9=!Do3(^v8>~etPN2t&_X((Ibb#yEnAvG}YPTPV)$=P2uN0j}NF_6O82g zDO9%d6sv`Z+4wRnx**Y3hLJRl`_>#zXTrEt# zL?aHb=@U2JA_8T4b7j_7WC>3!hE$C&ovBjQ1gD zcw6(4(8!PxQ;h9Bxmh2C!N@z0^_wKQjiqHnh%YBu=(c+9(v{B$_WX4K#6~WED-q@d(zEmibuCEhz`f2ycuWH;ydycv$7KG{!P&P-4UQT z`oD**2viz^ov||4aE(&*mbKD7q&xxvn((I?Dj}@CXe~)QxqHkZw4*XlHi2K}2{TG{rx{Q_S z6U|Pr#T^eIRi8eF|7f}^(B<>8DdmGxO6z^#j07L*jFEQZ2k+JJWmi~F_Qr$dslCTv zbub4u0>jmXv?0P|vMpe6RE|jOzTjn|Z);^7p6ilwWlMOIN^rUS0X8lw`55OWLKo&O zdi9yRtHmun4(jfGbGt4RD@Qp2K`4TCL=HH^RY{}}1?j_JVwF1lL8(#3S;%_~#xH@w zRllx~$&m~`df|Ss0g+*9d|S?FbRT~%$hO}MSLUXo#_|cc%-sN`5X;ze2|MStjquW9 zE(dVg)k7QV$h_WVos*uR3+=uS89v#{rIf~T>7C$~zyh*0RK{**Sw*sx%P2Canb(V) z5x?Pl=Cm;ovWK{Ua}zMHR-uh<_6JEz2KzTWujYME@{o@XI$e%<+viP^Rqq`{;=YFj zAM^l!0ZLb)o4oDD7pd0!4gyd9r8T5^_)D?G^jkLF?!JJRR92iA1cDt>2+l`C_vw-r z%{R-$)8U;JgjqUzB_z;c!A+NZ--Ka6g!!;M3+B^(eV>Q@wH;DErI8)1$_4y61>Y^J zkjRThykueWLT#kL^a#f?Ny|szO*{t-&`daHU~;oY04rOGXeKGmlLA!Bjy|^v3qQzf zz<>|$>m%R#Xz2!n`DbqJs76&I4{dVoWbkNoM*k#JU{iH)jg;A=yJP077&fCSD?unH zyJbzA(9W!Zr%n>NjcO@+NsTBoqF?1O}^ve#J9TgFR3^wxO$SAQs^^?oO#6R&Hd-L1Teu{Pd zz1HBiPQWd~q9X1=?35`g#SxS+sj@7x6s~To}iQiOlaMJL#z{ zOMkj?+J`;Z#A6=t)`C@iI$F#KFng#qM~sU_vfdk0IhEVgYIE_>!XE%5pQTy&su@WH zE=G0SI6Z2rc-xz|Rjz65J$+sQrdaTG`A6Y~KvOD32k+yZpJ2Z2j4Dnv2TAwB^>y&PkgpIj1`D4^`pAycXi2JCaRYn4 zgVajl{kf5oh#-n89FJNsRJ>XQ@m7H<8|`fV)gDRcn&B0DPmsPKeW&(jrnHObAx!jl z_~s%ga%>7#+rj+)!AXi6doj#nZIbn5G044n@A3x2Rg}vF4X)Tkt*EeSl<%X=jmDN# zeh1<-14*o&npzkB`^>JVQY*`5O?sfjmm*@7U>{!}$!^s$w`Uq=Gf_R%eSIf@ewS&c z0bM`PG#L)mkg!As#`-LF%%v)gk;xySf8(RYTCcB0tA6 z{uYM(71eN>(&N)xzXcodDtw;hCRN;e^p>cV5BUD7@|d@{?q@|scD!y5bdtg#ZgW7 z0mVhHWE$-BXyVM8s*bga3+&Z5%QiXVGwL+m4Z|LlUYAML=~rq5saI-k*jdUj%I=MR z1s3_h)~FjEc5=t{!uP9ni!;lAVe&6>=#SKB(u8U2KJ~cgk42pw2x$|`S$n=!M)FAi zu>Zl`gX+}fa8<3te#ojF2vWuapZBI0x{l)ElxrV_T*p0S90RPef0|3tIW<(Y@DXI{3Uq78Sy2w}%a*79yr zQMz0W9Kg{&`}(4wPh&M_GhDZn?cY+(?4{|vw2VXeF(>jTX?n87IoO8}Dq?Y&lx8#7 z!U>$ZJTCE{0~%K3g&S{8@mIMB9=Kf*mRnDDITj_)?{%-Rlz`XA6QQvW&r}rzTncP}Tv&}6edX^bM8~Zf>{_0= z^Gy)DU~i7nyIsf`(RA^`_@G@$-8tt&{9fCAOrU*x#Ln;$5$!pz`@0^ZexNIK>i01t z@!g&w@=fW~=s5g$z7yadboBnWHz5!9+{P56(QV(~J(w~bQ2pNxQ-*1DH1cfwp-*AX ztJY#CO04~H0sgyckX2_}IGbA9os!Qn3u*iI)5FfE6ziaXMie)uWg=}kdH!co)EzI; z8sDtz^QC%-dz04UKuiB*E={ZF0YTQCtgkv;Z!j9yM22W!-w3K7jij9NEEMEl*lufbiYH{b|8>_lURc_li_9`abcBR5V{P4J zg{gO~ez7E7S>ulfHMklKJs(-mk{*ju>OuXPpfWJn4_`_@a;kCuuv=OZXowO#8B+>s zmsE56qj1{823}8Y>$*DqyZJa|Bl5KCudIA?y=ehwciT#0#^tj}XoLvl?vc3qcGTBo zg1d8hbJMee3VAR!w}ccrEk%;%*bOkec&gvtX>+Fb+5XADUpMAZYm>)DE=m6dcWwI` literal 0 HcmV?d00001 diff --git a/assets/images/bn_map.png b/assets/images/bn_map.png new file mode 100644 index 0000000000000000000000000000000000000000..b04b5aa34b49f37ab94d59b80b0866a9b5991d29 GIT binary patch literal 43970 zcmZU31y~c_*Z&4GIweP^bjj#W0ck~AQU;?WgfU`BDx*`RQ-o0tkS--f8gw8b4bqLo zpYQMez3=aR-v51`vzzC9cJA48cka38jiJ64DG`_m0059`KT9&jeBt8e1OV_RIoR4#X$$i9+u7OL_74m25qbL?$HbtFZ9}^| zzp{Pn9I*YCpPgxCwMb8~i2I})P-N8A2qQtUUDgTrSAO7)6*v(bxlDH)v=&TR9q4?L zfb-edI5s8hn3YXmPnehgPU>S|UD}W?k@6ohHp^my4PG_`SxgR@ohP6+h3yZgErea= zyYeY1nP&0)Uq<&m{mr+?(Wb0__*x3mn@`>3Rk?OP?;Oz~g81l8V6-9jO>`Z-2p*ug= zSz{6rrs5@_`uIqA)Y6KCvKneA1tarO(hdv-pa&bGEWr?VwrD{8||`v@X(x3O=X%(P$V=>hof+5~`mKrjIBt_8d! zMIht)`1wZ0G1{?<5@H?)8rxfP8@LUDMsk*N!c~-Oa;CHb8;nUn^wq+W!oTaIpPr zi7!ln!%WYRP0iEWiA_>iR9KWlk%*0rP2St_g{-l<=D*qRdI}s+Utcd-5fOiXe_{WJ z!k*sFB4RQ!G9selBI4pgcPoT^;2yqq0YV-=oc|@{zvZYq`8asHc=@_`da(T?*UsM4 z&sTwi;~${^DgX67odR6`gXH1!Z?*0e6!~XHL`+yz14BKbrrqo%pY9{@3W;J}VN*i~Ntx6p6&Zd(ZF8$mF7K@Z_$(`;q;tRNj5^-qAnx z9hsWl69fqX0LlPub(JRpz&|;#Z#)A}7ZA?Q&i>OGUulB?LlpPf$kcli>1&g_aM*`9 zY+qAz*$a9YDdW6ZAtvJGQU&sHJu%dE{k3$ybc8a2K9h<)iUxPd9^Y*d z*LHb!_xpDzlaBSAPbQuQGvyymp8k4z?d!6(6VhB!VUQ#1w-x%m{A0)>Lq;hlGn2Hm zm)GKAXIEG_EuA^L9+S(ONnBfztUY4U=*xLKJCODR22< ziSwo>tKUvmQEvVY@a$G)W}S6qE-hdx$A^jAXkm7jTsE3hHio)~CP5nB#FCg1OO<}B zxOChZ7k1p~Ewp9T_R9Y;X&UW4lkrY{oCVB{XWJNE z_#UvxPq}wFYNoXD_%*UFQvcv%6g2+ewXD+8R;whGXbjd?#pjIvRf z53PW8GW6$6#w(AW1TIC>-V|rg?W-8bzfPa;8OLzCD_feGRDwa)4=elJ5L7bW zObdNn;=B{IZ;euqFJD?V^|i=h(VZB#82bThvQWC86^=B*H9y=Rrvz@1qG5~vE{j$l z>1GVcW|(c4KBs<=%TE&5ddx?w`N0O=GcGnCy>VEbH(^DCK3XTK>%Z69*8)r=8E79_ zFY!4@9+d*va0jbzkLd1syd=F`w;7=AG=t87x_#*XNGv<^gZCXM`=`F~-o!G%egG{m$h^JpedPa+S4?GM76};!1!V zrTX}7Znfaehp%cdnSHuyE-jFGr`Q|WbS^!*vr|FU?ajKI45wv&|>=tZ=wXAIBqR!v}z{n3VZ7&PY8 zbmps%?=YIbLQJZ3N}k`n#F5a`@c^_eV_Q<<{~_Z0oU_X_01fURV^_RCMw8FxZk@Xv zW02g1t*{6C8-R?6*Xzof1Nu&Tj0q@vVdb(Xhu&6Y=%?xFb*TDU4dPXY?UGdCKYJ2K6E$m%ZQe%TWQA|)j zq?DvNJ&^FTa|=Zw9zvJHp9z>4;QwVp%^IRG(PC%Of|kG?cg5s(=5rpalrfIU6*49* zP)^7oRngWG%pVkmgAr4=Ejq?LVjXX6_i-l`E9@P^^5#)-Mny`l%KJhgv^cdGK01gjkPv$OQ!)0 zqx%$qB+e``v9T9Y%sEmyILVh>ikEH;;2n>hKOY**OE(=IiG z{e0~&|GWmT)JrB#kVwL%w7TQQpKIb!BA2p!xM3KVZE+~8vIFl@4TT{uNR@YQV+L}; z(&k}$x!I>q<6}au=_fo4IfPOEfM4+2&fzWt4GT!iVMGMs@wBQ=qaX>KkFF1}+Beb4 z+gCYA=BAm0P(`VW?FfM4F#vbw=Z zc%VS}*Ztu%oiYvY>CX4{Z(|u$Q(ESN4_6K3fAjfpBdY0J=>U&1=lSW{W*%lJVi6U* zrow30>P)sem=wbF$l{{yms)k?{umXS-?lj1B1}yL<=jbV&IpmW_bGL?Q#p)MBA7yPye`O>$zicp`JJVKOiq`&Cx3F%*&Bk}wy z$Avp>d|_#$CQR=Y#3sYqSx-}hpqH;~#?N)XI7#4cQkmD-y(jwekHUrb#uu?onADSP z3A(AA^L%spPi-^sH;RsLFCTtYk6%$6DJ{m8DD^70VX5fBZEHID-5F>#s;kOV-_)sV zpam>~Nc`00eaz@<(URw&gR|uL&;^Ktd@%HyCb#eXm)B`b`xq{Y8whL`rkve%kNnR` z3;ToHV43wwvY_9l-|q+7FpM%xUa9DZx?ZYG<0t{n4M@N3V+8qEh>%9#46V^z} z%!r4|iq?0q5UH{z6|G{Y2NeIXr&sOe#Xj+5L;^}`Ft!@7lsSZ{fKYJ_Z}nuwqhG+{ z!bzX))RepiT;*KJKYr^)Bx^{QcTtHZ3pR%qt7Ml5u_wB47o)Y3J^ASMV8a+;_AB9~ z706ZxEtDx(Rwu4uv^NBOH672j^9wfrNu8nyIrg!2GgIO7D~NytI#LEe-aQdfjwTGj zNj9dfG+U@W&er(A+lRkAdE0!%A0=cWQIyq=FGopw)v?|u`d zh@pFeHlcNa^Ah^BBx}^iikyKT`%qzCEnT&MLzyfX837E}VfdOTL)kW#m#f?f1kX_7 zRD7yVmc0md44keh z%Gbl8#vv#Es5>Dyg`A6lI?$#z?uyf-&VqK@gWDnGy=!d1RnU%L^Nt#==d5hZ77s{^z*ljtY_%_4^rmk1NZrc zJ~|g6jcjaXs#d#*$4Uvmna;=D8EC6;7)UZzKjjw}jg`ZjI|$M)JnBwh=|t7Dhk73d&u^hrR>zZOO-o0~BW{Ux7n2 z^}NegbE>^9V)I@44B6<9&@GO9RE3)V=gU&Qjh$ zT(5wCvMiOi%DQ#Un~y$~=Y*@K^D z*=S!%iQgkwi)l>cvK+Iz8oXspDMI%Z!{h87y5u`f&YHfvOtzkYMJ!JsJ2|jl6JAEB zs8ZxhSSgGp7Tp#Az*DW~MU_OTI-P}dE>91D(;LQrG1@jv_grQxA$>xyg&FZ-$W<&BoP;RJo52LaE#t&*yj~&p=E3rM7cvUyzI2LNF?HY+X z95Y8R`Ee&QXzl|?+`S0`%iH{juRUA>4MwgB&9~_oJc`=UcZVpDSA%40WXQHNxN-D3 zN#edlYG7&(u0)hY#h=UmBYFdq^j*{Er6LCt397k+dq6? zQ^oQDp3A049b6&qAa%`L?<4B+g{0keq~tz3F+2HtR(RugN~ zX@b7xD(|0z#A&d8-6K*;3m?fWq^Yt3t)7M=dz+3^Z>LdW=wn(<4Nrh!#qD?-n{Dd1 zh@Kyhpo~mk++A+5Gj4vDuNm^oxxY2J2xJgj|ah`nGn3RJXupKe~^~4<7A)?F9}z3!Ib1 zG1Cd%I?7tgPfO}%y%Yh-5LyrJ;&PE~SR~W`q3dY4i2Z)-QfEwwhcd2tYUEa59}n^} zzNfO)c1r2=vaRQa2$5#!B$ctn=D0)$ho~kx%=ywpYysdn?7qFeWJ_@R{347_b^*Bbr5+}v1 zzoTzdB1m4@;ZfFjb&p!IWZnr!Uqh%6Ks5Td1zDbBC%t7RB}a&m?ZeOx%{|_5{cl;G z494Yz)U}hjzAPG`IL?xn$`1n!g)Kd~aetU23iNd02C%f|CJD~gXGPPp`;(_J>kSdT z+d^G#l`Qzs)^F@Uq!lR|=LOL(>+FDwC7RF+z-V4xH5_1>To(60W={` ze77xAF|BFvrZeSlyy*pGr1A8-&?;?PX>d{Nx|rQa{@4t85w0>Tz3a7YEMi(wt_;E; z>bjHyFSyx5uPs4Iu&y>GBN^E~0SS#_ju5dAH3L$B;j94s1-n%<&Lz_B3Nuv`Qdvzw zwIji^Kcs{QSzTE&Nm(S(xvR3`m@;@QNwN&P_@xw!tYk;~>LpElV2Ed$#Kj~O%a7%f za4B&4!GL|KoV#!+O$24a1C%zbvXDF#cxCz9NZj_@Q}4q%G`-?@x@@<=4#BOZU{qK5 z03DMnP@}wOIy`(-3~NMuaiaLarF9wv+mYEl`E&hdOtIrRY*C9gbCS#}=(PVUO|lPxl=1t@^gmzb*BkHerAoZT7sU{Kshc47+jALt;S!r&G}UPts3mT@vP3~( z$^WCHp+SqBo!ori5@?pkI_+~=)j%J0z_F^!m+CVtvJ$C1w zHJy;t*+!rAnf<(3q#2t1d;J5M;Bkl#)eYwDSzxkmpK;?8I40o_kGr~a!DWHi`c0NL z#s^{ygYGFdN1T1hssOKcBD2Hj_^eurz^LuEp$JXHc+>dB%?6R#Wy?Vf%JRBWa-T&^z3e-s)nyOzvI{5sECCHY{x?lO| zBspRq=J7J(jD*3Pa%Z#QFK*&-?~0o#N}N{k>EmtZ{o?gei4z7&i3R{t*S*3zc9@Y& zLGIALYT?f4U^6*`^fjb@14t(gvkp+Ajk@cYC zY*fN;9xQQrU6+nOicK#c>H1{2Vk8jW?9nC=shRi6AjM~=X|zqS^8>JUl=yRdL-Hu7 zyuPcVH)9+@!Zw)(bm0?Ys~Lc9+_^dR)jN3kGiyVw^1}f7r*>&(jYT*s@Rsp23335< z%ikI~2u_$AKZ&ikqe4snivh-R_~x4Ye&`uaFO*J5qU|(u^@#Um4ht$@)m3y%z4q}@ z>pToH&?+@H>+PXj7b|4+Qk{Hkjx=JNlqj4Aky?DdryE9AlMyj>4@wf*>^gb>Rlx=j z(DcR`L4L3@UH$~CVxftcx7^aBFdMB%Cs+0q*J@Ozd+>^?$n87%&Y8_+@ic4wWx|JWbb#Kn8m;Z5 zZ00Fd>bERm>*2R%2w;3~2KbCV(|k4P*L8Icw2^T>Y8Ff}GvC(Gl|MGVtRU1X^|7#F zkjUo1rlU1UK+dArfmr!<6+oH@xgrHx$Cf`Rvc{!o3s7^|@j>>|NALb>eEs20XIGQ# z-IrW@g2bYQwt~X?ARujfWtwV5(@bg6(_o`Rx0lF5Xu&>^51;`k%BuwR(1sTo_>Zhx z|YTnG#y>mWbSWYD&vG}!hE=dsc%^7lm2a05EG$VkDd z{z6{F;K&QL@owsJp>_9YK>_$i#$BePzGs(iVdgRWFZkvCcYxMtS$fo+rvRTuJf_GPZj?)&_?< zj3h{tbD5SU{{@RMe9(OVmoRsfmg-ADB#Hg!$~H&VPlxc| zUwzIg*v9zm{jq`rbL!OWgK|>laUggC05@Jpk~aP9IAro%`ZvE_g~uqC&|D$xk$+`v zX#Q!{IHI;I@QY;J!53KrS0Q{=R7g`o`_$>cANZd{*?uve=;Z2SX@2P3U-{njojg){ zh;k|6q6M?Q&(~8&b^t}StXF`nBq#<$j4Q%?#j{XYpRZp1vL5bl)39RFpG1@SXVZ_2 z&3Jo~Q$MF>E7FnuK%AqapCD*U!$!hWtay)7&{#xo!CNUcFgt_-)-$i{5z}Zja~ykW zwcAFi!s#H~8|VSn5NQ;hi+uJIImRCYBTQeb|M{_pGe06j2B$%~Dv93g;J3n#GDo+~ zLv#GMPrz)?lvmJ1&r2T#?}0C%Aws^hDRwE{2|tu8(!zq0&7el&QqNXaSK@q~a^usS zZeEZ7y&cc{2@{({BkpOP&2+!|iao{1dCyypt41& zyuKK+LJzd@o$dK!>Sf>a5TNPiN6pII1tbB{9Blgx)w;xqynBmgvqKvh%(7<+x}4#U zh8Y`8l?V@{&t%1`!=9t%w`3PE;OlPJUlzSIoh$cvHt9%7%IM9QUW!g(-9koWmB)D6 zh8>DMCY?nJT(*3WnxDNw>jIYorPk0(r+;NILELxduVPcJ_zYx(G@d2_^+1ir;ha?^N^3W7H}w5_BpvdNubr&zfP#L>g7$p z#*!3Q(VqSVBdN?)$jwvwNsu%>J$|ec5~R@4&cpex!u{v5TaR)}9IC@b4of2i8{a+= zIuKpO0>OLh$uwps?OxMZy1n($!!))tSFumcx8ZV)+SH{ z|5d4z;jOcNJnU3*oD!gKm79apmGM|TWX&&qq4oLSm4zmx#(6>H%gE=teca47sh+Vg z#9N%s4qOSF^ukOdFPXEp=d?^O_cj}xr)5_Sop4a5)U(*aNm&i3Xw(4L8htK^{jAJ) zzlpbY{y8g~cy&S-I>V7!@Ppc5gBa3Sx!G;m?nIa{hFV0hV@5wqC1o(7-i55KT3x0- zyMu?_okZSI^V0tVOYQb(;NhcY)bB1$1E?+K&#Y*y!ydy$JI{Bd9w%D#&}tkJjhp6;WQbAz%Q)NokQ8jEQIwDnYngu zYiOYXEIjYor@3!Ci8McdI^l>%P#}(XbrBwBMXWF}>j$C-3Pf}R#EQ5p`q*Y~n^JvnEvtLahyi6Zn;P=LWrbH9+|AtnQo+f;i^W$^P)kO40`BJ-qqgOSf z{cK)umXIVMe5K-6I#e~2W&7jwXY4@0+WKg$N2p*)yRHo&*a|sENKatp{tnJ zQ>TpC>t~^5^syl~x!=Vk6KPVtU8sJl&{aWb3RBlo*U|bBlq*U+XPgW{A(kxpW9`Vs zGB`>f@~$l6{4mj*1rOEUXF#+<%sun&X~-Jks)!9PFy`>@oLTtj>4p=k9@qEpJeNSs zahtYcO!4ia^@@USc?QJ}mHQ23V}>^H9KOE3|6&Io>jTyag@spagi(1GD?v)m4A_JZ zs4jji%WIZb(6%#~w?mrJn`fj?w_GrRYk}fa5{hh5+MB{cML8Fo~1h-tjc|n5vN7r!^fby65e2-{k z$}&^nNytrKwv^Dl#JeES(LBvAlX`kUO7X6*x|r&UMS>RWb5b)(R75z`n|qq$ zd=m=jj7$IW^wEzWo3j&Fs+mCnL`$k|PYMGxO+NE7%2u+DH+Xro#8WSSH8$OGwhu2p zRoohzUGa*WFG18^T9#2u*EVrUl^{CaE&)fJI{Hg4F|9n|I=gb3$@hmjztWq)W}>ZF zru&~4w)o61SO#F5nf1tz=yf@i_}tgDZW0p;fSkL=ovehB+JP&(pgFn@;oBYfNcB)^ z{Pob5Q!yzpj`M*s*ZaIO_ZD_tGouNUMRvM;49F&@{jY|fB}}G z(S$2oE>1P>JY{N|VkWf7yFmINlPxV4v;nHB=R2iD{9Q;!cDl6xGFi|cpWetL+0020 zndmeA(4S`UYxUn-*Eppt{OucT+{+U6%|9h^7QT+M`-JL)W7`5kTs>g>h;#)^5pC7$ z6Bs-tzh?o~aA#OhsRRSFw#_~njQC3P0ZP3M4vJJSmj*9Qb)aSfnR62q*rREiE>r*A@8Z}dG38ElRFFN z^=xxo+BEs94YXO0LUb^klcnA(U+Im{mu9qv4*|j9&3G8EX*4B1d3lN?q0A)V>dy;G z3T?R)(Bd)8Y(Bb;la{^we@CxafgiwA&8NfY@6Gv&6h{5f*L4IHp~N z18KbLBOeV%P2Qh(EpRcn)0v-7-k^H71qk+z^FiKbxoj&sO~LQ(z82vZi4G1KcQ+SI zBkebc+|rdbuXHENn@EQ2LOOcnBN9P>0})}jbN?@|#zEd7EZAq>aO(lvCibJFQ3SI8 zeY|MsQ#7PF=7U>_JS?`nH=G5R=IN3T{3VhRix!JV@54P8&kb)zo!J##8)UX$AD=ex^kH6_R~O*+9;% zL>e{(sGMlkvtP6V@XmRYC^`9oK3Y$ek5@rdpmy63+ejQoYSyhcHMb9 zzLV+NXy*dA4AVSjVH1>cw^MeNWy`oJxx$*&>vj^{SGA)A439g0=k9e6-YI6=rDCTo z+9NcZ;Ow_u*{T=Kyuy0&Rvg-=fkzI8OU_32FXJuuGTb< zjKJ?yCOOYzsk=lok4po{dJWSyF$>I zd-s50;^)NDf(2SIV)UF4GF>y_l>srOTM1zO_@B4dIIsFvt$spVBhdQ`Iju|PPje8B zW=#diB{Ujr!eYG5ZsXp`#iGsnr#QVf)wB4TKKK$6o^*G4teRTCI_v#@9m3Zrb?~zQ~NZ7x1UF=lOy-#7F28dpmp;z zAe5;rXSAt%&2E_DrA$5n;G*X5$le_0bdj;h;8MdNS9zh|89VZeZRFE)k&33aX{t~O z8LL6hwuT0eG0||-Xh@kcH0q6}!&V#pi2vPwB__+oi13l#W@VR2>S(ufv+>R{UM$wE zFbn0@^S`TCc+lkM{P++2yJe+fe^ro~V!D4wM>NGwHqW-F9_1NZc$VUJSi8&u+U^s? zk;q0?9-%|EwA2Wg6Os?Z6hwrnu`fa5u84OdZY@sWpTDfii2*)crS9-I*POvoLKnG( zT6adHWEl3--N5~-EX$YJ0#@%_z^1&xBM|0`%?(?`!(R@q2a25flFz45#9`lJA)pQl zYrYxE(9XjCpuva9_%cp-ygDz)wI^s`WESi;uDBWr*J`+n1+w1EO+@I?$y~K`jl3Z; ziWRz_HkNtB=)##`=3T8oZ?$e?4{|L%9@u!}i`#8D3#WLM%mW>GuDHz(SkwYNXWaXD zB(~cO=QUalUy)fM+j5rk>*3GB5HLa18l-WuW^w_`(kg&4$cT(b=WPb-s675Yo!yc- zVOkIwT`PXoRH(SfIjNTn%dVCgjy_8oT@9hn^tqH%PW06e4ED@7dY|q+);ttClc`g% zS?=`lu&Q*@ZU#B#y4w2#<(;@6_KjWUStR9hT4=`?)vdAELcR=^&6C-}Vo#K4^<;I$ zE0&Z2YVMC|&zEy0dCvr$v8pxTpmOu5>WK^#VnNO9s13oZn=+DhV%anC<49i9y_Ij( z=c9Az43d&(&8(??{7y5@XmTCAkJCpM1kVuo?h`2wG;poj#$?w8SOgk?K*3d>H6vi* zX|e>IOSWdU(SGUR!@LG`CSd3qlB^yO)Y+JkxDUS44eqm1=A7SVxr>Sfven07g35{g zUy;*8p2U~kTYU*o(M#Q$kA?eU8cV*_j^iKf z!XR>v+jTk-9nnobSgT}~R%465Y5VQb`<14Ra|kRT!}aBQ`@;tvASmL@URS|~L;jFN z3#C?ijjF+oFgR<l}FBgqU&=%Xt{*Ww(0MK&0p`+h8W}M`i;mZSTSAxsi!r8pU52) zpB4TcxQjyS@!uV(!vn=*3xoZWzG<2UTWw5=yDXyJ5~SIvOQ*0Wb{mkqwUSTc-;_s= zaa2u5SOkh54Xe$30ve0ciAl*Cb>x}4xUH! zmPg|FxH)*XlMUpXKRGcE@amq>k@kzMHrm`hv&tti9XxL4WaE_*i-E;z7_Wz|v^Q#o zkg~a~_LQ=;x`X4I&LkZ0%nO(s^|5SYxcPdk34Z$;yY&6>62kZr3{~b8OyuTU2t-yN zw1m5)swwKHIY4u`WOgtvd)#b2SBvrU%;K$%n=Zw{{3i&7r#ejd*pEzxciD%4z0-$2dpoe}UERm}o zHMbShGWw6-hIYH}c~7~oA--PsrF1C|)p9NUO!R7a50?4$Ds*4dHJ2)PbBefZjCpzD zQEsJ~^-4P^7eUwt3;z@2ZqFQ)07r4uFEbnU9IobUlc#O3Qj<;Ii<>T|u4l%dZcphJ ziKV-B0~CQC2a<7@N?i5BCH6ckZP>R77Gvsu4^aS0r}OZ({cP1-V8p%>H>Wtc5@4Ub zF7llT3xxp8>6&$MTxGbY$7jK3Eb&(^O!*50=gWi_H>aD~@BQz?6BmR`ghrtd3~WnJ zk8X0Y7H=+>)OIq}v>G>rw1B>Vg!CKic%6)w7bwflqK*9c_nAi504-bPbbMre{pDo4 zbG?48JmR*pAo5w(dR2ykA_S~;EIh71-Id|26!6U>1z=L|<8SQ%qlcSULDMr1b zD9RkOZoFqsvhoRt${HWUV<{)~UeR4vCKhqPz8CS%=`2!9AaBkK;5PceYYM~h8t%cu z-2ZY)WbnRETsIwLIrwmLYXhiTGeY*-lqnSVhx_gSDiSQ+7vLU|L3U6p3a*v|!Dx`b z&^z}g-pMUO1;)-8Ll;snvpU^h9&NBJXN{slnjcyW50#7oT;VeLMO+sZ^%wkj9h;x) z@B1pwoNp@WW)@0b3k^=BhKl6Mp~;U`i|XAm%`Q&yHlApY8-*uB^9rFM!86h#cyXph zEm34sBy-@_Pnu6hh(uj=D$<`C7Kv%`I}oaC|Cp)P;=PLXua-dnd^Jh>)y5};E8nQ$ z(r%WuH3_M$YrhB-6t35;l&_d#5b)prn}yG)c(&i9h9wo5GghV6JQX&6u6F|c z-^q$@59SBPnU0#Oy?|62hgVGdF*@VX*M}8F-667Drq|x%n7-3{>se8F`go+9nBinL z>zgy`8ZWGx_yJ7PRHFx>xOK`Jk%7>)a4c|CE+2T{esapv>bAjv?MiMy-7qbVjN1n* znEW=Z(J$f6>4OG)60PeNdf%aJUaN2fI?bhmf^Z0LKk9}Ogy9?~;y>W3%LNVtN^PH&Q9N@$8*kNXwQ z4G-~3KH;LU&Nwfp-Zqx5X3M#>E}LcGa;KYE>$sHw!N6c~CF9OwGzL?a=wIm>TWEmj zdDUbe%-s3eZ(Lc(y^d~=BdFoKLj*Z8337?+yDL)<3N@WSAp(yLNWb$=fdqyr5@@LS zxR?z--(uPTiweW7S$u`vGPfR34_txIUhc4L21$!1XNZ+OT|44bJIpmHU8(=`m3!ng z2sQlfljeQb8F<3H#&gdJvTxhm?2YK21UgUIgpRh?N&;sHytwI$h;%s0G~osRi0(-+ zq;o@nkX?~xuKtE9den#M?>drKMS!=?gKkvGS=b~d?8{YuvWzlfEsb{Coam*^+OBK| zw8yZ&m2U4h;r=hD=zxw$x%Tf$D4(UbJG@CkdVB1RQXV@G!E9FBloGDZ>Kit)7NxCF zleP5uBp-B?8aDxbZBRzU8Ngpg53|==xN>E|ky^QRa$o7EzgI3sMudB*LLu^CP5vkp zhU^(>^tGuo_9$=7t#V1}MF8#hVeWhO{O3IsDh=_D%gAcF;9%*Q@Z&D{xcuXir7ssP zf!Dtnp_q7$rYJs8CihhiHXg!sPo?wxb!V<^`4{Gk%O${DSF+#v9@=)mKmB`U7yMGU zHbXw!9|z~+v2|oZLk$+~B=!n_6n?yw%1X0QbD7S!#Ql5RkvZmr>;R)FM2hZRebwzy ziUxrbW0UcS&pOZx0t#W8msw7_ypGWnUasS7R%Pi0s}*9T>i`ruTI%z<0581Fvt$qw z?{@guKIx0!sk`|j`PqqAD#TnM5p$uIRG`%PxQYjKzTAvPXTA2z;KE@$&qIY1JoNP_ z(Ei(n-9pdNGamDHj}t%s%kBgu!QRmPi%^S)sb_Qk>rC5(60YvzWwWTwIr0}?L3NQ>?as^q*?*1J50B27RSb9GD z@VP`avwY1rzC(D1_b#q{C|RRY+j{17O1CzG<(01Tda~=*k65bN+bMZH?%8@wS?djB z;N7t!YwQ;<8CME=2HRotrbSZ1jFq#iM@<+DX=M$9^SJYPM(@yITK1hWktc#d^Op?D zV@JOv$s@GACkVW=EgvX_euHJ)XDPIL{_O6YS@-?rbNNxpNmH36c)>G#vhh@Whb?U$ zuBf+uvcjD=@oTG5vSBLlf%pgaU9^U<1lX9=cl(%Q=PqAy&FlxW?Qz$yPc*L}Wbh-d z1g82(>ggte)Erkn0%jQNS*2s8O|ODJRU?0TmX3q_PklIRFe4jt_ZTu}c2{W=5K0#W zJ)o1Z#0hQHEgjANZmsFtmM)P~s?dnv1FfdyME}AdEW-SuiCpTE05Q_kxj`)EJ7S4oLzvQZ{spV?A2Tcve4Le{0 zMe6-8&s?`|*lej+V;7?;PbuX}ri}A}f%vT0xr{8+C=nDaC$A-gQiG6OkPjkg&)0Iy z5DK8!*LqhV6Uw{rJZNEOGbc32`SL=k^P8+>lN=2~J#!Ed! zhraDat#I{tY4VlEtDAJQBXo@_vdO>A_M{y}L76N`!mQ@5c{Ao`@!@;zHg~hAX+vmU zh}fBgg_F_4AG%Ctifsi)*loJPyWq(Ot({bG4Uw2l)!Doe1m^SvDbm^)$QX!!Y1}b7 z=DL`F{KIqXx0oy8jp~V2^UX-e$@KBBy;i?Gx|e*OgOJvQh3mtLiG>TMGpE;N$#_@h z{nN*)BeJAO+nfc6{k?CVmU;_04rUespJU?aX*6ii%-QhI$iM zq9?HY`L1Zm5%)Xz9lMQ_@VoT?K?xMN+G(*F1&|`p=FWQz3<0q zN@u>1eCcZ9&VvUXE7)TVkt(T!mqPne)w+Y8?s)LMsA5nyraATs1`lj6I|(L@IJPRY zz_{aqVIvNh^o;I4TJhw2^z~%3Ep>!qrbpRPxl6J(?{gbrZ^Awr0oyTere-BM%s?Ar zW3xTJ-BH%8x2GcW+4K9b`7g~;_16s}?XuTL&zjy~Kdpo-$z$pcGj0|pct#%9g)2dV zrRpe|Dr;KC5N#6JCasQ{){3I-L$hm#vqnJN^nvq0z}Tf@gqj4!9pbv4d$4wE~Z+`SGtYVAP>-eB|E znqrW41*FynvpV~=N0BtYD99s zK9d>w?HOciJ@PyI0RlfRt6&PzF^ZgMWxifOgY<8ig+?6ar+%=63b|~T|G9fe^N2IO zmbpvvv8hOsD>1De((}4_wsRdPoMCgL%g7W6YAJ`z@EpLLj(42|fjt&&mJ9dy`w+&B zfs|Q_w+r)`Auxv;UJL+yyZ#Jv_2+dfG`y&EJ8*{Z8*S%M=KH{z3BHrD)(SLIg~+;X zRYrMPBNmhWHi?AZ1!ZW~%pC?JD7kyoIbxz3)~(0qek-rI*6SDFRKEUd0u+6h{dbgs z?3nWp>6`V?S32MIqy1bweyW$}=Xcru49vq{(r#N6l-1}OQO<9wps+PEE>z z7Eo(yGX)oq6?X8s8>j zJZn)zU2WrX_Ga<)7@PNsQR7wS{GroK>9cm|m0tUL<&>}L-jW`v^zioPZB-k*_W3Dz zJ zyxMMvwc=7=Zk9(V<{OA7;);&teQ8GM)z^@>dsiuP6MI1cSs_JUB`4t{!M2yn)$lJ5 z{OQ)S#ui#A%YhHjF9RVIkgWAHiM_4q)+MS8>@-TV$TxoT9`&{1m)2oen2+uOIghDU7 z>8#(~Tsb_`PfCzU+q)jk+3aY&{eIjSG9cEy@#Sj1uUdcMur4^VwEjlCdqz!yeA5ba zwNNB-JkeT#5WTr>;Fr zgSa~{g-ozZIF5)rmT8{l{kqXS`euA~ z7QFIq^KZy%x=Zc~OB)K@p}DUQGrF(mss-o)Ut6@CI9|E>L4y-CtP^Da?smR;sn7Y4 zHo-yCb`E9+riGbiPv27zuox~9J(R%dP{2&(-MvoA&`wW3-x+fAjMG=iGYm=4O zDAyV+ybn2=K6c-Q37ccD=f^OgU)C&FXTRL?1~-{m%d~sFKvCi|H);n*9yJO9ICs5)f^MMY5+k4!uA6nw* za6HP9N%n%uCy+dEr@$i^4#Bptdl55+*JyZjCkUWLH{kzmiQ#hsESbe?{m6O6CeC5F zc`E-wBgt?}V3pk_Rk&QnE#muL!h@s0LWk8*N!wyky`^KK?6t=P*VD328w^!vf1D<< z6XM;Lf;GW6jfhW&&=^qK4VToJxzX$G$PhLbKLwEx%shCx3{(S zDvEk-g#}S>TYtL6u(5U+$|X|dH_suu~k_b2Kp zuQ2s>?o1@mQrK1=I4vdDnWfU5(0++MUKKe=)#AG^8Dg{iv$q%$e7(^eANIb+m-j^W zCSs%BMk@putC(1u?gWo7XWST_z48yEinsd2(jT2 zDo@XPOj(C4PqwZ5d*+7C8f%NZi4fa=WFpZPs%5%GDd$T2`BX4i;a)dpz4A-~z$)-? zt*Z=eRGQW3tZSgVyR~7+YjIv2#+oYY)3UZ1x7REaJTp7-3>x}(-6p5~h$g;VPFpOv zMia8>LxM@peYx(V7S;8`Kz!t5#GS*E^8y;*Ueo8fJ=*lNFM3e7E&fk!9=o^&4w=aU z{w{p<=ZDFeKIQU}#zAKs6h#r-=Zf&NuLm-x7!Gy#jzX*Cjk+kDx~}HL7LIlAS-f}k zk;bfh@3HT(I|!JX#-AxlHaZI|uT{6N_8y-qWw8TGe(weS>X~?;H1dhc87_o8I8*X= z4!n5(3%~OI#ByGM)8)Gv{;HfW-hqRqn|su<8J#%`AN}XZ$;oLEc54#me=pK|$6LwL z7r?ee43F+ona)wBXSB!N1`T;Y!OOb2-_{oIYC9ij4MW*V%)*ow&A2shVf`QCm;#q0xgR)>@wOessK1hi-m3iilr3)JUevq&1K! zg=kcRjahjAr2iM?Jl1hDA~yj{8m!3nzE#ZmMvh3T)zD2$#`2T&7f&#W{dG(+=ZZPD)*w>QNWDF z{3#Vn`i8UeRKNCw=MgYuC6H(b1BrSCLq&uH7nD#9qaI90zi!2L@fGU;ic{a*iRMXoe)u2<0et|W`E zQhfMqaW-WljgQ+OLvT^~eu^q;xT1L)#+z?B_?+X4XaWq^A#_H?hpH9iJCPYLMFwOu zG5g}<1$i(`B5b@UOaL29S^@|3h6I{HIibfh1U8>D<)fvoKN&>MkNrOYyg)<0^1250 zdGDUW(j|s=Y8#C1mDLao)%yZeikr0qvN z#+0g7ubSa`j6cAl#R|mJQ+w}3j0O};$SCEVC{X%uioj-xA`Sp77woc63RSj)4; zJ#Ew8oK?76(4PBFYbWwu@VW1t=hyf>&+GqYP;0ekVl0%wyHTVq<~8Q5l1^D{qIsFL z(CX*AMK8aVxaeh%tE7=NjdyEJQGkw;WCau>0Ah_VnqKMAgNlLb@0K#(B9FSxjcUf; zmZ?{#jY=R-3tJ*Hbjrsi)NMY!SeP6T=RhY`IgRWSm{<{1A1By z*eLKvm6fusXvhJK&=A{JM*}?}z<6w{17|Phv@$R?{1=hdriGMbV~RIzgsR2LMIdhMon<{(kRw>6us78v&w4#r4pg9{p?m= z#*)rk)596#r#69}XHgP0CxhoMHi>QAZ2^qAG^8xPiSfDD`=Q-1devQXK3a{kQR+9Z zum3wV2bDa&9!D4X&brfY?*gY>^HXc@p8u{b&U(u z7^&4!@+=*D6if{yjN(~X;`P!s6d`SANxss}ectMoW*}n=9XjV)iNPT@uklG2j=*DA znreRXH=uhteUKr7))zc*4n>YPy2ueCvfL6qldYB9yw=ux(DxasNrxFYo7rQJ(qgD z3SiX2(mevEROZa{S-p%sDqhXA(4$v$-WEB(=|tM5(g#I*MNmqUXC?d z0!({SVw!BgE93*C!WyBr0Jq6aD|o}>4^6Lsskh&kqeW|1pQomGy7EIn6+Y}CY0(Cs zf4$e$I!GeRlJNe`0ixVHj{D-k2M7P!PpCCC=UwQToa@y9qZZm&!}Xt2X=~mRK#=RH zveKCP9CguL*L$1eQPOs)9#`uZ33{9cfa1|M8wX63%xiq%n_O7>WBHKtoBz_)>F@rP zt4?-l(Kujy-$$OA{_+31HT}T<$qPfD3V+T6(+6Jv$mw*?e>7u%%Yk(c-mbcpxqkBR zUv=|vFC@axahx9P_5$+# zHnFx{ikHz^HuUvoObE{yfVEA>fqT|@jc za{8t!m;LnK58au5_}6bw-}7tk9mX#heedH}r*C=L^4v-PU8A3CdcZ21^SOJc-^$9| z!@D=S6pcmy-WGQX_ch~B@{}B_0Y-?>h#fU7?OU-ZHRj7aJhOCj8vp?POrmoIu;N8c z)K~G{?bLGlL?c#tXdkvcfqY>a0LVI*QS&ld=TJdcjX8l*zgEZCq5?}DPK#gM=Ghi6 zqhHUz_`)mGr$07LpY!n-XyjM_iZ>QwE$dg)bN{LoaAi#ZTyBoxY%~E~&Nuh&0BZ2Z3^BS%53RC%8l5h06 zi@vUd587gl!@E}^DaW&;a)5DYFFS&05#E~ZQx!03MAr<-GD!QM8S<4O*9H3>im;}r z^j{VVAO+xB4T2?4nikH}I0mf%P=M$tiTvf(umv zBXnq+s4gv}m@7v=^Z_Fz0R$~x9C^N%BLXTb^Qa#kA|?5}NXdWXsm}qFif@sYgaAej zhaj%8d5wG-hEQfodfOk|o?iRY#z}@LFEHeJ%a<`f(l+k|{`kf$ulIG@>G!P9AK_fM za-`8YFgFyLBIT=rJMM=M%$ODxGng6o*aEI)f+j%Yoc@;YtW|a_@>Alc0a<$Q zsMo~HSfa>A+22lUBW?N#&*juH_xsWsU8VOP|HLcPfBK4h-bFupLD_x;gWs(^AiU#9 z2M1bnfN_cOP11ZE?7qoVQHYX+*g+iEht*Zj1cboXjoH8S87%83c^JN9*JT zybCPmU|?;6W0g(?e}pc*{t+v7@xC=2Ig(&5R=nax~UyQNAU z*A5PJ$N}HU0eLmNdp%WC`_M#9I>8T5vay2aD5_b0X<6u6B3qBR07yvhmcB!KJ_s1C z8=-11%f07!0vb{*AO;}Z+mdR4KPfBc2q+1yGSeYGKMQ1H&<(Srl%1sU7m zMo-?oURnoztxbigO(%NmDTgthcHoir**c+LuQ`yo6;PD=uMQY#2!Nn;5E{b=e`~q9 zg#o2UtL39@(^5*)@q8Kw)}H^d*EWxVZ~FLrG17dB1N$a^>W@1w<5q^0!-*gGhU-%g zhH{$p9=Uqu0Mju%f5vHiqYp@8>|-1}QXU7^28}`;n1nW6+c5=5#2^PKrbNty-j+ub zVDvqIzUx~iwGukK9+)!@^O^9!mvnd;$8Je&@6ehx>sjvSp?MV56)!5g*1Wxc@b;V2 z_xy@8={HD!{}orKf9O6vc%<{=fIps5`?=F%K6g(W-}bs8bT3i?XtYL#YSaPQKwfS+ z015D~iTlyJ=vpuZGB(%CHRw+%gZhQu9R!TT$*rD08ugq$J9l^)Q^c^(JZpFv@g^+o z;aGzs^fi=>B<6$8=X%aNe{YMh`?*!6tAzjy?`plEF_+%GX&Nsk_aqN1Kc1Q2=Dh9>cP8f*9ky z=;Zkq-rzN~kvIo$&&sDBpR)q~l)8uoDtQmZC!7c$pYuacO;3Cj z&b3c?7eDrmkF|yleb!Wrv z**LDlt@2WspUKK_uPX-&Z3Y-=^m7VJYXCXOeRpGz;90A0PxpN9p0?7rCz{WJmS1?^ zo#~5za-D5@fVxTcy{qb9v4B zEP?+2qzmTE^?*igGr-78wNT$4JJ{i6)I_cBJq!|m?jEJfybyVl4+u~99WLOxBlXXL zFZ_uc(=Yv@KRc~H&0p|ISEm2=*Y9o8UH=Vzm8eaP=!??+sL`Sj47l+2NHz|@+av63 z)BpY( z4^N-_3EOxV=T`lE?w;n|(+qKqU3=8IT77HY)0_bZ5YOi{hWj-(pmyD_X4sjC9U)fA zKSJ7<+XbETlnXtVObclW7!icfoDWC)#J8pQmb7naYjqt>>C6G&;oZ|(_qb=_z}Nlk z)6>8DO{e`Eq>@d#(OrJ%cI~-)nzt*)sy#+_Vs$RPe{PPCQhsUAg9!j~$S|O;MxsAH z<-Eay(Y3P3FJnC%rjiYF1Un1UnHIesr^bHGUBoLgAh>Sd(CX=uN5 z9p1fr!g`#Y7YDG6-|x29`Q$e}(ik@H%KzQo4V^sOzwu41VVz>vAm;0D<|+)_@?GtB`b z#AxA@(dX_hZM2M{nR@|q8s*4)aG*C1prf^Ud))WGr~&;bG7?HT6xKUsQ2wwP@;`mY z&FQ<|Ugwj6Z+O*1)9YXAMzetu`~xVKcopZ>JvN!1bL9Tt$2L0m^F+I{b=X;>E%lxN zNCA(%d?*2MD08I+B&OD>X1-MbqvqCUMbi^?#)2qc0CK+3bNcOF;FN2As;Q*?a*y+a z18Z^sh2y83Rk{9i)^i3EKu>OhY(A^_asS$R89z3wL~dcvd0_g$>&M!pyOvjR&MM(+ zO>fQo9V7pS?*PW7L|;0#(YfFZ<0W$wTZEmNpE%cBOYPD68jwKSQ@YmsrLJOVqoV~R z)_O;2IjaChylpIgSxTv{eH!i9cW;S<$F;q4fWck!?ooe~i=Ra4_}8{R*A~EOv{9EI z|E=2-tENlRkG}fa^yM#cquT%peHZg8GQhC$?sJ`PC4X*$ug3zIWv=ZhDz;q1va0}U zfMScVGe8lY*GtkIUNvYO$pBdeFv3)=1D^IxavIN9ddJ+o8lrfV(F+HB(6_dCuNNL@ zhGUFIfX%Ca>c;f;Kbj%q*n@ZPKmLhVrk{E37@KsbcoqGSvekPFYxcNPMWRRqvz^;Q z%^)KdZXj#$r{ZLXMS0&bs`Zz^WNLjYk%72Wqiw*U7KF{qQ1S07|f|&MG0SttXRFbsSzG|&)$!; zL1?}$z~)14?_iGSlqBr@i0eMSQ>Qi$kZFu*Jl|_A;?U|qU7zo~_dc%C@>}lthM(p6 zwVw$BM$IF*Hj&5s4h~p3z<}ucr1*U)yz2^<#Tpnu*BfGrHTT-I@~ic8B}y3O`i(9u z{cnETon)l1{`3c@H-2$oPE1BQEwko&73Z{+cyC7=wB~iM`CBt+&W%sVxuTfQ&705NE456mo|;tF9$vrd}YpZ#NbpNM+r>mHsy`FUPAB=(XO?*pK%Ud1_?)&UNW84HUTz|J}0 zNy1UGltvufm+BF_hCBikYkyV`JbfSP|A*Q5*{mE`-Sn%;DeP`yV%_?|(39-(W1WkSv@WYOd?^!(Cw><{GXN!L|F#t8iG z4M0-=)}iP%cZXSO8|$?LFj_gc^XKU7e(09!()s;V+q>tNz2&(T%%sx1_rWKH51Kb8 z_Wb+Z_Bt#h#KX~+Wd6Qax%X|l5Y{}gX%btgF5mFxjGH=fp*dDxl8?x)t ztF^)7QbyyC!<%!#IIChtmcR4A-<ykXouVJuUb1R)6ak5x*K?iyb&)M9uFn@pQZ|E%f3JY{F4(5cg zOXdQn)@x*k!2uXkTX})|@7@M%UVNo>&|NHS;o`_jW1%{xOC_NnLbZ?n zh_ab?XPb0WSLTJ`%?Tw2H}+MW8+)Prq3eJELTbD-H;o!9%dMxM|6I1wK`xL%j24S* z_&k$y#wZr0Jmvml$hAz+TnZp>IvQ^~T0?!TwcckONB57K4#3!;<>)w0E-t0q3BEjx zQ13Mqa)~(|{y4mh>oiIUpWpbdThq7x*JbV7(+&>XAd4%sl*;*(W=F?dIv zW3|Kwt^p1oblzPYynsYNR4g&T1LQ4p@nU7SzK`sU5O%J~2kLC~K+@Jc-!kn%-|r4E z((n?-rSa!;^SCej?s1;APW?>z`vT~4l7T8Z~7g-Ya(_C5E+0B#MT{>T4! zd-~G1TweXs!zSHZcopYzR3g8lhNc@`2xNB!3dJJ0J=^4qcEWEec;WI?>iCFPYoyBA&G9blY$_po+-061T)!M)aVCrSiG zp0&EWM^)TX@6nG|pr2ar!yvfel(l~_n0@KLzcIb__uPo?(rZ5D%Jk;fY`2LlgVdH@ z#S;^}Ki0h&DWZ4T1PlTS*_P$xL3HO_vn0k4zD^c*B@;-q*^Q|V?b z1|;`Z*HrrF`NViw8$hC?_As>pepUsHFvbUWA@5#jLrgj?8yoL2S*`tJ9^|fd+;hQwS$$f=;K~O)AU3694+UB9%JBc zi5FZ$r^?vG;z572|I}|K!rYD;}nJ6#-qWDBZ{rbz-hHJqc%`9f_$7-`0EMz3<;0-OKMcf7eZoipAX+0`nXf&08v&0oPz!yv z7KupMl+X6T!&(Wi=m?8=YSnjM4!pGLnR!{E-)ril5Dm4jZvq%KYj=-KmPWH$G<$Pv z3qM9W9$v=vT8lyXQ8z&5CI;#?CYPsp6@A)k04~2g>p4IN&}d0H9$fm0zZj)tzTpzB zwDU)u=LWsFMWTKf3&;V$Lrd@t4>QM{MA@k!@zipWL*`uq6gOgBDyhx$wRQmPb5urn9=LD=TYzce?-okDQs^-nv)!@SX@|5Dxgj z-0g#&%&?CyAcPPf65wzC?dX$7|M{!;qtof>F{@!N3XKmYmZ(=UI7?_K@sH?9DG z|6Epz5%p5W+4H>hzyA8^)AxVrSKfdA{^`^2|LT|il~VrS-+%h_9S!Cr{pRHN|D7Cr z(T~n{eU8{Jl_&K|{m!Nz9RFVm_=j)gihO;dj{f5Qf9Ki-$G=vz4?3bh-Jd^Q9)Gz1 z^b;{1;Msrv=AL`kd-kcRc>^T6UG=zZ{>xxHA3b`nDL$q=)=exm6yqd&uZl zm+!IeV$a|GIx_y7v*kVM|4r7-US<1c24GYbhH@k9yHaUKxWPnwji7_``?v4Bj@0j{ zzH!g_zuR;2lHw|$qF4FOq>H?1D<#yUt-#M0*U2b7V6gjHfQMBE!=*L|p!16>J07<7)w1AcUHn$#is4ki5Y$C8D6e?x3Ubn zr8DE$1MJKwM~gg5+e&L2lJd%t-_H)Xa8$MpnXFL1w1@w2kq*CIw5YFG8pwq>7Dw|Q zy3=e*dGQT;a5VM7{If1cSSUL_#PaL?_azTyMK=m*r*QAcpGuV;Q#lHJ?v*>SOaW+a-J}} zI5mX6<#-j3Q)pBhrxTwYN<}6r%2OjV>=ZiuI6B~Bpr|HI-n|lNw6dZHt;5Tf_VI|k z(Zg2qKBj+%-@SC+m(2d|jOj5k|zOTTS${YLfe;x3qE=%Z)S22{n z0yuQ;fW|`BLfI8vtMRtLyFcI1Kw=RinJ+1slEkX*277@%`8F}L$nr7j0D$8nfnkhq=9d9* zW|OkRiZ|NMe@__Lr$Fv%pwR}G99t810U7 z0HcwWtjezVs139EotHn)4_oF>0$wrPi&w@d@hujR3~0a~r&Ybm?4Y1Me; z&)AYwouY#_S23py@XZ8!z+;GN+jA(*v)58Qs`N?#xk@khUdrn{+tWjR=dPa4Ewg%V z14kvD^t@vIcHZ4Hb|x>yHJ_RW7?p9p`vQ-Y!HcPi8E7B)schcGFQJhaD0=F+VlDCsq(W`+HZLY&b_%LV8lq@d1*RT#0K6o zMxi0~z`%I;?j7*+y?(4%j%~R940U#t9rd@pj7O>T`>h6 z>L<6r2pd>SJ;KiL^_v@(G1 zq23*Ql)B2TGc&~-waCNrQ>!YUZM20LSmQNdkqI6te-7}G0k(5_{*&_3dSRFRdQ(wt-knui|^I zUw)~Mj814)XxHbpH1tmzGv}YT28{r=9${y63|pD2Ya2NF+L=D@J%~3Buo)K7V?Y8C z#p60;*Is_z7T*chHRdi*hm(J&30bBpM(D_V#1vIKpI>tbGkFYa&@rg?6v=wdO7?)%V zyAV&k@0}E|#GC28GD@o<-1Qip*#fF5L+ag9kTG)vPni_Z zeFo2Ps9sU`J(n>gnSR%l9;R{~?NvPTlhT(}2FwcB9?Nw6J<}E-U$uY-Ajc-`biz0; zK0C)nZJ*_rAArb0mfWzj|8DL%W!#Hn-lT;*)WWj2C(F;+qRR;E!#>DR2nW5~Y5x~Y z#G+LQkjW|6{(R-FC$NkHM&(6<(Gi3YSl!lgbT)$T+&oH0Lm0c6pQ2H3*-e=Ac`P_v{Lq0RZJ;+`y*qON~XSQCbRj@61Q(rA=B+u+TZt$zwL~@ zZN1w0qZaqMLS5Z;jw3)QHa)79G*^k3sp%`ZgG#WK?N(C)ESCg~R?!$IMh^-d8?x@X z8qN6Wbu697IP+^bsnO0e&tpVSwwJM2YYC@Ir&ziGW>&cASQ0gO90j(HJcWK^k5>y% zV~+rdt-NWJ8=2FutQd9<)l2FcZRBpiv2;30N>+A%rNOx>YwIa$F<`kgV6;kO#E8d{ zLd1xMebxDmN)ck^<$7x+QF(>#DNwXRs4v9xsB7EHI7-rQ`IO2WV6aMdO>ox1?XX9-19Z^}V~ju;;x}k5?{Fvj+^he2#n$00H1Cq1hgL zsRfEWpcI$G0(UbwO za+mpyG+4H&DWRG37!}_m-Q*>58}-iiGLDk;TRx?ss+j{Y+R}HZ6-?MpBavODTdl^Sb`Yj(izS4c!&Un1uay)-VCjij;tDApk z=VwyEl&v%+p%yd(+z3(oCb~!1nS7YG#Bozt0xJVY+Dh+G^h$#eyj|=uWv77ZwY@)Urm?z`1IQq zY(vX0I#|j8s&5@QsE!@uXA9|j)I5Scg;)!c21V01d-Wo7kCFIU;oV~R|X``6!VZeC&`0DL3 znYTF7i0KF(UP;#xaOC$@sTNqPawC_O7D2!(9n06xP&xhJH(1aHaQ?SW-R0%TKsfvY zSXrRL(fH40n7RQJ`d-A`cm3y-eIV^sHud&-MqZ^~h4Isgom1|sezGrCrmI|cRXYSY z_8x4nXgkpXV`d^O7JpK=u!AlyFvt z$Fd)P@38YxBJ+Num%TiWq+L1}IaeYxJasv|mEwV8+Smo3e0$_f)Wb&EeIE6}r(L(> z(*Q;*FouiH!#X{??jI2t-bRelNP@SLd5rKI3!jbRpOFxaJ4DBiw^L5MOMngYGO79q zrFp!D3?80HceD|g-!P8P&-&fd%Sf4yvd%3hb!Xl{>gO-G0v%bs%nA!z6C`x&pTisV zt&AJb7%A_ZJyUu_&)|avDt2C)R_ZsW&+2ZJM+sM)-&i`2u@oqnJZg_hxpPD2bTH<% zvh&(b#}ppJu);!{-d@I|xA_77UMtrYiC}-Z4z*e&@{(HKU+Mz#y@Aw?(Q!m<*K3Xx zx7DP1(xFALiBj6uE6!#)p7pfzv|=Td(aGt^@X zt$NO0oG&UIjSZ^p8%y|nTEJ++Vnp=C8M@d0(=NX|(g}~6->87sJEjglazWYQwPrt+ z`}_!E&Spv{ME&6P;@tiF3V6<@WzW;#46yv+JVW3WI-wEN+sin^m>@gDeC7>AoOpYo zGv2_LH`@QDt(VTVwS{p#z-&oL)hcA41qIA@c`#bPa=Qv+%axNFr4!FVh&Q>xUP~)i*$g-z^Eg85mrFkOO4kn)BY? zK=SBCrC@z_`wGcJm4$li5o)8OF_36p>JV=;uF-Z4*w4s+=NXm&Ho0Y}I-JY7tG(LC zIWFQ)E5}i5=g=9jxdEeBX9#Zg{6-8i9l=>zp#i9H`lzjcFiwP74M0+-rK2gU12EHZ zp^X74d*b@pe|I{Uou4K$A#Wn9S{c(zQr&)0zUQ97Jk7Ia-L2&nb!8y~cxUW-@A zgSOMEGJ1 z2Cx+=ceKi5#$}=uPz@q_4Tea<%>+Ct5lmRpVo1Gui>Fs5ltq;fYuJW4> zt8@X(?14{6hN^?XEah(Mja+EM7IF^dRdWqqay)-MfFpckgUFv_LvvjtlBa%m25`hS z_n4~DvLaRgUpfG6#oq>u2}zn#0~{4GYH2mU@v8F}kyXrQ=-jAK^=i{fj*>zau|AY%XGak?U_91X zH|zO#anDdoNQd!++2_*xN&qlao!Z&nbyXzZuY40<}CMC=|>7Idx_ zB*yBV4z*{<+y}Zb9`^P25-5$T<#LkpvgfAL`?;0F(Ug4y#zGCiaPg@@Wp^CD@NV#!s)aW9Qv;nAxuBc2K#}xpks{|kwoW76vjmn<;pi1sc5lFV zwpP98sbE_e<6Zu6S~9;;ZOr>21Mk8LUOeR;o$UejL^bUcAc>K*`H>D|ck%dG0Wp?6 z_QACDuY9Bp-)R91bx~?Na_bR&GB5>jo=vo;p5b-MXXWL%K#^yQylMW*tJXB5jKrv4 zBhzhLxX5+8X3w-ds&Mow1Jn%|8`L7(9|;RmTC2rf$@nPes=;H?Nzn>?{w@P zitZ&V+5>y@LY(8Jg!pQh(OGb*fb%N5Gi<IrkOC6PZ_pee$_{8=apyb?|l7UNpv-R5Q;b_XT0V7gqOn59( z8_;58Qu7;2JF(ebJUTIW577!9&=6lMT-#p8d9|Q)HpSKG_69KQIe(8f+I#UzN~SIE zj{=W?0c2-;J+>^v9n(@CwL6x#v`kK=J`o`TR6;WcCL1um)IsyV7^$4SrSQ1@9Qg`? z^3*AYS8!i5T0#aPn!T4)h^G~rUsG*R0>j zmwi=x&HMDrg$$AB4qd-E5}qLx?brY8bM)`%p`?xf`8wy)sbLNA~$Y>?wQWLhMRm>P}r7 z<+K5N_8E@oq>&94Z5yHu7)>&Rj=A$22@6qKrSlkPe^o^RA3B}A&C7d*^Nkh*3*Vr< zf3p79UNNm@SlZ?=w56VX>YqUt^=_S-=JMR#*r)Kw*T5-lHbvMZ(z^Q!;j?l{$xYoy zrs&QycpuU4o}p+b%Hn~W4=%rb1(Y28^PD!K>iX*UHej4kgV^Pq`Hi+RQH8T(?5{A7 zkyXF32DZW`q%qm4>y+o!mb`oHJH8coARh$UEjIEFUAOxqjvKIRDO1O#E7s3Uw;&%r zc}FK2YbTmheHRd73oz(NQv@Fd9vy2*I-7nn!9^dSZdhc59q!$BsVC%mNADv#RXXJ( zS2afUd*}hS9P$CBo#WMfz5VjgP5y4cII$v8D83d`0ti!VuMqZCp@$J(u8?uZ_!FA& ziJzekoB2(5Z1j|Ug#3LOU+uhf!5Ltj&~yx-^#&iUJb-{lKJm#hhhiSgoOiGE4E6w- z03bQ`whwpmK9(*j%heBLWufTYcSbztw;>-;VhgSYEH_{rp)rX(05)G7a0~&Q#H-QU zD|nFr!hpsQF^abE9Y)vYnfT9!ymp}mwu0Br=lcrhE9l3{azZCdy&=a1Fr-DNVcxse z<)$uaPd(-s8t+6za_kd88D8$ZSjMkEtJfZbem@`1ku9rY@frh;sQ0T7a_Sk1Mh;ht zS^*eUBm-|L?GN%nXHGkOd8_^34H(Bt=+f|Zij|K#9j?H&u-%EaX(fy!2 zGnMh0^7^@ULMC^HJu!7kHO6uFWoVJ^?L>|qwSvw%>rnCeFZpI0yj$Tp?fNl z5_-PXH5(`Z&;gPw<3}|;sf9d_gR~ZD_D(~2Tyr9X^sOo^PCwlWOPDM+U^GcAbR-R=Yur=IQ|)U7KEQ@l5ITj$HYz8j4o@)3>Xwg<3m%Lg3Qx-$RkR z5dRx6hNLq18p=b3`NYVfw+r9TZ=XM>1h$t^*E?WHg^l#vZSDdD&MaPOlWTq|cw!Aa z9?V!Uk=4r+WIAxM#seKluIVMbjVpw&0V=Vtezhv_>>WW$%Auxf%?7~ewUN!aG{Ct5 zqe&+7h~SPyr*d3K!84&}@4S%WD3|>^PXZW-BmL<>g8TQNFV5TCi^%%Fe_cDA)vN5e z>Gt-Jg!nLz+|{44c+s>qknJpb8P$H+%6D-?jXrJb<4JCbjP~c7E$g zfDX<+BJXoYr*|X(-;vm9KLies&mDjFo+EX|`9DV)_5Q64e5<@|PFjz}E3J6UO$Q)_ z{XDZ+1}~~D=*Z1y#&eVnuo&uJQv6^;{^_QH_;vumNLm2xQ4R(2$42#(ckh7~?M3ZK zkgAJW_h}cg=;7+qgq8q|4-eg;_F@YuLXFgq#UGN{Kc7?rzCkt-!~k0e+>IQC8-KAQ z?W1Q{lum4W8N<>C9Gn3@;Z)*Rkv#*BJFhB6e_1T^L#8*4PV*NhlU2Q|boQmrgdFb? zY4e|OV5q-~Yg73hbP$`_YW^d2&MpJApI($B%U*fcq^tC2^(jK90b@vkAArv(w@>`! z`jJMBppNs1n&HhtZ`1~$|s7B6!P#LpSk+)-lO&HWqhw8bl7-&dTp1o%Hoyx zrSAXBJZZ=`)IS_Kz%~G;Y2&p2*;LaGOE#6J3w;1cypQfjj?8Lhgq#^L|L#s$G#Rqb zsXP=YO1+zN|Ei&A>Mu6c|K03;@s=vA;{EQ%M!x;eCd*%3*ZIzl{bcI2C6fpkd^9WVkdGwM1 zjmn>IcP1Pgdp5)?9jgK|S6smsD(4E^ z^?Y{q#WDc3nnfh#s@7ZSgt@4@ zx&dRI{Hos$dF>3aF*ma`BsOX;gc?m!wiQ3sR9$J462Rana9+!ntFGlkBDH1YeLA%> z8&IFklMWPb*c}$HoGp9p^XNDLD0i`p$-3v_4S1?q*39< znU6Qq1|mN^qYnV-bbkqD7`UU6 z@ma4BjR0hHZl&{OMj0u*pMcsBjgV0&-@9MK=x;A$el0(|Rm}Nvx;lygEq7k^JW7s! zK4(M1*!mXpMQa9VC5v4AGPgeW=RaO?@#$*vJX1ec?<>uYP6v#*vyTJvm^Us2?s_bw zP+$!(IwHhSy69-6%U+F`MuFBdjrK0j)^uVyGPt^zk@jnz#MWLF+CK{ba<=kf&;)P< zKyE6R06^!98<=t|A#5V6YC{eKm7eMc)O*~!LWq&_G53*UscchnXm$VU4hYqsuiRGx zbp7cervM|Bb|e5_$eR}ecPs5}cPxkf#wIaoH#Yxc#3)CNMvTwcan!_F#Unbgzfl~k zq5A9S==L(cmm?jXfy7wbegDR}U*@s$!5pedS7Jqs|$iv1?I&E|1ON=}51hGN>c@2nynfE|v~1az!-KW1Zp&5P|2PV`~Du;caTU zHA^nP7~zN4(E-QMl%LjRT{%5K$&U^wGTkLc$Os!lo#hbq@Boh@O+OvdFZHI4=p%ey zXP#46HeH}6A6HpD9Fu|C>16iTYS44)P_;M8MN`8Q&wT6+7+-5zb-trUISPoqvKc&# z;*ZQxGA8hb2qV7dE`}u2=Nd-4x0i9|+#&1iIa1b^gb_55$gvkO2n4XGFpuSJ{KPzG z_S_wh)G^b6kXMXq1~4S+>r*U`((BY2K=xS(d8)boE#y#B-!1a5O#>Mz`#fsB!Lb~) zgPg%P{rS%hBx0Aam(T``P6!GFA*2zb94avu4u=OtBf}tS#B}uru4KIGJvGp2!Sm>j zkhTcGqCF_^P+GQ`pp20`BYMiH2t)nrtH(3flcv&J{a#9k84&;Mz!=9CgkH7WxE;QF zeaWRod3&Ux=K8nDZ=@5X1;3#*lwOavtMG^YG20sA*RCP^*zOJ+FghuzCGVp^kmA8A zGISiNco^E%=VOFqbmwbe4>gKyQTE;=Ve7ziH)uXvCNqHkNcn6UEuU8%$uS?=x9ul* zym$5A0O!hS_uba`{xxsXl!KuE567#&V{@VBZ;(9QLsPpmA#S5!9rPUi8g=} zWJKN9@~YlfgAjBZkk*5+AAm##r^=Kd-x53W)PPZqltzKE(grxXVJSXQJ`7^2v+L17 zcA8@(P~&Ah3!KiD2>l!7jY{!$wyZ~xn``jl17LNfJspt^U@_K_QIFVN=1+|v5K zm#ug%(Y2=qj0ymaa2ZpnH4U)mDCMYd82C~z=rOJR`??Z1<7LE4Yav+lyf@KiQ}b_n z@lXpAV|-!!2`i;M-gH8Q-&ft!k?L=~T~179xz~|ti!Zs5&NuGAePWOPx<=n00MgYb z`iwWyJd?TQ>iL}VnzhIm01aTgI*pvVh3z@B;My@99jtE85C`udPC_rls zSAlDL=Bh^ww6jyygt4oof6+lyB=Q$$JbrYch+dTPRefbx6wLSbF5Lpsozg9xN-Qkh z(nyFbr8KOR2qIlecXx-t5+Whp(%lP4cm4PGetO>P{(i2RxlYW?xz9O*liDc6vXU|o zi4ltDJ?V3aF)i%%0?g}_hWJ_-Dy9_T78cFJ|kmyy!eeNpN)ah^inWJ_6#F2Z0a z^&ZoTD{BT?3)RG?FvL={*0ORwcxO9*8RrZ=%sAtne3RV^ZtVKAHI7nXWb7r*x$Lan zm!C}Zc=IGoqO-^mdV@NyJJwVxa$5-3r`VxmgrGXptTQWuR&>XT|0S@|dJ!6!Z$li_B5sOhmo*^X4ZT2K@Ul90m%HkX|D4{|ynNCLR+r~@XFcXw zXEjomik+S!j3x1TAc;w(_vpa!&F~bGvDEX?(VH9;YwTKm=925q`M4=fLh|BGn9?9u z;&aE9Hi|H6TuiAS{Oi-HkqEt%-Q_G*NXzsU%_xVkN zdnha#hB7`0*V&8`2U4#n5du)ta}ggB*>2vMlk)zL*R*_~%Db0x9Hr(n5%YyMz&e>H zF-mk&IJm5pFX|iZZTMqR<_aT8l%LipY|~QmRm`TxWMO5q`RSKow%u!8pc0fzMr09SJ;;It%?z>S!^qTK>oF_D0SHGk2CMgS!xMS4J+BG*{TzW8(nH_Ag3YNRGP;AIgBOT*BnrklxM&dk9eN;Mn zEZ7n2;rwxMGG{H`F<7sUs^xPC^92NHrN1?% zk~+|#*Nr=qY!tTS88K2YVkHln(gi3{C8jkgc(u?5l~XO#2zhCN`Po;rp{9>+QH_3HlfJ5(Qi)lmRi z*6I_=)QYYEqI;7f>-@oV!okJbk{=2NwOr}P!wC3giJ@8tQQYf{SJAo=+dB225>7Wg_Ox)VF9n=~oiV44{9h}hH$|h6c>!}>{2bC3 zl9>|N)9#5L;+^m)85D&Nj^|&AG)z)lB|2lPscLBU8Ti*4}ffXiEtA;$fL6GhqT zwJ(l(*b8s9kgK=HpO2zFqY-Dn=cg^eY*uHbEgfH-r%#SM%Q1Eva^Kkywr8|#%=5|r zyFCJKa^`+4)N6ske4_~#UWrJiKhIT1{maz=bVsE*jplrw!_y15Nac zz~4t>L&MnwFVTaJ%=F^nf7`Xql~7HJ?1Tzz7>KkAq%0U$*3uOPy!LzE+Fn#=?*EL% zHn$2~BggDY1chrvJ0;f$3t`huJQC-@y;4oQnjVB*CDW6oH5Y>`j*OnCB_LPvs*9a- zl)@v2o&J9IxKhmv;k(2tOH}-vYeIq~mGF#G@bAo{yUyx-+4QiANKa1nXPKz!t3xI@ zey`HDJ4$}@r4N6@Gl^b5?=!YC-YN6~>swaKqOgr2RfnjN*5h7~ z$CIaD-$bdxDPqO0`Ex!-iroduB8vAnx<{Dg*^$#9xt*cv(|{Q!gL6V+PYs_7j+H&q zG1kde9!un~40M`C&elHfLJ7Xf4>IAM`&?^h6<(}{pLHI#it4kexq?=w%ejhms$Y^trW}j)E;!Vs9V!O#~+-OmFg?zhhEH+;s z|D?vo9L}j8i;~yISG$_e0$Xux!G|-|bl+SZl%_jMXWsXh;q)DWJ6+4W?;h|m7rc8RUut!-KRd%&(Lx42 z)YZ_q2$hWo?aS0%-TRxqR1V9XwH!a0JiCA2{BbtS`SL1-q=%4+UMBs$HXAb(pOP0? z7n~;L?k<=VqzDj^lJ5YRnh`AQJLv)WHjhf!ptF&wVT@9WtN_l*{wPd%fKM!62p|E{ z=v!PIqQ1IdvzwGTYdm>YcA@pvl@QB*?;`Ijc94b;GC7&{#i2NI4^1XEde?x$9ha8; z=Uk~FWX3>>+Qlxq86}cE{w{H%1i!TuAi)T*#*W&Wy3sv1G!6m|IneJaTk7*|;>CJb z03G|UMCb~MpVUFvf0TWo0330sM5x<9Cr*mKZmb@-@>k@bLoZagZir_r@(FLbVdVBy zr!~MrevPAjgvSseF2{;%lp-0GwH)M(P5}eO!~w%4!@)~4Cn9N@XKKKsBQpi?Y%vTT z$rae66}96_jorRd@B-@x;{lZi)~Dgq2L8RnT2Z#90~hD}3}QmL$7Orjt_rZERKeZw z?|emBy9>LP$jQHq>8d{nq_6U}Zd}W6`0hHouXK9R!{FZ3T_sY!6rpjsAGa$brhm^4 z33a7zctOcK4Muo0KKN1^qW|a*FRWMgp+2Q}*pkZ+DT%h1nsV7f|1>-wnR3EsN!{{3 z*`ZV^du+J8eA}(}T0eXK>*~a#Gj7)7YYNN7w=}D&_OlOm^1Eu*EsI@e$QH%HjbL_( zURCAV!-WbToq%T=dTr@*k(UO1UJ}t)g68Nfp$M`5MXWtbXC2&=mmBMl*;q={@x=ZY zKI@+=ZkxQzTf8vLf%V+3Yu;XE`^B!dLd+zYLnixff1;iQlmCg-z|k%BoG&MMPbZ1^ zL?8v^(J6UNCKK(>iV7jqSoz2Mtpir*h9>V82$}2s6WKNM<7EI6;b?AEK zULf=s5+vVktxB87>w5d={S%BijC;A0Pqtr1vrML5F}lG?Ci1h#CzAM(@V<$sc@;or z%tQsdRb+PkEFV5aX$sO&V0E}piwq?+w@28e5BX&sAKBumARtH^#5lz7`tFBqt-#i@ zD-v3Wsx>e_71x|9NxLu=CsAUy_4Y1M1HRTo&$FLIc!~e1C$ydjc`=B0MQNJpDpnwP zl62hCSYD2Dy;I#9>>@UcnSeR<6aw>6KIWTk2AXJl0#eo;W{g5N|>m<4TFBzt#rqU02gFlKt zK&fHh&i<`8BDX}8K3cq7W5TMJ7s2zU;Pge#-4qN}lf~1Mg(H3S>x*!=S*?cnOaz~< z2w7_FNknyLg~FWZ;DaH#`uZQ2JtV(;N1xrdK~<`dK zZ;~(@e`%@fJYubth~&Tfe=m~vFta|hLgjW3OSxb8VK7Me*E%q^vRLZDL>E}21rs&8 zvSzxpFl;E8gpF~eJFCr1Pzr-HmvqYcEuE>#WkK{AXCf#B6k$MfWKJTG+wIytviQvN z`%~aZOI!fo*Ihb>GYbvFn*PMxB;V$9r0rEojlwnQ>C-#pXSS4t+%S^oFb@@iImbAe zq~@P9g2uK1`6!ZDJ`JjHcMbedfH{aGv|1YHlgn`)U+I1J)I5iaUnQEcuNTB;|M4lD z_U*CYwHmz-ihH7+(sqnf<|QjRy+Rs!T?0*GoaY4~##3tIbF9n$KU#3dQ@iBxZ28?y zy$1coSKtJB?WgE6)@3@}?|_g!RJSmD3%h-tK%)2Mq~`hZ6FBf9PAf8g2Vr9{%w@GR z6z!ufMGx_2oJqIciC`Lc?CzCF+G|7%Pq95e^K^z#!s!66yB)SXh8`zLLYrWD zCvjwiU!vMMth8L*J?-t3x~MEY8w*tML|cQ0@}g7B;8aucp6XbO1RLtcC*`xMy3tp& z0rAMXLJ2A+Ls@WTbypLHJ$wNzMLQysM3H#19-I*Ey4vUi$+7cpp+eNuiP2ytMtH|G zvBKnE?#K~ChYpXkhlvDx{Onw>frC+87Jc^4UOP9p!=8EZ@j16iY2J6(Gpq9sNKPvj zgUB%f`M*7_hJ^$Uee8Orj>Wc=vef(4V%qJ0A+tJE^->6)A+7i%#3$t!iy@AXb}UwS zsaFlDhQ7@Brtg{(37{1@{8%|!t!eL1jD7S|lvG@!tf=ej7vi@gh#^;RhttS~#{&I% zd-}%rkc*ICLB=(cZ|RZ&ky!49TJGWW`T{~x^H;oOX)V{%7-EIz2FA)e|FW=HnImtL zdpHN`SCkwH-8Q2yqS}=9_LXb>(4G93PjAbg`hNd~#tm`LS70#R)?sr&kHLc+)69@= z03(~)V?DKH{PXXKy#)!-%8|+zre_P9r6~_d<{4EFCxJ`%N6nJcuQH`E$kKt{nv3ht z(%4$N6ffV7aRh%AxM|kax+G0lAeJdlp4dvNqn^WTGCIWuU}?=ICUvRZr|ndYy=-uh zlMO~&@O88tm+-;}tS577;#K9A7QU`8=EwEY0~|%>Ih|k;H-#h=l*P7>>N^XdKqjS7t#BsyQ*xyk-i$4c)i|nmaA?g5 zXe`c$QFYnkw>(y$OD(wen-L;~FMj9z&GmS@-I5esybCa>D4NV}r1<~!UrZ)WEfBP3#oFd$G$>&byOf={Bc-hF%D z9f2!9wLC)L?w`od5!;_-j!UT)Tb<@i+H1Y@cFR8ARWC)At^RENOL^4XtF+uw*k@29 z32@4}zIZ9eDAN73d-idR2bV$@!=_Iu7c%`Gg-3irIh#2X)iU0iU}J1Rv+^R1v7aXE zTF4%IEp8w+8;el9K0BGpvY-Vl`x(TS59|>#SCBx`Zyy@mtFO^$-S=P9yWdwun2!Fn ziK1~kZ-uf`iL-5j2oGOr!){kw zO7esu2HA8_BIppGCF_!DgO;0i-Jz<=i`?wqWL-46_Pf`$XJ+YyIRXAAFcUc-OTzXZ zTQI3kQr7YF z+pp5HE*(Ajv2L4v37yz5d}I267JMFzYr@Z|NN=sMe2;5nHC9+vE|TIay8A@h9*IHN zOfFCydY;9R`BDQRDRK5{Q%~XF(LkNh;a~-=%%B)AR5j#c4CO;T`LsIZlSQA?UFF{# z0bKsjIRrWrQQh6YZ9L|QwsX9J<}0ef%BHOpAlzZ-BJ$YU5%9x1oKzqug2zVtxc46r zoV`5svpr1U=%bsN$7m@{mDrjxtn|YFxCzH%tgfV_@0SjQ4}9=QRQSnLw$Vt`@|mq! z`A3*84=HM&Rv(XC!NUDVC3Y&-F!ezEfi=!qEfGY-Ns13gb@#o{3AUDFV{D703wPUQf!x{vv<6`c`X@~n567yF%21}@}|4E1cBjj)!SnY==ayuY1jLT5pD zv{*THNQ0^nS=+%MIPrxhQ;VWEk+5rvXtXKc(!fKjKC!7mKu4|nH~X|cR#xgaO~01k zHuZ zcl((iB_q#HWmM9!BDJu`?UwZ8j^Un|_Eoj%+g19}%;8HGOZFmOjt)row04&_H(_Yw zO!OHeVT%=eYmva+IjXv6fR8@^WJnd-D_|U8t$*Uw-}7`Nzw2I83+1(Qw_9oG=ujc9 z(N{`4Q@!Ayk+TUg!(mk8|M+hfBT_1|c3$KhK;u-YYng^t+z#v-#zZE?lfgIvg!n2U z{$tw|{#1d^l5BwqH-1T7)ZWzicHdUmmgD(7L@e=aA#x=lY{U$2eHqgV8#txtHm0gF zDbe#wt{x3PvD9r*B!q0&PR5-Xzmsgb`M~OLxE>-gI{z`fZ`q1bRi(u~URsiM@bBVK zScX%`Mk?t@2OMk&y0brYJL**#u0$i-7&RkhtUTGs?S3q+=YVG_PF9cR}_Ry*7urirK}e>|4hQC2iY(Eqna1qel3;C z+Lmcm$7*D2MD+Wf%`rI*|6Xl3dv9Ej3f=?~Wcs<(islVQ)DgAcZ7^|$*Zlf2%{U`0 ziW$jRgtwRI&Gb0#%zi{SOX#}4R7v!*ZhcIO^;^q`>cA=IH>(ZbUFDK{O0rb|-0@#T z85_U@5y6dkq`RI*{91Sz4>IOs7U_eBxfiv3t8n6Fx+__D#4|}WiJr+W2Z{~1bmP|T zE25;_W|;@L?hAxL*Zng)=kV@^Y0^!Hgo6Vd)j$3MJ1vMG>l(mIaG1N8kA^d(a^1fJ zkcNZ7#DRv-piqyexUW1n(AK(%JFq@1oN!>aqq>npb4%GPR5$8Fw)~WJ`-7lIOEjFH z-LB(L-Xz4&MUgMxhfuu8#^Ed{YJrAv%MY)&q-pHj+e{=g0gyRuzU8X%0kOZ5`*A_x zqhig*KIb(a!vPd#g_^hKq2(JiAgg?`P*v!x~);8i2S-q z@l-DM*3&@qwYUOc9(6fy$D0_6ma3nzy*55qVn{_%b7&8PTtrHll1)PE<@0wNFnp4D zT~lW;Gq~30ih<&g>u0)g=IcNa=t5V%@g9`0`}yezALou5u9z zvg7M&qDE2#EFo%fGLRZ$-(L1G1G429!_MOQL$*8zA9(i&mummJiW%l!nV3J4jAk0` z_;c?PfolYIa0*R6eSeslyBp@{7Ir7{`*A!Z0ViirMW=E7JVS74J5>i7o^h-6`m!0B z;cUlztk3gxVXcrw)eATPFdhADCXe%{|Mc3j9I0&;&@u3$aROugKqPad6w8>(X$e}xDjhlcWeT#vq3m-EB&^o{q7q@)YAk@TT zxEw?bK4`jx3g=E84LrW1hKxU-?w=c09ZvK<5l;Zowh6e$RQI-{zDEJsTeXA)+a!P$ z0VKMp9Q3wD704=l6n-I?fd?^|W3R#>a}ljX_vNiaXVV+A`2Kp{j@;U9=Z5x$w`7G= zwa~$PGH7wX$Dw-^xj=71oi@C!d47VpVj{ta0M#t7#S8dWlH}0K7QwMzm5kSVK3TAjkj=(6Qf*fcHp9&Dxersz>6|XW@7nq|(zRXp%|PYc z+$!^x?P8nin_$e9q_3+Yb8XS~40s(`sbHXY)o`r1-?3VE(jhvRkb~T=`Xm1*e=)bG zqdx%;vH_O?jgp4pvKt5Nwejp7R>z-c<(m*YMP+JS6j-dYdYF|fiZKAID@sL@2TlcM z8zM~5;HQw0K~?e}+tb*t?^xA}-kxxsrZUf(kFitE?A%t!)fKCizI0D+j|K(1X1o?6 ztH=SR53i(K^?lN04tG_$gvab!P zuERp5MUPu2?cvEp=A>fgKgMWUn*2k9jE+g+j9C)_OSELh4d>(ahHatCIz%l=+owT^ z^CW7x^ooI9MvXbdw3IED+v`%F9!z}b(B}inSjTDki%0@qop5G;4u5!2C^Q-}o4u3Kn&p);rfH z)E$?Fmab--iZxs&nfj;yL^Q-q2Gz%VML=JfOm5H!ww<3HY)kmQNuS3xP~*H7^TdoS zcUI4p`dnIOR_m4%ROS;=4t)c8<#o-T9;EU-B)X5pHDIi^a}!jjCdH!P_{vgPf6HXMa=I(-V^!xQmj@xtu}4wR#r%DcHj>?jyQO z?bflm@XPnx5_FVu%RZ+g_1=`FAks|s(+pY<+soBC8bsSSrLPEcxyddZTYGF4=nFU) zaX@e!$VG`JpFur-@r!l#XS0fG4ldH?Xci75pKRfi<7`S+jZW@+a6kk_A7SNID)woA zJy1?`qAI3c4fb`HwRNw#7fR3wE8PS2q<*JWv4a^10BoG?9OO6)kFBI03~|;)LcTd z_T}Xvd8D1f*AS^1;oGX4f8dCbX#!kBiW1l~Uy~WWmacstSxdpn!DkD_v>Lw779UrU z%5Sc-S_H9rL@jCX>oz}DIavouBkG?D0gkFnuFVI2;*<%#Ze1lL0Uy|@K65fx_p~$J z=(vSx&83=~vZ}TjqJFC)32!<}qSKe2B6@rP-8-p(eU2Viz~+6^Yi_Kc7R$`10m86; zNUksGyx19zWdl~=J>1!i;YWK>rb}xxn&X-rhj_D3@>Lv0RW$)`I*O95f?Y7hrTRNg z@C2kUJ9ZGZ%fsYgTcdAQfwe-~*?2w8usc_4WFB~0e#~cYD^6#4c2Vw=UXR%iYMFWB zjlzgM2Fua=>&YsDvLr7{$Ya0ttB>&jPbS09dDDSPR55A60#jUQE`{gL2FO=^T}jP2 z-AezA96QIkxmrHC$e>s3@T4F8}lf~s<@~@rvm3rXfh~7cmT@! z$%Gjfy+&uhN~}vac!tbFNf)smr#9fb8eoo{!lQul;n{b%%@KnF`Ty56!H!=zd=(x# zZ2_}w%8-Acs|LOU8@?N*ai!UQP}A+4{0CtdTkx# zV(tIO`ED$g)}-WQkB%06F!nR#jOugLl@(+5cc4e7Du7i`dm>`S@ek#^jtJ?9a$(~n z6Zxjc1;g+eJ}sX0AwOD+1kSc#sfPZgTkJ0VXuEmb=f`|fMU^+kv9pwY{ z@T5{pniqrcd=$`xSYtd$u=UUQ`3Id4*;AuN}reP$)MbUkW(y%_gs8YWs&o8*MHK}=zYIj5t+Mn?|w`$7tdD5IpJIZ{HEx% zV>~Rv`CK(e)PpE$cl!@udGFE$9l;H07=&E~12bH(8PlWA%VXj9j!Q zk`b()^XfLPUi*_G(}|Xz?8cN~iOQg85ncE+g^R2cKQkQ>JhtlCcX-xS_PaN$>H$ht zHV;XqO(Vqau#7{w(p@y=2A@|a+wo2{y@00*!2TJf1Ua5^&d{lPFkduncg4xd-6v5d z5}u5*-XQD_)y+f;2W}41nhdJuD>O|SQYGf<+KNHPEYu;HWcU^69DGRn2ba&3j_p88 z_T(bhpgcaOn@zkg#&Y9mU=0}^)|NLW6+V01;xe+d2@}G*qYRDtqJIG}ci0aX$txQE zlX!R89@l>neQtjZAIkrB7C;U}sz?`Qz5K+W9Q#Cth=gt?58cx061;cwD{4{5ID_%{ zZ|ePTI-4*(TP>*-){4AAZ+J1rdqWt=B?hde4~%82BAiF30z**+pAZ%Mggj}@6*xuz3)FJvbP zQQKjT{`0bi;#YW#KXy%+sS*)<&ObS0^|Q7n3BQ$={2xXDCSa2 zLJ^Uoad@qKLcRHZ`ZAFlG^!$PvO`ltaUMPi`UX#iNLJTBY)fV1Q|SK4ZRq?d?$IP_ zgt+~CO;mE1)HvwE8k;9oP)`-i!3A16l$HeHi7!U4U3!zX)_YAL2s7U_LnpvaDT2n- zEkZdxJIr2bkO4zi-+h#Q&fQh`Jb-2IDJ4jfi`y{yMfIjj z^;+%sU&;k)`&G-pvhma*%Dx%3Hqt&`BI=>3KA5$9QWrA|9*0Le-GaVvsr)pA!$G%m3NGzE@a&*Lc(!*K1)q*C^=6tP;nDi1S2YO9zEg->-^=78fCVJ}>S7 z6i}PGsU_mM{|`T_&_pcZ&dW}t&1g;kL8!7NY9FK3N~$CFe_?8fLd|d9{7+=p1m(Yw zXQ=^h5(*rv{Z~d4S`}9iK1H>OSNb_m(0{>5JQw&mEXC{9e`T1U%)+`bB4;bxMsAb3 z{tH5E$1X92VabI5%HRn)03+9U@OOFVi>l>+5r*6n4bx8Mz<*^Vb$PHN*XVKJ?AF0r g^B?dlpgf}{%-7PHNe_A<+bjZ96g3p8-dcYBKZ*Yr?EnA( literal 0 HcmV?d00001 diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/lib/config/app_provider.dart b/lib/config/app_provider.dart new file mode 100644 index 0000000..6f11d24 --- /dev/null +++ b/lib/config/app_provider.dart @@ -0,0 +1,19 @@ +import 'package:mohem_flutter_app/provider/counter.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AppProvider extends StatelessWidget { + final Widget child; + + AppProvider({required this.child}); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => Counter()), + ], + child: child, + ); + } +} diff --git a/lib/config/background_loader.dart b/lib/config/background_loader.dart new file mode 100644 index 0000000..0683157 --- /dev/null +++ b/lib/config/background_loader.dart @@ -0,0 +1,34 @@ +//class which loads components "in the background", i.e. ui does not depend on it + +import 'package:mohem_flutter_app/services/shared_preferences.dart'; +import 'package:injector/injector.dart'; +//import 'package:revocheckapp/services/firebase_service.dart'; + + +class BackgroundLoader { + Future loadBackgroundData() async { + //init notification setting + try { + /* + final isPromotionNotificationEnabled = await Injector.appInstance + .getDependency() + .promotionNotificationsEnabled; + if (isPromotionNotificationEnabled == null) { + await Injector.appInstance + .getDependency() + .setPromotionNotificationEnabled(true); + Injector.appInstance + .getDependency() + .subscribeForPromotions(); + } */ + } catch (_) { + //something wend wrong, set it to true + await Injector.appInstance + .getDependency() + .setPromotionNotificationEnabled(true); + /*Injector.appInstance + .getDependency() + .subscribeForPromotions();*/ + } + } +} diff --git a/lib/config/constants.dart b/lib/config/constants.dart new file mode 100644 index 0000000..87c2c46 --- /dev/null +++ b/lib/config/constants.dart @@ -0,0 +1,8 @@ +enum YesOrNo { + no, + yes, +} + +const String icons = "assets/icons/"; +const String categorySvgIcons = "assets/category/svg/"; +const String svgIcons = "assets/svg/"; diff --git a/lib/config/dependencies.dart b/lib/config/dependencies.dart new file mode 100644 index 0000000..c9b7074 --- /dev/null +++ b/lib/config/dependencies.dart @@ -0,0 +1,38 @@ +// import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +// import 'package:flutter/material.dart'; + +import 'package:mohem_flutter_app/repo/account_repository.dart'; +import 'package:injector/injector.dart'; + +import 'background_loader.dart'; + + +class AppDependencies { + static void addDependencies() { + Injector injector = Injector.appInstance; + + //add dependencies as needed + injector.registerSingleton(() => AcRepository()); + + // injector.registerSingleton((injector) => AcRepository()); + + _addCrashlytics(); + _loadBackgroundTasksNonBlocking(); + } + + static void _addCrashlytics() { + // Set `enableInDevMode` to true to see reports while in debug mode + // This is only to be used for confirming that reports are being + // submitted as expected. It is not intended to be used for everyday + // development. + //Crashlytics.instance.enableInDevMode = true; + + // Pass all uncaught errors from the framework to Crashlytics. + // FlutterError.onError = Crashlytics.instance.recordFlutterError; + } + + static void _loadBackgroundTasksNonBlocking() { + final backgroundLoader = BackgroundLoader(); + backgroundLoader.loadBackgroundData(); + } +} diff --git a/lib/config/localization.dart b/lib/config/localization.dart new file mode 100644 index 0000000..5d7acdf --- /dev/null +++ b/lib/config/localization.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_localizations/flutter_localizations.dart'; + +class AppLocalizations { + static const Iterable> localizationsDelegates = + [ + // ... app-specific localization delegate[s] here + // S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate + ]; + + static const List supportedLocales = [ + const Locale("en", "US") + ]; +} diff --git a/lib/config/routes.dart b/lib/config/routes.dart new file mode 100644 index 0000000..33c8eb0 --- /dev/null +++ b/lib/config/routes.dart @@ -0,0 +1,46 @@ +import 'package:mohem_flutter_app/pages/dashboard/dashboard_page.dart'; +import 'package:mohem_flutter_app/pages/user/complete_profile_page.dart'; +import 'package:mohem_flutter_app/pages/user/forget_password_page.dart'; +import 'package:mohem_flutter_app/pages/user/login_verification_page.dart'; +import 'package:mohem_flutter_app/pages/user/login_verify_account_page.dart'; +import 'package:mohem_flutter_app/pages/user/profile/profile_1_page.dart'; +import 'package:mohem_flutter_app/pages/user/profile/profile_2_page.dart'; +import 'package:mohem_flutter_app/pages/user/profile/profile_3_page.dart'; +import 'package:mohem_flutter_app/pages/user/register_page.dart'; +import 'package:mohem_flutter_app/pages/user/register_selection_page.dart'; +import 'package:mohem_flutter_app/pages/user/splash_page.dart'; +import 'package:flutter/material.dart'; + +class AppRoutes { + //User + static final String splash = "/splash"; + static final String registerSelection = "/registerSelection"; + static final String loginVerifyAccount = "/loginVerifyAccount"; + static final String register = "/register"; + static final String forgetPassword = "/forgetPassword"; + static final String loginVerification = "/loginVerification"; + static final String dashboard = "/dashboard"; + static final String completeProfile = "/completeProfile"; + static final String profile1 = "/profile1"; + static final String profile2 = "/profile2"; + static final String profile3 = "/profile3"; + + + + static final String initialRoute = splash; + + static final Map routes = { + //User + splash: (context) => SplashPage(), + registerSelection: (context) => RegisterSelectionPage(), + loginVerifyAccount: (context) => LoginVerifyAccountPage(), + register: (context) => RegisterPage(), + forgetPassword: (context) => ForgetPasswordPage(), + loginVerification: (context) => LoginVerificationPage(), + dashboard: (context) => DashboardPage(), + completeProfile: (context) => CompleteProfilePage(), + profile1: (context) => Profile1Page(), + profile2: (context) => Profile2Page(), + profile3: (context) => Profile3Page(), + }; +} diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart new file mode 100644 index 0000000..1157a89 --- /dev/null +++ b/lib/generated/codegen_loader.g.dart @@ -0,0 +1,94 @@ +// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart + +// ignore_for_file: prefer_single_quotes + +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart' show AssetLoader; + +class CodegenLoader extends AssetLoader{ + const CodegenLoader(); + + @override + Future> load(String fullPath, Locale locale ) { + return Future.value(mapLocales[locale.toString()]); + } + + static const Map en = { + "title": "Hello", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "profile": { + "reset_password": { + "label": "Reset Password", + "username": "Username", + "password": "password" + } + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": { + "zero": "Your amount : {} ", + "one": "Your amount : {} ", + "two": "Your amount : {} ", + "few": "Your amount : {} ", + "many": "Your amount : {} ", + "other": "Your amount : {} " + }, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": { + "male": "Hi man ;) {}", + "female": "Hello girl :) {}" + } + }, + "reset_locale": "Reset Language" +}; +static const Map en_US = { + "title": "Hello", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "profile": { + "reset_password": { + "label": "Reset Password", + "username": "Username", + "password": "password" + } + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": { + "zero": "Your amount : {} ", + "one": "Your amount : {} ", + "two": "Your amount : {} ", + "few": "Your amount : {} ", + "many": "Your amount : {} ", + "other": "Your amount : {} " + }, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": { + "male": "Hi man ;) {}", + "female": "Hello girl :) {}" + } + }, + "reset_locale": "Reset Language" +}; +static const Map> mapLocales = {"en": en, "en_US": en_US}; +} diff --git a/lib/main.dart b/lib/main.dart index 202509b..df63e0d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,115 +1,50 @@ +import 'package:mohem_flutter_app/config/app_provider.dart'; +import 'package:mohem_flutter_app/config/dependencies.dart'; +import 'package:mohem_flutter_app/theme/app_theme.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); +import 'package:sizer/sizer.dart'; + +import 'config/localization.dart'; +import 'config/routes.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await EasyLocalization.ensureInitialized(); + runApp( + EasyLocalization( + supportedLocales: [ + Locale('en', 'US'), + ], + path: 'resources/langs', + child: MyApp(), + ), + ); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); + MyApp() { + AppDependencies.addDependencies(); } @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), + return AppProvider( + child: Sizer( + builder: (context, orientation, deviceType) { + return MaterialApp( + theme: AppTheme.getTheme(), + debugShowCheckedModeBanner: false, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + initialRoute: AppRoutes.initialRoute, + routes: AppRoutes.routes, + ); + }, ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ); } } diff --git a/lib/models/account.dart b/lib/models/account.dart new file mode 100644 index 0000000..830b62f --- /dev/null +++ b/lib/models/account.dart @@ -0,0 +1,43 @@ +// To parse this JSON data, do +// +// final account = accountFromJson(jsonString); + +import 'dart:convert'; + +import 'package:mohem_flutter_app/models/parent_list.dart'; + + + +Account accountFromJson(String str) => Account.fromJson(json.decode(str)); + +String accountToJson(Account data) => json.encode(data.toJson()); + +class Account { + Account({ + required this.parentList, + required this.selectedItem, + }); + + List? parentList; + int selectedItem; + + factory Account.fromJson(Map json) => Account( + parentList: json["parentList"] == null + ? null + : List.from( + json["parentList"].map((x) => ParentList.fromJson(x))), + selectedItem: + json["selectedItem"] == null ? null : json["selectedItem"], + ); + + Map toJson() => { + "parentList": parentList == null + ? null + : List.from(parentList!.map((x) => x.toJson())), + "selectedItem": selectedItem == null ? null : selectedItem, + }; + + Map toJsonData() => { + "selectedItem": selectedItem == null ? null : selectedItem, + }; +} diff --git a/lib/models/config_model.dart b/lib/models/config_model.dart new file mode 100644 index 0000000..0245ede --- /dev/null +++ b/lib/models/config_model.dart @@ -0,0 +1,12 @@ +class ConfigModel { + ConfigModel(this.endpoint, this.organizationName); + + String endpoint; + + String organizationName; + + factory ConfigModel.fromJson(Map json) => + ConfigModel("", ""); + +// Map toJson() => _$ConfigModelToJson(this); +} diff --git a/lib/models/parent_list.dart b/lib/models/parent_list.dart new file mode 100644 index 0000000..3f2286f --- /dev/null +++ b/lib/models/parent_list.dart @@ -0,0 +1,26 @@ +class ParentList { + ParentList({ + required this.dbId, + required this.text, + required this.path, + required this.isSelected, + }); + + int dbId; + String text; + String path; + bool isSelected; + + factory ParentList.fromJson(Map json) => ParentList( + dbId: json["dbId"] == null ? null : json["dbId"], + text: json["text"] == null ? null : json["text"], + path: json["path"] == null ? null : json["path"], + isSelected: false, + ); + + Map toJson() => { + "dbId": dbId == null ? null : dbId, + "text": text == null ? null : text, + "path": path == null ? null : path, + }; +} diff --git a/lib/models/response_models.dart b/lib/models/response_models.dart new file mode 100644 index 0000000..872893b --- /dev/null +++ b/lib/models/response_models.dart @@ -0,0 +1,34 @@ +/// +/// This example was taken from +/// https://flutter.dev/docs/development/data-and-backend/json +/// + +/// This allows the `User` class to access private members in +/// the generated file. The value for this is *.g.dart, where +/// the star denotes the source file name. + +/// An annotation for the code generator to know that this class needs the +/// JSON serialization logic to be generated. + +class BackendResponse { + BackendResponse({required this.id, required this.isOk, required this.result}); + + int id; + bool isOk; + dynamic result; + + /// A necessary factory constructor for creating a new User instance + /// from a map. Pass the map to the generated `_$UserFromJson()` constructor. + /// The constructor is named after the source class, in this case, User. + factory BackendResponse.fromJson(Map json) => + BackendResponse( + id: 1, + isOk: false, + result: null, + ); +// +// /// `toJson` is the convention for a class to declare support for serialization +// /// to JSON. The implementation simply calls the private, generated +// /// helper method `_$UserToJson`. +// Map toJson() => _$BackendResponseToJson(this); +} diff --git a/lib/models/user.dart b/lib/models/user.dart new file mode 100644 index 0000000..e09c282 --- /dev/null +++ b/lib/models/user.dart @@ -0,0 +1,9 @@ +class User { + int id; + + User(this.id, this.userName, this.userImage, this.createdDate); + + String userName; + String userImage; + String createdDate; +} diff --git a/lib/pages/a.dart b/lib/pages/a.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/pages/user/splash_page.dart b/lib/pages/user/splash_page.dart new file mode 100644 index 0000000..c593f56 --- /dev/null +++ b/lib/pages/user/splash_page.dart @@ -0,0 +1,48 @@ +import 'dart:async'; + +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/utils/navigator.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; + +class SplashPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + mFlex(5), + Txt( + "Logo", + fontSize: 45, + bold: true, + ), + mFlex(3), + Txt( + "First Time Log In", + txtType: TxtType.heading1, + isFlatButton: true, + onTap: () { + navigateWithName(context, AppRoutes.registerSelection); + }, + ), + mFlex(1), + Txt( + "Already Signed Up and Logged In", + txtType: TxtType.heading1, + isFlatButton: true, + onTap: () { + navigateWithName(context, AppRoutes.loginVerification); + }, + ), + mFlex(5), + ], + ), + ), + ); + } +} diff --git a/lib/provider/counter.dart b/lib/provider/counter.dart new file mode 100644 index 0000000..0272327 --- /dev/null +++ b/lib/provider/counter.dart @@ -0,0 +1,20 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class Counter with ChangeNotifier, DiagnosticableTreeMixin { + int _count = 0; + + int get count => _count; + + void increment() { + _count++; + notifyListeners(); + } + + /// Makes `Counter` readable inside the devtools by listing all of its properties + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(IntProperty('count', count)); + } +} \ No newline at end of file diff --git a/lib/repo/account_repository.dart b/lib/repo/account_repository.dart new file mode 100644 index 0000000..770cbb4 --- /dev/null +++ b/lib/repo/account_repository.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:mohem_flutter_app/models/account.dart'; +import 'package:mohem_flutter_app/models/response_models.dart'; +import 'package:mohem_flutter_app/services/backend_service.dart'; +import 'package:injector/injector.dart'; + +abstract class IAcRepository { + Future getAccountList(); + + Future updateAccount(String dataAsJson); +} + +class AcRepository implements IAcRepository { + static const String ACCOUNT_API_CONTROLLER_MOBILE = + "AccountApiControllerMobile/"; + + static const String ACCOUNT_LIST = ACCOUNT_API_CONTROLLER_MOBILE + "list"; + static const String UPDATE_LIST = + ACCOUNT_API_CONTROLLER_MOBILE + "saveaccountselected"; + + @override + Future getAccountList() async { + BackendResponse response = await Injector.appInstance + .getDependency() + .getAuthenticatedAPI(ACCOUNT_LIST); + + if (response != null && response.isOk) { + return Account.fromJson(response.result); + } else { + throw Exception(); + } + } + + @override + Future updateAccount(String dataAsJson) async { + BackendResponse response = await Injector.appInstance + .getDependency() + .postAuthenticatedAPI(UPDATE_LIST, dataAsJson); + + if (response != null && response.isOk) { + //if parsing failed, throw exception + return response; + } else { + throw Exception(); + } + } +} diff --git a/lib/services/backend_service.dart b/lib/services/backend_service.dart new file mode 100644 index 0000000..b2e4de0 --- /dev/null +++ b/lib/services/backend_service.dart @@ -0,0 +1,127 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:mohem_flutter_app/models/response_models.dart'; +import 'package:mohem_flutter_app/services/secure_storage.dart'; +import 'package:http/http.dart'; +import 'package:injector/injector.dart'; + +import 'http_service.dart'; +import 'network_service.dart'; + +abstract class IBackendApiService { + Future getUnauthenticatedAPI(String route); + + Future getAuthenticatedAPI(String route); + + Future postUnauthenticatedAPI( + String route, String dataAsJson); + + Future postAuthenticatedAPI(String route, String dataAsJson); + + Future deleteAuthenticatedAPI(String route, String id); +} + +class BackendApiService implements IBackendApiService { + static String _homeUrl = "https://check.revotec.eu/check2/"; + static String _serverApiBaseUrl = _homeUrl + "mapi/v1/"; + + static String get homeUrl => _homeUrl; + + final ISecureStorage _secureStorage = + Injector.appInstance.getDependency(); + final IHttpService _httpService = + Injector.appInstance.getDependency(); + + ///internal helper functions which executes the given api call + ///and wraps the response + Future _callApi(Future callback) async { + Response response; + try { + //execute future + response = await callback; + //check response code, and if not ok return isOk = false + //200 for Get + //201 for Post + + // print("res121: " + + // response.statusCode.toString() + + // " Body:" + + // response.body.toString()); + //if delete request sent so server is returning 204 in case of success. + if (response.statusCode == 204) + return BackendResponse(id: 1, isOk: true, result: null); + + if (response.statusCode != 200 && response.statusCode != 201) + return BackendResponse(id: -1, isOk: false, result: null); + //if response code is good then parse message and return parsed response + return BackendResponse.fromJson(json.decode(response.body)); + //return BackendResponse.fromJson(dioResponse.body); + } catch (e) { + return BackendResponse(id: -1, isOk: false, result: null); + // try { + // return BackendResponse.fromJson(json.decode(response.body)); + // } catch (e) { + // return BackendResponse(id:-1, isOk:false,result: null); + // } + } + } + + @override + Future getAuthenticatedAPI(String route) async { + await checkConnection(); + final token = await _secureStorage.readBearerToken(); + return _callApi(_httpService.get(_serverApiBaseUrl + route, headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*', + HttpHeaders.authorizationHeader: "Bearer $token" + })); + } + + @override + Future getUnauthenticatedAPI(String route) async { + await checkConnection(); + return _callApi(_httpService.get(_serverApiBaseUrl + route, + headers: {'Content-Type': 'application/json', 'Accept': '*/*'})); + } + + @override + Future postAuthenticatedAPI( + String route, String dataAsJson) async { + await checkConnection(); + final token = await _secureStorage.readBearerToken(); + // print("res121: " + _serverApiBaseUrl + route); + return _callApi(_httpService + .post(_serverApiBaseUrl + route, body: dataAsJson, headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*', + HttpHeaders.authorizationHeader: "Bearer $token" + })); + } + + @override + Future postUnauthenticatedAPI( + String route, String dataAsJson) async { + await checkConnection(); + return _callApi(_httpService.post(_serverApiBaseUrl + route, + body: dataAsJson, headers: {'Content-Type': 'application/json'})); + } + + Future checkConnection() async { + if (!(await Injector.appInstance + .getDependency() + .isHostAvailable(_homeUrl))) throw NetworkException(); + } + + @override + Future deleteAuthenticatedAPI( + String route, String id) async { + await checkConnection(); + final token = await _secureStorage.readBearerToken(); + return _callApi( + _httpService.delete(_serverApiBaseUrl + route + "/" + id, headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*', + HttpHeaders.authorizationHeader: "Bearer $token" + })); + } +} diff --git a/lib/services/firebase_service.dart b/lib/services/firebase_service.dart new file mode 100644 index 0000000..ad76959 --- /dev/null +++ b/lib/services/firebase_service.dart @@ -0,0 +1,180 @@ +/* +import 'dart:io' show Platform; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +abstract class IFirebaseService { + Future get token; + + Future backgroundMessageHandler(Map message); + + Future messageHandler(Map message); + + Future onLaunch(Map message); + + Future onResume(Map message); + + void subscribeForPromotions(); + + void unsubscribeFromPromotions(); +} + +//https://medium.com/@SebastianEngel/easy-push-notifications-with-flutter-and-firebase-cloud-messaging-d96084f5954f +class FirebaseService implements IFirebaseService { + FirebaseMessaging _firebaseMessaging; + + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; + + FirebaseService() {. + _firebaseMessaging = FirebaseMessaging(); + + //https://github.com/FirebaseExtended/flutterfire/issues/1695 + _firebaseMessaging.configure( + onMessage: messageHandler, + onBackgroundMessage: + Platform.isAndroid ? myBackgroundMessageHandler : null, + onLaunch: onLaunch, + onResume: onResume, + ); + + //monitor firebase token changes + //https://firebase.google.com/docs/cloud-messaging/android/client#sample-register + ///The registration token may change when: + // + //The app deletes Instance ID + //The app is restored on a new device + //The user uninstalls/reinstall the app + //The user clears app data. + /// + + //for the first release we don't care about token refreshes + /*Stream fcmStream = _firebaseMessaging.onTokenRefresh; + fcmStream.listen((token) { + + });*/ + + //ios specific settings + //taken from https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_messaging/example/lib/main.dart + _firebaseMessaging.requestNotificationPermissions( + const IosNotificationSettings( + sound: true, badge: true, alert: true, provisional: true)); + _firebaseMessaging.onIosSettingsRegistered + .listen((IosNotificationSettings settings) { + print("Settings registered: $settings"); + }); + + var initializationSettingsAndroid = + AndroidInitializationSettings('app_icon'); + var initializationSettingsIOS = + IOSInitializationSettings(onDidReceiveLocalNotification: onDidReceiveLocalNotification); + var initializationSettings = InitializationSettings( + initializationSettingsAndroid, initializationSettingsIOS); + flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + flutterLocalNotificationsPlugin.initialize(initializationSettings, + onSelectNotification: selectNotification); + } + + Future onDidReceiveLocalNotification(int id, String title, String body, String payload) async{ + var androidPlatformChannelSpecifics = AndroidNotificationDetails( + 'new_message_channel_id', + 'Neue Nachricht', + 'Channel für neue Nachrichten', + importance: Importance.Max, + priority: Priority.High, + ticker: 'ticker'); + var iOSPlatformChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 0, + title, + body, + platformChannelSpecifics); + } + + @override + Future backgroundMessageHandler(Map message) async { + await myBackgroundMessageHandler(message); + } + + @override + Future messageHandler(Map message) async { + print("onMessage: $message"); + +// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project + + var androidPlatformChannelSpecifics = AndroidNotificationDetails( + 'new_message_channel_id', + 'Neue Nachricht', + 'Channel für neue Nachrichten', + importance: Importance.Max, + priority: Priority.High, + ticker: 'ticker'); + var iOSPlatformChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); + + if(Platform.isAndroid) { + await flutterLocalNotificationsPlugin.show( + 0, + message["notification"]["title"], + message["notification"]["body"], + platformChannelSpecifics); + }else if(Platform.isIOS){ + await flutterLocalNotificationsPlugin.show( + 0, + message["aps"]["alert"]["title"], + message["aps"]["alert"]["body"], + platformChannelSpecifics); + } + } + + Future selectNotification(String payload) async { + if (payload != null) { + debugPrint('notification payload: ' + payload); + } + } + + @override + Future onLaunch(Map message) async { + print("onLaunch: $message"); + } + + @override + Future onResume(Map message) async { + print("onResume: $message"); + } + + @override + Future get token => _firebaseMessaging.getToken(); + + @override + void subscribeForPromotions() { + _firebaseMessaging.subscribeToTopic("promotions"); + } + + @override + void unsubscribeFromPromotions() { + _firebaseMessaging.unsubscribeFromTopic("promotions"); + } +} + +Future myBackgroundMessageHandler(Map message) { + debugPrint("BACKGROUND MESSAGE RECEIVED"); + print("BACKGROUND MESSAGE RECEIVED"); + return Future.value(() => true); + + /*if (message.containsKey('data')) { + // Handle data message + final dynamic data = message['data']; + } + + if (message.containsKey('notification')) { + // Handle notification message + final dynamic notification = message['notification']; + }*/ + + // Or do other work. +} +*/ \ No newline at end of file diff --git a/lib/services/http_service.dart b/lib/services/http_service.dart new file mode 100644 index 0000000..ff99ed0 --- /dev/null +++ b/lib/services/http_service.dart @@ -0,0 +1,36 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:async'; + + +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; +import 'package:path/path.dart'; +import 'package:injector/injector.dart'; + +abstract class IHttpService { + Future post(url, + {Map headers, body, Encoding encoding}); + + Future get(url, {Map headers}); + + Future delete(url, {Map headers}); +} + +class HttpService implements IHttpService { + @override + Future delete(url, {Map? headers}) { + return http.delete(url, headers: headers); + } + + @override + Future get(url, {Map? headers}) { + return http.get(url, headers: headers); + } + + @override + Future post(url, + {Map? headers, body, Encoding? encoding}) { + return http.post(url, headers: headers, body: body, encoding: encoding); + } +} diff --git a/lib/services/media_service.dart b/lib/services/media_service.dart new file mode 100644 index 0000000..0762d7f --- /dev/null +++ b/lib/services/media_service.dart @@ -0,0 +1,26 @@ +// import 'dart:io'; +// +// import 'package:image_picker/image_picker.dart'; +// +// abstract class IMediaService { +// Future takePicture(); +// +// Future openImageFromGallery(); +// } +// +// class MediaService implements IMediaService { +// @override +// Future openImageFromGallery() async { +// final pickedFile = +// await ImagePicker().getImage(source: ImageSource.gallery); +// if (pickedFile == null) return null; +// return File(pickedFile.path); +// } +// +// @override +// Future takePicture() async { +// final pickedFile = await ImagePicker().getImage(source: ImageSource.camera); +// if (pickedFile == null) return null; +// return File(pickedFile.path); +// } +// } diff --git a/lib/services/network_service.dart b/lib/services/network_service.dart new file mode 100644 index 0000000..50a861d --- /dev/null +++ b/lib/services/network_service.dart @@ -0,0 +1,25 @@ + +import 'dart:io'; + +abstract class INetworkService { + Future isHostAvailable(String endpoint); +} + +class NetworkService implements INetworkService{ + @override + Future isHostAvailable(String endpoint) async { + try { + final result = await InternetAddress.lookup(endpoint.substring(endpoint.indexOf('//')+2).substring(0,endpoint.substring(endpoint.indexOf('//')+2).indexOf('/'))); + if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { + return true; + } else{ + return false; + } + } on SocketException catch (_) { + return false; + } + } +} + +class NetworkException implements Exception { +} diff --git a/lib/services/secure_storage.dart b/lib/services/secure_storage.dart new file mode 100644 index 0000000..7622f7f --- /dev/null +++ b/lib/services/secure_storage.dart @@ -0,0 +1,33 @@ +abstract class ISecureStorage { + Future readBearerToken(); + + Future clearUserCredentials(); +} + +class SecureStorage implements ISecureStorage { + ///return bearer token if present, or null if not + @override + Future readBearerToken() async { + try { + return ""; + } catch (_) { + //an error occured returning null + return ""; + } + } + + ///returns true if write was successful, false otherwise + @override + Future writeBearerToken(String token) async { + try { + await ""; + return true; + } catch (_) { + //an error occured returning false + return false; + } + } + + @override + Future clearUserCredentials() async {} +} diff --git a/lib/services/shared_preferences.dart b/lib/services/shared_preferences.dart new file mode 100644 index 0000000..890a616 --- /dev/null +++ b/lib/services/shared_preferences.dart @@ -0,0 +1,119 @@ +import 'dart:convert'; + +import 'package:mohem_flutter_app/models/config_model.dart'; + +import 'package:shared_preferences/shared_preferences.dart' +as SharedPrefsPlugin; + +/// +/// Taken from AlarmGuide Project +/// + +abstract class ISharedPreferences { + Future get authState; + + Future setAuthState(int authState); + + Future get configState; + + Future setConfigState(int confState); + + Future get config; + + Future setConfig(ConfigModel config); + + Future get promotionNotificationsEnabled; + + Future setPromotionNotificationEnabled(bool newSetting); + + Future get helpAlreadyShown; + + Future setHelpAlreadyShown(); + + Future get useS3; + + Future setUseS3(int value); +} + +class SharedPreferences implements ISharedPreferences { + static const String _AUTH_STATE_KEY = "auth_key"; + static const String _CONFIG_KEY = "config"; + static const String _CONFIG_STATE_KEY = "config_key"; + static const String _PROMOTION_NOTIFICATION_KEY = "promotion"; + static const String _HELP_ALREADY_SHOWN = "help_shown"; + static const String _USE_S3 = "s3"; + + @override + Future get authState async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + return sharedPrefs.getInt(_AUTH_STATE_KEY); + } + + @override + Future setAuthState(int authState) async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setInt(_AUTH_STATE_KEY, authState); + } + + @override + Future get config async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + final configAsJson = sharedPrefs.getString(_CONFIG_KEY); + return ConfigModel.fromJson(jsonDecode(configAsJson!)); + } + + @override + Future setConfig(ConfigModel config) async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setString(_CONFIG_KEY, jsonEncode(config)); + setConfigState(1); + } + + @override + Future get promotionNotificationsEnabled async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + return sharedPrefs.getBool(_PROMOTION_NOTIFICATION_KEY); + } + + @override + Future setPromotionNotificationEnabled(bool newSetting) async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setBool(_PROMOTION_NOTIFICATION_KEY, newSetting); + } + + @override + Future get helpAlreadyShown async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + return sharedPrefs.getBool(_HELP_ALREADY_SHOWN); + } + + @override + Future setHelpAlreadyShown() async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setBool(_HELP_ALREADY_SHOWN, true); + } + + @override + Future get configState async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + return sharedPrefs.getInt(_CONFIG_STATE_KEY); + } + + @override + Future setConfigState(int confState) async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setInt(_CONFIG_STATE_KEY, confState); + } + + @override + Future setUseS3(int value) async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + sharedPrefs.setInt(_USE_S3, value); + } + + @override + Future get useS3 async { + final sharedPrefs = await SharedPrefsPlugin.SharedPreferences.getInstance(); + return sharedPrefs.getInt(_USE_S3); + } +} diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart new file mode 100644 index 0000000..5202825 --- /dev/null +++ b/lib/theme/app_theme.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import 'colors.dart'; + +class AppTheme { + static getTheme() => ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primaryColor: primaryColor, + primarySwatch: Colors.blue, + backgroundColor: Colors.white, + primaryTextTheme: TextTheme( + headline6: TextStyle(color: Colors.white), + ), + ); +} + +extension ExtendedRevoCheckTheme on TextTheme { + //add custom styles and colors here + //taken from https://medium.com/@crizantlai/flutter-how-to-extend-themedata-b5b987a95bb5 + TextStyle get price => const TextStyle(color: Colors.redAccent); +} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart new file mode 100644 index 0000000..8510500 --- /dev/null +++ b/lib/theme/colors.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +const Color primaryColor = Colors.white; +const Color accentColor = Colors.blue; +const Color appBackgroundColor = Colors.white; +Color? accentColorDark = Colors.green[800]; +const Color borderColor = Colors.blueGrey; +Color? borderLightColor = Colors.blueGrey[50]; +Color backgroudColor = + Colors.blueGrey[50]!.withOpacity(0.5) ?? Colors.transparent; +const Color iconColor = Colors.blueGrey; +Color? headingColor = Colors.blueGrey[800]; +Color? txtColor = Colors.blueGrey[500]; diff --git a/lib/utils/AppPermissionHandler.dart b/lib/utils/AppPermissionHandler.dart new file mode 100644 index 0000000..3e3877e --- /dev/null +++ b/lib/utils/AppPermissionHandler.dart @@ -0,0 +1,36 @@ +import 'package:permission_handler/permission_handler.dart'; + +import 'dialogs.dart'; + +enum ConfirmAction { CANCEL, ACCEPT } + +Future requestPermissionGranted( + context, Permission requestPermissions) async { + var result = await requestPermissions.request(); + + switch (result) { + case PermissionStatus.granted: + // Application has been given permission to use the feature. + return true; + case PermissionStatus.denied: + // Application has been denied permission to use the feature. + return false; + case PermissionStatus.permanentlyDenied: + ConfirmAction? res = await showConfirmDialogs( + context, + 'You was denied Permission. You have give manual permission from app setting. ', + 'Open App Setting', + 'Cancel'); + if (res == ConfirmAction.ACCEPT) { + return false; + } else if (res == ConfirmAction.CANCEL) { + return false; + } + return false; + case PermissionStatus.restricted: + // iOS has restricted access to a specific feature. + return false; + default: + return false; + } +} diff --git a/lib/utils/dialogs.dart b/lib/utils/dialogs.dart new file mode 100644 index 0000000..a6bff7e --- /dev/null +++ b/lib/utils/dialogs.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +import 'AppPermissionHandler.dart'; + +Future showConfirmDialogs( + context, msg, positiveText, negativeText) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + backgroundColor: Colors.white, + title: Text(msg, style: TextStyle(fontSize: 16)), + actions: [ + TextButton( + child: Text( + negativeText, + ), + onPressed: () { + Navigator.of(context).pop(ConfirmAction.CANCEL); + }, + ), + TextButton( + child: Text( + positiveText, + ), + onPressed: () { + Navigator.of(context).pop(ConfirmAction.ACCEPT); + }, + ) + ], + ); + }, + ); +} diff --git a/lib/utils/navigator.dart b/lib/utils/navigator.dart new file mode 100644 index 0000000..f9855a9 --- /dev/null +++ b/lib/utils/navigator.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +navigateWithName(BuildContext context, String routeName, {Object? arguments}) { + Navigator.pushNamed(context, routeName, arguments: arguments); +} + +pop(BuildContext context) { + Navigator.of(context).pop(); +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart new file mode 100644 index 0000000..e0b0f03 --- /dev/null +++ b/lib/utils/utils.dart @@ -0,0 +1,311 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:mohem_flutter_app/config/constants.dart'; +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import 'package:sizer/sizer.dart'; + +Color getColorFromHex(String hexColor) { + hexColor = hexColor.toUpperCase().replaceAll('#', ''); + + if (hexColor.length == 6) { + hexColor = 'FF' + hexColor; + } + + return Color(int.parse(hexColor, radix: 16)); +} + +Widget spacerVertical(double v) { + return Container( + height: v, + width: double.infinity, + ); +} +Future delay(int millis) async { + return await Future.delayed(Duration(milliseconds: millis)); +} + +inkWellCorner({double? r}) { + return RoundedRectangleBorder( + borderRadius: BorderRadius.circular(r ?? 4), + ); +} + +Widget spacerHorizontal(double v) { + return Container( + height: v, + width: v, + ); +} + +Widget mHeight(double f) { + return Container( + width: f, + height: f, + ); +} + +Widget mDivider(Color color, {double? h}) { + return Container( + width: double.infinity, + height: h ?? 1, + color: color, + ); +} + +Widget mDivider3({double? h}) { + return Container( + width: double.infinity, + height: h ?? 1, + color: borderLightColor!.withOpacity(0.7) ?? Colors.transparent, + ); +} + +Widget mDivider2(Color color, double w) { + return Container( + width: w, + height: 1, + color: color, + ); +} + +InputDecoration txtField(String label) { + return new InputDecoration( + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + hintText: label, + hintStyle: TextStyle(color: Colors.grey), + disabledBorder: InputBorder.none, + isDense: false, + contentPadding: EdgeInsets.only(left: 15, right: 15), + ); +} + +Widget mWidth(double f) { + return Container( + width: f, + height: f, + ); +} + +Widget mFlex(int f) { + return Flexible( + flex: f, + child: Container( + width: double.infinity, + height: double.infinity, + ), + ); +} +Widget mExp(int f) { + return Expanded( + flex: f, + child: Container( + width: double.infinity, + ), + ); +} + +spacer() { + return SizedBox( + height: 8, + ); +} + +Widget floatButton(String icon, {Color? color, required Function onClick, String? title}) { + return Padding( + padding: const EdgeInsets.only(top: 12, bottom: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FloatingActionButton( + onPressed: () { + onClick(); + }, + heroTag: icon, + backgroundColor: accentColor, + elevation: 4, + child: Container( + child: SvgPicture.asset( + categorySvgIcons + icon, + color: color, + ), + width: double.infinity, + height: double.infinity, + decoration: containerRadius(Colors.white, 200), + clipBehavior: Clip.antiAlias, + padding: EdgeInsets.all(15), + margin: EdgeInsets.all(1), + ), + ), + if (title != null) mHeight(2.w), + if (title != null) + Txt( + title, + fontSize: 12.sp, + bold: true, + color: headingColor, + ) + ], + ), + ); +} + +navigateTo(context, page) { + Navigator.push(context, MaterialPageRoute(builder: (context) => page)); +} + +circularImage(String im, double width, double height) { + return new Container(width: 190.0, height: 190.0, decoration: new BoxDecoration(shape: BoxShape.circle, image: new DecorationImage(fit: BoxFit.fill, image: new AssetImage(im)))); +} + +circularImage2(String im, double width, double height) { + return new Container(width: width, height: height, decoration: new BoxDecoration(shape: BoxShape.circle, image: new DecorationImage(fit: BoxFit.fill, image: new AssetImage(im)))); +} + +cardRadius(double radius) { + return RoundedRectangleBorder( + side: BorderSide(color: Colors.transparent, width: 1), + borderRadius: BorderRadius.circular(radius), + ); +} + +cardRadiusWithoutBorder(double radius) { + return RoundedRectangleBorder( + side: BorderSide(color: Colors.transparent, width: 1), + borderRadius: BorderRadius.circular(radius), + ); +} + +Image imageFromBase64String(String base64String) { + return Image.memory(base64Decode(base64String)); +} + +Uint8List dataFromBase64String(String base64String) { + return base64Decode(base64String); +} + +String base64String(Uint8List data) { + return base64Encode(data); +} + +Widget overLayWidget({double? width, double? height,List? color}) { + return Container( + width: width ?? double.infinity, + height: height ?? 60, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: color!=null?color:[ + Colors.black.withOpacity(0.2), + Colors.black.withOpacity(0.1), + Colors.black.withOpacity(0.004), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + tileMode: TileMode.clamp, + ), + ), + ); +} + +Decoration containerRadius(Color color, double r) { + return BoxDecoration( + color: color, + borderRadius: BorderRadius.all(Radius.circular(r)), + ); +} + +Decoration containerRadiusTop({Color? color, double? r}) { + return BoxDecoration( + color: color ?? Colors.white, + borderRadius: BorderRadius.only(topRight: Radius.circular(r ?? 12), topLeft: Radius.circular(r ?? 12)), + ); +} + +Decoration containerRadiusBorder(Color color, double r) { + return BoxDecoration( + color: Colors.transparent, + border: Border.all(color: color, width: 1), + borderRadius: BorderRadius.all(Radius.circular(r)), + ); +} + +Decoration containerRadiusBottom(Color color, double r) { + return BoxDecoration( + color: color, + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(r), bottomRight: Radius.circular(r)), + ); +} + +ShapeBorder cardRadiusTop(double radius) { + return RoundedRectangleBorder( + side: BorderSide(color: Colors.transparent, width: 0), + borderRadius: BorderRadius.only(topLeft: Radius.circular(radius), topRight: Radius.circular(radius)), + ); +} + +Decoration containerColorRadiusBorderWidth(Color background, double radius, Color color, double w) { + return BoxDecoration( + color: background, + border: Border.all( + width: w, // + color: color // <--- border width here + ), + borderRadius: BorderRadius.circular(radius), + ); +} + +ShapeBorder cardRadiusTop2(double radius) { + return RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(radius), topRight: Radius.circular(radius)), + ); +} + +ShapeBorder cardRadiusBottom(double radius) { + return RoundedRectangleBorder( + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(radius), bottomRight: Radius.circular(radius)), + ); +} + +Decoration containerColorRadiusBorder(Color background, double radius, Color color) { + return BoxDecoration( + color: background, + border: Border.all( + width: 1, // + color: color // <--- border width here + ), + borderRadius: BorderRadius.circular(radius), + ); +} + + + + + + +//Decoration appGradient = BoxDecoration( +// gradient: LinearGradient( +// colors: [ +// Colors.green[200], +// Colors.green, +// ], +// begin: Alignment.topCenter, +// end: Alignment.bottomCenter, +// ), +//); +// launchURL(String url) async { +// if (await canLaunch(url)) { +// await launch(url); +// } else { +// throw 'Could not launch $url'; +// } +// } diff --git a/lib/widgets/app_bar.dart b/lib/widgets/app_bar.dart new file mode 100644 index 0000000..3ad0b80 --- /dev/null +++ b/lib/widgets/app_bar.dart @@ -0,0 +1,28 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; +import 'package:sizer/sizer.dart'; + +AppBar appBar({ + Color? backgroundColor, + double? elevation, + String? title, + Color? titleColor, + bool? isTitleCenter, + Color? backIconColor, + List? actions, +}) { + return AppBar( + backgroundColor: backgroundColor ?? appBackgroundColor, + elevation: elevation ?? 0, + centerTitle: isTitleCenter ?? true, + iconTheme: IconThemeData( + color: backIconColor ?? Colors.black, //change your color here + ), + actions: actions, + title: Txt( + title ?? "", + txtType: TxtType.appBar, + ), + ); +} diff --git a/lib/widgets/blurry_container.dart b/lib/widgets/blurry_container.dart new file mode 100644 index 0000000..8bebc4a --- /dev/null +++ b/lib/widgets/blurry_container.dart @@ -0,0 +1,62 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +const kDemoText = Center( + child: Text( + '', + style: TextStyle( + fontSize: 25, + color: Colors.white, + letterSpacing: 2, + ), + textAlign: TextAlign.center, + ), +); +const double kBlur = 1.0; +const EdgeInsetsGeometry kDefaultPadding = EdgeInsets.all(16); +const Color kDefaultColor = Colors.transparent; +const BorderRadius kBorderRadius = BorderRadius.all(Radius.circular(20)); +const double kColorOpacity = 0.0; + +class BlurryContainer extends StatelessWidget { + final Widget child; + final double blur; + final double? height, width; + final EdgeInsetsGeometry padding; + final Color bgColor; + + final BorderRadius borderRadius; + + //final double colorOpacity; + + BlurryContainer({ + this.child = kDemoText, + this.blur = 5, + required this.height, + required this.width, + this.padding = kDefaultPadding, + this.bgColor = kDefaultColor, + this.borderRadius = kBorderRadius, + //this.colorOpacity = kColorOpacity, + }); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: borderRadius, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur), + child: Container( + height: height!, + width: width!, + padding: padding, + color: bgColor == Colors.transparent + ? bgColor + : bgColor.withOpacity(0.5), + child: child, + ), + ), + ); + } +} diff --git a/lib/widgets/button/show_circular_button.dart b/lib/widgets/button/show_circular_button.dart new file mode 100644 index 0000000..c2bd90c --- /dev/null +++ b/lib/widgets/button/show_circular_button.dart @@ -0,0 +1,23 @@ +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; + +class ShowCircularButton extends StatelessWidget { + VoidCallback onPressed; + IconData? iconData; + ShowCircularButton({this.iconData,required this.onPressed}); + @override + Widget build(BuildContext context) { + return Card( + shape: cardRadius(1000), + color: Colors.black.withOpacity(0.2), + margin: EdgeInsets.all(12), + child: IconButton( + onPressed: onPressed, + icon: Icon( + iconData?? Icons.amp_stories_outlined, + color: Colors.white, + ), + ), + ); + } +} diff --git a/lib/widgets/button/show_image_button.dart b/lib/widgets/button/show_image_button.dart new file mode 100644 index 0000000..8e60d11 --- /dev/null +++ b/lib/widgets/button/show_image_button.dart @@ -0,0 +1,43 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; + +import '../txt.dart'; + +class ShowImageButton extends StatelessWidget { + String icon, title; + VoidCallback onClick; + + ShowImageButton( + {required this.icon, required this.title, required this.onClick}); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onClick, + child: Container( + width: double.infinity, + child: Column( + children: [ + Container( + width: double.infinity, + height: 120, + color: accentColor, + padding: EdgeInsets.all(30), + child: Image.asset( + icon, + color: Colors.white, + ), + ), + mHeight(12), + Txt( + title, + txtType: TxtType.heading2, + color: Colors.blue, + ) + ], + ), + ), + ); + } +} diff --git a/lib/widgets/dialog/dialogs.dart b/lib/widgets/dialog/dialogs.dart new file mode 100644 index 0000000..9a5a822 --- /dev/null +++ b/lib/widgets/dialog/dialogs.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +void showMDialog( + context, { + Widget? child, +}) async { + return showDialog( + context: context, + barrierDismissible: true, + builder: (context) { + return Dialog( + child: child, + ); + }, + ); +} diff --git a/lib/widgets/dialog/message_dialog.dart b/lib/widgets/dialog/message_dialog.dart new file mode 100644 index 0000000..4181370 --- /dev/null +++ b/lib/widgets/dialog/message_dialog.dart @@ -0,0 +1,38 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/navigator.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:mohem_flutter_app/widgets/show_fill_button.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; + +class MessageDialog extends StatelessWidget { + String? title, buttonTitle; + VoidCallback? onClick; + MessageDialog({this.title, this.buttonTitle,this.onClick}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + padding: EdgeInsets.all(30), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Txt( + title ?? "message", + txtType: TxtType.heading3, + ), + mHeight(40), + ShowFillButton( + title: buttonTitle ?? "Continue", + width: double.infinity, + onPressed: () { + onClick!(); + }, + ) + ], + ), + ); + } +} diff --git a/lib/widgets/dialog/otp_dialog.dart b/lib/widgets/dialog/otp_dialog.dart new file mode 100644 index 0000000..3790c44 --- /dev/null +++ b/lib/widgets/dialog/otp_dialog.dart @@ -0,0 +1,74 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/navigator.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:mohem_flutter_app/widgets/show_fill_button.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; + +class OtpDialog extends StatelessWidget { + VoidCallback onClick; + + OtpDialog({required this.onClick}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + padding: EdgeInsets.all(30), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Txt( + "Please insert OTP Code", + txtType: TxtType.heading3, + ), + mHeight(20), + Row( + children: [ + Expanded( + child: Container( + width: double.infinity, + height: 60, + color: accentColor.withOpacity(0.3), + ), + ), + mWidth(12), + Expanded( + child: Container( + width: double.infinity, + height: 60, + color: accentColor.withOpacity(0.3), + ), + ), + mWidth(12), + Expanded( + child: Container( + width: double.infinity, + height: 60, + color: accentColor.withOpacity(0.3), + ), + ), + mWidth(12), + Expanded( + child: Container( + width: double.infinity, + height: 60, + color: accentColor.withOpacity(0.3), + ), + ), + ], + ), + mHeight(40), + ShowFillButton( + title: "Check Code", + width: double.infinity, + onPressed: () { + onClick(); + }, + ) + ], + ), + ); + } +} diff --git a/lib/widgets/dragable_sheet.dart b/lib/widgets/dragable_sheet.dart new file mode 100644 index 0000000..4d91118 --- /dev/null +++ b/lib/widgets/dragable_sheet.dart @@ -0,0 +1,26 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +showDraggableDialog(BuildContext context, Widget child) { + showGeneralDialog( + barrierLabel: "Label", + barrierDismissible: false, + barrierColor: Colors.black.withOpacity(0.2), + transitionDuration: Duration(milliseconds: 200), + context: context, + pageBuilder: (context, anim1, anim2) { + return Dismissible( + direction: DismissDirection.vertical, + key: const Key('key'), + onDismissed: (_) => Navigator.of(context).pop(), + child: child, + ); + }, + transitionBuilder: (context, anim1, anim2, child) { + return SlideTransition( + position: Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(anim1), + child: child, + ); + }, + ); +} diff --git a/lib/widgets/dropdown/dropdow_field.dart b/lib/widgets/dropdown/dropdow_field.dart new file mode 100644 index 0000000..091b6f6 --- /dev/null +++ b/lib/widgets/dropdown/dropdow_field.dart @@ -0,0 +1,78 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; + +import '../txt.dart'; + +class DropdownField extends StatefulWidget { + String? hint; + List? list; + + DropdownField({this.hint, this.list}); + + @override + State createState() => _DropdownFieldState(); +} + +class _DropdownFieldState extends State { + String? dropdownValue; + + @override + Widget build(BuildContext context) { + return Container( + decoration: containerColorRadiusBorderWidth( + Colors.transparent, + 4, + borderColor, + 0.5, + ), + margin: EdgeInsets.all(2), + padding: EdgeInsets.only(left: 8, right: 8), + child: DropdownButton( + value: dropdownValue, + icon: const Icon(Icons.keyboard_arrow_down_sharp), + elevation: 16, + iconSize: 16, + iconEnabledColor: borderColor, + iconDisabledColor: borderColor, + isExpanded: true, + style: const TextStyle(color: Colors.black), + hint: Txt( + widget.hint ?? "", + txtType: TxtType.heading1, + bold: false, + color: borderColor, + ), + underline: Container( + height: 0, + ), + onChanged: (String? newValue) { + setState(() { + dropdownValue = newValue!; + }); + }, + items: (widget.list ?? + [ + 'One', + 'Two', + 'Free', + 'Four', + ]) + .map>( + (String value) { + return DropdownMenuItem( + value: value, + child: Txt( + value, + txtType: TxtType.heading1, + bold: false, + ), + ); + }, + ).toList(), + ), + ); + } +} diff --git a/lib/widgets/dropdown/dropdown_text.dart b/lib/widgets/dropdown/dropdown_text.dart new file mode 100644 index 0000000..4031a98 --- /dev/null +++ b/lib/widgets/dropdown/dropdown_text.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/config/constants.dart'; +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; + +class DropDownText extends StatelessWidget { + String title; + + DropDownText(this.title); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Txt( + title, + txtType: TxtType.heading3, + color: accentColor, + ), + mWidth(16), + SvgPicture.asset( + svgIcons + "ic_arrow_down.svg", + width: 10, + height: 10, + ), + ], + ); + } +} diff --git a/lib/widgets/extensions/extensions_widget.dart b/lib/widgets/extensions/extensions_widget.dart new file mode 100644 index 0000000..caaaba9 --- /dev/null +++ b/lib/widgets/extensions/extensions_widget.dart @@ -0,0 +1,235 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +extension ExtendedText on Widget { + showOverlay({double? width, double? height}){ + return Container( + width: width ?? double.infinity, + height: height ?? 60, + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + Colors.black.withOpacity(0.2), + Colors.black.withOpacity(0.1), + Colors.black.withOpacity(0.004), + ], begin: Alignment.topCenter, end: Alignment.bottomCenter, tileMode: TileMode.clamp), + ), + child: this, + ); + } +} + + + + + +extension ImageExt on Image { + Widget circular() { + return ClipRRect( + clipBehavior: Clip.hardEdge, + borderRadius: BorderRadius.circular(100), + child: this, + ); + } +} + +extension WidgetExt on Widget { + Widget toWidget() { + return this as Widget; + } + + Widget sized({double? width, double? height}) { + return SizedBox( + width: width, + height: height, + child: this, + ); + } + Widget inkWell({required VoidCallback onTap,double radius=0}){ + return InkWell( + child: Material(child: this,color: Colors.transparent,), + onTap: onTap, + ); + } + Widget sizeSq(double size) { + return SizedBox( + width: size, + height: size, + child: this, + ); + } + + Widget fillParent({double hFactor = 1, double? vFactor}) { + return FractionallySizedBox( + heightFactor: null, + widthFactor: hFactor, + child: this, + ); + } + + Widget margin( + {double top = 0, double bottom = 0, double left = 0, double right = 0}) { + var pad = + EdgeInsets.only(top: top, left: left, bottom: bottom, right: right); + try { + (this as dynamic).margin = pad; + } catch (err) { + return Padding( + padding: pad, + child: this, + ); + } + return this; + } + + Widget toClip({double r = 40}) { + return ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(r), + ), + clipBehavior: Clip.antiAlias, + child: this, + ); + } + + // Widget scrollable({Axis? direction, bool fadingEdge = true}) { + // var scrollview = SingleChildScrollView( + // child: this, + // controller: ScrollController(), + // scrollDirection: direction ?? Axis.vertical, + // ); + // return fadingEdge ? scrollview.fadingEdge() : scrollview; + // } + + Widget expand() { + return Expanded( + child: this, + ); + } + + Widget align(Alignment alignment) { + return Align( + alignment: alignment, + child: this, + ); + } + + Widget center() { + return Center( + child: this, + ); + } + + Widget padding(EdgeInsets padding) { + return Padding( + padding: padding, + child: this, + ); + } + + Widget paddingAll(double padding) { + return Padding( + padding: EdgeInsets.all(padding), + child: this, + ); + } + + // Widget onTap(VoidCallback onTap, {double corners = 0}) { + // return Clickable.widget(child: this, corners: corners, onTap: onTap); + // } + + Widget ignoreInteraction() { + return IgnorePointer( + child: this, + ); + } +} + +// extension xScrollView on ScrollView { +// Widget fadingEdge() => FadingEdgeScrollView.fromScrollView( +// child: this, +// gradientFractionOnEnd: 0.08, +// gradientFractionOnStart: 0.08, +// shouldDisposeScrollController: true, +// ); +// } +// +// extension x2ScrollView on SingleChildScrollView { +// Widget fadingEdge() => FadingEdgeScrollView.fromSingleChildScrollView( +// child: this, +// gradientFractionOnEnd: 0.08, +// gradientFractionOnStart: 0.08, +// shouldDisposeScrollController: true, +// ); +// } + +class Position { + final double x, y, w, h; + + Position(this.x, this.y, this.w, this.h); +} + +extension KeyExt on GlobalKey { + Position globalPosition() { + RenderBox box = currentContext!.findRenderObject() as RenderBox; + Offset xy = box.localToGlobal(Offset.zero); + Size wh = box.size; + return Position(xy.dx, xy.dy, wh.width, wh.height); + } +} + +extension StateExt on State { + reload({VoidCallback? b}) { + setState(b ?? () {}); + } +} + +// extension LocatiionExt on Location { +// LatLng toLatLng() { +// return LatLng(lat!, lng!); +// } +// } + +// extension xList on List { +// T randomItem() { +// final random = new Random(); +// var i = random.nextInt(this.length); +// return this[i]; +// } +// +// List append(List items) { +// this.addAll(items); +// return this; +// } +// +// bool isFirst(T item) => first == item; +// +// bool isLast(T item) => last == item; +// +// getOneByOne( +// {required int delayMillis, +// required Function(T) callback, VoidCallback? complete}) async { +// for (var i in this) { +// await delay(delayMillis); +// callback(i); +// } +// complete!(); +// } +// } + +extension xFuture on Future { + thenWithDelay(int millis, Function(T) thenBlock) { + then((value) { + Future.delayed(Duration(milliseconds: millis)) + .then((value) => thenBlock(value)); + }); + } +} + +extension xDouble on int { + Duration durationMillis() => Duration(milliseconds: this); + + Duration durationSec() => Duration(seconds: this); + + Duration durationHour() => Duration(hours: this); +} diff --git a/lib/widgets/gradient_app_bar.dart b/lib/widgets/gradient_app_bar.dart new file mode 100644 index 0000000..c3ded30 --- /dev/null +++ b/lib/widgets/gradient_app_bar.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +class GradientAppBar extends StatelessWidget { + final String title; + final double barHeight = 50.0; + IconData? iconData; + + GradientAppBar(this.title, {this.iconData}); + + @override + Widget build(BuildContext context) { + final double statusbarHeight = MediaQuery.of(context).padding.top; + + return new Container( + padding: EdgeInsets.only(top: statusbarHeight), + height: statusbarHeight + barHeight, + child: Row( + children: [ + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + Expanded( + child: Center( + child: Text( + title, + style: TextStyle( + fontSize: 20.0, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ), + ), + if (iconData != null) + IconButton( + onPressed: () {}, + icon: Icon( + iconData, + color: Colors.white, + ), + ), + ], + ), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.black.withOpacity(0.4), + Colors.black.withOpacity(0.2), + Colors.black.withOpacity(0.0001), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + tileMode: TileMode.clamp, + ), + ), + ); + } +} diff --git a/lib/widgets/images/circular_image.dart b/lib/widgets/images/circular_image.dart new file mode 100644 index 0000000..dd64efa --- /dev/null +++ b/lib/widgets/images/circular_image.dart @@ -0,0 +1,30 @@ +import 'package:mohem_flutter_app/config/constants.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; + +class CircularImage extends StatelessWidget { + double? w, h, padding; + String? image; + + CircularImage({this.w, this.h, this.image, this.padding}); + + @override + Widget build(BuildContext context) { + return Container( + width: w ?? 120, + height: h ?? 120, + child: Card( + shape: cardRadius(2000), + clipBehavior: Clip.antiAlias, + elevation: 4, + child: Card( + shape: cardRadius(2000), + clipBehavior: Clip.antiAlias, + elevation: 0, + margin: EdgeInsets.all(padding ?? 0), + child: Image.asset(image ?? icons + "green.jpg"), + ), + ), + ); + } +} diff --git a/lib/widgets/show_card_buttton.dart b/lib/widgets/show_card_buttton.dart new file mode 100644 index 0000000..7b9ac38 --- /dev/null +++ b/lib/widgets/show_card_buttton.dart @@ -0,0 +1,32 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/widgets/show_fill_button.dart'; +import 'package:flutter/material.dart'; + +class ShowCardButton extends StatelessWidget { + String title; + VoidCallback onPressed; + Color txtColor; + + ShowCardButton({ + required this.title, + required this.onPressed, + this.txtColor = Colors.white, + }); + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.zero, + elevation: 20, + child: Container( + width: double.infinity, + padding: EdgeInsets.all(12), + child: ShowFillButton( + title: title, + onPressed: onPressed, + txtColor: txtColor, + ), + ), + ); + } +} diff --git a/lib/widgets/show_fill_button.dart b/lib/widgets/show_fill_button.dart new file mode 100644 index 0000000..168521e --- /dev/null +++ b/lib/widgets/show_fill_button.dart @@ -0,0 +1,41 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; + +class ShowFillButton extends StatelessWidget { + String title; + VoidCallback onPressed; + Color txtColor; + double elevation, radius,width; + + ShowFillButton({ + required this.title, + required this.onPressed, + this.txtColor = Colors.white, + this.elevation = 4, + this.radius = 6, + this.width=88, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.black87, + primary: accentColor, + minimumSize: Size(width, 45), + padding: EdgeInsets.symmetric(horizontal: 16), + elevation: elevation, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(radius)), + ), + ), + onPressed: onPressed, + child: Txt( + title.toUpperCase(), + color: txtColor, + txtType: TxtType.heading1, + ), + ); + } +} diff --git a/lib/widgets/txt.dart b/lib/widgets/txt.dart new file mode 100644 index 0000000..8591277 --- /dev/null +++ b/lib/widgets/txt.dart @@ -0,0 +1,170 @@ +// import 'package:auto_size_text/auto_size_text.dart'; +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:sizer/sizer.dart'; + +enum TxtType { + small, + normal, + heading1, + heading2, + heading3, + appBar, +} + +class Txt extends StatelessWidget { + String text; + int? maxLines; + double? fontSize; + Color? color; + bool? bold; + bool? isUnderline; + bool? isFlatButton; + double? pedding; + TextAlign? textAlign; + FontWeight? fontWeight; + Function? onTap; + TxtType txtType; + + Txt(this.text, {this.maxLines, this.color, this.bold, this.fontSize, this.isUnderline, this.isFlatButton, this.pedding, this.textAlign, this.fontWeight, this.onTap, this.txtType = TxtType.normal}); + + @override + Widget build(BuildContext context) { + if (isFlatButton != null) + return Padding( + padding: EdgeInsets.only(right: pedding ?? 0, left: pedding ?? 0), + child: InkWell( + onTap: () { + onTap!(); + }, + customBorder: inkWellCorner(r: 4), + child: Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + top: 6, + bottom: 6, + ), + child: getText(), + ), + ), + ); + else + return getText(); + } + + Widget getText() { + return Material( + type: MaterialType.transparency, + child: Text( + text, + maxLines: maxLines, + textAlign: textAlign, + overflow: maxLines != null ? TextOverflow.ellipsis : null, + style: TextStyle( + fontSize: fontSize ?? + (txtType == TxtType.small + ? 8.sp + : txtType == TxtType.normal + ? 10.sp + : txtType == TxtType.heading1 + ? 11.sp + : txtType == TxtType.heading2 + ? 12.sp + : txtType == TxtType.heading3 + ? 13.sp + : txtType == TxtType.appBar + ? 14.sp + : 8.sp), + color: color ?? + (txtType == TxtType.appBar + ? Colors.black + : txtType == TxtType.heading1 + ? headingColor + : txtType == TxtType.heading2 + ? headingColor + : txtType == TxtType.heading3 + ? headingColor + : null), + fontWeight: (fontWeight != null) + ? fontWeight + : ((bold != null) + ? FontWeight.bold + : (txtType == TxtType.appBar + ? FontWeight.bold + : txtType == TxtType.heading1 + ? FontWeight.bold + : txtType == TxtType.heading2 + ? FontWeight.bold + : txtType == TxtType.heading3 + ? FontWeight.bold + : null)), + decoration: (isUnderline != null) ? TextDecoration.underline : null, + ), + ), + ); + } +} + +// class TxtAuto extends StatelessWidget { +// String text; +// int? maxLines; +// double? fontSize; +// Color? color; +// bool? bold; +// bool? isUnderline; +// bool? isFlatButton; +// double? pedding; +// TextAlign? textAlign; +// +// TxtAuto( +// this.text, { +// this.maxLines, +// this.color, +// this.bold, +// this.fontSize, +// this.isUnderline, +// this.isFlatButton, +// this.pedding, +// this.textAlign, +// }); +// +// @override +// Widget build(BuildContext context) { +// if (isFlatButton != null) +// return Padding( +// padding: EdgeInsets.only(right: pedding ?? 0, left: pedding ?? 0), +// child: InkWell( +// onTap: () {}, +// customBorder: inkWellCorner(r: 4), +// child: Padding( +// padding: const EdgeInsets.only( +// left: 14, +// right: 14, +// top: 6, +// bottom: 6, +// ), +// child: getText(), +// ), +// ), +// ); +// else +// return getText(); +// } +// +// Widget getText() { +// return AutoSizeText( +// text, +// maxLines: maxLines, +// textAlign: textAlign, +// overflow: maxLines != null ? TextOverflow.ellipsis : null, +// style: TextStyle( +// fontSize: fontSize, +// color: color, +// fontWeight: (bold != null) ? FontWeight.bold : null, +// decoration: (isUnderline != null) ? TextDecoration.underline : null, +// ), +// ); +// } +// } diff --git a/lib/widgets/txt_field.dart b/lib/widgets/txt_field.dart new file mode 100644 index 0000000..c081229 --- /dev/null +++ b/lib/widgets/txt_field.dart @@ -0,0 +1,155 @@ +import 'package:mohem_flutter_app/theme/colors.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:mohem_flutter_app/widgets/txt.dart'; +import 'package:flutter/material.dart'; +import 'package:sizer/sizer.dart'; + +class TxtField extends StatelessWidget { + TextEditingController controller = new TextEditingController(); + String? title; + String? hint; + String? lable; + IconData? prefixData; + IconData? postfixData; + bool isNeedFilterButton; + bool isNeedClickAll; + bool isButtonEnable; + double? elevation; + Function? onTap; + String? buttonTitle; + int? maxLines; + bool isSidePaddingZero; + bool isNeedBorder; + + TxtField({ + this.title, + this.lable, + this.hint, + this.prefixData, + this.postfixData, + this.isNeedClickAll = false, + this.isNeedFilterButton = false, + this.elevation, + this.onTap, + this.isButtonEnable = false, + this.buttonTitle, + this.maxLines, + this.isSidePaddingZero = false, + this.isNeedBorder = true, + }); + + @override + Widget build(BuildContext context) { + controller.text = title ?? ""; + return InkWell( + onTap: isNeedClickAll == false + ? null + : () { + onTap!(); + }, + customBorder: inkWellCorner(), + child: Row( + children: [ + Expanded( + child: Card( + elevation: elevation, + margin: isSidePaddingZero ? EdgeInsets.zero : null, + child: TextField( + autofocus: false, + controller: controller, + enabled: isNeedClickAll == true ? false : true, + maxLines: maxLines, + onTap: () {}, + decoration: InputDecoration( + labelText: lable, + alignLabelWithHint: true, + fillColor: Colors.white, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: accentColor, width: isNeedBorder ? 1.0 : 0), + borderRadius: BorderRadius.circular(4.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: borderColor, width: isNeedBorder ? 1.0 : 0), + borderRadius: BorderRadius.circular(4.0), + ), + disabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: borderColor, width: isNeedBorder ? 1.0 : 0), + borderRadius: BorderRadius.circular(4.0), + ), + prefixIcon: prefixData != null + ? Icon( + Icons.search, + color: borderColor, + ) + : null, + labelStyle: TextStyle(color: borderColor, fontSize: 13.sp), + hintStyle: TextStyle(color: borderColor, fontSize: 9.sp), + hintText: hint ?? "", + contentPadding: prefixData == null + ? EdgeInsets.only( + left: 12, + right: 12, + top: maxLines != null ? 12 : 0, + bottom: maxLines != null ? 12 : 0, + ) + : EdgeInsets.zero, + ), + ), + ), + ), + if (isNeedFilterButton) mWidth(8), + if (isNeedFilterButton) + InkWell( + onTap: isNeedClickAll + ? null + : () { + controller.clear(); + }, + child: Container( + width: 55, + height: 55, + child: Card( + color: accentColor, + // margin: EdgeInsets.all(4), + // shape: cardRadius(0), + child: Icon( + postfixData ?? Icons.filter_alt, + color: Colors.white, + ), + ), + ), + ), + if (isButtonEnable) + Material( + child: InkWell( + onTap: () {}, + customBorder: inkWellCorner(), + child: Container( + height: 55, + child: Card( + color: accentColor, + // margin: EdgeInsets.all(4), + // shape: cardRadius(0), + child: Center( + child: Padding( + padding: const EdgeInsets.only(left: 12, right: 12), + child: Txt( + buttonTitle ?? "Search", + color: Colors.white, + fontSize: 18, + bold: true, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/user_image.dart b/lib/widgets/user_image.dart new file mode 100644 index 0000000..9ff8102 --- /dev/null +++ b/lib/widgets/user_image.dart @@ -0,0 +1,26 @@ +import 'package:mohem_flutter_app/config/constants.dart'; +import 'package:mohem_flutter_app/utils/utils.dart'; +import 'package:flutter/material.dart'; + +class UserImage extends StatelessWidget { + double? size; + String? url; + + UserImage({this.size, this.url}); + + @override + Widget build(BuildContext context) { + return Container( + height: size ?? 60, + width: size ?? 60, + decoration: containerRadius(Colors.transparent, 1000), + clipBehavior: Clip.antiAlias, + child: Image.asset( + url ?? icons + "Blue Masked.jpg", + width: double.infinity, + height: double.infinity, + fit: BoxFit.cover, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 18fcc0f..1910eb0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" async: dependency: transitive description: @@ -43,6 +50,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" cupertino_icons: dependency: "direct main" description: @@ -50,6 +64,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + easy_logger: + dependency: transitive + description: + name: easy_logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" fake_async: dependency: transitive description: @@ -57,6 +85,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -69,11 +111,63 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "0.22.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + injector: + dependency: "direct main" + description: + name: injector + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" lints: dependency: transitive description: @@ -95,6 +189,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -102,6 +203,181 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.1+1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "7.1.0" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + sizer: + dependency: "direct main" + description: + name: sizer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" sky_engine: dependency: transitive description: flutter @@ -156,6 +432,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" vector_math: dependency: transitive description: @@ -163,5 +446,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5346e91..c383e65 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,15 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + path_provider: ^2.0.4 + injector: ^2.0.0 + provider: ^6.0.0 + easy_localization: ^3.0.0 + http: ^0.13.3 + permission_handler: 7.1.0 + flutter_svg: ^0.22.0 + sizer: ^2.0.15 + dev_dependencies: flutter_test: @@ -61,6 +70,11 @@ flutter: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg + assets: + - resources/langs/ + - assets/ + - assets/icons/ + - assets/images/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/resources/langs/en-US.json b/resources/langs/en-US.json new file mode 100644 index 0000000..c9f87fc --- /dev/null +++ b/resources/langs/en-US.json @@ -0,0 +1,38 @@ +{ + "title": "Hello", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "profile": { + "reset_password": { + "label": "Reset Password", + "username": "Username", + "password": "password" + } + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": { + "zero": "Your amount : {} ", + "one": "Your amount : {} ", + "two": "Your amount : {} ", + "few": "Your amount : {} ", + "many": "Your amount : {} ", + "other": "Your amount : {} " + }, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": { + "male": "Hi man ;) {}", + "female": "Hello girl :) {}" + } + }, + "reset_locale": "Reset Language" +} \ No newline at end of file diff --git a/resources/langs/en.json b/resources/langs/en.json new file mode 100644 index 0000000..c9f87fc --- /dev/null +++ b/resources/langs/en.json @@ -0,0 +1,38 @@ +{ + "title": "Hello", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "profile": { + "reset_password": { + "label": "Reset Password", + "username": "Username", + "password": "password" + } + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": { + "zero": "Your amount : {} ", + "one": "Your amount : {} ", + "two": "Your amount : {} ", + "few": "Your amount : {} ", + "many": "Your amount : {} ", + "other": "Your amount : {} " + }, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": { + "male": "Hi man ;) {}", + "female": "Hello girl :) {}" + } + }, + "reset_locale": "Reset Language" +} \ No newline at end of file diff --git a/resources/s.dart b/resources/s.dart new file mode 100644 index 0000000..e69de29 diff --git a/test/widget_test.dart b/test/widget_test.dart index 42d9b33..a360c7e 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:mohem_flutter_app/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget( MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);