From 7cea57d5ec6b6645723f2a1dc5028d05573946e7 Mon Sep 17 00:00:00 2001 From: Frederik Benoist Date: Sun, 26 Feb 2023 11:19:35 +0100 Subject: [PATCH] =?UTF-8?q?apr=C3=A8s=20copie=20des=20sources=20appli=20ec?= =?UTF-8?q?ommerce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- analysis_options.yaml | 3 +- assets/images/facebook.png | Bin 0 -> 606 bytes assets/images/google.png | Bin 0 -> 5418 bytes assets/images/logo.png | Bin 0 -> 10220 bytes assets/images/mastercard.png | Bin 0 -> 7066 bytes assets/images/onboarding/search_product.gif | Bin 0 -> 253662 bytes assets/images/placeholder.jpg | Bin 0 -> 9482 bytes assets/images/twitter.png | Bin 0 -> 1105 bytes assets/images/visa.png | Bin 0 -> 12708 bytes assets/images/whatsapp.png | Bin 0 -> 6505 bytes ios/Podfile.lock | 38 + ios/Runner.xcodeproj/project.pbxproj | 18 + lib/config/constant.dart | 28 + lib/config/global_style.dart | 168 +++ lib/library/flutter_overboard/LICENSE.md | 21 + .../flutter_overboard/circular_clipper.dart | 20 + lib/library/flutter_overboard/overboard.dart | 396 +++++++ .../flutter_overboard/overboard_animator.dart | 27 + lib/library/flutter_overboard/page_model.dart | 25 + lib/main.dart | 133 +-- lib/model/address_model.dart | 71 ++ lib/model/category_all_product_model.dart | 118 +++ lib/model/category_banner_model.dart | 17 + lib/model/category_for_you_model.dart | 23 + lib/model/category_model.dart | 57 + lib/model/category_new_product_model.dart | 80 ++ .../category_trending_product_model.dart | 85 ++ lib/model/chat_model.dart | 38 + lib/model/coupon_model.dart | 101 ++ lib/model/flashsale_model.dart | 201 ++++ lib/model/home_banner_model.dart | 26 + lib/model/home_trending_model.dart | 42 + lib/model/last_search_model.dart | 219 ++++ lib/model/last_seen_model.dart | 215 ++++ lib/model/order_list_model.dart | 59 ++ lib/model/recomended_product_model.dart | 215 ++++ lib/model/related_product_model.dart | 80 ++ lib/model/review_model.dart | 82 ++ lib/model/search_model.dart | 29 + lib/model/search_product_model.dart | 212 ++++ lib/model/shopping_cart_model.dart | 50 + lib/model/wishlist_model.dart | 99 ++ lib/ui/account/about.dart | 74 ++ .../account_information.dart | 269 +++++ .../account_information/edit_email.dart | 129 +++ .../account_information/edit_name.dart | 92 ++ .../edit_phone_number.dart | 122 +++ lib/ui/account/last_seen_product.dart | 157 +++ lib/ui/account/notification_setting.dart | 108 ++ lib/ui/account/order/order_detail.dart | 484 +++++++++ lib/ui/account/order/order_list.dart | 164 +++ lib/ui/account/order/order_status.dart | 231 +++++ .../payment_method/add_payment_method.dart | 351 +++++++ .../payment_method/edit_payment_method.dart | 381 +++++++ .../payment_method/payment_method.dart | 470 +++++++++ lib/ui/account/privacy_policy.dart | 64 ++ lib/ui/account/set_address/add_address.dart | 167 +++ lib/ui/account/set_address/edit_address.dart | 191 ++++ lib/ui/account/set_address/set_address.dart | 260 +++++ lib/ui/account/tab_account.dart | 211 ++++ lib/ui/account/terms_conditions.dart | 64 ++ lib/ui/authentication/forgot_password.dart | 115 +++ lib/ui/authentication/signin.dart | 250 +++++ lib/ui/authentication/signup.dart | 177 ++++ lib/ui/general/chat_us.dart | 345 +++++++ lib/ui/general/notification.dart | 155 +++ .../product_detail/delivery_estimated.dart | 269 +++++ .../product_detail/product_description.dart | 94 ++ .../product_detail/product_detail.dart | 818 +++++++++++++++ .../product_detail/product_review.dart | 145 +++ lib/ui/home.dart | 94 ++ lib/ui/home/coupon.dart | 171 +++ lib/ui/home/coupon_detail.dart | 143 +++ lib/ui/home/flashsale.dart | 344 +++++++ lib/ui/home/last_search.dart | 180 ++++ lib/ui/home/product_category.dart | 453 ++++++++ lib/ui/home/search.dart | 252 +++++ lib/ui/home/search_product.dart | 458 +++++++++ lib/ui/home/tab_home.dart | 970 ++++++++++++++++++ lib/ui/onboarding.dart | 70 ++ lib/ui/reusable/cache_image_network.dart | 55 + lib/ui/reusable/global_function.dart | 53 + lib/ui/reusable/reusable_widget.dart | 227 ++++ lib/ui/shopping_cart/change_address.dart | 149 +++ lib/ui/shopping_cart/delivery.dart | 662 ++++++++++++ lib/ui/shopping_cart/payment.dart | 371 +++++++ lib/ui/shopping_cart/tab_shopping_cart.dart | 369 +++++++ lib/ui/splash_screen.dart | 78 ++ lib/ui/wishlist/tab_wishlist.dart | 378 +++++++ macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 6 + macos/Podfile | 40 + pubspec.lock | 320 +++++- pubspec.yaml | 22 +- test/widget_test.dart | 4 +- 96 files changed, 15106 insertions(+), 118 deletions(-) create mode 100644 assets/images/facebook.png create mode 100644 assets/images/google.png create mode 100644 assets/images/logo.png create mode 100644 assets/images/mastercard.png create mode 100644 assets/images/onboarding/search_product.gif create mode 100644 assets/images/placeholder.jpg create mode 100644 assets/images/twitter.png create mode 100644 assets/images/visa.png create mode 100644 assets/images/whatsapp.png create mode 100644 lib/config/constant.dart create mode 100644 lib/config/global_style.dart create mode 100644 lib/library/flutter_overboard/LICENSE.md create mode 100644 lib/library/flutter_overboard/circular_clipper.dart create mode 100644 lib/library/flutter_overboard/overboard.dart create mode 100644 lib/library/flutter_overboard/overboard_animator.dart create mode 100644 lib/library/flutter_overboard/page_model.dart create mode 100644 lib/model/address_model.dart create mode 100644 lib/model/category_all_product_model.dart create mode 100644 lib/model/category_banner_model.dart create mode 100644 lib/model/category_for_you_model.dart create mode 100644 lib/model/category_model.dart create mode 100644 lib/model/category_new_product_model.dart create mode 100644 lib/model/category_trending_product_model.dart create mode 100644 lib/model/chat_model.dart create mode 100644 lib/model/coupon_model.dart create mode 100644 lib/model/flashsale_model.dart create mode 100644 lib/model/home_banner_model.dart create mode 100644 lib/model/home_trending_model.dart create mode 100644 lib/model/last_search_model.dart create mode 100644 lib/model/last_seen_model.dart create mode 100644 lib/model/order_list_model.dart create mode 100644 lib/model/recomended_product_model.dart create mode 100644 lib/model/related_product_model.dart create mode 100644 lib/model/review_model.dart create mode 100644 lib/model/search_model.dart create mode 100644 lib/model/search_product_model.dart create mode 100644 lib/model/shopping_cart_model.dart create mode 100644 lib/model/wishlist_model.dart create mode 100644 lib/ui/account/about.dart create mode 100644 lib/ui/account/account_information/account_information.dart create mode 100644 lib/ui/account/account_information/edit_email.dart create mode 100644 lib/ui/account/account_information/edit_name.dart create mode 100644 lib/ui/account/account_information/edit_phone_number.dart create mode 100644 lib/ui/account/last_seen_product.dart create mode 100644 lib/ui/account/notification_setting.dart create mode 100644 lib/ui/account/order/order_detail.dart create mode 100644 lib/ui/account/order/order_list.dart create mode 100644 lib/ui/account/order/order_status.dart create mode 100644 lib/ui/account/payment_method/add_payment_method.dart create mode 100644 lib/ui/account/payment_method/edit_payment_method.dart create mode 100644 lib/ui/account/payment_method/payment_method.dart create mode 100644 lib/ui/account/privacy_policy.dart create mode 100644 lib/ui/account/set_address/add_address.dart create mode 100644 lib/ui/account/set_address/edit_address.dart create mode 100644 lib/ui/account/set_address/set_address.dart create mode 100644 lib/ui/account/tab_account.dart create mode 100644 lib/ui/account/terms_conditions.dart create mode 100644 lib/ui/authentication/forgot_password.dart create mode 100644 lib/ui/authentication/signin.dart create mode 100644 lib/ui/authentication/signup.dart create mode 100644 lib/ui/general/chat_us.dart create mode 100644 lib/ui/general/notification.dart create mode 100644 lib/ui/general/product_detail/delivery_estimated.dart create mode 100644 lib/ui/general/product_detail/product_description.dart create mode 100644 lib/ui/general/product_detail/product_detail.dart create mode 100644 lib/ui/general/product_detail/product_review.dart create mode 100644 lib/ui/home.dart create mode 100644 lib/ui/home/coupon.dart create mode 100644 lib/ui/home/coupon_detail.dart create mode 100644 lib/ui/home/flashsale.dart create mode 100644 lib/ui/home/last_search.dart create mode 100644 lib/ui/home/product_category.dart create mode 100644 lib/ui/home/search.dart create mode 100644 lib/ui/home/search_product.dart create mode 100644 lib/ui/home/tab_home.dart create mode 100644 lib/ui/onboarding.dart create mode 100644 lib/ui/reusable/cache_image_network.dart create mode 100644 lib/ui/reusable/global_function.dart create mode 100644 lib/ui/reusable/reusable_widget.dart create mode 100644 lib/ui/shopping_cart/change_address.dart create mode 100644 lib/ui/shopping_cart/delivery.dart create mode 100644 lib/ui/shopping_cart/payment.dart create mode 100644 lib/ui/shopping_cart/tab_shopping_cart.dart create mode 100644 lib/ui/splash_screen.dart create mode 100644 lib/ui/wishlist/tab_wishlist.dart create mode 100644 macos/Podfile diff --git a/analysis_options.yaml b/analysis_options.yaml index 61b6c4d..dbffaad 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,7 +7,8 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml +# FBE nolint +#include: package:flutter_lints/flutter.yaml linter: # The lint rules applied to this project can be customized in the diff --git a/assets/images/facebook.png b/assets/images/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..81b68819dcadc89617b6522ebd0eb8a4edda863f GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xg+b<>&pI+jYo_}{PA?#X$%aE>pfi@Lo)8Yy}dC@)KR42 zq5k2cw_8lg?3&N|s{UYO6J+657R$WE`nE=}J0o>r#?sbnFIluFAG*DiduMB_!YrlC zEjL6}G|Cls8&AIZGjY}2xy5y97S=k(+UD=yXYFjtwK-6_X4joRN5uCWDxPz2No@u1 z`;GIgPRm6e?cJ99&`gXmxJJY0o6YOBk4-YBebgy+6P%G~onTz5CU}$QRNSfOGg*C0 z&rP+Qm%(*Dbd65t=^1`jC0+*&8aA6pz~KP${+5HXe<)6dvhV3SN}uoW~qNyr%gJYzS4VR zRad(>@08O#0o3JKs+Hb*5yBbLJ1h z50)Eqe4a6`4^9`HXwG=PV1m_T4w(e=jC1Pw3HGIK%$x38&HK0V68BlxF4>wxp09YS iA1o=onR!?2gLH44z2cO<)!e|?WbkzLb6Mw<&;$U%Z1^z% literal 0 HcmV?d00001 diff --git a/assets/images/google.png b/assets/images/google.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4650dbbf59c4adacd3225dd0fc601e1127c023 GIT binary patch literal 5418 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 literal 0 HcmV?d00001 diff --git a/assets/images/mastercard.png b/assets/images/mastercard.png new file mode 100644 index 0000000000000000000000000000000000000000..db720b2251b830ef99864aa244dc39bfd89f73dc GIT binary patch literal 7066 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

Bmq;^MBkcU&&+%f)M|ZAe-CdW;eO1gcOAH z14{5_IlGyH9>`LiSNXsOH1JLq#8aO0q-QeRJ* zw5<|t!CsYW8nXg+0DZkE1Xkd^9^}L-Vnr-SIoZ3cb~695HB~@u>Z(|(KK79XuxeB^ z8rjWKGyx`PYGH%=Rm?(?iQZ!XX>0m{7}OF2y=#FQVvqwQ2m%c#xF`lh(7O%TpaEpa zKtn;>0}<>N0VB}C4l1zR9qd2`h0H;8p;CtE!Vyvwt*l!giGg;KHU=7ap$iZIgXX#x zy0Pt}39`G-)Iv4^@=bsXI4OhKW(*}_^bH@_GtjTu6$fC>~>1QpnTdY5Zk+Pbp@1sL;yEdT=* z|JcZ^O@H$T1rD z+X6^F_mLxjBNH}|1{r8p(kzB9rcscC?r~2IbM|yt6?){lCRqYbjzJ7a4S^g~84BHH zE(2x@TqsQUyiTqGn6v0*I$vN5Er388Uf{hQSD>}6Apca*n~0dY&9 z2C&6|2NHY(5YTzhmbK=C!JEzURsaO&Z2|wT5isHy7~lwpXhF4apz5ml7?N8kwloG1 z;@$Qji-%yqVOfjnN9rJ!EXYBbi;M_^i`WAKMgjv!4PA_nbH{)6E_Ni|XboT>;d}6J z4hjQr4J4SwIqvwup_0WFRN&zVRyk|qiQt5Dm;sN5feGx*+Dum+vKu$?kk?IJd#}I@ zNO<_Z9c*YV|8~wr|N6lTj)(yGIN78@ta1&Lf`s=`2XW_d09c}f9jZ94Oz#~NHXZVn z51;`*20#{#o@44JK!a|lIg$?>gEAbz1VzX+6qsmt$d7o&hMDt+eGX{{?6>OLJ^kI^ zEr2vM*}faM`s5*Cde^cb_RW=iXgvRqYh=@s0~voXLmOOZe*XL6l4g4Xg3ACvXFS<9 zn6rBIjUD|yvc}Ngff*!l14IYD=!i!w$hB|du@8&|;)Wx0Spb9IE>r1yW-!0O3WFV1 zIsr8>aKSN=;fhF9a4#GkL;3EnEFd6Kh z!efcoFaT~Vr1|0rDHMQ_Ni_e`>VOf;!oi|KU)v)T?6@TLLFmLytVFmH#L}WK!)TIS zAt?tau8JZQ_yR=<(6SFuj$d;zSyIJy(h&|=Fw?TXMVv%mlti4O#O3xS4aw<EKz7=qQ6JR~}3b{DLMi5Ea8a-%P}MX4~WkVBCR2$KYkj^ zscbs-Qcj!nya|A{VH>XwXg^hh06^0@qpLSC(?bLUK-%n{@q3|{+)WTGyp+tX3g9*< z;5}o5IRxah0zI)8;;$GhK5*1TS&9N!*n%RHEgo=*4(JbQs{ty|&Z>ORsQQ2%41gr?0B$ouF%-rsBc#Uy#2Mrr7nM>Tx~M7bQrX0$n1TNq5CVuZ11_kt45)%{15$GA00aB4If4l&olga@01n_bs~Z6)Rh6(j)Nkz0 za72SCh@Uj2t{WHvEcm}K5K0{qgDtxP^Abj~;!lr5&Ax&F*RoG1ts~T5wRo8I@P^EHL zvSQbEl~+fE*IJx`#A7j#BRN~N*Bi`IC+tOleb;Z5GGnqu7T|$xbD%2VQEq(IY4ywb z0@VK|JW?wZRpYZ(d@0zDJXi#n0VzNOl2y|v5I7P<03PsBb!9?6g-t#Ei6))HK%iJ4 z)5}08{-h!s^QoOIFPV z0oe#Vr47qd^sHL^EYA^JpQ6`}^&a=LGrslS2`E&*bVecFvYfClE)(D7($;Y0L_R`S z_eCrC%~`|}feU*ix{XC9N`n9O%c{MAyDfmjx_~eXDvvu~(>&K7J>axTU<2;k#GBH! zszzf~)l|L20Vd!ArYaG(D-t%~7v>#}ondu~VfEEvSfySY_TfMTVQv*-wes5_He&cB zV%$~YtE%B7zF{X$-w?LT3^*>U)5S!+PbcnL(|zAO2xAW}SQqPIi%NojGvEIKu&-BC zxWZ83eL6$G%TrZ#;oL>y>Mg5?5@SBz|;ymuCSD1!ZzyyBE?)V`W~3W<~&I)?}&Z z1Y?+nW+ntqFa}ECWE6;C#uH||MKud>PhTu!Gqzh9u(egAtpJ!VsomchYfa~YK}Bw4 z`jq9&Mdlv9gIkD(R1k(}=mbo-15QYVXb|W=V1!qQ1!iCdfwsYhe(3*5XoXi`1Zz$O zh;9W!=!4Z#;}xI)E^K5vMqgS6Le+I(XC;jD{KER(+E_-_Sen`I4bVFV;(opZfJW$o z#)MbU=xB%rMj!;A255oqolJh{Xy9m{?gWI6hH790kA|c(1_ae4&Kx%BlLo@X-RiGR zS|PS%S#E`6IEJVugfMOeVK8e)Kx*o#U5%$xP!P}>|9H1$Yun@R_soYqdrjVYPf7UFuw}p-VZp#VJ$_X z)ao%VM`PY+oVMe!QqqhJY_Im?)b?SqPUm@LMAkm%*zRN0?&AN*Bv{`j$HN}%Qa)|j zmZRB5V8iy>;C8CrM(*NQg3Y;Kuq)g z0`CK3I0i~+aAV-+JNUHq6g;U4GqBe0?%wYH=3z5dW}t2bi8cm}K57I|?}rwKR^a4L zI0lO5XhsNdjXnmBzJ(3D+Rde{a@}v{?r;D0>Q{ILqef+h{%M4k@uWuRvtDwjZtGLl zXp6=K9PiCkEK>hcV-8>K9|v+U7RG`;hHajPwx;r9m~j6IHwI4NY>b}p_3j-rSM!U` z&p?=C-67Nfvckaba4&amD#qP{u7)AE>p=HpEWLw4C-lr7^zOL>O*V8DpyQ_~!jaim z5AP`-@ADxRN5-~p5B|}du|pNO^qqI*SBLd4 zr}g8mZe6b`Q^)mKuk~IZcFMYSV4o}9we8*}cGEQWW8Xu@eRfQI?){N=Om9V1xAu+Y zask%%LDS+W_Vyhn)h zkdJu60To8R_>4b!lTUe+UwM{q`IR4{yOEo(nR%J7d7Bp=2OFK>*?FG7d7uAzpuZ4m zM?O|P*o0c`FFtyrFM6b3dZ&MSsE>N3pZaW1da1v9tj~I_-+HR=danO^uMc~$AA7Pd zd$T`#v`>4rUwgK1d$E6ew~u?dhx@s&d%Lguf?|5T-+Q{hd%t)4ybpZAAAG_u{Gqq{ zzfXKmGJM8we8)fhs*ilhpM1*4`p3U~%+GwtzjrQu`ashB4+?kA$88t|{m&15Wgq=` zEq&8Jebg6yP7Kj$h5Xic{nv+myP|1FDp#gd=<+4ZgEXLxs%g_k%$zzEGHf~Vr^AXEO%g3?^eEDk zC=Iaux$~*c7&L9#)ByEr&YnQG77#i#DcG=L!7gpu6f4?0=56vop={7-^h}U`u(doF$OamIM`qi;BsKbjafdP zZ2Gjx%3DFRZjDv+=%A2M)2^*iwNux+nGV>U5O(a+wujTkojWmyEgl47s6eoSk1a)_ ztZ=zwdGCS}qST;K!}D3)z()%wZeFr+z?aovi6JnA41!U(dVuTG> zIOp3y<26>^c?sr~o_e8m0o?-#WRSrH6?CRX8Uszy!UH$ZP=*5tM36=iG0^9L8bjm& zL3SKeG0=w`WcI-Y-06@2YX;VZpn^Rn#b9=q#jpheVuXNz898v^g&HQPQ3M1R=pcjx z-8m^n0bkV6LKLM|Lltuhl!O!(4U{CP zH7x5WZ=uy`XIFT#x~)>x9v9e$1HB0ap*{#y1CatO7VHBo;3p>n8w|n$8X17fMT=HQ zWX7i?uwjA#+|lV%aN2Tv@NEQto0bGLv@lQwQFP{{K(?yOTLzp;apVbH1p0&t7)Sxc zq5~!IMScRs&_Kl`J3Rj|wz45SbGP0u%&>OZTE<%fAr{zBem(zMZOTCT40Ft?(#)-^ z1P*O;kWMFt>Q+iG&1%z54-LT6FLk{bfi6*Pm%&%Bnze5UKtX~B46v{mu{vM;kOLI} zz+$!!dlj~J*A`s1*?FS9Tcs7GkOKySEU<&yFm#py4DiLv02(YXzH$i7t;{%Fb4UF- z=unOQlhuLysKD1Zw?=sy4K#oOeAnq?K<4_U6m1{G*7eg)*xs6i46i|6;>%NG{`{5{Bz zg8Sz=uln%{a~=Pj2r;CeKk|}Ry5T^efCh};{Ob3<)Zq_+4eW~N@;5riC=fOAJD>!P zaKH+l=6(-Mn*&34l?h5@fCSXw1v98Zlu&?!k@4UMTO+rfMeuVB6d?{@!#?u3a55WQ z01#2Q!k)CShhl+Y3{N#f_2uj@H_V}2NJttZzVL_hOQ99_N5wro5sFIen#_tAkrpDb zi#7D(2zRJFre!gRSZpH!%_u=GD(!h_Od}IhwMD+b(T#q5BiBl>z$7a10$kuh7h)B+ zzNkx!5QG31gn&pN_>6{k+=&$L#6}cev6FuUq#O5GGeRncjGd~&AgG|Xi)0XP344JS z#xTiAS@QpqI>}@hMM;)2{?U^{`D7n;i9}M`(uO)?hzWoe$Ou@#XCO$XBeQtMV%}1X zI}#=xf5}N-_Hvop1f>}FI80(r(`!rYN(x(m9TT*GmtTn+HPM30V`B51Wb6qzxv5WY z@-v^il;bUliOg>%;RP*7fd*)40S#oJpvfGf1TFy2c-m2H*3{zy^V!XP3bLd0`)FUj zDa~vGRBT(zqd6Vw0Cpafoh)Dhz)Y4o4zTp07dU9qC|Xg3%+rLFsfb2J%2ALKwWvnL zXBp{v6haPVr{=t=L0LM{?3|P-3xH}=bN8CBN%W~GCEJ348dRamFPKL?D_VmnN~Z3u zKwtk2-2>7Du#ys_XX^AoPVZP%5p{_M9^~mo!TQg!s-UojCF@2Lm{xwild)roAv5s? zf`o!Ci3(sR-YWP|XO+{Kob62oc#s7z)BtZh39MjU2+Sp5@vyG#XJQvCTgNJOsUKXa z1yGWPFd*if=H!|XA?jEM3=s?kq^nyu0xFtF0Rvh*?OQ20Sk@Ajx`>VKY_qGE7+T;2 zIGvTCYWv$EzFb|zW*wwCZ{JYlM z(&(9yl-Au?nOD!=l)Mh5DRH}U0`_vC!tHghK|hN_`95-MEo@K#1vp*)T35jT1u*|w z9}C%rGE)kJD8m6FOi!L1AOPjv>{VUxfr8TX2R4rCj@v0j_+-|iRaLG^;P=&@Qn8J! z?L#M_Y~mB6xXQMT;e=OUrWXq!!xVt;x6tVV63_^!T0V0@EmBh@P}m4YaKH+Pd@~6< z(jx{mBa=sLT`6C1%KoKtW37B;%bFJrPxt@@MzDe?DYKRjXaRi4e1s&x=ggS(F>YN- z2N|@pJ`RPR;XvSKHbW{~6F81>8QxOBAMt-~=oQGBn3_)kvEc*LPK> z2Qoc^P0!T49o_H-zy#`yL{_d-1#EOn*yO`zkk1RScB`}PYC(&Im@=-L;fDW6<0vT_ z*SLP~5ipHHmRk6rEf=Ka=J)@4(F;j^X1vRYVigzy@l4g5TvfE2!0Ps$2VSZJ&JLL-pxMRt~|J+whaZ zW;3setz{4dcY}A#@?87ZalM9Ivm_2UwuKu?lp9sWgp~1uEiN0HXMlX_08B+M7;o#j`!-gMK?*;%-tos9A+3$UxBF-BSPW$|&d^@Vg+W z&eQ*BSnfwhy4int_Qf+EPfvIJ+e1@Z7p zJmZbO>e9TPtZs*VZ}u0^DaxGWU01yZIa_)(UUr-~Bz|fzxO&!)zvKg-wbZw7`)pbN z`pB=d=}}hV4wNv zU;j-YQV~(X1t2CxllyI;`@x?A4i3njp0xdllTjJ@@n8FmTu^lzDQ%u##005vpbT=7 zY{cKs#1GktAiv>X396tEqTmm%APc_T>%2f&WQ7JA;0)3r{Vo3l&h3D$8J+!cSoj^_ z{#~JUSs@Sx;i^5K?72n^a$pn8;9kX(`u&Cu-e2k|Ar_ik8@}NZwxJh-m<%Oh9CBb8 z>XZ~VAr)5P`Muh0XdxU9;{LTA`jL}7+2I`)qRi+aCZ%B?hTnBTT?KC8A~NC-BH~j7 z+9i(EhdJUMHJ~IiAM7c`H#HwB`d{p6Vg~+-f_35#H6keL8z0sd3EmSf=3+Pj;&7#+ zFV3M2#zkYnqSr0aECwGW+9FBmA~TkvGd3PD(qSFyo*3$!Cmy3$)gmR17%%qXFDe#o zu%Zr8BOy8&A4X9!ZevEl9x8^TIEoGzTH^r{AU2xb6e|DYCVitJ?&B(cVcN(e2LdFR z)uTOXV?C{-FZIYfCZfaDltU(*Loxvqq+19HfG9E#KN8163M1v+UhnZ_RXE!)6*oFysPniozsFf(kH#BP=CqipX8Arg@-EGUUQ8Xu>4y0&kiE zZ`NjQ=7K4dLN17cW-3DH!5nce+!08mo55qch35~{UsU2Hat2~a-2!jErzx1i6b!>{ zZh>{irzu=Rc1nU~o~Cd7LKm!oq6EP(oM&AT0wq|2D+nbM3_}no$`i1GEEuS9-lICM z=Rc{AE=Z?!P5~@bK`?LvguTFS%4cuFrY2YuK|nxUVhW39p3lL~2G|(TVZg(n(F(`{ z7&O5g%s~^#LUq(tA5eob@Ir8kYDEPo&cmbOV9yFwq5RAYTi~<%c0*pOXlp3gw zdM5{vLog_UCb$ARn7|{Pf%G^;&|S=VLP5$*DV2uR&1?ZKG{ZKS!iREcoY6rdAi<~7 z!8B09ZoX$OoWU(X5q+gW3P`|Uq=5)P!xkKZtVT`iq`sMoXr3>?86&_Rgcff5kl z5e$Q|E`tbEf+ZwF5r9G(;Em^PTDn1Mh+)fxPAW<5)DmpNB8blTvX()rC$_qZG^A29d}|fNXE2<=o6P|j{OY)V z>M7&`yTSr2?1FHnDJkecutGx`aKRuv0Ihxp3q$}BP{AQ!O3zUq^%(09paLq04)hYrdM9ea1LUSO)jyC^?7-$13tic@M0Yz5msf{4VVyO2$4H8@`-{wLvOv4Ba zY9b^8&UFCFqO1piLKS4{@0!BQ(imh$YFi}?{h7cU48s%HfEZ*$dG&!MNWv!Y!X$_# z?eu{r5Q-fng45CgfgVB-u&jpxlGft!Ku zfQHXpMjzp|PX~K&@^sA|Jc1=mLM2RsGR%VD@vd=|?x)UyDbQ(tPJz1)CmM`G8;rsp zv;ZkM49*I~1+-}aJORCW1`m+JB9H4GlUYR`SKrjqKD#`E|xU1hL3LboG1`NU(oB%W|3QT=8G<*RI zc!8VNwFr2@GdO@-?*L)Q06I(bk-RHkk^vhGC^u+Bh)mom07B~!3g(*D$pnEmP%MF# zaZjHy4?X`iFGpKD`z06KLMiwHy4b>#oWU7@!ZqM2?3%(dv;++BwHL&I2Vg-V#}W*S zK&&7Mn@+(9r1Wpk=oOejN-x5(7_OldFD1CbUp@l4<}|Td9w?6h*Py8)+d%~+LNI^= zCrkrVOKL%9YtQuo9W(SJ_`*Y*_7|Xb4eSE@hAb{@!!`VIcWy8#mH<>P28Z02H-)!G zxWX+^!Yfb$Q3GX01RHi>WnmRK<@NzTbM_i}_G0K_9$AA7J;A>y>aqIg}&mjpKOu-J>N8fjQKIE(jiJ^MW!|f-msG zO;0&VDp4Ldrc;h%F6*+E^U)exQX}vJbc+Br@IWanG%uWaBeXePzN?!TXaACAme09Y z*174CfN7Ht+aT$i8~SeiA3@s_J}P=uGCGwDI-yg#(~vEuXF7!^C#R=4sLPY6YkGz% zWvW;Ds*gIWyWXjz`m1kZtj~Imzq6~~dMV}lk(K()^t!J{W=`59@YN!+FFUi#;s_FZ zvH!Y3u6eYl%~#sGnrFMVZ#%blySJlfvsZg@Mm4o#`?sGvx^ugwCylrp`m;fMyQlwq zwK99WmnFHEySdwYI@Y^q{l+vZJHN~Oq~rRu2fV%K;c(`=!3W8@)B8a!OC@bszwQJG z7!KXQ4#qfy3p59Kh=}np1*F9b-q_0rkk7~u&Ctb%$y7in1HcC)X$bi3Ld4w-752pm z-ox-J#@pD+<2?}py>8b>{fX@-& zsL@n&G)J18{9F_~#88r`5dEN&&dXbsh#aU}Ee0ASf!lP*3xt~uC#fAxh*c%kWfOBL|3lPfUj2RmEtqn}{>>obqUjh=?zzQHfU){Z50l*C` zPDHIt)8Bwr#eUewIrMA*?UcQ!bOr=8zIBsH^w&xZ5KrbyKxatL@Z^BmTfp5556f_d z%3MH!xU+<8f$^XSPTT+)9LWw80MRCX+((pzEGnoRJ)(C0`T+z1fddH&FbD$!0|XFq zAV5$d1Pl%fp?FB+@ZkoG7$e#UfMCZ35(*=ranLX$3=<9;GWc*wLJkvYN+4(v-~q^$ zEkeR@XolediWqeqL9&O#3I;uHTtct`3CooNphVb+QGo-Y1-Wufa25Y*g%2BCZ19LC zVob3j97ZS#1;Ge387a7U0dxld0c=147&#rnkRaj(NCz^^ zu*eZ2fEo-5Pkt3%IKZAuE^hcH>X~*6<&rBqJ&@U{#0DppBS#69W%U?EYLd>nRdsx@ zE(}1qkpu%;5eZl{V9|f7nA{31kpEh6$)uvN>P-UzQ}9VR%r*)^1Q8~<`kYR_s zByw&8GLYEfB?Dp@Z7a7NFe3#soFlC*)Z`l|L>AO|iHr|Ykl_EJ3QTBWsLd*nQ2@{? z$iXrYTsYCi1j>+sgO4hb>O`2PoFEDCoQQ#iBp|Q}3Z4Kcp{Yv%&@#)(8ffZ>GSLHR z1p+U%ke3PcZ+|%k# zJiFQ~xQBx43#SA2O66rs(x*mK>o||4b!bRF}8lgjzL)g})X?4~rKax|dnLL^&NSbDLYi_qKcHJdNPg%YG8tkye{<>v@upZkz zvrjI2>jh1FTh^)39-B8q?7sV5@8q64@4gq(x^1@kMqBW~4R`x+xV@&e@U#7Pym5{r zHyl2^E4Q30vFpZMZN>k-JaED_Z#ePH7joR!$6*9qanP?~9CFo5-+T7jZTI~1*yHP3 z)}4f&d@BnF4v8@PrhOq}k~_#W1s537Emo<7)?ojSpB*v*F)lajaHUjtxEepiabH{a zb}L=#p$L?Xfx8Rr3#o+$Q=4tg06O^CVjHFnxyq}rw!s6v(ZS4~GQbPOWd+z30PDNrlmbib#8oVYnrU8W*q`SpgG}0C5F|H+la7fkM!57^`G4FeWHUl}^Vc5ZK@Z1ezVn z@^&Iv;fj)QT*)4HLnlK{@g|Zw4pHg_tc+X_j!AIMoKg}hDSd#G3xHFLVg(fpNp6m4 zvy%j`;*tzuAOm;_R0D!S0*z#V1Z2>f7QL9MK<&#v8p)5bw!i>9M&&!(Qs6olkdUVc z?IaESj>Omp$M1y^n&q1#>{J&P2bhySP02wTIwXJ*M8TX8u;CPqCqSoB51pi9s5#YO zw^%vT3_3`|4I_|6#WcV!VtHZ;CUZ&%Mlf)94Cn|f@GmU+=we+OQeGrvlR#wXO9o(< zv;dQl6~=`vkG$mt?qxBMxUg3u{T=_ZfCNk&L~oQs^h$1=7{(->lH`aTa3%w|?j(5=fU!=$e!{sgX4W zC_-aF>#z>8PMO#ZU1`87Bp&H#0V@da04g{D5Y9_9bVEn`U_x2lyYX^_*xiOiP(ANUA@DL^S7O|n=cSPsxI zVBER@=ca90j#V;1C-X)i1=cN55_VE7n?%6ACjb(EoH-EjUZA|mq@zB!JGjPC_@o=r zi{~~cI~@yowZKCi5QE%gC%;a+cI4fYsa)mJ(ay*`KC+d$ytpV!xyxY|GggGjWF>Fe z%wtwFR=&KuDX$sM5G-?#(~RagcNwpAzVn`IH)lO-+0J~nU7ol0W@HK)(YR|Qmb08_ zM?<-eZidl*=*G6XnK;mo2HT?RtT?_PS<{FnG@Z>rgdvdbel&P6X1I_u1CXH$6aWAX zcmM=%hWfN@vjBEMuw?&EtIh;7$U+&?3z9B$K`|BfK`qfx>o_Gk069f*!+t~oTyCTT zv3#4vyd3G>HB$&%=zpkTdb zfDm%p1vOdWDO2z>pC4S=+}0My+|rD{7R};wduF#?11h&+D|#SCZoDa>e2>Z}Uj6(A)zyUMB|hf&qY_g%ZkF ztb+r-r{DkXuFn`PZ?t3UQpg67Y!9IjEC|eDbR_X0P66}{ z!V1v}zc6zElR#|0_C*m(<7oD;)9mefj7t*hWDMPf18k!OY{R-%Gp-tUPznh#26slj5C}>jEdREzNNnpD_iop23~4eA+Cni;CJiC<%>rHV zb5!xgEX2Vg?BcY7<=7G0DA7HZ<-fL~>g?ym{AI=xs9NspVNhrTXVD$GW_2K<*tm`y z2Qf-M=)lrZhp6VX4ie(JkQV(W3AzX?E{+}#Y#*yl^sw<4S<&0D@E8Lp?}SX~q)pp$ zjd36#0ltL46aW@uavQmEa{dm2fe~KwF=x{=EkKwms}tMfbaGAS;_`7Y zAsKGLHW2_5Vu2El^A(iyIPV}99FsDqAvzxu1>&UI&M(Sb@CYO46*xgNXQer%6E+cm zI?2;+rhyZVa{#7c88)*iE2z9OQZF^YDF;&y74s_Y;1;4G8iXMdG?PE0Ar<_y4;bMU zmVrQ{p%oMXL1TeHkD(P*Kma;HK~q6O8DR-#EFUvYB)gLn*6hS$G8JIyr9kUbv zI3YqwbVZpV8d8)IIKdbqAsURK8Xn*lt|hfRhyy=jM}kv7_tQpg)G;#?Mho;vJ3$x< zv=vN~G*iJzCFKrGv_Nwd5d1K~4Dv(&(lny`rucCl@Kzs6_T}4iS<_@6(xsQ)?ej-GAq$qca`k)auN@gFb&pBNC$%iZzv^p5(_rd zP_v^J7IiFE(}a^ND@`c>5MWD|bNDhe<&|X-(=Q)(U?=uvYZlL37H01%Q*#zCaX^BI zkY@)?V{H~NNmgZpw%cTuXy0;777TRG5NQW3Dvx$$qc&@JHAR%rYHJ6J>S!;wmTq*_ zT?7VfvoK|Wl4i?xb$anWq=>n3BAP@{^ z@DFlRH-Ws4bz9eUUl(>`S9WKYc59b)GvEVwj?OsnaT(Efe^+&bS9ph)c#GF~kC#?c zHcw1S9-Ci1^=uk7XW;LvZKbg{m9pR&li2uSAEx) zecRW4+c$4jBR=1ke(TqM?-zg57e7BR3H#T794BqZrKo zmx`;{imw=pvsjC_n2Wb~Yrdccux#SG_i)RYWq-Dd)0m&un2p=mjo%oK<5-TZ*j>l~ z8gkBTtY8ta(rz_ajprDU16hy!kBUzFs8Ib*^=)3?3 z(tryFVc7ar2o_-w+${jU01HNg1Bf7ud3Jxf29hb+m0#JFX?-ooWr>-;o}Nu*_@x54K}BlwPum6;sr+G36QpFWtIxX*`Dt?jx#j?vcQ?2IhO^m z3ig;Nf;mH=p#W8RYWeq@@tL6`8Ev2TPO6}vp#hiS0wW}PG)!O-=C7UQIiU+}p&MGH z1^Htknk6RMmbI#xQ_M7kEdy#VYQvYFLwclZI-hs81;zlSqdA)mdLu-D6r^Aj7Aa?C z8jWolsl&OXWf`TRs-S&Y+RB;zKo&8LI;Chjsk3?~ms*y204dacJ6tKmAS<$A9FsT!dt7&Y8luLt|4omNOH;1SG; zQW6`nEzGe^F@F2fs@Ynw3EQ&Wn6J-<1e9S3RG<%f00llfv{7KRC8o1~Bdt9eu=Dz| z8#=Qq=MnC@uFE<(N( zGSSnF=f-G(g}U>5ClmM)=+z-Cp%J*-5+oCp7oo$ca{+Ulwr0>dkJX!P_jD@V%x7Ulu7=$4h zj^Q#r!4on85`?=CcHA92!4lAOm8nd`S)0U_*~A~=KqcTBQJ@Yapb#XW$?JeO6udl# zhQ?<+6@tMwS-}$^!5@~=5_CKhJb^unTyAA`x0Bqrm;5vj0S3~bwXgvjl;99V1tRRj z!(hC*VY3OyfgI3a%Yy+vbzIEfTN2zM6EcAnT!CX9?a1By%@Y{KRe&1i92I7xLw;dR ziUAMkyZ{j#h|O33zUHpbD1W@M9nG zXFu&2KkFU;@lE`8Bt;LrLFQ#X8+;!h!oeFpq2^V97V5$DC1Dd5ejerlyCpxx_qKm+RJPH~tNF)Y=g$xx)C_o^h#EBFo0)Y4+qsEOKJ9_*GGNj0nBukE5 zFrlJ>0}vSwaPl&y%Z8W#VAhl=0Hz){aeDd;=S`hDK7|YwKu1p%y<2(uRGK%>ox7rp zs?NNM6#*12QV4qe3KpTlnGRd3JX_$R$+c|Tx_x^RCEBbw=?1!s*JTemh%`3g6D}N| zJcLmorAMm~VM^|x&fC-{uidO$yN11dQ0&#RbJ1di3p%vu(VRQios8P1Om*Qt4K9oh zT|Ih=2@yi)Sk)%gf>OIga|Lte;Xw>)8JlxqY11rIswE9Qx^#~U%%1g)9cut-3`EpH zQCP{F?hYDiup~lRZQj^{3nxxI;pT_TDL$W$KY!@z&97(w51=Dt5yqZxdk6tvg7C>l z7Jc@}rIuET>7|xB$^r)+P@o-+durKd5L2`{U!a~1T4<{x9g667 zP&~oL6}A$AYpz=Cnnka^{t9fc!V1fS7&RcF>Q!f!S_QMFqS|1xtGY^Uh0+HI$LhL&;H5fwf3=EZM_LB0P9K?Hnath z%<<1!FI;L1bfKK}$14NcvU3#cC$-aRug!Mb_r48x-1*7%qP{aRuyNR1@13$hSw!)5 zaD4ZC>d{>$eKvGyyUlpxi$6ED+}1MJ!pMVPPB`X4h(H~1T6|z17Ne%U^y!w=nFIW0tam7P0RvRor3! z6W?gC3n*)2Ti^l>c2I^iKp_fJpnm)59~HokF^ zS>S;Q<~Rj4_@E126oU_Fz=I;>%!k3L!4~+~1}z#=jHBz%BN^$)SSoLpuY{!i2q4Kx zPV$mCiJ=g_utXhJQVoD4C7Og8MwX4QjIflYGnuCmTe>cnK69fruZhiUYIB=Ae1;`4 zDFjW*kd$WdfxnC?Oko0VnTC3%Ga~>`w?XqW(ySUatI5rL>T{po*QdMFlRXVGE$dLeO~sg;JAl zOACw8f%w>|xQ0qJq7bEMNi15yjUrU1Jl&~3@3#nJq6u7G;ldNRhg30ma+ByfWGh`Z z8fMaTrZt65X9U^PtnPHH+^pnJiv*2DKtl^OI0F|?Inq3$VXZre!3}iK9Sn3w2SbY% zOr6xs^x5<+IPIX=RH@a#`czraDC{C=rP5HEQmuQSfeyKlgGl87S$4ha%Vc`Iy&ABA zkpw7Uxe8jrhIX(eLO>Y|t1B;zVGx7lUm5CHNExC5Qc$2QD=llUi!4&J4CU-Cu{zq| z3Rkq|@>(xZdr}_;t&cR&Bq_a+N-qonuC;yXLL~-G)AgvH44HvvhYQ~S@PgO4zcK8o zM&W}P`0AHb9Odo;x`zg!!M4i0E_S0T!ror>yW;IHakan(@-pPKVTFZBp|AomRC0zz zm_ZPhdjsg%Y6~#f1LrDh-}~lw!xHMNe}%aS=zbWT!WA%B!>ZgnX0eYz;9(UI8#Fc8 zpbQwWZiY3?VHRJLa7DqvfLJD&fto5spNv1Osn-;Y^*rFH?;)50&IJOqlff{Oz z;Tz+aT01U)Iek2av3AghBSP_!Nu0eC5Vo*m$e&WDz{#WXFj)QD0+w%!+bz4Q%U?#b zpGl15^&-;?1*YxrHt2QX8Afs17r& zg&@{)M?2Bf4lAg2_%FX1y0tUdHQDkE?wXl~-19y+tB;;s&RJx)UyJhC|#}gJ?9#+)Z+6QyUfq*PgJRP=kJZ z``aNG_*KUc^OOVm&9y-c@_37j?JNUc*I_n8c~mJgC9?{Po%3J=){B*V126?wJq!V1*jGRG9zD zQ@e=m=W71X1wXOGA8TwDTl#l?P1aQF$A0ZcHJxGqeg5Zw4+wz~xB~SidIU%;rUC=d z7kvt-W7Jmw4H$nR7=a^5f{90gqStuBS9MvSPzlI^bK`*^D1tLcf;EVJ6KHXNmwcb6 zf~3@fw)cVy=pQmjgEnY{4~T;SsDp!`S}N6p1!DsmxPd{qG%-kjsfC3_sD)d|g(+}? zVaH~xb%iIje?JI>R9HGz2zzA6hFs`|Z@4Q!=Y=`AgktD~cQ^)Sc!p@0A9F#3Z3u{2 zD2Rhdh=nMKbEt$mh(+>js9KPS zfO|NJVp55lh>5Gnimix=ow#_PXl$YwiY+Dog(^WarwD_l(FJ4RioXbq!3cqm7(Du z832+^d6r>`mv^a`dl`!GqK|A@fo>^~%NUn#;U6Fn213b~dC8c1$&`y}Qmn-S*8`YS zS(PX0N;y`T6?GwdAb5}omZ900qxqCia1nKZe3dDBf_ar#xtTXLmnQcNq6wOhX_U1| zn6`?4o?3Y)Ebt6r zWu5Oyn*gevAQ@^3kuEz>pHw;jnaXIM`6&{JVgh2IbOPF*0&1ZYdSP|r5-o5A394!q zaG%2CpzY*=o3R5OdW{vTqM>=AVySdn00AbFE+>|tewlT^Iiek5Gm{|!F`zN&7?D9L zq(eHS$Vg-Ym4q6S2LGsN0>PpO(j|vX(0hCPyuDINmj%##N|d(00b9A0v*7O z7_$R8S5|4NrfZs}F#w`jaich@k}tTUlc6d!lUFM6I5T=0dGecbs+oZ*sDnzV8-^%- z>ZggCCvl34;2Au(1gVc2sgp{nj#{ZOb*PA{shi5Fo$9HII+&6Qs-H@#rE0348UR#! zs;kPXt?H_9TBeOUs;^2PtF<~n18J+ds;fi7sPi(bzxu1Gda1igti>85AQTV)A^8La z3IJ{ZEC2uy01E*~0ssjA0Nn^2NU)&6g9sBUT*$DY!-o(fN}Q+=gn$ZaC~Dluv7^V2 zAVZ3jh+zc>7$Z}vT*({Vj%bp#XfbHA3a}$7VpttYex`PWJPQ1ABzE-cvFx=CW)d1CMxNqkW3E5Txm!?8NdP& zWC_8RTuR{OmspBe<%wP%klL6~etCfjEwpLDn{civrw0zy877(&>7Z^3TBevw3dE?Hj-q(bm32-kDxsB@TB!qA8dRdD zjZ&GWpr(%hO6du-)@tjim!2qPtDU9@>Y66mDr*OGn)<@ANhFIbt`hJ>;;x*!>MIDb z4oj?|Zr-}=wx%w+svDbTroe6sO$%(b*22oIvg2SX!Q+%a_ z7+1!H#<+2u=d2(jdf~(+f81uS4W>-l$_*o2P{-KD94XEP1VFRRNV6%hgE~*fb9pX5 z2m#DOE8VktQvS0Dv8t!B@BZv()?QEh*n}!+G@NTN5py;gQYDkc5t3>v_l&+;?ux zfWIkt+~D$B*|?kIZFb}O?koH3v71n+v+zm4FX^SzIk4uy@x*`bb?y-J zUbzRD1{l0&3=(hs^>)%8H1zmE(7f~Lo<898oe}6~?bv?a`OV@pU;6ukLMZoX1H!H@ z<=^+)wS8v4@4wXqA|Ge!M>YIOY+J>;o%;;4g^zGKhg(p)LYSptUTB!3=JYgYffU?gaRe{rJcWW4K`!dSRCUZIFU6M4DgqHuM`Po@wD`t2%29fBtm7REvcoJe zQjcBm@=Xl0hr61@Y9+daR67eDNyt&3jmlj=t19vJB%t6h71LxP57A- zCQLM-#zg4Zj+FpgCUd2PTADy!*vXL2ahDHuA{nU(5s7Yvq7{YI2?U3>NhPX?;L8{4 zM#)Qt?B}UiSSLbE`csr1(|)M>sO_+7I~QtojWz{kQHTIcc1HEA3Gt~`fci=_dNTkX zsF@|@G@`iP5?xD8$v+VS)xMtfuYjHENv&!%ajI0VT$Ld+4H;H=jdh)W#ba3yqSmai zbxyuxCK#{;1_mb9H|wG4Lc1!zppexK`~fT~qngJ&rg9)uT`OUg*G26AsjCt4Odl<= zMy;=M?U~TbiCGeVQ=}le*&8Hq*UR4PGBvyJC9XooSYN9sH@qu6?^eS*;i6Vpq$7o|YBS7Y zgLro;^a`bdUCD$RKhDNRjGctd#`*@lES>;HH%;gLh#(d?O>+2)tPInEmHDO1FZ5Hime&-uOIl)LojDzBN)^}`7k_=;$W z9C;`nQnJOmhhIoXnsT>GGyg74&O)0O(Xf8662Kf4M?bo2Af_CI{d};rvf5plcIcaN zNNg+=``Gez!>65MYda6ott{kq&wTys__*u?a%#f*|{~%SH$)D!~$QAEepkHutk(2iA4FY@=q56ujN6?Rp<% zga*;>tRq4NU9^N0j2<<)1AcC3t1-8bia^xnN@Jrgye7u~Ha0N3{cwj98L&kI=Zh;M z&!_J5YXg1LWHh?i89CF9_&^zrAc!@r(UPdIn(I}!y49tt z$ne1D{`o;q$>>ji>yM)v*E?QdwU?h|ltcgfEJuj{ziTZ)xcivykKdJn1T8emYsZH# z0l)mWZ4eeExBSSYaPzmiYHgHb=LXMOwERuwa9aOHm>5CHGTeDU{uH-vi` zCw?trU)867=r=oZ6KMX&eccykeJ6MkVSgCtYzC+;5<@D0=7IjjJzo<-6bN_|IDrmf zf*z(^DX2eyWC9EDA1(z_KL&p;p?NiUgv3^ZNw^R=SQX}zd<2C3P>Q=@hebxj8k}irb9#vqE9il5vZWxqUS&qKg6|FKYEa^L1*Mmj1KOsVuRaungNR(=cmgX3Y z_O+A%$%ri#kj2-N8@Vwek(F%ei~XpS|41S@>3G>#mmTGj9C?-{nGj+|ln7CjA4z_2 ziIN2wVma25fN7Q+p_dNXBZIJ(YpElixsMufe~~GflSzExGmukxm>xNloLPaM`I@jv zjvH_UZ~2!?B9BQmD*0lUu7jBx@tD!rnZ)^-ZeW~>Ne6lYi=_!7_OzQR7(c3Mn0VQm zS{R!J5t`Sz2qa*ePeB0*)0`dubDGv?neKr%z!`tp8JgH>5X$)k!nhQns5mD%B(FoB zsX3qLd6(J25s%590J@&q>7GX*2HBYu`q`CyGM9k~Kg#z>ih`TB0TGEQp!5cy7z&3O zM+d?$EcoaPGI_{b9xY? zXQsT@dpzPFJrEy>=c%76B4w9#V|ri%_=ARk9a-n9y2_)#^C^%zs*WKCF6yhhs;I_# ztbLOZq6#j-+JaLON&^tBKQXP;Isg#i9M~$Y+PbaW+O6LDt>8KwtfH*H%B+o1Aqc4# z;o7a_DyQapu85!#sf`azwEy84v>vkOnks zv~F++7y%I&KoB8-07-Bp3!$_Hu>vXUus{p2H0u#WE1xN_0o{qSZjb@k@e>U20#SPq zhk%L;fd&zf0}i+c6R-m@ivYn_5gQ-^Cr}U-khK9o0wEx`8nJsFVE__<0ghV}F0cVN z&=6`60vM13IIsgI&;XO`5@5grXy5`nYY;nt02;6ZFn|F8Kp#!vwE|HA5pebfB_6Jz~qO!4?!;2iv}^E6gMEiPyq(F3byPf0gj-vB_IG684?(55>v$i7GVf3 zRTSH*JMP+O7#y>OtQV9 z5@aiL{aO1wq24*}SbFl?aKmZUdynbvKZi@g# zP{?@yQ3kA%0b#JUicA+`FalBlOfnG3bCIKN&;^+6!(80SZ3knZjCeKdt)r~LE9|o( z{H>=v5(W@DXp9u{+R7mzW#PEWNleQc>?ht6#t2%%x(pH@@B$Ek01b02F-Lu$9L$fK z03K{leJsUm%ogR#7d}`ka1_5SFfac57A7lda$!pjAOH)H!Y&cOin{^l#1Vs>P2rg^ zF3_$ALWk9g%LD+&DlyKNJXi<(#g?&36ABVE(YA5{%$8xy4miXhfjizT$BJAg{>;o} zv8hxn(Y401(+n-K+!ovFC_k*q8g0sIp{*|<0vVtHm;tdzE7CyBvJ>*nVzI3&@Vh1d zkt15X595(>JQln$ZadLDbOt3eylxs`9mhfX8TI0<$YT{}u?GVhoh< z3LuRTcYxJ~Ai-_x2u5QT(%Q5Dj2W1)3F);M0=948l+lZ!HY1fC*_W5Us!n43P@QPz{Dn5FFqLYXP~OaSWE7*o+|A)`1Cm zpaPL#+0~H61aS<)U<_qQuP5LWfY1VK`m?F?Z!a4dcTi`oPz|8`Lt=HNKrtRP&oY35T-svk4snFjrZQ-Yv&;$G06JZQo zjR}-+3?Ob3B0jNJ%n;SU-zy%FE#Y3Q~+t)M&ou zY~JQ>{^oEV=W;&hbYACn9_R1p%20#k4cpAjOdJkjjq8lw)4`%S+#GrT+QNoT=*W8K z{Ost{xW&b~r?fHYx+>|G&Zw@D>ATu1oX+V-K0wI&=}J;KYMvRmQJAED=`TJRslMvE zis3bW8LtlOud2{WT^qNd>bAb9vwqOK-s_#d}@LRtu7hM&g@+3 z?9>zN($1w3WbN0!quFj7yuR&8QqAR~?ci=Dp8oCS9-*TV?dfix=ict-zV74>?@;pY z^d9c^e(&`z@7}KO?#}O{?(cz!>hmt}WRmX&pD6&3?g_t|^-=H*uOiYC@V@e+t2*i3 zPVx3KVn-09zCQ5`|0Lu)%zV~laXLftT$Uj3si@|gxH#~d!R~GUihL3ClwF$fAusYK z@p^Js^Op1S76uwJ-=(g{Yb~Fmp0e_cTJ#-%1U%pKSk!?)|3L_^r%Yc5O;89O|Mb75 z0_r{WmRI#|di7YM^~qy%c}QMAkJ}Ft7LIs=IUnxh#Xg0Q^*vzrW^X)y;$YtcmSF$( zs@kiXs`14LQ#aEDb#MfBpZ9&x^eCVLOAz^Xh(%rx_F=&#R}$QWUq78#P-vF-SRw(9 z@A#0v1Pm_uk{>%1)AnHTVCFpdanCqrI5Ag+_>12I5uo*>Kl+k?`lz4!tGzAL=|d>( z6(hhVU%2^!HeSJhD>bI~(EKG@k0rd%`;t!yzn}d$36GrrFWhuCrDh`#kgUS}o+l~* z^=c0YiclunPdj*z`wL+GzrWVnU;4lwDv!USmgN;g4-f?e4kTD`L7f&Y3Y|*G&>=%b ziux>}7}3E+j9?NXJW@x(kq8ndE}%HFWJMJyDW*(0apFfnC=AY|>Bh|hn*js%1z8nsvp zs85|+a6OKE*)b!=jYkUU4cXVEN|u0I_Wd|9W?94(pc3@D6YFEhS3y7kS-C`yBLa{7 zd`j4CRjZLjyV1(Eaagcp6PM+Pux%Yzg($71Wbvi{#mN~rdgRSSWZ#v411~X2m-t7R zGFOug!1VNJ=#fJx`Jlw6ksjBRRJZxTZe^vQLr2fLRYF(P=_jaO{y8o0*RkVLdRx0T z?z?$=Cm+7jzY5_1MB%vG4tt=znVf5kJO+zeA%*Q$P^u&9s`~(pj*M{N!UG2qufeNI zJMT30*h7$@_uguxj}>KuNTS*bw8*aQFuW@w!TRgZxZ{p9@Wg?jQqU_9i~K1C>)faj zyM`jP5u_G07$E?vJ~VPD5z$jGEE9q3fulm;V^NVpT#?JZh;CDf0}cSW5x>6(Q9(`~ zW#Umr91c`)x#mbC@ya4CY{(-Gfue)M1dbU0VY&{g1PxH3vLvxYFEvxhqS(k}aZEF9 ziwMm-K0PHFH%m3uEjL$^lO^Ts#OzhT&>FFTJy+{XA?a^q(%_dbXwRJy0$(TV~i-76;7qZ;WU3^ zw_J`%f)%)QH%3s%V;6)sIuq(B>Y^r_%%Fwv08UomfyF9#L$sm=q>xRewdmq)Kh<{P z7fY=TRUFAx%-oMXR>=bp{4KcFuTDn)`8vz6tL)7OL5O+0na$I(<}OFR;1sl01Y|9q zc|N6WI8il|K88re6l00R?GWo%J9JuW%2v8av8hWY(rW0w^X<(Hfy!-cuyduE?3>Yk z%WZ3U-Ve7;-MVGW*V4MUzL8etyYHq0S6$YdsdfzU=nkdB(C#qGdRFlav3l}>%SKQw zH_hDby;I+Uc<%ZdvHM}#c>73c)lg4;b&5<_tWtNMgB>zFMCkr|B*5?f4LsId&UUM_ zz0y*8!r`hPXaJVK9SUk^qDT)x`QO&VI&&TDO>3hqgG3 ze)hYO3%97clqt(gld2&BC}6_{`fxMXp=2bLg*(b&?*o7AU>-A;H^WfTgl!?=13NiL zLZ)k6UtG?+L<2;tQN|{nS)&?FWJx*B@kDhrB`O(L!0I`$m;3AGFo~%V^qFdv&SK}d zssoDh$O$l2o&#c&4|tv zds(EaMj}vE6tGeMhXj-=H4B-_vn`IKe??4S1^XCUx->h2UF$^K%GjpSl5hf?o?Bg; zBNaB*wVW-BK|gDhN!~U@iB)WSY)Mtt#x}UG1@3>C2HB2DmbPTEt!{;4Rvx}}w1|~y zZxJ-1%Vbx2lg%P`iCbLHqzt)pU2Z{5=G??VK&c9dDJFXtRSt#7r zqSnI_Mkj?U+)sZ3n713|aAH8b+ID@HmC7Y?f)Z#f3@b&ck*e?jH2l;SBSywCHtvXP zeBB%8I4es3W{P`(9OQ!hR>AZ&DU@wRH74^o%h+|QF0EV>!`=+WUluZ!w|wSE{n*Q7 zZt;t!eAstdnPetL^Og;u<~6e!zHV;tnA@Dap43^;8Fq4>^~~lm`x(k}KJTFIjAuh1 zTGEGR^x+)+=t4(&(wmlZr2~Cw1_eSYIT`CEY6$6ha$41h26UrA{TNZdF?U)K0If~! zOjM_O)w}+)>k#m2@~kvw!Ok^Xd3|gSd)m^#7R~cw{Y(%QIEcumc9oUAY-U4-4YdM_ z1tBBt_f=cm2=;ZhMPr91%t00>a8w_7p^QopA`g)P_qgrtrE;4a)>9GIPiCj2M{Gh# zuUzr}lka`-GT*o>`hH9bu5t?`*kl_HaW7W{g%Ln`#4awlakjU8G`X;aCW7*Bb2}j@($EAeCSeqqu@CTpKlo@0FY_Zg zAl>Ez^$C8m2BElu6Rr>i3-%5aY0RMsxi!+-DQ|BhyMU!cltL5L&;%zyfr+T!&?dWh z0U0OH{O0d`D9R`WG0ISe+WiC;gV=J~ud*ncq@g_ny9o*)HJ}KFL_mUIScYY|19|I{eG@%kwaI%g7!S`b1vr~zBZ!k;h&y>qmx8I8OnHXn?_Lt_w7kUw0gfiEZpn^1=TpwI+q z06#~VLq}k^qHsbnB*SV0lt-vIpeO}pcmzvmfg!TPqPVqZ3qd_ZwqS#dNoWKdGepE$ zzC{E=<+BM}Km`$a1QA$^$kH)MM4U0KL@QIiSQEb_48<%XMQgi~lTt-AW5pvAh?#-K zOOwS}v_)Nv#IrK8S**a%0uo(RMP4+DUo4noOvVEkMq4BRj1#L`i$-ajMrxEOYrIBm zv^8c7MOxcNZ~Vqa1V?ckM{+Djb38|MOhxOGp4TK!6X(0R|Y6hl~M@loDX5fEz#p z2$;#CZ~+DwyB7>e*zrgSNP`!E0wdHrl~g!r12Bnn$s>RPFxU!&?1~mZ10mW03}}=5 z>q@`M0iwW2P((_^$$*>e$D>S1_VTr2tgsxAfdI2gl=^@exCEknND>%|8Q6gQyM&;; zHMI;F3D|&ItANN;!nl-5mJG(OZ~-Xj$jC&!H)^@5G(9KiNMq3eTD%DcK#J&lCi^=~ z$t$&@ipnmVO6Rh*^iWM%J1Q(usWuWL)J#p;e7nbN&6b4C-6S>tvWk?*T)5oS&E4e9 zfIJYCQZeAXOyLa9d7L%FJkHjGO{3U~-t4lu%rPCfIDQNPoj?K`umPqU3I~V*2YgOQ zvQFe|PqPMbPbx%`-#E2H3|P*aHSY04PYX9oT`N6ABj4O9a@-3dJz(l+Wfovl?gs zGiU&RvxyiOfoFohq1b>6_z4WCfEDer1jW!T(}5INLMXV%osdWm*vKT%zoO8|3joU? zRk0yWk2Ay3XKE7;(29g$$O<^mqX+@U)QT#7%_|ki45df^XHuy%9W(=dQ#n1(g_=`4 zy;D5RQ$5{NKJ8OK{Zl{$P>P6{ zfQ4*SZ@bfDn57>5Q~`JePDll2FotSSyd&8TJjFx*H;7nsMTDOa26mklr>NL&v(tI| ziE2pKI@(fpD^XZO%BjGFv{cy7a0FZp*F&^biPcjeg3Sb*O?u;2SbVnO^x1ekRiQ0f zqg~Mez}KUVN0M3EYGo%s?NprIiWi6gK$Y4&eWng*0hO&*tgTs0{aUAGN2?uMcLYGQ zJ=?T($EICdx7|0mecRkbNx7|CyS-by&0D?QTfXhv5Wz)ZbX&m1w>{0(JRIC0gI2^P zGi6=eFKb-Kg)+#E+!mW$%H1R`gV!=v>c5Mo|sj(Un}#?F!L7-AXB4 z)n(nvOHS_8t6pZ*-t86N@r_nEMc>u<+1d2n4wQl} zm|yzkf%?5)o4A6%V_)VVUtlZ5=(t}29^jq-+Y+{?Jf+}9(NsMA4 zPDeJd;wyIIDkj(K>|rTp#~^TGG^XPJ{RQGAmf=+Cs=;v${Mej?Jz7AP7C0HDR{LrHVQMif-~;r zFAioejwfqd{w-W;)5a(pk=Ag*tJ&ufS?qZvG0w9pzCI;s)Fy>vN ztV~X3bzW$OZs^Qv<+zfXY8HV14=_1zb}oIc3pHPD>NCPz>W??pHkAp#ER_M!e zXq9H?hjwO{ZfSoCVu?;kc@_X0AcA~0#BOeb8ZgWJShK3 zmR9P=f#+_^Spk58EI?yb3MJON zfcAkI0J$+mLi7x<(y9QzE&29(>DhZ!(S3V`vsIloh?zrY_I7TxM57jbQgeiFL z_?2HEe*zRQoCjZV|3+)Xwr~LFtTr})`JMtOFKD6&>n+Q1l~My}7=@B6Ng9}eE%@m7 zYw*uiq``h{y=71w;o7Y|3Y1Ai*t= zIqdzt^?v8asp{%qUEN(>U0qM#YprX!{m^rSGZLH94eS;8Ua@q#3I8}?6Pje_j!$TC zadO%Y*!Xx(^oO5YL5m3>6yhNpa?SBZL{yWNgE^{btq1ntoLqY&W30p6mHu} zYIiRC$CE|shE>@!+l4(SlSkingXVR(2N<(v@mVN~> zt5z1e)d>kl#<^sNh0(2rGZV%0gocw;sZT9iB1nZAXM317SZd8{p^k>bPl1igr(D?K zeS(*K3kXOd-hBPH2=|^34Oe$qUXyDGAC>;Va?Y_b3n;K$_r`B)qFsW`d>GN~rSY%pSv0ZnMq)a8N&SP$yYQ z9eCJzEWsXvU7lXw2(Zkha%gHq$S<`(|h#|tOs_5e%TnXe<;;x=pP44g? zygQp<@vc95Dx=;n$G-2FXlng-SuE7ez83KE#lKARk>uag(MQC7;(!jgPa8^4@%$af zPJnpG&rj6P!$r?cQh}|V-jvx=UxaS4Fr0@D{W7xyr>S51Cz=}60)=R9bs7;cNq{F6 ze`rwx%Qd(4_kJzUKR357euqdI>|cgpE1y_glDr{pY2sASmec?}>h3i4ym2R#3N6u=(k~6)CebGv6iX-z%4horxd}^pHvT z%Uw*+JK}$1C_IlsKbcU#LjT(tfrgFea14zd*z{YRPuv-0fgmK$I*02+IX2OD>r~~%66r)T7rfhtE0(1))$E=`kye(KDnuW= zv-QKH&;-(Am2=Vb|8z!7qOw&$D1kC8N7EaYE`EZ?RC@!3C~J+@9yqzX8F-6cYhnT4Pvk-kxl zIjDl!ElaZI@qZkOU>eseBx=~$7$)BYEs5S+umPidk2$mVU{R$fSdGD*zMet`P-@%X zTNmS630NDk=c}Sm&1)7G4g-a~@BdVx+jWRI!XB=c%Pao;>8fz6=)R{4G%-OK^0~!r zR}MbZI--u`UMCo%n#$ru81 z$61a)4J$r5psiF!KoH*32YNeBFvucy2q4RdsFo{gBvtq^m04a1Ilv~{?-?s{nc@!$ z+-2Kia?#^z%rQQckp0xS{(f^%5j#y*7gtB+^A1D$u$ozB*Wn_oy5)Sj7S?FH&er_Z zzoszFnrek5c)^->2oU^HZ=r2PPwhx{jm^56oM&U=XML$OVnnxq!=!#swcsQ7;9EUZ z!vR`k@oW;5B7L56w4$Sn>Vi~xz!;%XU~@EyQy=dHsn6Nv1WgRb)dWudmh}`lX;X_9 za@#Xs%QVMCAKw>&%|O06q>lw}<|Y2tt&cCDN$@W!kO%QEsjz=;TapsPEK$}}lMq-% zMgskhjrz@hY}AT?8vy2iZPa%EFB`ReQ|MR%2{q(@ZPcC*u(GK{=GW}%R7?3xHV;aL zd@kRAZPdRCw9G5#i=-p^3#2{67hY}DEoD<})mr6{QdM1FSk*hAZl&XQHi6R6M%_mL zpy~EH!vc9$7T3YA@@WJVl9R95)+p2tTp$1v(9viXtH(>_Oe+LJ#UwXiF`Xr8wjiZf z44teM-1{+;gKe4!Mc~E5Es|rVI)89GnkZLl3x!H^EY3ESMt}Hj0diPOQYlpo5^F~U z7!;0ECEwIW;8!Z`&DC%6S}iQ(zr%9Q?D8rkW$ZHV@9Vx^BpgnFPXIoPM`P4h6MvrD zK0KV;|Kf8WOw!|vymvjJnP}?r7j91o+4Q(_^C5BE|6WNGjINkX6^hftu!H`C(4Hn7 zRj{f!n7jb0A4%gc{}qGQz#it4CNel2O<|j35mT2$xSNPUN&^LUe6h3i;8TWVdUN!! zl=;SMFq0%H(?zbLi2Y)rL;`^`t@jMa>q%fCv`9{G>!o0k3O)R)I}tHlP#jGg8H|GPtGM>xzNG3 zW(&qJlkfcpT)h|D2Sk0FWLXB4-GZxUUHjwAcACeQ-T+W!x$qzKKOOn5#@lqY(*0JL zJIn(_?=Kt&gBi_b?YOKrd0Pl2Cukj*w>DOg3GV-rqGnod=~uDMOw>k~1cfpx?pADl zN8%dbaQEe#pQVY&uMk|eeRd=ZkI}l@(kCY-PpvGpEv%py9HR|G-{_g zeer;9xI33+IFNJQ2WPPL`=a*-sZsmR{mS;-`>zJKY8R|&r?SWMC)xnRNF zp+w0d>fb*))Jq`Js+)y8o5EJlZ|mEcILnC}P@kwPu1 z5cWkB+=uC9h#*bg3_0osvv%DxNU8?rwJ|vGQ(mY{;xG!`Lmb+wKF_=t7J+ik&e~gq zJf#&JK99d^pJ6(9*s!{oY;>8wu`E#AL`#}YVVbvDYtm^`+#l3JHIpw!0Oapyk~t$& z8$^#sO4_+;opEBxtV(2;PHaCw>OzqL05FVkDV}Gg#M0NQdHta=#RaeQa32@@?YJCG z5yvEnyY}SKVv5w$fRzoovp%J&d01ma_h(CO+xb?)M$|Nhvd+n)bp?kCW1rDcA4$Ru)g*S zaf}giiJOHJ#%m`tVm+-HWH3XvWlsoaai4=__n&=wV1UtG)M@j!+RKY$k7+#qoY65O zunTindCT`(KJQ0f0+2~@)}B{|>?)&D5I1+k^_ubas7@+h{+D=9VRm6ZUzne25zCWuBL9q{%VO{M)p0WZAO_j~!XL<)uq0?MnQx?=~B!3rU2MLdm zCeZAg_6iLs*4?(>%Glb@_)F}-LtSY;y*OKPA%9)`RZ1QnIc4C)rh*v`OwAEEGa9uv ziY$=sO#I0yqEegwlWM#@oioK<;#w7X5PYM%4>@5Ns4`Zm`;hMas_pj?=tu^qp4AAV_%3;iY z#bO-pnwjiK zl4A^$TS4VD}1Bt8Qw-SDE!+gN#XfB9W~{U7mGfPj1Z6``zDBz(=E03!x7`rFDk8` z`T4b3MUi_i0W&K1BJ}0xihPgkII6sx&on1#|AZ&=v7DGIl#UO2Z3c8CCF;1#+18PT z`-I9gpf87Pa{XItC|;tiMTG`NN`8~*0l!_BM-MH1poqhk_fAIEV)AwYl{4cAUna}8 z%{exZ)b)Kwe54U=e~~ABuX_xp{9cx*LKwmT?V2uq(oR0@UJ?a)7YuEwOBr{cU{V9P zfty=?LV7d&?*QP*tJ~S}Z)bn&vZ~zw+zqYV<9cfzh3t#fSnW1OfraZW+) z`&OZSVo#;C5W8%4L}18#w&skx_Fp6}LH>}E5C0{SB{vF?@ zl9#A#C~@iCnv+(!UE-R@RAWRy`VyR0a7`Fhod@gfCe|Nv9HdSld5$Mnzi$o3ez- zk|+QOBIJck!`z}ziHNMMB%FPfU6^q+!|Ys@1N~?bw+I5C8TCT%t?KqfS5$>5y{MTabyGV%##Wn@`tdSfDX#?_D2;ORu`YjS&wZx~# zc<9K)mrvq0@Fuj8@NN(%0JQPDx)S=<5=M9v`bjt%a1#fF6Gy@1gT9Hm#X+;mzS9qU zop?!0!bzQ1iC=w_*47BYO-Wm8N!t%e-*b{=5dLGM-eyVO0}~;J0`hQ^k4Xf5yONQb zlUg2)gjYGKzps-h3@NrAQdQlO>HG-J4ASW1(kN9@*c{@i*3+b9(s<&Ce1+3_Nz(-3 zx#^DXVWajFOw~rYR(oAiUOdbcY&N{AMH&d8- z245;lG&ED6G({CJ3q36TS~<%;H|rI7<-DHt@ijk6n(Z!xD>LT!~rb)jSPW>>CfRzGIdlBU$3WHnAPHm_&3l2TL{`Zqb`T?^;Ace_Q977U9N zj2ae<`xQ*)7EE^+%*N#*J><`$iXq_GqNtE?=_M$I7jEQIuAKOO&7pN)MBIgU6z-pp z?Wf}aU6aDRoOk2E2algBg$s|XKLvS!Z!f%=H}!ZrV1OTOEm1045gK!0&IYC)SA+W$;cN}1SJSKOrT7Ltn7T% zG-?5bj;u1}raVM}v<|h%mJJ_w(y)UC1$__{IwmAVROSyTOM-y&;S1m(=}J#DaOZ;Q z6hM1Y2gX5DVx3o^@l+Z5>yxv#1|^XS#s#tqPno}Kxy(jY#txd-0*V?8+{sfrBmHU% zL~}#aUSBbZXTXd&@{!o6e!Q=q2y>v!k}Pa4{vm+!@u?z88Z~OCrb)GO22$Tq0}h4O zud&tk)X-oUR9wFXqj77`y(*hPXw4V;wHY9k4xkI9CJI^=msbw`h0sJ+n*pg`=Ba($ z0LKGrBWY^8{40|N1M{DJhD8Iy9r1pz)Ndb{P>lmK9qXUbkk6lpz6+Ms^c3Sk8r7u1 zsjAHx(v|18jZ9+IGmvUUG&Ixd68vBJgf&fOer!~>sMGLJge)A*EP!S!4|+or#&T0* z0RV`CY&9#Yd$Ca`Jr26}Z*0kHeD+6)-$6UqZ4A0-ZC$8pKttpI)nbWIclIq9sfoK6 zP4hh(n&>9DVF#^Xu%loBIQs*uF4%W$@~_&i{?Kfg-PTc6)4f+Hf33ZC<;%#8Di+G)7_u`ihK9!b?7LSsr3{k z)K{JMSv^%X@KlCMcfAWhmrCd&k?u_hXcETPNrsaL$;(icsdeWhbW4z<7Y3l6OrtI} zb?1xK;>kOt@-|N`fM+ghsuLO))KKvjN>bwM+kVyP`~|B6YPO9Mc#Qm(iK!yI%d@0` z1GTTZNcX1z^n%_-nEN{b)9*Cq|v(0aH^SJdm82t7esX~vFRJc;J{3gZcn8cN;57? zNUP20LCxvwKBPcTfuSxzF~%__iW3Ktr)L#mL^TCPHO63#1rQf9kn&<)%IQmd!IzZ$ z5!eOTF~1e>tQU=O$XRvHXR9Mud^Ya_9D+1W--o)2(bS+le}A_4VvO=E?w9s!S{X9^ z{cj5hvzslngYIH3gPlE1-Mj&wSxd9kI$SVUnAm}*4kSw&2!fP;ah_IpoqwrAAv#6y zrmg<#kFE>@BTCHJZJ{)JcB3nF^K5|AFw6wMx8`%qwn7$$h`Bn9!$<;jj?)V0J)Jlya^;u@09 zUMrvrCK?@E74A@(r$j(JKrY>AD!`;C7;`PJ591d_GkW{Xo^eYp#rnUkwMa)|x6Ow1 zAT)Q0Z=EDTY}!hPyf9!q=RU{lqa7&!?s2H4>lDRv=H5JE5;q%UeDAaVq8#yVS2ev2 zVLZsGxbU;GIOa&9MkU-tvllJC{hf{5Ww*)G)^rsJL6wRp&jFUYilYG>2^f_T+ln-a zQ89P}3++3P44F^{dV)%ay-mK^5Jy34TjNKSoqwpu{3yo7z^Ul^ag;zVZ+^~Xy&w2- zY*)`JhVHmCN(+=XO=9qeO*9aOF%5y?h}D?&m^b+Zb(F>y)BeQR5PDjK1!xRVCsGvH z?QPxdQ8vU1JSJ|t|8&7!TId_QC#$`EMwDvO;%)MiXxP<}JTNry+EVnJh@Dn8UGchD zRQwLEN}NZBuJMaj5aX!LF#)OG+ZX5`Xj2`9;uQ8xD%Y(YBy$>=Cr{gJKpKLM!WFqO zN?Ym}ZT~9$_p6NS6C=-QyTnhg;JB7gr#*8AUQKOW(eukkM|Im9Ilq6^ z7Iw}VANKYW!|!vxve1jup4EcSc29ndd^~GvsBmlkno%6l`}U~VG(nN_L>PZ*ziIj4y>^40^y=-(Shq%ue3OFftv%~PsjWo(!2$|Ea#~*-|8E&UwO0r zH<~UtquX zyGz}Bn%#Sc-1`>Y`w!d){)4ivIqdd_s;x9SwP&EP9?Ec%J?7JO_VXpne&y%Fdm~Sslpwm$k$VC#n07m0&_Oq=Ws!SAr~qzUr;N$*U4 z6uyMq{u{{6bo>AJ4YGLPFsx}F)M;s;Oi;#-DbDoZXFmwe^bVRP>Tje=u*~-s{-=6X z)Zb1e7-fkP$jxL@u^Bu4Oer`kT3k3JJUD_hI9`#M0ihCdsJ~GVaMWxm^0as+hQg5S z0PHQrk+QM4jBm>f7IzgANd-RcuPyE=LEoS;fL#HlEsTf&LUmv?T@DUAII7HW`B0-k z#LJSx@QN;6DJhta=6xGYa-m8lm(50om1a5-B7*--?3e;B8ZX5cdh<{?z0%vUWJV)z z`jx~WwD0dX!O+c)O0@2pkrqvwhD79NsLj_uW`k;rSm&yfP8LnEgzN+>Ybr8<56_?q z<8}AfZsp5(4zkJXqq#y_s$dL1MM0Nr-NuH3?mfV`nS~OAgti5R%l1~8?f1>jci%LX zX8QSY>n?swWd>jbdwbh2EaJNa_1 z&ylJOVum^bnestbDlDbXsWNyNV`K`w+A>@}PG5bXQzP;odPLeU7!d4}bof zWhwt#s=s}(C|Nne)dg8xjSNHuxbw{-fTnzB6<6rHTNo4f7=J&HNTfZh*(-t6S-wBC z1e&^ZX@vTfD>(2j+vQ*2zr}mF$MnHG2&iW8O!ZB|*^N!V z{tEweG+S!B{LhDFk3zsVe!Sg>$xto1ofW*b)+dB>)0O2ySmIsZ-&7Yw?ygTQOuB=Z zU*$EaGw`e+5X$}o_Rv@GR`nAT5VMjbN~*#q?8_XVOkhu(qbBd{kE6P}ELKFSBOk^iz4ArKacaTQf^Msz9Q( zGfEk3ST+1qbw1*uTft=6ct=$Y8nm;TJP+Jwo4{= z59yp~&vkxX29v~>bzT=q{M_q(JE@m$O~>tJ`q1NW*)vwn&}3R>RKjCd=Y0+PmUjiH zFLnag>kxKU5D=Mil#XRjE73MXKqe=Qz!nk#wTxy7){-^{0{=(ulKGF^g#y4rc>76UE;{AOrs9zp9D3CP<>r!Cx$BLib1ZCcIO&xdM4|F3Vx`LEv-bZqnO%j)D}3UP&7GA8JrKyV=~Uxb+`u zP|@4ZJ-4saAg3Syp$7fg8A*UsYxeg&UcXX<%;x()|GmH1n<>&9c%=qCT%Z1!9~gkY zQiFscdJcrj=9<7coBvXSb|P@7SS%v(wMnR>NNlTqMpKCTTEx)kvFye&WPu44L6gM0 z@l3m4cM~{jCU+CLzJm9Xgz7nX>xTu(Osp~{2U9AQ&Kc!p zc9HfqhWkDtcaujM0l}yhdYaz)$BAghgEGkEI)eZV-hJ2!R>Am!G!mJsxr~Wd-jpQ_ z#2ZiGlj05z1<_l>(Z-0{zSfM_TW45|H#nSyjW-INLBgugSYCy@i1BsDNG6>N_J##T zbmyjgcmI*r&wPUj)irrFv@LRM?3tZ7}S{`fsUo_nvuVArB*di9u1A|>2!DIX$0TL|0aHu z;mkL6ywp?M5rkxTnMdNYB7+6=*m7+U?hJ4x%Ue<%nR0sI8C}qP7`z}gTKP*n_IJ3z zC55aqz@wKM&UQK}i(fB*V_cNT-+(D^d^j_0nHaWmN;J#W*J_K|w&@ZORExzaUP!xc zX-OgXMeudG-(EqS10A1tqpolIsq*cdaho{B$tM;A_PulC`iiV>4gU-vG!pBRC@{e; zjzzn-ZF3;pO{NEH*bi>R{DbngCA8&R0exZ1m(D-t!0a-y_xq)EP1{csc|=hpKvo<{ zv7?zE4NW~FXK!u()cxanissmn6gkL5l)QY81+II3_)^5%bhq%Qm-cou_xI5)$;YOi zixK@-3(>?IHmU3Vbj=VlwFtAqHKjskg3%!VC&1G$CGnHOw;s=@Kh6q{cyVD3I-prB zAQG4sNirqOQOp2QaV4BZ9ui(yyGo@zScYL~7eV2?fckJJ_1gYt|D z562B}_$Sv&G*T5cA`+^}Eu=c8_%m~&S+h=e9=_|xCJ$D6IgBo^QZQIcOn%?8l#8VV zd$Q*Jr>B*267y=oo<;3%?%9_(s|ZCVxNe&x(ioqs$bUyJ##YW~0ay+cacL|NIi$P_wBnJER)Q{tOrmxhL z0E14Z(GfONz68b1OtM2TJyIaASO|p;jfH%;^J@kBWFqmVe>uB-d4fHa8W~_UJBuP` zHojAyn6#yeou+>>I|!qkWaPG=+{WDT*otba138jg9v@RIv@nH^Msj5R>XWXHLBj$L ze9ZJ8p4dsC;Frw};++##gK2YBUSB3EcF|?BeN_}(fpdRs{-Q7JPK3)%EUrCtGtzMA0Y_ zMX#Me0T_$!yQ*4>{Wi7tx22jBzNMJLgjuLF>(@kzEnbpg=r*BYQ?0`{_?~v>-G#G)!n!u0ejmTC=@{zd zcjVg~%t4)sL3A~H4Rll7Ju%?bP0@N2#q6aL;fJ=_)}Go8{K$eVAF7L7r8GYQ>LYw zoL-KisGWy@iw%THGW;e+4-ri4(kHr8j>-}{EcVsAludQ ztOHZ%$#Gd0Cm06~5%7QH?%QBD<@fb)wMpWXbnycnnCe9+;!{Fs=ptEsx zWj+YVia4d|N}y#6fa`9E0+|f`=t!#X{5CUq;&aW1`PoAL+DCuqk6e5{L^GdOTUqiJ z6e^l$kAg~4iwKYbWc+^i@{LZBKM-|nXKZVfTX%5RV2Z=rV~P({yC#Qf5pEiQU z3@6I*1Sydq^L7j-499$K3Tyx{G}qc=R0JMyWQ#>b_UZa}KIJqe3J7KtV>sT>2Dovm z26-VUnHm@#%cQ^F&0lsR@$)@`8*(sXT57*n;K}^qQ5vhGS__<=1VXy!E)3V81e`WdR`NHpR2ENg!gPG z`wPgL0icg96mU0T^zcox%Lk0yiBgE7>A2vxV~e_Di@tIJ?uGD;;9y`3qE`{~|F%En;$G>>Fu55wr7(NHqL=%WD+U7({@Ar6HG%4Pd5DZUY$ zr1^L+)1n_j-4Q1LHLM^8Gl|Ee_nTj*CHjCgM%7>dtO>oF2P3Tsy<;bIMkzvMhkaI2 zwz)FGaU=pWjl{G{#+)R&HOrraufPQpe(5`w12C^a$ZJ3$Je>zMZzr}=$rafyrv9Fv-aIUjL^k+=J0qKl(9Q{B z?wS>fH5e7nbAgPQ72R)+9)%KN5bc}ho9JgBhqnvt@J(1sOUOowl|1IRwRHK*?39Os zF))bYeSv)F>$DMyR^{r;AQz*_69FA%4ZulSQl>G?iTGNj2Ej4kZ%KfJ2T)gebzGo! z(xD&vLR(zHRb9!BB;KBS{C}$4T7*;Dj^F0w#GNhqw)>Fxt%b>ru?Jotdzhod(4Y^v zru|ZOdWbsR-Mx`ob+F!uGMo>JaJY|1i>SkwFL3V+m`17Cy;$*a# z+dGA#jY0j-JN>y()A8KR_(=`Hwmib?3jchmK9eAN4qD6Pf<1UpT%cg)I80Aynq#a6 z4r%J|10C%+hL;N2u4t|R9tTCAbV6uYNLOYjaXQ^@lBA)>AWfPF6uf4hwswK+_&TH6 z%a}pRbba(Ewn6^F%)@bzb|=TKJeb`x8TV~50hJWk3$dIBB6>)pxVUDN+ao4D=CCp5 ze#6UdD%LENeS6F z0J6_AG!4I%r-r7Bidb)fbC&E~;@|)@Q9%5oUi`FQ+D2%6r7~ZhG-APB=t8$!Yc4lM zsFugFc4L_hYji@)Qemc&Th~$e063o70Hd4*Jrp} zl>vPYMViraD@^-<{QiSreA<_4uI^BqOVV6i-zbA}gv_|Y-1VY$QbdA|xJ_SNolqpH z4fNSXI-CWa-SVQN+>**JsP`26j$pBIE7F=`3BCc_eNItp317B|xQD1;!f-j#&r(ye zs_9ryCz|0={?L^j)~j$o^ru&6rf zuoMz8AiJw@b1V$IE84l1*^K7;W+~U11{&})l&!!u+^H_gqu7@9h<#sUK_p}{V(e%g zC3K-p9S?awDIsWsEZ$sNYl+)EU2TF^1Nw<1ElRQ7f?#>TK9~oY^VO4sDL6;Ev=F%I zsDw~^BKowaE>4&CIMz@8{}FHY(_ar@T_JAej^~wXl%p^|QE3p4yR3=?UKUX ztBE3`=NXo^$G(JEPY%azp`WSU#*<{rDr8A$d_+raIIzs@VgH9#&~)HBw+G?%B=1DS z3(IR-uc6p45k2A8$DI+7?QO%e7t)Sz{F$g4R6D*8E}0;+;oZ z53ilMla}xMRzdW}(5LFPybh)%BaDoegFW8a$Z7)txr%t%txA82DIT|K1}i%E&ia^4z! z<@;NKv*HA$MLy$+-k!D{CG?}^_q6s7qHp1}llMZ07@eGSW<Yxq`f-=|d)(iD z`>~#Tpqrf$&B6`U4fP(JlRfY8Tq`%rnyqb2%hBso$Z^leM^o8bw8gqHp2x7&fq3T9 zKD16q11CPJy%KmiLr4G$WofgC zTKNX%2}HQgF#cjcEy6g|xEE1o+;xa4JYxJQp>H{o;r?X&eQc`OLWB44WD>_HHo}xy zA2Mgfn(FxLGh8m`F zCb&m$^o4`(d3Hv(Zw_#d^Uf8ggT1%9ZyK?A;VN+fB(sEZzHl4JfIsqHrM4^J7&6#9 zLjpgU=Nz3of1YC~T(}HeV0~HmfG2LeW0mgT*u=;=p~}S2w>YZ4zKFww_ z9zdsqts=&TGP!ly+C>NKpXFEoKxD~*On*+qYo$s%IK)#wCH79+2NijwS6J9&Vhv*y z{R@sK&p46{ntkzPf*dgS%2xpC3-{|B2Xj_G5a<0)A0p-|UHvjDE|4A*GvtNL0n>{b zhQMOm*LDW^cvnXkC}d8DN6Wbi{fry%;j3Bd`l;M7o;ff`XtV9Rin7o~?$abj>NjSY z+BZ9d4JJ&^yjw#-?&Y6xe+S}R)scatIQT;db|kI2U`QrUl!n> zQ_r`i>4O%ki;cD@--NWREPXFU=3V{$vfUTdTYdHY_;c$`eF7dL-uKz%x-Fap9<&;a z9XRCv>8CC7MeaQBb;r0O^V&2F)3v1Xqr^eyhXWS3C zB#zOCEL=>&KXHsda@Zu3dSV^%Y*R;HG!PXZVKi&mapC)F@3Su&0gC}iZNPB&r{nnk zaM}IGma*~LQOaZhox#uu&;K%(TpQFNHGLf(D zP%Ih=95N!P{6^3T=_OD%RRkMKGiG_3y2#v5bv7({HfnnI$_twObT-|8HoJW`_wQ_h z>U>G^e8u$qYw-Enr}K^e^R4ak?SJPxRKIs6fA5?AJ`DanTKwy{|M%JU@8AD^Ur_zI zlKgXH`sWoH^ziA=Q~#gm?LYti{QA0%4H<{!ioFAZ{Q)Y=pi2a(e2MeisoG=_alwwBSYh3 zdJ0SGpGVL6M=$34k^yF~l*hu5r&8>v-1#S=q9+5wr>21`7x<0R4-Qr+iXRo&kL#M2 z?r#)#Y{kIt*n;`sAm1SNNT=p4jV6W(4Kj|3H>WEo;m_8W#@qQH!7^|U11D*3)dj?U zsr_Gl9K#{M$_FkLFOalD(MBK`Cu=YKcRcD%&+$or#X~^+7s)cHI0FzuxhTiU^av+d zERPIeNRAnco(jhD|7lTpfF@WdAWlX;nZa;rUnPT8x50XS=|C-q2~!*Zaof|#$M+20 z%Ef1<1L0xVit$%Mdo+vUaXE5uCW!?wbfxzlCXZH&gv9g&aF$Q?n=HnX2}N3M_1Yv> z!)MWkcDPFe(So$2&$E}x`};AvnZ{9)qEzPQ)Dv!x^g2wYxNNu9E>&Bj(HqMP46edS zo%X3Drj;nEZKjX4TWu27wwblNq-`K9L-GhV?0e9T-k!o{qmarg(_?>9cU#O#}FKR>4Shs8`hAikCfdZkgo z8GO1ux{n`VtvB{xtKU&ATH~3xS0f0VdD6&)|5+1DWWS-ySm3?=TB_VJm8>iiAvYPj z+s3?gamFGcxS4{jrtMsoB58>*Mxp^KI~@G1j%y{ruV@g?h9V@J*oPWHz*m9BS-?q; znt>yOVsiKE6!k;AA-%lh%>zgGv$jq%mg+~5Lp7^JL))x>R1PiUD}dTinFcVs^K1EC zhR55?c>bjcf{Qg7*h*-ZZ7i8*vs#uApT`Kc*E1A8(=W@hZkB7ChTBWv>-mOLn6UJE<=uJT8*Etuk#eAOUfU(vJNISc1Y08n!1O(PFW#&S8 z=%ow>?hB3YqLYy_NxE2GAtRAWSz!e>r;s6hoX}q+HIneX!8#~|EKA~QD0P|r(fRvZ z#e}F;!0Jr43R87i8k3VPW11eF2oo8CjSLf|7!>!3EcEdq0$Z4jwG0}G|NAPdvOVi- zrhekVe5Q@?dZcNCVJaxYm;&cs!~KimU(fNCxnDR@O}U%IM)oo>SrF&jNW!h8pCAGa zYg8uOomA9l1i7L;IaC6|sr#&i%Opk*f7aACTr19QfAv7d#ZN>{xd1fr)EO>xud4~| z7iMxu^G~QI8|xU69$T>jp{aX!VWwWkw(tWi!Vv!BXf_Nxg(hsArzw$ZjE;1EWQ8NE zIk7@!S}VyF>8o+EfQqvv9!XUgdL18JkUF!5p_$bT^|QNV0Zt_L+22UEz%Z=GYFUEJ zi%5oKT2u{XS?P51C>H z5OF;0_{%Hf;pIURIc17dki#)01A)=6{YZ@RZUZXHmPG#$N=7`HQE8WCK+`#(G2ctD zqwZPQQh;%`aWiYg2Q&}Od~S}DJF9ViPQR#Uq#@YAPqQ5OWY|urhDO#))aXt2uVb1a!ez7 z)G8Q^Rk8mhpRTN}Or{W#41o{3e!SX4!giZLvk1j67zW$fdSAxoX@nD3oKPv3WzX6Szax8%vI16|JFbo}SS$8O811 zF7STqPjDR|oYi{>qH$pv?a^9JVMs6^#W{m`lOau+*wQ6R5N=Ai2Vy3(APyC!3zU9U z2}(sY5~1ecEGURmISwmFhQFi(#wf6>jGnm5g(t9ojPE=I>8@YS*r;8j?Ay>rtujY7 z29Dwp3B?fbG+?18&LN>+##-J;=hBqzOGn@+7Fps|5m!sIQqjg~$Me}b-XNL0T9XM_ z5uqklV~P@sT1FVkaN@E8TAjUPG+#E3awXSL2?ais>^YgXDH={x&rXc4-}Vh+0=k;l z{!^ddk0J+iIx<@7tqth`DVHrm`JSv$HF@rW+jyds10?__8KQSYvG@3e&L zt*k{UAC04{jB4ODEYuArx|;Lw|3vgM2n8*$y%$>x!y1Cff`D(x|?SyP(E=PK$%j@|Nidpq_0l$-;3#TD|tDDsaPtWrbrL zZPR*8+*u(bkpgS7q}#**eL+IBC_q-I8tN+LN#vXb?Arpysnq); z$k$-s`|+vzF*#WZffGMS5quv($tb+`R+uXd9VTUzhjIni1u=w}?EGO;JQ^ikA5G&c z!B@q}MKZtMRX#1xOXEP5eiED8cEUXe8Q%0oH<09tC_28C40{7Zf5#S0p7GOLa&mZs zO$kxJ(i|06nVEm%O*8|QEyfELEvAUM&(BjT+cY7QOTEgTl~z-)irOKt6>|U}*xh>t zl>&1f`I=Vz2fMR*DapVfGNQ0vHotMZQQt9f#j1TG$= ze~3V&eW^d}U>#ras$S6x`p@L%LbnOwl(7gb!iWzE-H%okExoZ%V)<7k0H!dY;pCv) zai-~dC^ocHQ5lF|6+xSx;rl*ZObwu375cm^`FuL`VkC{cGVsQJ5PT*2A>}@R*23~e)(Fwr?mHb ze$Q{Ukrm~N2f2=!-VygHIr-6H33UX{vLQ6#$T>@36Wb_Yr;f8sR-!;wYF5^ST!E}k z0cjaZ3W_jvM!*7$$vK5B(vpkq%j;Z@8W1R07bxh}Dd>R0GW{EL_Z37*5M(^YtojtO zNEAemhi$DD#nWp+Fn}9@Sn%?w1%sj}>yWN94{rvS&+|CdaTpN}0ufA!hz0=zj9{Or zXcnj-c{BEU&Rqmfd;DR*nn|jbBiJZH;{*X!$7Kgy@mtdNz%Bq1tH7lnj=6 zP*yFs7ObdC{75mKg_hgAKAF%qi8Bc>pB>Fpp8?XSsl1s1$4ycd#}OBFVv@`b=?n+g zs+zD*^p2{vcyjii&6Hcux&=-XLT9=O6wn`Lbc|Kn2-I@ls3y41PKaX<5R7{nPnl=5 zBr8vT8J$rs?N%Yzm^@SeX00wC`F|L@tEjfxfc?`+2yVfHHaMj?6nA%bcZUKkR;0n* zwG?-UQi{8Kp}0%2;!wQM4DbK@X4cHvthJAGxL4MGe$RDFtbv2li={9fCYDlFe#TB9 zR(4f{A4W;|Rd*W!dna%^I_VuAm2ZDlwpbE=QmAHP1lDv&_Qq)s`jT`^>Iy&c1Foc3h<;%{;;bCx|vvf z`659yeZ``VLa90(I|ZJ^es)&Jz^3kpOrn{@qSvS+rA=InsH0P=<5150$Oe2?$ApwX z@aQ#&DMcMBqX~6pkX)asa!P0YiRGm#j=Vrc_3iQ~{srok#ObQ}N>8L8n-zzpg9Pda)`VVFFEW15>z zK9ozO97kpr6cEe60c=zZ#mEog)(10qY6{j&W}tynd%`sj`QJjKNce-3T0ml~nlGAA z2L-0mG;$J0oaL6F0a}nbG*p%{#C$pmD^6R%Mv;aS-N@~8{5RxeK$tSwJPWH13O%M7 zEQ;NHUeE$80iWkio5v8LPH71@#0K52y!>o9o}z~4jSek3CO$OkWsEMCAWDG=9WkxL z9P*-@!BUSDuM)xZEOq;G}Ejv&FlN3HKJc$SQQl+jD~ zfxF9;V&o4C!b0s7^`b@={U~SBpQ5q3!NY6y6eWv~!b>au>Y4RQ2+3zH&oq6%J>3v& zOo}+I;As80b^U07O5(ga7WHz{-ekOp!5;;KG#mM(xaA9TwG_|g4W^|(PvRN(YLWK_ z`Pha9l!k>bYLLiEM2kW8DYgUIQu~U*pl}yR-w>l$z5K?YI)JC5#IW|%u&A2rCR``M@m+qjp~xR2GiU&MGo#dy%f zc*w9~IFscZlf_POc!|lEeiQA_7%4sAWOEdRm3`RRv`LDuMv^&78NKOs%X&~rs611Y zkov~nThk=_Fvr8tgf!Ei;379_LxOozY6JaCkXd9Z`jv>;wTjt|iCK0h#;wZmT^i&r zZu5Qu{SjL=uVwQw$Lu)(i~Q7#XwnRTqk$Tw!53uI2sLjQ!PqKZPNOu4_1r?0ogFqV zZDGY)9@uLBKBG}#j_G9tj$4YQw7_SxAP}`6RJ9;F!-IrRmeu?qink!mwICx_YVzDB zDaHCyV?lLh@hZ2Nm;rvBX@JgVN!zsf9XT0kYlAj-3sGZ4<7J6iV04?h^N?!vAHVqTWwu0~qFe@O&xNp@LCsP3kj?9#bf3+AfH>l#rHsfOfOFE*|!#p|g2 z!&X)O!TfLcb-1MlgU!3QHp)~s+9)8X5o7l;nS_B)`p_{jZFJCwzdYPJ6hrUH*U7ZrM7s4w$5IA*17vGxmYg$_HEPm z&4}%=&-Oid?M!6ttTy)DbnUWz?0jU^S#I}z19v!G?c_G>imeaqy6iOQ?E?1gKCi3! z)=n49TmBKY2Zg~2rlkyw2c7oq5IU*{&#^DK&te^|aQr_(C_l@`3E6$DH#=YKQNMS5 zB#!#jq$jfuK~Zm&pAh-XD}1fhn_wy}rmdPh^$WmZ{pw~X3K^I|C02N5U&MA)RQo&Y z-{B|EUQz9=9Ja0~RfEP!+x!jvf+;wCltZzqW8K+NUGDFGg6Z3f{ z9UPYml2G8fX-$B4FXLD={4<7}%FxxYYL&R9MJ3F)URu4ik3u7~lize}Aq zshs+d&IB9Ln>NOIh#m2m2KR8zp^$f1Z0`=HPXFY({^oV$vF|Qf2$Bc&&JUd|s2ZTd zoc>Imcew(dRo@~0onPzr*76GQRrM5yzf2B{Z8NOyOj)2UrQE*hr^s}9);;?-^$r8| z0&DYPjlqoz+EGMx4#|{2HoN%ha*+)`-L36Giw$;PMFQf!+snOt?sCIvcE`RGUPlVQ z5fZpQKIV8Hv*LFDDswqLa+u-zF5c*68d^lY+MSI3oGBuR&&Pwp`-~FdMkOZJH!gQR zX85pwR{!?OrVTDWOMqAxYA#>(@OpF1l;?i&!e(atd3}CPdDp_q?l!idLcwu%9;Sb4 z>vFcg!WDPM&Fj4Q;->RSTCnUr zc_Obc!flH;DE5S=dEk0w`a}K8n5wot`%$$iyNo6xu@uv5FC4TX6gOjZt0eYYyzHp1 z#LKC9l4T7*sTP99E}(G7NUYCcf2W@NGb$p}i>}ry*b5TKbB$8oQ|bylYgq{!vI@lg z@Co53b#dKd3xrfAv2M&_=S#_RyxWC?KsMaArq$cd8HQ%S$-52XU zmhe3k9XyoD?Umo%-clJ~J%-vM6xx3&{2h1jn1z_2UtufofwH@1Io#IHiThn9DL`w>>3_59o4jSP7| z4ck4AWc(d1drBVunDO>$BG0@J;nUyq*AM5py8CYz!glujX-4hY#q>Gl=IO_$-LZ_v z&S4Cb2;U{tIFW^KJTjq zPHlcT?hcF#2>fN||NF=N)@IrW`!85k38&RNS{wcL;wTTL?y!iZMT zO3Bpi-N*yC_U`7B)Ep%&$)O!EnJ;hfGfPp42sx$GjU|^#GDu}J=^;*#HoJ79!mquc z>6Z-yZD%eoKr-ovAj>3|nSByua%lGY{)%`0u;ZzL!tmXBL7-ZH?D zL=sPc&(Ju!obz@{P`JErj<)2iUcWOuhG-0?%h|m-OXgPnc4=Z781LKts zGwWbELu^UKtgaCX>S}3);V_U39hlrzOElZWvE=VM&SsTujKy8Yd{Z#6@RW}UXhFUn zc0qd`-r`DKb3LY`POk1$ZerHX{yz}tk%DFPXZ6{2$C&1%8HJN~K42?bSNA-`;;_|-;!yY|5rAS>5} zwG~@I-?d>K>eBOY8u-rum;~DHxZRFXE3fWUUs_B(Ue*A%pLj}A1#h-w1W+F)miC@B zmV6O?g4juG1cCVpJBhDcY#9Sf0k~u${YHkF{cFfR7i_mZYDV9`v)s$@ofdz$kRjhI zGBF14fZpF=f6Q6!J1e)s51l#x2(g~uxxZ#>5nz1_@?DdmS?mU2TxhWBFMSQoC})p* z?M}djgrb`qGAP2~cl+nd(UxBF)x&u>_(2a)*8qo9N64Km1N{-xQqoYKt;mB4IKI_% z5eDlE6STB}sbnXHWLO2lh2&vBmn9MjZ!0K9!F^*hrRk#qNb>wIYjGn(^bK}$_iai@ z%)BzpXRjrDxkLVK1Q(jRX}|(c1|_9?B6t2wfYX#I1 zH=&z{S_8Z<8Q+%(!gLsV=)keCDMBw5ykrT~Y0^@d%mZTbokxF&=x^;ey>uOaS-EqJ zdlIqDdsIzv7##k5DdQ^h5g+QT6YsvZ75la|siw<-S}bTG@y<&<)NuqwajsV(BJ|bk zA-6(y?Ugjsx5cp@ABu`7Y}I5hhIFFm(l~5v8w+*9?^-yqdG$>c48^}^^l2An3+rC0 z52TN zycZqlraVH{j6VFk$5tn0;vtz7b{|&kqtue*HUyR!k8%=L-Zyj7^Feq4*?&LL|FW#M zQ#}|7+z@$nb>RQ|6pyg+^8j`RMCvX7T$p`gzEioQf)o>*`|~lCbIEJuxX#?|MsIe* zXt2lnOfZPziHv58r%V?*5{fDrt&y$g|3WY0eZ55Mth|06PFeo??&H&BFkB1qPDY7~tUd_a1|B>Lt4U<5pV2W-)W(uu z8f4?Kz<3f&nR*-P0a>J7<#AB0af;(?n@Idl{tL=Ob>xBw#Nyx|_8m;{0rPs{X%?^S zkidpMdwM5f>b@@^rrT-2QC&3S->25sO=%RE=X1y_5twc51*wKb>Cthgl6mQdr^;m? z@-QLccB^}FuB92Guk{cbO^2~HR+~xdc`VD^yxoL#I+=1$e%Fd#yN7o=Fh4;f453_h z0rYkzp^tbfEEYMq8v52;7Cp4HgTz?tE@@$77S`C(_pYo#oLcp3XeTK+KRfYdTdn!e zQJ9WTG|#1&q-4RTRGPR!Kc$q((O3}XpeiQm3&Jl?@gNd@PO{DmP%QZl9-pqYof0(W zAan9;qfBDgq(SY|Z3Ip`Bi4O~g=J3<+F$; z`10FlxBGk2AUyGD9C;1jcq>(`8$ zlRlf~ckhuNMw*{dj&;JhoM$4n8XNG!#0N?IHKG)kB(E9S?tNV$$YiNNl=^NhD-$!` zUpWa@4Y*ma=(BaKUIoZC<;x=OT?){MvCbhPnHp@tmyz7NeZ-3#$lscr=l)b*MG33L zv$^N~!ISOYS?2RA*6?H1cqgAh-IR~jk&l&Whv79W(hGx}XJEGWxFCCL2d8|}4g&B^ zf1G1vMl2G?97x1_TW|2$ZQWtU!GP^@*jRg!-|h8RZ7mh#>gW zgZKLCML+=KJKU zXY49XEk5#IKGEq_CL1-H3@}p|k%GLYC(pbc`k~3M90iNK$A}o0M=6~~ z?uVoFLc(X}Lr#-}2oZedvm_$0OdmjIs60sgav?zWF0}p5U9ld?`YPhBIc8HnwwXC* z76hxmKH3(7)s=6vsE%2FVFvXH?@9+ceh$o7K;^xJXjue z?d8|T=+=`6pN_=vJHYI_L+*fJ;V}i3!alC)$1rPp?eK>h_3_Brd%Z*ZFdz>ZlBZB! z3Uz6wZEJ>)siV*8VwKxQJ8fZ3@uPQepf|xjm4cjfhOI%qPMwheqn*x*(Tk)Pgem&+%hdmKZJ*af3(JSIiROoT7>X=ennA4J2Ouyrj!SU?8@yR0` zj)0Wt=0qEb6yD`DQ(uGk{O^Pw3`o%f%VChHE!2pY40T#a3XmveZW(~;q`DkOXXnK1 zi%%<_rn5}X5*ErOKw`R;7HW_JN=o^_q#|75Qq1qty@m459M%6N$YbzV*GXmY4H}Qg zAR9@3AzipFS<_H5#WI9l(xalH@I7uZB?c4+;TVa(QQyHZm*I~6>Z$xisXoIf^jt9s z13H=yJTC7Or)TN0gHZ^@-Cd!X9vYcpr1BO9AI?Xp4yiGXGLqhLpnimXUf#-FE8#VR zv3DT^!n_qwdIf+e!=g2s%%=od>f%!423f&T$fg{S5Z^o$&RD$L#Qv9XRlsg~-(pr5 zyj+1A*n_%mfc5Sohm9^*U`zgFCvpytVoZm&Ac{$Bj*)2&Fh-h};GTCakm*WXXhNEk z;2mrY!TLAo-0)uz{MmN|(DGnha`)<&uQcAr@ZJKHgXDLjZq$;EFghOUsQ0r4@3Dr2ly`+-7 z{pCkrP0vdTh8;j}d`W%;700_*xE-Z|Evy+GKb#aqV@6`rm3e-X|Jk|E&HdHx`VHUy-_huVSYB#{^E*ezZ>4~u9SAs30${^c7fFl-3-q!YmN~~Ul}lK z2E^6G>1zV-z6M#vfH|{|B~^)MBO~r$9XH&-#$q~kQGyGJxFqB9^mjGI-%K63=^j|@ zI82U+((Jh!kF=OWE$ftvfCq=%!0AS;W8}{_GSXhA9g!OTdpdWIddmdtGU~c!Ur=Zg zU6CwYv94azzhN5iwS6;A?4+cqpAVF_H6`I~<~^YGA~)NovqB?cecRLAC&@sbNYOw8 z3H25ePZQ!6r*3Iu2SHm?BXv`qB-52_7rEJ`ki9Ww8mxaRSqOd0QwT6fM)@?#D&JSX zi`?q5+D6x_CAyd8{igjeRn;+(Dy11&Yfumrq+mfJ@>NhNr=qHV972p3YKtZ*pq}rT z`rGkfgYvW8^T(Ykb!K?yF>fRR@>W`l6PLlZQ*fug7nYkO_2M^E49>s2jGk{yG8>(%LMlQ48M-}Fd*M-fnx=`a_W84(JzEXAY2`a zhZG*sY>{U6b(asz&4DIKKj}7SjSN}sjwxo92;Z3C!7p}^1i&h7!@j2cKm_J=E+7%_ zC){F_Ro3_#-A2DA`0fZf(93w(rvoB^m4x>jpJt2p_^W+_Ve`aai>L;`D;9hqgqzU8wI+%&AlZq> zt~bp+df3~4)H566x0dIC*#?1tw3Vv9^Ntl^RoqMKoH>Hdb{Jz>T3S~}weW^R1@JP- zs)yPu5@S0=G8natLoIf?5r0EWhr6HovQN>GZjU=P+($EvA?dNh@3Fd?g~`aJYF|VX zl+m8aVZiJ*B{JU7TYP49m?Z6Pk8sCCX7=Dw$JEK%)M<8By+0xPS|82YCr&uPukkei zjbX*_7s?M^1?qr6B8LzIF#omGGOP2@_T(@FwxXQ74^}Cx3Tm5Q!kFW zNZZi#Ywd0UYRIHrTebgBh-LUUkHN0!yjzE9G7)j|0emXN)@v7{s9`#t+_~2bA|x$ z%Sr~hR4Jdx{E=n6HZ_u{iVf@9bQbTN+U2|nC7zPV!n{9;WVm2k*aBe#($F_huIA4g zXP;cU?{3C~UQdt^^)iBjiuk0(ih}ZHx_N;(l=c)*xY3e8L{qW|K3VZn?D|si=@LfL z;NxHFCkm}V6@_c8pUK)B7Q4_G^_8Z$KJ1riut{k{E9> zC<&yr%^L%^**mu$av<@H)t`fQ<|lkxNHn0T0sB$5RJA_R&ub{0J6N^9B>i`9uz?i$ zzyjpKmm@Lxd~NGfeyZ7qEfqYv4LUWPy`L&{)fPa7#C?u$`;5~dVmAgOw)N_nJ?Y-f z5(x_iwr%UPUrZX5f&Y5kYwJN=gmfEgd>R(*h)K$ir>v=mwf?5Haoc;`1)Gw+@r`$f zwOQ(Ln|Mxp(>PVe>;S1U5 zSDa%p*H?|E2fQ0c@l(eGY_o#Ai&lvzqh}L+wZGfCj;C{f+a>`gyD0mq4rfeHXxE7_ z^iCs9@IO(V!C!(^w*P|FJ)Fah+5`EoXJO;mSn+7ux5>(@k8nR-;`jOl1KXgRmsE&L*sCiV zu`4>WD~3;3OnFz#-B&D|S8Qf@_IFoYFTpC0*&p6dfBwg3aUA|5iZ~X_`*TlmEp2u! z`{`Of@A@TGRo=W-MO>@Dy3rK7(Kfr$ePyio?h2ZAL*IR4jJPp>b!#bhYi)LG`|0-o zzt0kCb|3!fKJq0}?Y@uQypKoRC%$?}7JEoFdr1HEkeT<8)%}pY`H+ivaQ+mp+bdW5 z@2)KHacJhQy!+AO{lki>6!M?jx=m7tlN%?ozsppnt_goLUHRO9{7pFf>xTQJLDj~_ z^)$eKuwmh_x^X*-SQb~i`;oU;D)#)d_Hp6UvtQnG0wP)B&i!u9Y+>`=-S&_EUuJiE zbz~cxPiim3OWq=3;?21j;^Fe<`V&H<@8-T65rYFU{BP!RdP^b{orukPX?j~K5)YLu z|No1pkkbEeu!<@5Kf!9!Yn!PTJcUmA1y9*&T%J8p%M*H;xs1j;)F@VM8-y6{t$Z)kt+!D7NbjU`V$g^)Uh9&zaJm}oyoVEJw0LUVA#i#|^kuP5 zsVnH4u1jPQatM|Ph}1H+{42$9lDl&Hn}U`4fzDvM||xcQn`Q0B?S!d9 z)W7c(o!P}H`P*;Jjn+uwf{KF**Y#(=d-88TUik%B+`a9Oe*TFPIFhnAffSC6uKeay z{5TyWN)$z|;T$H70xHCG3EU3B5f!~G>3-XYClzhzvt%2|%FOX{hW?tsda!S2wp;>g zu*z0|h798P!UrC^NHKGMT?%#a_uV05lcdumK`|9if`F0jXJm>jQ#Gk;A<@o20J3Ho z#1rjEGe%MLbyzsNa%*^;VQ9-xgp{~dokCD)3eH$eX(K@&>7_GPj~be!9HrR(RzLoS zR0&2@)AFwf@z$J1{f(-3~&b~xd$Y!j7GMY3PD*9bpPFUZe2EwH=bgO~3^O-+iQ&0UO`8 zRf-DBvUIR0Qxv_qMLcJCihafY^ADWr+b93gO8@=}|HSK`ukS$$=(OtqvysXI@`S z7KBy`i_=_x1HCFLdX=eSJ4(Z)Uq#uZ8^rrQC7G@H(Bg3h16Frakm>D76(tj7lAVfL zeaRFU@D7RfY!ym>n@}y=WVn6y|8rgkS~M|{r7*BWwXW-xInq?Tv?0>!LbJhFqfwB7 z9+pT*5__yDppv?aVjaZsgQJV^lrSoO5njC}J;bx3;|s^vyA4|)YL?3wAW*Yk-o-O> z?hfc(_sr`_+Mo{Nwq)9PtIQdts4Yp7P%7zv8YPoA@ zOEfeFcUwFnd`JV`u-uZU0PV#t?Zt4$mL|-CgfWE%O>mb40TaYCnQRLLSrPWZsoC;F zM^w#%c&;FqI5!yztFPz^@_qQp{9w)t4kh80tMni(i3qQ2be;R1$TNs7r$eN&lKF8a zFy;##^frc1-;$&wGmZ6-7u{vZk~HVApNH-U!=nwy#hxHiz62pHdC9=}Z92*rsic!@qnC52~o!5*qSeK?erCypv(wk2vwhj*&-Qw6b zNY&F7gzE$kB5>0yOTmZ{QHBPyrtkf9A0edtg=Wh{F@nh`s$Ns!1G|QnV%nZr;uoSJ zvuoo;&l&U)Ib%|;K2D>05KGpFLF@!xWlX8KNuvMNP!mo{*Rin4M-b0tf?0zrqU$Rq zVZX#9Se-c;3Z@bQol3$Oq3JPssx*X6XoDtilG+T)FLChYkMQZ#BaL_paT)UaDZs12 zg2Geyaa#l9ff`t&{I@cRMCoX?1z5TOEh4Kh2N7#S^dDD+4X;WzvBI{cHG1BVfbeAK zr6r`zO6D7#@ zyPFfeKIh6U#L2kkd%+Tm@}~!M8y;jr$b#8F=>g6Y`F!Tqgb2h9iSQZAUF z8x8sd^`K$XI~E=eOsm){bj-|44=3lZKNu2A$t~Yge!{bM=FtbIAEpq?Dv25D`;b(< zb)ui9MlV=#A!UBshZhf6!_9;@y~;Vx?!NFgWoqlrnN8*_=$*@HpVp4x? zg*I*B@%n_CiCzQ~qvUSet5wCQAA&kl%ya1qg%csTq0MjPk1!}iP=$&^aC*^z68ykW zNDp_y=kOW#SX4*#ZxZCQ#3Nr^@3klmcQ3fdy zmYCoKz`ba)3h?gu6_DF)AyFL8aq568MJ|c@ zv#;W-y3Ffe1RXv#1O?v8@-i-Hni-E-@i*T{t*Ow&`9lKdE_|89C5Y`|5&R<-x4zx1 zP~Y~(wcoDZQojVxz0V6gq2jr_%I#1|62T&8L{hjkrDzIBfpXG~IU1K!@nH<1=8>J3 z{nlRe@n>FOV7PlY_qDTEdt@JJ;Pg_<;?s>=L8ru3pwjUPT$peLcgE&xNa!mPX!oZy zG(s^*(EJIs`aZpH6u<>|O4_qZJ$fUKM3_LzAF``JmCp&fc;5-8?q2@Vlk$jdjuy;# zm@t!qEXof~lnAr5uP^$N^MKjxHcsyT7i4muKB?5R_ZJTe?wL-J;ATQO>F@gOCt9&8 z%$5m6l?SK=iEoz2ptJPRig$0^gSs&j{+Ube!SE~4gCPmHDlItSA0JWK z%WeP<68A+16iW)&A;Gf?#LR?J?_wj`nfe%*B5A8dHZKI0m=XY&?YIu{h|DgUa^N(k z2&Fxoe5+4*p#LC5RE#c#dqnu;O+TW)?d%Q6p)H6pA3GUC?9wTLk4LhzNNTeMi0~6L z@ClKEH$xymd^B1-#bB>*tTHX_=TgCM;DoMlY~zVR`DGbtX=xdj9_f={>vqD#Org6& z=?*6ls`}8M{*u_kAZ zJ=sXGb7Mg9DBe9F`)N44v5weHMmk0z??j{&M-?9{u8u6(9Vi`WxA_1l zV;PMR4?i@=gKWVn?f!~()%029aa4z6q1j^t*|D^4$`v0bM!;->_sI0+6V(HgnR62` z=Y9mWWLS$3qJ6F?8%k8!L8}p^vX?#f>srA?NNIg;qQrW9Zv>xwz1N4rV@LQnH2#49ttOc0#j3m@V_M^W2-^_rafM$_-4zslqZQq z&-hs@6>Gy?DJD0I@k;1MnBN1k0HcY<8re!1bbAHKBT97>GZ1y2)B2x;Vbd%!(X5_H zU+nP_IdqDAHDj1)n$$NF+|N-wVfe}UoUgwFs#3Kc2Yw2EpA{-LFYDF=DO>r-l%9OQN|aa#He&m-QR1=4r*(w%nO~)QG4QR zoucc+=@{&(>DI*Sh0PmG&UcjS&~(m!p4KsUnW-$-*^q!|-OF=}%$l3%TFB^FX=|y! z)m5U@anzYXQ(g$SZw%Uc8+@N`-jeE2qwB8n(@J_evSor$YLEyT`T9_$rE1naZn48z z2Na1Jo*6=0&n~nj=fy2%jjiXKrq@h3%Ky+77RfGh0UuXhO0`-v_8BHLn1d571wCUU zqQrw4)Q4ho*n@hIwK$OVee?mrI?x<__Zt0}fJgzR$h7%jQ%IzXc|STV2>Q~Ii=*Sq zq)_Um&c{WV^KsY*6WfDaYKml_Xj)r2JkYQ=iBX);FarMR=aXf}=jD%{k!FX2 zLi{{`@dfDlLp03wwC)#@O3;~MVP!I}t2$SbgV7Xukb?Mu%x@zzbx49?$SF%8`+K+- zc6yWNDpL~L+ocssrtx4n^0((a?yX>qi{NA!ka+~ufwcw?Gcp4V2sSjAutpjk<`6jm zQlTN=MKs5sW9$O79q6Mt&GoGAMZps2xJ>Y$!s{jhMiJ%6MoefX&`8xPA_QIjA|a{i z%lDm?@9VAd>uU~?wmo5F_oQ*FVL^#iD2XV_a3LR%{;rDYS^(OxMo5Tw!jgFoOFC6cdQ(dVS4+k~OQv|s z*SVI=wU%$XELo;3SvM@%&MevgS#sc5aZ*`vu~~78TJfk_A>Nu=@w!^^1zPdPTM6V^ z3D#N(by*2dS&3{|iJn=B{j(Csv6lE}u0*=a=dURx8e`46E3Ip7{3TY_%UW`LN**Of zrYlYnCq}7sS9vH}<;=R~-dg>3S8i%WV*x`}bgx?8M#t4gFVIFm-iE|W$Dr26sLRH9 z%EpA(Qgvg`>}HSI#LPS$?ZuINTcd5IYHMR_Bq<|_gR(;7g@^Kt-BxAm)Me{DW$UtF zD@?hQ16ae~-$qm2gfvC5p}|Qng0IyV+!^eYzo1i!!+A0zSzPSAH&XMUKzl$iSk%m~ z>tGn9L!}Q!6Nh`=9DG7K>_wiZj3jWEI1JT2+(Vv65bK9BnIA-^+j}fyfG>bFXhEFi zLGD4-u?vTB8yHydaJGw7?v(xHCX>AUAbN9Lba^AH9=?4KhZKgGOkI*Bj8LHIIz}w6 z=DNeTtwY3^Lt)_WSrHw7ZtRlqghJjU;onicgh8KqO_NO>D{Jxd0G$2)js;z26}FD! zBJ)!4C2#tD!ra!aX@_q(Ysk|C`Cf@074#5SClx0p=`+X5EuABntxAw>Z}`uz>G<7V z(Jd_VoRph-Dkm|owNlLCpJnF?!kr8Corf5r6 zT+h8lEi+{`3hj3uWi8swcb;^kJ{au$D@{h|T6QEZ7mXhO-9PWki7wDWR%_ zzp~TNQBy>PDdJ^%8pwG`RDZCky9pb=F^TXpQS+$o2Ox2pmv_Zjl%?d%2SZ0_qC-91 zaqryCZawU7`MK|EzIi&7-570xe7uF6@(z1G1C8cyUG#1&)nuZcZll279=JY1V&1`O zFBj_XmF(`=%)A^E?mT}0&CBi(#iZYyhVM4?Z(`0BUozHs)cg3m`yh6oq*uOS>T1jDNg$4LAHg5q>uzv%#_NP`-)vmpoK4?kJ@3%659V-hGA3iy%|8-Yqf|S< zuB}@N^^gM_>1OWx?7T;h1K+B1U-z;b?oe;r<{)fnL9nbRwI~_+q%3Vj7AST#;*$qO z*~jP)f1A5MCMR&S;XaC`4yZ+d>defkyL~cD;p8?!P@gQO`ZnSYKk`@J%^BBC5q_;d z9)HoGjG{_Iu|7Ir%mS9qx;CA|do)n^|B5}yjCEf|op{f_^B?l|&qn?1R{V7K7b-W& z%v*lC6Yu-^z@Jw*c;y~|=)LY?O?+y674TCoU`OvcDz%$L`blaUj$d?kBzi%01n)e& zIKb7JWHcM(k(UGq$Y%R1@Z41#+|O+Wjl2>cMMbGo3u>zgL}%1rkJ6HRhme?jmLc$u zA$%M~NFq{hc2FawK*;|jZQLfYEZ;^;;3a`VDZu!QWL)`sg~Tx==x!&6Nr0h58oB>_ z=CZ2X`S+F02nZc#`pJG4FqJL%?(F0>d`E_WQBs>995z85#*<3yGd(_715tx}QwSMsKfb>g3pW|2;n@9O!LMAiDh?b0W+FLAl5#A{-oLM58?rLCB$uS{Q}Ur#L+ z#s~}@uEeFx^IIe^{pR0|zefJOl;A?)i=U)IygZw1Ut@2TikW;@oT8tlzh`xiJep!_ z;3$A#a5knPrO(t$Ze{pv+h(w>f*U!ypZRVDT`TMT&xhTdfXdXeT)&}1#)8X1((?Qe z;x;DFNQO%KqNJw%!y&0`#wt$h;YwE*(OdSal z?QvZP1(3q6_fe3=t^X9GiluLYZS7Y-#A)pertREg(@jL+cNvFgHRmRe=jYBP3xu;2 zTy^uXDj@eVIcdBbI$OcuZlPoMv7CCdS=}&++CR=+D{58!evD$X{m%?~bLDmH3CeTh zbR~_;*9lwEC$~uhDV48t)tV*^@{$B9OsYS7^}Px)Ke#Z?SYb7cSUUFyFe78i3y#Uy z$_s966ll6R5DLW_-mk_9{o~&ilc@}L`z{+Ou>aYqOW+_eWt_>bAVyU1cgdGP!J~?* zu8+ofa-@*|t+w3&EFi!u02Yw%|5V#}|66UNZZrMAtL^_`hX1R!<)V=>3Ge^!YTI~p z1}>k@@xRRQ7ykGERomO4x#A)JRomq=Me;iF%43Zcvt=)iQsB2?yh2UF1TYdUz)DOApHtYPhub-9lowjiHJoWJVlqOoT!MFi8|fewb!H z7uRxGo|Q~)hG&jxMGF;jl z3mDQGW=x0se`2f2#Qj+tXZ-FE0bG?f9q@w{7jpEw_SjZGr1jWU>BPoOhI66%Wks>=e&ScgMlAzesI7%K0(@QHe#JF$Bw|aUnq2K7`yGEDv5T1gb->x( zdh4IO?AG+#5K$H6it#((>hiPSmurai2hoytHYt{y?a1??p7Wlf+z^x3gt1R|;{@aV zy&C2JKBWF^?l!2Y3yQzdG^q5_oA6;kx)+k7)Xx*duH~Wdw^Lxx^e+afm;p(HKz-=Rg6&gvscr zP&dQ7;OjxTy%bFYAp%D~`q!-br3tiGuxvi_)f!9Q<$* zv6dXXY^x6bgUk^S-mXzbH2}OvKF6v{PHTPgtj3TZ!O}U|ChF#E*GiLcq065Op~P? zfRsKuAxrZ!HB7soq_Do6$Ky;%Q&S$*#*VOc>b295u!d-FGB+Wk&fk;KLC~E zCIm?ky7rBmz05Axl}cpc`-sWFT-I!!dhv0Hl`5TAW$fQ^QV9DpUqjTXIm-tk^ushb z8X?d@;ZGR_Vj0LM zFEcYuD=5&?<3pV1<%XQM^s7N6Agz`DaGXgU`<7mzh?dvA_iSbSI)=D5iyH1A@nVSpePlL|^XbSs z)zY2mnEQ*|Je8igVC5*=Y0XOX!rh(pKy1t{fLr z9@yzMgvPIpd%YTI5NiO83>$5H<)TDLeNdF(Gm9=Yb1myGq})QL20Wt3A2t9tj$0>V zw$vFO?L4zLxbMSpOtI5MKOGyvMZP4;90@CM8-O8u@Jf{-+LCzJr6(N+4louc z0#dox2ggwi|N6Cp06W%rPS64X+}Ott;+v+ok2s#$er^=Pm!0;YWB_7gUg4cSE&o<` zXC~%i$Ic1uBQpPry&`IX+miDt^}V>B5vz5~`WX8tol5JXLpX{@?T$b5fcVEd(^x3x3YBC|x3A5=o}4-8j`&EqRrP8;mPZnl(& z?DfpTQv4{kFHtfC_p#9c<9#5Kjv}b7~yd)=-FFl z&_XZO49mL2>Yqzc3wYwnQ~-> z9spo$Zg&~N#lXaNUU_>>QB!QD$2&XT@JEDv`W+oXNVE(bKz})n0LevWsNH(d`JIfqHsZGiT|~&RUwLvoT|CdFNg1!`T;%k5%nXzT8b~iLaiFiok}XgfK$Cnn?Txez_$BCPfe_ z^f-yH3H>UBxMm@qMQ}2B%lmECr1aHwrE4X?UOjQm9#(eJ$1>!Fape|+e;`coHYLLB z2bGt5iN0a#lS17^)3})^P19lQR5Pd!!Rx4=FbP(ltr*QY%SYRMOL9Ym9o;+EKytKy z2!9?9&yeASd;QI~TPCdXj!gX|qxRC8t>VG7)dj?h7h%l)?$|9;xwQg}*3V5~W;@@^ zGT$Bo+rt#kd^;b?1*R@>tfn5!rfF0x7>8ye?|>V2DFiEP%e|!D&Kc%MbB8&7&CCXd z$*2dah6euVho|hrPjsETAa))3?#6B?=sh1rAy_X>vt1(Q{DCvwy>*fYlLm*13KGn| zCwRZvmyO>a-s~{C$aE9&Uq4;a1IXV(dT}0%-LTv8A-gcl)@gTXdt`c_P$6}sU?PRp zOG~7?(9#rlJ;pF>CbKz!T|_=o69>2h;>c8r2`xwOpZ3~>U{R6;GoaXH{9mlSWmHsQ z|EN6yLkt5$H_{zLHw@j~-6=6hh=MqDr!+_j2nK>8A>q(nBGNc?BOoFuGB3}0{_k1u zhj*P%XRp21-uuh`xYydh`?{|SH>a8b;WcyDA#8hu<6sY`iw%y4-9hQ71kEqzMBS$e z0lbiw3Zfp2`<*1Y4^CWD@#Owhk_jM@>>L@aooK^hZF2Rfn9jR&FPOpj!6>cp(_x|R z)95}7xGxLZgMzJMz{{)=n!KqnV;=XdIM!TiWKMF|;60i7WDF8yVi6lHBiL;WHNrwW zSp%61p=@+wI#v)rLX!Rp&u@G8q~;<%TO~>~rW%M58h%OtOa8b84J+I8Y{|-cLFc<> z8mn}N`dATjesG^%NWAdQ6ewW6u6x*a!DS~#NP-~8*#?j<_T8xHcX5o*+d}_Ax*~s)7c@|7i~zWhm}G-v$9R{cXnY3ja-~|&bedwFPe4Q)L#fm#f93pa^(;zbp`Ge`4Iko{vFw8psL-89?9+%G^Y<0e2xC%d5|U=0vkT~*;W_dYdVUT4s-sb` zShP&sR0j8VWy%4laP*>7=8O)k~+) zI;6&P3w)h}0)x@=GHET7( zvfvUr{Hl+J-q?gaS1pgQ3anf(O-u4nNDXr6O*WTpZDXTXK}Z(6^ZPFVQ?)2BGaCUq z6LMOGEJDcxf_qanu2;W`!!%X0L%mZEDi6%H3%?gU+nr6jy2b61ZG5(J! z8ZAHos^92f>8Bz?#<0BiVRM=!pc?+T8u8ScS`M&q)dzT$&|9weNIkDP9lLaEE7LhM zK?J;Q1B_VCmU|Yv{gcI5p5+a`!wt|>_1&&>Ce?n%HI;P)Z@6ofb+LCw zqs}~kJMJdsi;Qe{)!wM>uFIlooCcm5;6wF1@DT+-z}lVRqj$(M-{DyIpM=Kv`)#V? zN_!wv!X~&tqbOaH>Z1ZNN!Q?E4O0(l03Am1PjUT;#K*c_>pfk_^y_#~Ma;*nG<9E>kBzrKEW|5p_*x>>Q)yhXC*RSpob#LBC3w4-FA|lU@8BRa?zMZ<8q~q$jsd&)sUgtsY}5vFODIs0!~RS|0NgeGD|}jD){{s2 zU4I7=3s8b$#!^AKd@rnmQ5D65NCt@Ui@9J^Tc!~f{7$w?I4N4PK(NIjaxl+tIO%4v zcV_rg5b#G>Y7I%hVqRbC%yYlPzQ5tZOT$gvqGOKmp$fk-Fc9SYdaNUje}Y12e)GAx zbhp$&>(I>zv~qZ@bGR&9Jxm~U8-L=vjz2A{2m^AEP`%K2y7k8cis8E|@Xf?`d-{zP zR&M0WEsx1BBq>+&K&#E6p7uH;I($~ZmPn}Kox@uZN{k?&QWknZN3>@cbh36g{O z$O-d<@RAzu%_;6TMN$MP0izv-bek}~mHQ(=MPTxZY0p-Tqs4)V3Hl37qwpSG6`St{ zO>4kGj|xu*sp630e>B5L_ARa4!#Bc5g8|laJ@-|*=xvqY!vXuIg=`DATb*3H^(Jsm z7fT_}fz<{pdA8$~269#5lkd0tpUUjt-yS*jZXZliu`~j;r$P5vL13jHIw6<2zk?i!k&RUouZ#3ZGnRR-U0ms6e9s|5eTZp|(Y+;N@3>E}*N) zD*^Jutat0?V%J4wNBMyiV!8jJwmm=ZR^0nj7*#+uu*icXExtmHan-WLzC0QG-u!d{q0#rf#%MZ>E+1 zDYO6k`t+aC;myM5e|L$`)?X9LUw8zlM56&Q_ykOMpTBk01Q;^| zF`mhWdMx!l$NnVpNzDvq#V9;_(=F{BE`u`lqW7FC(KO=SAwVZ7-HULW(anp4)!Z;K=^?_R$xO2aHRuElG$obgy@3H%CGmahEF z{&{C;iSSbC=P1)uMt`=sKzUZW$kxEtK1vFLcVF59D`Ikt>{;#Xq?96+p2Vza=+o{m z7wcS`uE(7muXKQM!U{ku{^s!A#^zY%-xZ_?->Wd}_h9<rrJ?{<;pfx0zWaWY**K*`l#BT-2s> z;S?Rr(c*3z$i2Tho=+g@+Qw>A+Q^W}f!7=%WiC)olBPkZijF36?Z$n*U&4`mT~p{s zN>oJ~xPs2(P#XW}PH8XyWsA-QnQZk;^V{lP>itKo6sf|w7r$G}zsOxhXX*=DW4xD5 zw{jCcqaQKaJNMv!u~&B)$g#OW^}&?I)_6%@_xxL+ctw2?b^Rw@%;HQeP_87e@`?yi%DLhC+R$5!9Hw8JtxP;M`O*e zSLfw(5$SHZ45O=s>a5U$_V^qnSH5ey+4Q2poOHp;{ycZjA`c3W@SX$)9gdBk>}XnS z*J#(H6Gluakr`gf0!nmu&$QG=Y1ml(jFWm__OW`t-{`&;lY1?0AwltVU$Wawh|1^f z`?{{#TZK6zQ3E+TGW_y>848Q)lxF$E91lxkcb_(_C%-~cxAi30lGIZ@GmhuiH6b>O ztD>k+Qs*pWOG`hBVcOawkk3-dAo15{j@tmoVNp<@aCOQi1VpQLJtf1higIwFUzBs5 z$fzxrIj&w_?AWMbs5stWkx2 z;nYMLrIKpQd-kXdMJr(41mzozVcH-Zlpcqtjk<5fM#|vu7!Zh5EnaX)DUzO7riok! zjGNdxKoKA~epfbf`oFjRf5+cAY zN@S3vN;MJU$_p)uQtKd7$aRT<0ax#so6giD} zQf25wQ~!OZ_I@7S%RRWL0vpp0peG+iVShTZw`laMx~e1_i3wq!`%s0aNJ>v4?@m=w zyNn%IF3>Q@RMp=nR*ge7Y*M4$m4@{Zh~%LC%fIc}8umpVwG2!GkGeP0rZ%ggl_;r6 zoGV3{arMOtH4$-dkHhR@uf}t}XOLAgYVKWNb z?P0z#UP=7WZ>v_u0^cxpKNWw; zwYa&0jq~(?$Ry5%F>6DckGMTWcRGgpLmNIvH-wPLubT9ww>t9yB9T$DwbWMr2#StQ zx%ajH!+iR_>S-g)0G>l5BPoQS+=HI~siIK!h?n;+obUKXLzDkUK$JiaYM>9YDm>K= zO7F1WnN%UtMKf!0nhR1!J03x_HH#ZfR~tAb8gYeixn&3^r}LHQL^D<+YW@oUYZ zr3|ehju=g~uXAn9JMEWnYqHiA1cJz|(cs)=5Yegh`@ljluPa+ja1ET)RVLNiuGqQ2 z(~8X-Gr&n$)E|`F+q`8lAaYwr!v8{z@*rIZw%XBVoEuKvjnZP0-bk_Hy<1dOQY8?? z;BB8jBn`%h@@}ncdSFkjTigk=W$$mrMOHGlpoZ>zH`Q#~+N$LM2*S^Vgg+J8*XZ!> zae2^kYkV)QQ(3K%^ck@gZw!ZlOfM6jmK)g(?NFGu79yqz$jQV(Bi0G24|(bJ{+yo8 zelNlKvwaFkPK|L(Xiy}s%bNxFw3ntErlGRB?r65P;ool_pQFZ>Y_;XvrsW;OyoYt# zoOI#v*dSy%HU8w5RGA+RVR;+ua|o2!sUh0(Q7F*8PqvR&Q`E0#SZ4vi_`?RD9r`KT zlhB^HXG3-EXU4 zq^1*WC3vK!=m(|y(Th%dF|?niHHz0LiS-)i9Zt4YsIP?a7yF|we-=$Kc%l--kD};2 zvj#-ERWe+%IOwPsh6nG%<8S7+&#&C2Nd)`OKa?{|)oIUebWcF-np3GyTfCyvfo@c#U34D+*tCc=l3b?j68g0j7N#%vFUV;azqP$by9u9TJ^GjUXQD+#^D9M{zlxa6F5>9)pRW~veduC^ z$LhPp!M94tx9){O?@DK%d8QLC7+FERv_ci>eLe|WJ_#>+;@h$dh0Q;omB5=zFhUg= zcA4JKl&Zu`5YA?0*3)Qly3c&Wu$vAZ{-b+g;-#aAel_#HcVmj@e`7S`)Ev+;4#u&5 zqW$}g>Lbpo_au}_sA368T6mNCcnj5d=^!jv^d`JtWn3W<0FVF-I9+v7hkyqnz~l=_ zB+j{8y22a2q7r@41a)Z9g;X`pL05Hk0p#EareCnX>({e%xHep-vD4gq;KMmFU>ZO( zty%jb7Kb~~Cl>#8EkmZ@dzve=SP83|JHR{Idk6Coz~LsxC5}>Z(2%xs^{> zNB{4|V0Y6;d{&_4EFfS@Gw=o-7ne?s0Gj?pCx)PDgPUj&3Y>KV1UVXtjlC zrUkG#YwUhX-?^RYauv+A6UbXS_!Mv0!jF*1S(1Euu$W0ZPCuFoC7)Z3RY?cz6p53IxmBrJB6rG8AKRX6YA#z!_|My0^O z?TzOg+A+m%v>R5?RZOGr@W$!{hk~p1%A57*%+P@8yFvYELG$2!g|UuYKo7|{6>BFB z;*2K700!QGYqS9baNU+P{dYI-?94wgVId}N;)xOsWx3jX`mNm(g`b-n_$vuh+&|j+ zjSa>T7I6;G#~F;qjWt%sN-l`J&lSx$9gn3Kn8BkCQ6(rcW`Gg&{_r<0BvS$-y(mGHqkBq7x(*Nib#u3dO8UObKoZZ-PS7 zL^60rk;s7NPEay16V*(Vg`5OzGp}9D)K8muH%+mGcK5$GZlp@? zNhTDK1oM6J)psFOblf8)F>UQXcrj-y;=G7__;B>0q7$%ojW8ceREs5U!9oYIBw;#~ zNC0#iO9oljS_Oy$&~T}BO3igD%XMncb(*ks9BslnUH&?K?K(rtI^*Cv)3PRk80&kgRd4W5Jz-uw-|+714e4S~T8!EYNvI~&568zPWR z1kI)>_ompLu~>6c!g5p6b5rWhSe&palfU^dcwV+;Q+{w$;hP)#8r4CmF-UfGgnCPb zdrMWyT~&BP?RG`Ob4xR9D;mC`Wx1tOyQSN*rFW8}oxf!;xMg^`HDcj9yX~&UvVoM^ zHZz2=YI>Lm8?dl!TO@4v`LA1fdW=4Cv2EDyw^%ot^sp@Run%xp@mRMe_2gLfxINl- zzTI{YSoILzkz?`n4A`OK-tm(1q~b>7L_Y2)h3@!I&IMe0wx{p-lI~{NxjYc|3RdzA zG4!%g^z!*;hYIj|UE}Fr=H*klL%_Qlv9lAo<`vcQB^2U`KAE6oNqWk?xADgJI#LIN=FWq>QV?w!^GWB#?Nmk%5TUJ%v*8P_bhr> z;qXJeulNK7p?5UA?Rz%Wu{wwU@YZj*PWo5^LK{h^V zq7SN%W`zB}-yVNAJf6L4h~*!3*ZR>J>r-64OY$?CR`A29#M$-V&n5}L3WJZjPmZMm zzFwZ3ti>{(4sYL%(zS~xC!J)y7F|0z?raJ8eHR&<3^?Ao1IYva?i~J+x_hV2ztyxo zZc`%bPsHdSpn^cKGU|^d>L1O?N&eABSkIZzF-mr@*{Y` z4}6}Wzn-TzEXTx=8sv5Uxa9!iO@>oO>?!@rvrEVs!{;D!qf=&)Gm7sZ*^7>CXb{oU zpyd<7*0E^Lmq%O$XWU-rJc;MLLuX9$XZ)=XSjq#jYcYqfqru8}?k78hS$@1YZLWvs z^wK}Y3(omoo=bgxAXqL;ix(sORO8x`@1n(@01>PCwU2ERa_UrqCq&gLSV}8| zK|~Q={v#__O_)G~DiK8PJPaK8`O0yP3hQs;r+n|~=PmcoCD9PRd!a7!A-vL|(zr8) zi(uBw5V2ukb%p4XR9t{1ocIc0ISagg5#2eax+)OUyo}elsf5l3XHrLO5wd14SOX4Lv+xekj`vq|uHc%sNh(FZZ!B{(@!x z+8nKz6r*6Tu5I)SjSE9Pq)SWu%;0@Y28dlQBT9N`Mn4vsrSRL}=w~Bk*f*N+Xk2{c zj|;sbnH6DUZlDW=!>aY$re=3Q{!m}R%dA4D^VHq`vp$G|b-3+4YS`tY!WL-rc&I|# zLlgH)&fMVDh|A9vwuyt-dfJ2js!_TH;OC!?B+kP_A6Lk@|2l?66rCHsaylT+hyhq>nh-Znrn82Iz7LPXNNKeZftig;?{LOIU z%GkT@iQUM8tLM+~|4l#is=tjKmHzknyD!mYL~rJwaHl7EgBOeMZ0QsJ^iq4& z%bgztPyJ|cY8sEue&3!e|J%31^%LG+Iz2tfyuC<#`rGL6&&#J*Q@G3TPk;Tuo#UR~ z`859QMqK{J{r37dSmW|7a7A%#<7mOHH8k>0Ky-J8D@XnJX6QQ%PWu03-;=cb?7qgN%lIZ+bUF^xUs9G~ta0kK0B~>6} ziv9z^5{ao0mn-}Sg3ZMODS`TU{C5znSuUCFmHKJ(9Ry4Df(CO3!CJ)<<=S59wj9kO z@gaVaV=Zr&TTz>%&-GdxRy)Ff9;}YFzWe?e4TiGlw>7T!B~c1Ej<+>!4rOuZmFc&? z-ySOzzrXgsAy|V?&HJ-;X0ILZAlSqCMz^stgN~Nt<+k9%wTU|j_B$pPde5-4?QC=S z*&PJi*=`4#exW8U*!Ah>b39Yaf~Y4a(Lb<#fjH%9hm)(Hvkw`qm95c#f1j>H#~p9| zBR^jK_+aHEzN`J>XS-ES)b+kqxfaJ_Fe#Jo?p0RWCLWUV~BK6 zF%Wh{qZ)%}9k~X%q`nR$R~`Wenfi1Q=BLn5*tn+>Xvl)$rqz6d#rDnDcyOiuYkb&) zZ&`g1)@oX8lw3j)N4jJXda%fs#&-`M`UT0A7u-v~%ff&(cC}?93NOjN_ixvQfMnX) zo3{MZKJWADFW0rjUhJt_iT%nPkza)uUmzT^Uj_nO~?@r1N zgNfg?Tk>j9Vnv#mE84T6gXYgpQH^0qJk3f##6YVmRY@(G4+?M`J?ZJ|PSqMiEZ8uY zq02?95UGbxbG36gK=(U_@#G^SDbXi*Ux^Xab!z(hu<}KY2_9U9Fz81&m6d+b=kE;A zc<{9(R49=(@n$Vs9j)BC>Auyx5MC;p#Cov~8os zPGbTC$_Azn`%ip~lZ`VFn$d8=>G4xsTVQ`+@0Yx({c16f=uHWI8rk|Ztwp2f?{jLK-1>25W=O;Bw$SLLH7`XJY5u2QA4)BG6AN~r9jb6OQLF92-(xvDdia@s0rMCy z2G|7ut^83j6Kd8LGw0aI)9`BteP*NbJ0$dN-o%JGirQyoO)`FS3&)D zu$O7gD-nLN4rDWqR;=hr7DcOo=`e#*4KwKh%|OQT3pu%($|s!fRo34R%eI&tIvHK5 z9(B^)LtJJ_5sU+?3e8^-g#ypE?5&Eo}?SLW?#g6P$22F?9V`1MmYnh(uj)>>}#^XyDNoN4tIiixTxzgC~l*2&F*YfKQ zYtauI|NFLf)r83C*QQ@IsJc5;%{xzs>RgM+oKHtu%`7XnG`N+LBc{R>DFea|Q&YYt<^E~nr5 zb+5;?j9;$fTk|8{Z>~WdTVH8ETqtoP)nFmnvG(4o1nT|OB7V`tVq`5a>xyDNmTag! zmL1o3vfBI529hQR-h{FhW|1|penpwiqx{4eQ)2oVZI0{lZ7utdX$oVjIrObgNr;8h z0f57nD{b2Xq_iEFrZO=y?4r3F!B7#ue_QnC7o!zH_Qi(`W+F9q6KGYUc9wR@G;LPC z1Y@Uj0@q$IRXcVICHp+#g;+7O9x$)6U-99QWV zrMw@*FHv}`;cPptudeBmR^PwSTp&OX2ZmhEZ-wuago+npCSBiNOzq@)B!>c|^shqk zXW!8JEX_BP8A`DpVS%nca-d_v7e9!Mm;Bzf_|YoF-7iq3f5JLgw`5Kr=jX;)gBp|? zzgyL^c4_m;7k4tT<07wPJ(ZQJ{)-`-$KUGiFGW?miI}Gw8MT~AaAKH$MC`!<4`KA7yTKmXM zE*tK!GW+-k?bG|xPyMa9+&!Tk8ldhl7-$CY1q04Ry3XHmpcpU~Nm>ht!fz8P4FeBJ z${h@$ANN0oYpYtjF?}iU_Hl*|pzTJ{ux=Er1qE|x#LvVCFQZ@`2om}W7>GAY!P!8? zm_)eEtn3_LRGTB_C;FsS;M@uhXthwda=ALMI*8e_J zL~t~86{S}ePMDa5e!RzKLm7hJdrXqeM$XEI4f0TX!0D1?XOi_4dByEU6W1>8(!K}1 zLu#>fe%XyNeJ|Df3f%^hoZTphetV|W^>V{{(kAP8GPZsBYG`O1oh@RBN6`t5;rvS# zuv2^~dpI%ZDh~7Vp|n?q@o5OOE`viQw!TGRb_0B7%{rm&G0ppc`77{`wTs&&Q71Y& z>73|u7HVl4zs;KX6&4EiCMBXz>)S)@*JXfAj7M-+8HufFbjeOr3+9)+=~uiFJHPNf z>R_L-|IrBtoI;n7W}gdOL^++*5@5c~56sUWqE%f_=#t5pQJ!vClK4jDbvXNKocIn0=6XgZ!j#V}TNGWchyUb*3G3Udaxc`~h#`l(78Y^}+2Dg{Cl~G1yKHNfl0Pn$00Cov zDV);aS=T_yT!qN0T&s4+_E|%cb@TcEAVEe^k_V zPSREZ4fHe!x~!qlfu%L_hLUuvY6V`6vkU6_BD zOt7NwzAKK{n%?1864M24DfjNyswT+g5`3}9^~j&Lwiz)qw##| z5G7MTOS)vEP+^Hl#b!wr@wB#h8W;F+BNjuZudRs%NSllszoig|y=9PmL!t4i0Kd-n z92FK`x2wfpRmdg?@V@+0Hu;iQQ&P%T+gU~DUVOgu)-PLpE33->2DGk;HNt-UAZh2~ zO;j>~$o!q1y50FccN^{;4-8RFS}>-G2FbG8e3P>v8fy^$)8OVDEYqsKVTo6z$!ZZ& z`Dyb7=U+ZWZm=c++iA2La@pHVzPZH4VBTFb1dnF~#Jk3seXjEn6(jz1g9Y{RqkU>) z?VtpWIK&9eHK@~)3g&%L^sM!P+ASW(GzG|q-@@V0Jwz|h5h?s`D}*IWEovceygl$H zpVkoxk4>W0v*i!|_|6<(Ej%>zjK(wL{S#U9vXj=x%@|;;XM14@?O7$bJBF9wZ@ZD^ zi{BJBe~T4_`h~nx-zz3~y?sr4MZsru$|O>9PX}pLR>GZB(nd?&>i^z1UkpHlaXU^5 zL=PH45uL%6Z>VvxJVVPgV2<1-JwtA(*6I7ARUy1m$gXu!g|ZOegjinV#==lwwLa)%LC%jX$+qb4TPc?&kiK!htEss2$F&Grs=m)#0u;Z!yCPWm&E;jO z=Y)JXq)d&glcgn0-Q>S$JiZnedtCXMW}IgGFIS*N&<6(3CcM^jF`KuXd=L{>4}igx zUCJOu3Vs|dwYaOr*U$BjIOqWQ&D1vcZTlJ}2K<(|&!zm8XF+?^$Vo=U$CODy?B01L zpS75XL^^3%&H>U0teaHNeH?qI)r)eJb>ZA?EhDpU)g*)n- zadV~o4hQ+-mIrPmLcHJm3fZ^P@6tv_0^E%yb7vpN@IkDOe0{P3y9tfzKOIrO6eHf4 zvu#rUWrvUIf}W1k)~EpikztZaHK(Zn5~N6~AhN>v zDa2TzJhj-c>hD_K@oV{(c;R)1_dT#{GiY6MNXW8@(@*AYg!1fHKVwE7vkTxvJ#x%P z`6aFjm}pLZ6+zwSF)pM5Hb+dqdFe`@(%o#&t2t+a{;jt0jOmmEIP+^ZD!=?;F8K}YJOA4EgbEG|7ru9p1eVDUXGxo?IX%I)8%UNdRi!O;$1TmM zEp0m?OqnkevZ<9uH$}w)P7;vesTAH>DqqT?S2KzL6y?My~)Y&GHhMh!K>T%HQfgd zw+9(!TX;%`&5no7q)aVQ8|~eP?I(<^9IG?O)P|BwpB#?{qmHD6nTETM#!1)v*N&ze z4%sP=zd9aQPu%-fcHAe-y|i|mnf`HY?U>+hR4$C&YB=7EI{8+1a$tCTw01ImdvZE? zRPFfV@@@=X_Tx|YkDIk0w^7uTH$NhU@8yy%cqsj-FWs711Ie+1UnWM@X;lgYY|RKm zQm08w(J%(Wv!4kxU#_+w?=Uflb9@Zs`;+sxxj-${O@VscC$iET4 zcK(bcX^ZVMmeUEEdTO7@ei2#*3duG6Zz>B z=LsAkh^!zjpT=Ke{RR6(_#!I~c1kJx!Cvr)Fp_=4=KHGsk6)3eMCI_TZp0-&eV*2s z$XUx25VQpY9d0m1f+|UqGOyO*q+pjPlcy0Xm#fGya7AOU(%m0a|3)4&ivcof9Zh-w zCrd8BDmNy|LwH{E_!K;B{_bFJSM^=C>=$VTu!olP#XHiLcchU>c&9Vbmn@QEKus;S zA;$^-^+wv;I&9NZ>AWF;9V?_tJ!%Q@r`?J008P4yCf%zb$;VX?wpI`|RS@+yLfcPK zuc!W4zjJ5x(dl8EeF8&ICY9G62FMK5XJk5N$Jg2 z+s`-jRLOWww}{K@v(inQpwZfF8vmm%=Ck;(y0}o(BVo&$QUyf7Mym1X%h*^&`NXS2 z)f(8Gh*WCx0MU@SgLX){p#OXK5eseOs*Dp z9WC|pKjpXBz*M6zQqofAf8Aeb^7uW$VY-Cl=<%f$;_rNGDeO?3;q!$vZBEc}X(cIk zch=5qkL{(@5|!x;^8nEc`a{n8FPyK(gn3H(Q)xd6QJ68(Ek3@CxH{X`T1qACW#OA5 zmKL9XyZk8>)n1!x%&N1E0ecbcGor9T#?WET2w^5A2DZv9YnitAoLGr$b(w_x{r((X z)un2AOHP-8=lUzxa9V2f*MUk5_nU(n{_vkx6$y~uug%w0wW-OqB_!dZHY()fD|t|i z=T)MjN=$Gsbfb~8+M~KiGdtFGChnOVr|ck|6>k$Bv&Zy$wY*zDRWgmis|s~Z`oQL& zh6;MGGD$>~t_SGvqxQKu@4w-zDFL<1;XK(4S{rTRiYA+IumYQQ=^7%(2j|Y@b7uf-ok1SBZY8AYhRGTXCo=Iew0t zq>Bgb3ymNdh5^A8Yf8JgNv;B2!6uwXPMYG0x zoNNC!tKxZ4(dHS=-Jr0^h%$>9nqnHu9DRx&Fy(vR0xfM@4Qg~#1L>GMuTLCjGKY`5 z;F$W-R+ey7lMlL0jkZhiuyHzp)9ZY)WWhy^e2Egwmb{8IF}>z1aCkt@K+9%DluQ%D z`)IqudB z)Wa6lZ14NV2h)c=w@|&F%<(y~?v*3@Q?8JsI_^Rm&+5$9Ad#b5u|f2}d3mh)H|Fti z4RB%sUyiOdB`Q78Yg-_mmwz2cN zMtx@{hL`X4k}X}T5kf>`WHI=E#6$(XstrTC=?D;wm3%)>T`5jb1f0&a6xugi49{fN zZ!Fi7$qZvCg||#`G}E8uvf>hgF}fDLA*ouZ7X;@NuFQcRgx+lTonLFaQv4n0cUtH* zD!(%1s*_PQimO6kR9wERx+?2*R*^BqptTE~W8|8#glx01%zJjv0)=hP8glzPWjJX6>`R;XvR5CW& zCyMymjM@KDWo&6<iI10+h@ zf<4vNVvp$oC^W`U_Xg=Xtvxcm!gD41h=~{UvVjJm`U1pZL&9M$sSZh7l&`KTR9uEi zDp8G8rsnuUcRIR5x*j|Z-~a*O3`E+g@@gN>BO8-V_*O85AoHq}&G?VSLa8fb4Yo+! zm(MI9T#Zo%>TbxP0#jL1jqg3}|MP_)*WdnyHjdBf-}uiYEx$d&5M+WNg-c?s>aS;k zSg?FQQHHZ02It+A1fdO6(Q5Q;5q=c}q-hhyGY_exT_GH?Jx(At2YAu$UdO-FHPHtP zaZ}LNtNQE3<|DQ&Takr5h&$Xbt-k`uh*S;w4glLEy|`zo0ij4zV>0stekiU9a0EpI zD;C~E#T*W0^*a(=F(R~GH*q4WY(#JtZJSQEaj7rojvv3$w2FFw89%Wm@QkCONV)`u zD3i3n$Ud7*PDAa;Q~ai2}V`zVUgM1ms;oe0U<1jxPwWrANB_;=TOf+t>Js_5-)1G@^4`a2DHY zzY`i5vAV7E7u)TKh$iy^`MS$%ep5cvF*Z*3{oUJ4W12NA*^N4OsH%kcxk!HCP`GdV zjDFnJI4H^lxUW5ho{zxhLFI%9!KX5dO2UH-H;YbkxLz0ntA>AL0HToStw%SyQ$W8m;n~thAMmr}VaJZB^)gU`^Zf@CUA7F~&7BzQx>R1QRc`1hSjN4_rg_Odl%w!pwt`*x72wm5-n%eXtk=zM=INjLR6GFC7`45)0$ylc$!S;K1m)sTgji`nHS3%^ z(PIoA?DY8_JD`1ETI7nV8B5z$p~g*_snfZ8yU_AG$f_%ouz(GNcRLt4lnRR?V2$1z zXP;m@T1sqtm*Dc|-H5l8?Rz{(kDWq4XQzMt~ODEItwwe}uzQy@CKP?0F4f*9{L zw3?oww5J%*)xnX>5ZPMxf%=UMrd}@^Z%>8&q9=FwGu@j8l9irtGXRWEiT9!vBm)4| zsq(v%J%cOk1$UBSFdtor1oeBRN_zOJ(EwwB1PKudtMboaG)}<1H?4zPd{lmE40&e- z6cg+N5u%SG&_p+-xyU}kVfp;mc|SVT)FV}HV~NJ?)JFQy)JbUdv>q8hGR97Inf`tm zCLlc_55DKfMdW^Lc@H#pI(7T=Av(LR?YBG zgELn{>a~VTa~}tjW+p(>SX|SiS_L7Y>3-Af(X1ikH|P>MD4M3}^G3y&O7lL6rr)?G zie$*{XprTvW{`qb0O!y@ajg))AwBb<1uVaz8c43LpTR{dGUP5)40AyC^e<_l$DX2T z0lM+ccm}-q2?&VsL6UqSWlfs4FF+&6CGOSH@c+ZxUA9FT_y4{hW|(1!fuWI-X6Q}< zhwkp~4hIE9LWh)=7U>QN0Rd_0?rsL8q>&I6WpiEswfC|2zPIkIXYo7F16K_d!P-UGs?pcvjcbf5 zxIP$tb*aclhW&8Ig`GvyqtTR!P`V539+-Rj>9>H{@z>#FCCxF7iDQk)JB=v<&1qWA z8E(y4Da|OeOzUJ#>-1Rb>`v>PK>LDL`vac}QI!X%16SsUD4YkA-+Udm`*P)%(Aw1C``l|znO&>F&Pe^qYeU!;Gb;&JtskqfS z8^)eu4~~59!Cf2S)*n!qdU%;rJH~fb>DvUMC20-lssN<(vU12N zFgg}!IHg+hPV>Y-`>%lx%utukP>;t@U)s>XvV<*eTI$!pp2a*|gxFgDZ#ut67qk#Y{EizL`PD~?UjjA+##&G20D;ntVD*2TF65Kk>9b*$u$n-$Ej8rT*v0=xb@$4>wjSDd-Ba6Udvt+3lN}E`G?^9VNDbHSpR5}!?8uzf6W-WC*!FBG99#P9 zyhr&ER>ELfFB6tML)#-4>e-j}Y_b}7 z5fG}WgLp5qGhq{TH7trQwEU1&Aihkxmuseowp09yMz;1FN8{^0`Zw#7^mK==8%lu< z{X>0|af-VC?iBMn$I97^T^qDRL=-fn6{`r1C{7NmMU`tKr*lQMeB<9$X|2`s9+1MA zBxST>8<%q^rkXpA2&<1iC+M^rnRIv^5w0IK_~~?M9pce>5Q!Xry*X+#@_;EG_b(jw zMS6^rEx6Sfk1AP=H6PZAFeMP?|RMdIi2j# zpB|*2&gXhY7@Qs{&3<<}K505Vm6<)`Jvod#z1W!fL1B=;b9%Kfd)+zx!RPEJWbT&G zYb*Wex0C)=&B!L98-NIfkv8}{;IZ=B4NLnxf8eCNQP;N}7s7|?*y!c*Ky{a*enCzN z;!&_qI)sa!M4wQ&#zSbp|B6-y0Js4FCIB9m;D5c|^+U80#Sj|$|Mhxx|A|%#(2qSt zD+k{($j8vWA`VeaM(BUE7_N8hkLUfLXr+7#lz!!A}30 zbD13zf(g0akcY6TVAP=0Dl6d!xVH@9FejDAYB*LV`H?hiJ|wP$O$!BYM-o3?2x|FD7Yex<%lhl4Yk?UJ9o;2v! z;#LrG19T2+I)K#2(NF04b-;E|-Rk;kD|Pmg&oc603i~cDzK46}dPJ6d>P1Mi1JD%t z#ridueRJPTS1id~uDSL-VWq zvwC3^!|^0N#8ObfjU~vV-i9x$A&0ho3OswUUFF>{EoXVwA>=S6^yWSkuYL+UtNUie zmihj#LY2|JgHqpRYlrBl;MZ{o<=>j9>xjmX(L^jUvTD@k>rY%gu~t+yH(UGlt~C_% zqNkVNh?F{acNDDuiR{^ZidFJ|?87TEk@rcna%|`|vHy@?r3}BkK!Z2$=0U4RS@EBQ zavYk7K>Ch`VKwh#q1Z)~*m^lB;%*^?sSEI9hu!+<2ZKWx$MQX4&m5jdPBd&+ASq>Fcd zx6kR+J)-wnZM)hI${y|2{KReWSq1GOenn^aoRY4Za~G7(WDGFsaHxT`7Q?= z5+6_-9?58P&*~q5t4OS4?D2Ga1y#RmROmj_+zFb;Ljj51?ev*w#20%_hVC_$u>NLJ1Bu6pmkM*CHICwyRkqzg^WDmgAhHOstA)d~n&R zwu`mkLkRWZiAc7i93+E0&}t4IV>Fil)wLs`yKW^^nl2(`4$XPMCOj?^#VU$!)H=(P zrPPyQ6>)iYKnCKhjZ_0MJ^9aFEl)ma9_2lzbE3wmKvX{z;A|1A&>OPJjY_1Y4yw;L zHub|RjmRpGrsdhcw#qteHEml&@?0^A}MSs)svy9pHpLX6nc_3de4;Hw8S!x^g)&h z=^WxWJx@dOQ?I;QKDCQY3{BQD2dOg3N*&r8cx>V=^LI5szBC?l2?Wma{~9#bIj=@e z?d$+Gl)w>?b?Y=ie{nannj?rw_#Re5#X z>t%2w@k0KVmYWTU!0go8dEqvGTUJpfCx5ddbjx-K2IL=T1r=?mKJp!sXGmPxB>u?Q z1taGncfDSlj2ddHvp9drir(}MW~3B|;vc(tG{!p@N@GyjO`4w=8v~x}H7|Ee8FIll z*J1AGqEMMg0Aokeiv<)pq!Q648ig|{V~Ka<`O%@R5g!WgAp%SNCb2dOCej5I9sE{F zkEb0hG-UI2&v)TXEfJig@{|Y@0igJ~flJ+s2C_ebz@sc`NGw7U4^xxiUWtQu6iVH) z)X(ddk1yHb3hg`xa~gO`fAg)uE1e4GK!xBVK`$V{x_+v+NT8y2{%FD;LcIvOSk%!~ zxaH0i=6zI6gYQ5T4*5axTsIC=6EN?$z(_$~fUh3~q}0ZDp((u6k&}0Y5|7KXy)0MG z>oh6~bWbHBL2Fz(n1y92spz`f;A;l8&b?_HAm;OMPQA8+JC8?jcz2i|8g}0lA*fTwB0`sqkLQ0OqBgz5j?buu zA9haZ!T41tnq)xNlW`l$mK(UrwuxO)TmIc*$Gu>?R`q!cc`_O&&QhKscIY=5ss>CS zBs*U(KQh>QY!;6nY)duf&ed!06V24YKv@jw>K^{l+XKKO>IKr9*??CqL2IUO#791& z4<%F!fQb2M`qvZ6U%cAOqnC)PLC(i~ptdv|^5uzJ~bRYk%>x#Eg}HYJY=_l>p@< z8qS2*fW*tbl`PAHh0F>D8LO-HqvmL7iXZp1!2;oXi_x0C{=K4^x-nju9$L@*?(Tj% z7mxC-xtDfm?83=6JUF)(iEqXEm0z7r`!TVrV~jb*ufZ)&h%y@<1T!!qAF+yr+gzhO z6r6h$V2qT|O>&RL#fQ><;QD?rzLIvQG6y~a1>C|9o&rb#cmi#(yC?|S_~Cm1e=}o( zb}|iX8<2RuOE$!#&Nq;!SA1@c590rdIn<;NuC#;0Rg&-4REy1b1BUBBYWd-K#&{)2 z_e&hd_HbL7E`h$SRFwMduCHjKlbE2+oPD^mygwa=z1}(6Sn;iAI;05`eo*4jse|{fkf2lt zztILS#RkufoS+32^QI|Grz^$*8V=ok;~o_}*#kzSV@GeeZv8^1lV05|gia{|f0h|B z8saO7!l#%Zy-2tyrOO-@yCfP!Hsb;~kbATcze6nJz?3j!3zQaxcQOE(MI#=3qi3k| z8Vc5#2#=wHT)CJ)W(BCj>E2|-GcCQVy!6`~icQ_dH*89L(~CMDN?4qQwIt$WCq<3J z{mCTa-=eK=GLk;t_b2h}a31_jdN#-ufK236fq&F-R4j$L1I!kuk~MI>u9#BDRc({) zA_?tCXdZ>$iE}Y3I(6*RGc1b(ZLzwjz}ULDy|;*E4CJ(2d7>82Y6-TND5?_;sZ!BM zxTXJ9oFc{gY}q*N+eMlWd8#9MTtB6Tc0OzcgY$p^zb3*~&J8-wg_A#kNtok(zq9P8 z(o^Y#uRueVot+$S(vTeKoGPeVJ^Z)7@Lywa@-P`I0BKJhe9}wdqkU8(Jk1Xm9BM~0 z-Kd)kWv>j4$TwwTkjM;^uv+nO`YfEB2k_1WLFZmiaJ0h9Z%}H#lWQfRciKfbCN^Z<;x`d6EDsCI7Ys zL%<5aUN3*=6{HjeGKUkiqTU*E;>T1Nsk|yke3WwaKq%2L>6HRIH%`)Guw9oXwYa0= zeQUgGjK5}#&ofDo3!I?OCVi=i4akUJ-jy4KCq2#&e?#pUMqRqQ1q>82*k|L2`&g>_ z`w6C~aE>t*=$#AHC9n=iy$~;$3UhErp@2#FjZHycRNpV5@?-*NA0cfH$hqtlG)73X z%NQi;@#whSN_ku|hYKC8LE0DfskJ&eXvz3NH@omw)1@K#R5yOT+I-y~1>Z>^J;eZc zs*{z2Bm*ih?v+Us>Mdv=e?_N6rK!Xo=?(zLd__TbW31ReGE|aF3U{$s*nlmk%zu92 zrXdB#>3sTm^?v9k+!31~r5ju z8jcZR1hF=S58Bf2b{K9!p>}Em*%; z?P?=^n_L1vE26}51!adwpYo=@;NchB_5Q*iEB7Zvg}m!a9@q@DT4tH)JE90t+yy<; z1M$}Sx$x^m|M8C};W6k%mW6#uc7IGWWy*K>WpP$JB+FMoL^c9t<(%LvPG9#g5C7p(R)K)u9dGcPzqAQ#;^&-ozsx5M9W!g54Sf8lScMu0 z#vTy!kXQSX6;PW3G6RL3LVP}(a0Pay`S-f69W?@OjGR{62`zXe-Ya71e6F3>Fv7-u z)YUqdX)f~_JWr~H$L%o|8>DP6?Quj;o2V-Xmn#J8E0_UGWyKD5dSvulw0p|;*cws< z8#ZXLFC$YHz3U1=p@^zxx1M6!O4H;QoJ0#%B^7<^!UOn-CRb<4!{+y7;;~}f>{~4y z^v-?6Kh$&*`GZWIwQ0k=)zNcxd2VaH<_k|h3qBEj9 z#sbpo=~DFU?lV^JAdd&YJh7|n&+sRqp)Zc~iVWr%p9Kg2pFFc6c5>OsZ+?iw_&mlp zf1qfZpPXHG7nBoEj*qR2QenhI zR%G!jLdTE*;B|U8^W`J}rpAuqUof85ch|z<2L`lv{{1FMbB|Gbe;ohGV+KS*%zyzM z`Sd632MI9u+=aL9Uo!I$NJw_f!b1Q=>qUgEqG^lfDe{g5G&@ih4j_l z%xdrZdAM`s+RdajN1fIZNC9vZ@NwcgGX}W&7pvf2a~)CcIt?4GBiI;-V;;|$2$=GF z?y-?T%6((D!N|OU+}YqFVPJ-B9LJ+?suyNyRtGvZY1TIPgI$+wF~2o8|7l>3Elgd< z=NUg>raCYLRv2h*j+iv!!@>r+*H-VVt-X$|yOU;YLNXRYDd6%JC+~`jQNGjG+WgKo z-{}-@=eFo6pGDQ8w(aHxPbO4}*p2<07x9j=>`v`pcnkN=$}U;TJ_ahFA6vYmC$no{ zwQKZx*Ccn>taI05W7q26t_}U3oy?wt)t(aLx>F4WTjHL4uDwSMMeyIf=Q8^~R{MUh z|NmZZB}65ZkX*^?K=Ho7BP#bG){5MFqu*|{O#_Jfn-u{vkH; z<8aH8mjJ_a|MXH^8YBGXQdM(H{Ojev9Fh64OJv^DyT6yR)>o%qUA)h(<~klm!mfmK zziBEifXE!gOUV47I2_#|(zW*8Y_-6p$Ef@W5I__^;TU&B$;th7W?(#^x_-{q?CpA7-`s{^1_` zPt}|IclhuQ3g3%?x07Y4#@4y*0oZ81Vv&+aO5xzPVr&S+LxBPPZ7|_KUhfgfH`Ubt zm)9E+K4%CSOqNib=&P9BPJYYzmfJzU(&)1j38e@q8B(N_DDA7oDlvO#SY_JirD%G| zW|$`cBSz`@9haz6vimnQACTtBUCdSBznxib^fGFajuS5*fstS>d`!cm;5P1yCg&@4 z{am}1AT>@sf*7nDWCK1OH)Q|9JD~mXVMW7EH;RIjSoxPB=;L#PA=^ijrIzNE_ltvx z{iPbQ&*gREwhkr~#ZC+B9jUyxLL*Y=4n1`_+j9xbzjz)mH(qwq29|9j^lE&F%k?Y^ zyz^XarAkz1>ugdq&gekib7YS{7x8s71@R@j9xxBh4M<0``0onqX>&uDh+?t)Tq7vPlBw`glI8umlyD2kxCiW@kh$r-x6OpICx>ummEhOHf zisf{Do+;B5RGj(?@+&k^N>ZAuNm$;{1JJkNa9J#IVt%Nl?;%k^PQ>^88zb|#sBc24 z2wjJ0lhE!1DlPu)KJr*6-AGC+pXrixcZ1pqe%+#yy17%EIcA_>fCgSg3aSN4;>14_ zp6$_zg+q_3rRpz4oww+GP~v_yTWFlzhM=;KjGtq&XGscAOJVDFjQ)#EyRoUvRm`vZ zwB%qT!j~b0LuqJa>PTl%W2#Js`k=b|&C;J^vG7HQ$xb1@fZ70)8gDX7x;>z&oRmmn zEj;D*{dHfC4mb6!etC_&zl#Wwg-Mm0=bdAswT!7V-HTT5!NM|u`FoIsE%(eW)ALq( zC#sa?_^@}e$tdet&CR+N2RmkuR*Jrsh4acOM=~NiiI*w&0j#5nf1!wCJBZ zC<%QcjwB{*9^#nM?Z`)1S zAc!7G5*jSYJz6Rc=i#(X(kUk)`GB4hQ3y$9H0mL}`K^i#v>|3=3CW?@>ZRQP;OO-A zkT4}Ga8Vb;dEQ>~1L!i5uP!yZr>nC*Nezn!ZN*wMG?L_D_EaN(Ykm|lBq~e{=dvn@ zO1H$ue&*Jr`T*}@hmM$5#eq{-1NyGN8n}>+#DdnQFg)kN@wAg*S2XH z?aI|YyaceLX+}f{jjiH8tCgi;roT}1Nveu1m!p#JpjK$ojg8B3AphYy?C`xSM1dB_ zya`oe3Kxi0Rt=$E &ArP)dhw;_YfTm+;T@yC#n&ti0UW7 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 literal 0 HcmV?d00001 diff --git a/assets/images/placeholder.jpg b/assets/images/placeholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..77540906c45ff9d1b69f846d3e65167019b85669 GIT binary patch literal 9482 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 literal 0 HcmV?d00001 diff --git a/assets/images/twitter.png b/assets/images/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a21dab3124095a0bb1033e3c4198a73d222393 GIT binary patch literal 1105 zcmV-X1g`suP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x010qNS#tmY3labT3lag+-G2N4000McNliru;RXyA3>12v zY%Bl(1ENVpK~!ko?b}<3lw};p@z1=)Zn3pSM_SsFLJCT26FOi}NZpi0VMW>LB9cx< z6m=I-L|p_G1Yv>3cBX zGtcur-}m{?GymuJf2T1#x=pG+EXFlB1qa!;^*c7=MLdHY&`5!YumY#oJ<5{wIWERn zf_|**Vpu8LO7tfpj7RDoQ3V>o;slH89!Z5-l<4kI{;x(7u{uu|^(S?YplbDiyInot zZdVVugS4~Tfp_9LT!TSX{E>6eS#`L75|oyeHKZTKFBnx!XlBvOEREWV|0Zp~ zy&|0I^2BCkX_R$_&q|LIBIBt;nY{F@z?aA#9YtwgmB7Qex<=SRQ|AJu0K*eFz9!%W zq9#@;@S$SYR7d6Fs;mw3Iv&GfxAM5%=htQESmBD)0u0aMOPq&8DgeF{`(=Zg(^6?y~*)C~;FlwVZ8OaZm*qJU=zpQ)_k`f=jMl-w%Pm!2UE zz0ZVKHCxW`YwCA}*d66o8R_BN^zk*W#JU`P4dN|v@d{BHm4~gk4|8(D4&hxHbLOd3 z)C`Y{tm}1QosQ@1?_A-A99<-AR^T;wRX80#iO}67eCE6w#KYpZwv@V87T|d}3J>5- z#Z>tY77pY+IIIlNN#*ghU`%+z%d~I1_Td~{iz`%gZ)aZMNlgeBay32?6@@LL%GeO) z&m%-FXbw(GVJp9ZwWvj_fiu!aN2`+x;yvIVa1Xc#+ym|bOaJXLP4rtL{_!zgv?-Cl zD7`P~l|&@}>+2p#1zL}#i70noB%eFc=~|aEZl7GG$3)F{2|kwJvFHSirz}fSwlVk{ X;xDN2yRmRh00000NkvXXu0mjfbHns> literal 0 HcmV?d00001 diff --git a/assets/images/visa.png b/assets/images/visa.png new file mode 100644 index 0000000000000000000000000000000000000000..d24e5138f11bc5758016bc2691a9bb5ea8bf86ef GIT binary patch literal 12708 zcmbVz1yCGKw>Gx8CTOt5-Q8V-y98a_7I$}Nan}eMB!uAZP6+OS#oe7;-cR1T_pkfg zovNAX>3;e-(&wC=*{O+9Q<1|!eTNDI1B0O;FRk%<9(vs+kP%;xX}gDsuO}2|d3`q+ z7__&4?XWO8d4w=901{g*J$F52B|#9_k=4usY;MWw?dbf9hJg_l^L91^*;~2;%`L5M zokS>4TRSO%wiY6kx;)D4%Fa@jHn#G1gTX z?)Hl1_#5kN19k_y*?|8y)PE2E7Xh!XRaX96#(%4eqvPKq+}ve6U(NWJL;hQ|o0gBW zC7Xt&8`#4YWGUnMswUN6);J4FxmudJgI%@2V26J%l-j>l21-f&wJ-uedR<#53$T|P z!+)A%DQ)I%DMHD~&dJWgF2KUhr^Ue`$RQxe!^6bRC&?zk_nJ3v#gk z7wBu$SeUt+{a?WrAVDjztE1Vg&9;ta)|PC}PS%vbzdb1^1$F?tz6yTTj_W`7D@aMH zxq_{19bO0AG~^_K3Nli>90I(&ES#(y|4>(1Sx~{r&E3oiWT_x6Liy?+R$E&OK>;o^ zOFpYtBl*pF_*u*>xy@Mk*wW2kJ_HVdh&FZII>6yDn=hX5MdB9drs^S*fY705rvP11 zqEP~U@3USKl6LqOIKclQy9r;k+Wac?*Tfl5V`)w;7l&Ba^-ut3Gh0{|JP+NoSf?yRA*oSz5lGil( zOq7cVD%mhxs7DWU<-JPm`9i8T9ArC9oJ0*Sc4=5`2wXX0?|aT!#|@rA%WS2+a(>UfD!z zpHJS~me2Tx6MhUlwc$Ui!)j)9Pop?rX8E&S#3tv1Ct=1_OL8R1wSrLvmk4;gD90-c z9x>iv*szL!W+-^(uoK6t0@&7W*sGys%n?+SQDLgX3>3gUaE5P5@KW4m;YWRtR{N@! z?N4e)w=96+FJq3g8eWUi=ixbA1o;(roE74F7EDwY2-DO!Tf@eGuX`7e;eDFy)Xj_> zGVzn|XZ@k1d≻f(4+^@2q^2Ms2+Nmz=8R`SxQ_=33Y;&h`?UAxzvZzC)M-nuuN_ zmf$G+(l5zuU+y{|;!Try>!boB{Z==j8fE)n9_clotN75LrP=f=(UaE@ztD(CJ&`le zSrt>uPr>(5+%X;AK z)+Eonc$wJU*f`TNh5A&E&v2VeeOqoVit1X@-J$S1!8B0_`XLi01Yj>uw)awizN@1S zbP)VgBJYys@p(x7)D< z8_>pi$(s!q2tzE}{p;bWMID@P1k@mHGE$wox66(1YA*T#NqcQH1%x4{$d#xca{og%MQBzc>hF5aAF>{=-aUxEl&v#Wqes2|j@93jp^psDa5w^fOMD^2&_>7!nRI3hhXZzCI^BS()*{)In^R1qB)ouq+nb?N4|S= zYwKLa2ceiymUf@77{@6#Tu?9<$ZH@ByGo)&uVSQItikA9I^PBEk%tb4g0P+zZ0qu> z0x(L*hsFfS81FdJgbJh+d%>}%(PeeazqEAIO8!bUn99u$^+ zrA+mt|0r@4a%n$9=7rn=5;lY;3Rwuz-xXA3faYI!Xy@fsZh2Q6XuC_oMUYGv1NWRH z5fEy3M6}#21V!5V%JID%Tb70uH|NeS;HMn(D;D*D{OKu<%nrO^deP6Nwats+6c=Xq z*bXbZYYbXn9=U&qx$=x4an5+};oPg0tql(3kFQ^XvW`M`u}to+&ZP7@PF2|xR3>D) z-qD8-s+FnOQxJ$}V1ybZ6}h=0fT5G83qAv4qPoUjlgmjj-6MJGo9EhP^p2!V9fUEo z#^ci70aAE*I*h^! zEpmr|p+7Bexuz#)XnE)IWhY6nlJZfupT4VCeT0(}yC9=}0UCd?HDo+8P-6g!Yn#pR z^2Jt2mUf_8Jg3kTNmFp_;_QmLL|Z@>)$rgDh!zsQ;@`zZnM@9n`}F=w(|tONB&BP= zKKN>4`K!X()E!{Z8+`8bG8jl>Z@F}8=P-c^YPj` zG-&0SmCVnH>FOx5x?9L;>3q8JWg}wi9=g8{z0mBCVjX#_QYEd#^wccVFEo~8-s^Gf zM}f3u8UCYp82%l%O<8cm=>WBbd5u=rg4x9bi_-X`Bv5JljjFvE3%rU$XQ)9kV#?ytM~7d9_6Al7B=uhb^Cm z^E1!42oiVWc8vzVSqdr0&ZU&`vuIHf0#m4-*|)C`zuGGhuFglD7#+rY(N>%2U>8WQ zhk&?cd=(v3a?vT)8H$Q(cHr-t? zSo7cAUU?kT;XiO0$}FWD^=&PKl9jT1k~E_@`CnK%2-L=a+c)1$h#gV1{nQS3sTLTe z@eP;%hPYsO+Z=-|f)>FEr8^++h9i8G`-*Dnv9y736~I*e`9mHnw{C>;BtP$HEL9fA zpoQCdRuAh;q45Wv-j7-PnsyaW(2;)a^q#pRtQ#HnBv{o@QItMH1^Fs0cQYuVsYor( zw!10(zy!X7w)hdIE1EMhBle)m54$EZP2RaZL;S_w2ntm7+6emj6hrM^j1s%EFzg2` z=dF*1KBM_TvgOlAOobls+6OuMOpuijSCW!i2c*Y=`Ll1te_|)|gGf(>wrO3rp2N+o z>y zZgw3yVK+p6aKZJZy*q*@xC{|m+o!ibttQIP;{a8dLUpv(4quKXCXMNDT^Ak6p^N9* zA3P?+AeZJn8YXX>rIT>q)v5&crH17@*CLF{?)oE|QWUl#i_I4hTlh_lvLL4Ah`Y%Qg4p`z#mFfN|An6#f6ReRp}NKrl^b_F`U#%?@20$ zm6*#{h`|mHE4v=PB|*^KbX*TWGI{Q|FZSbu4irrX*XRO3flAe>40HmA5@zUa5Y^{F z!$re`-O8G_oIL9A)X`n=8vvdN@lVxB7USuQ9+D};km}~h<+SUV?dh?&Ippw`LXGR4 za;i>RQ~YrUCq)>P#GF&?1xjg(^TN-Z)^{RBaYUuDgm2o}-3n*K)nYh&c}pgRausjC zpRHrsRkM!pUd)OWYus`BL`*0K=*T!%ZHmGp#f~2`4edh(tQ0@D'khzg^K`B)S` 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≶PIs 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 So6fI4Lo`)&RZ5iL0sjRb>b)=k literal 0 HcmV?d00001 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b27e5df..56ff20c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,15 +1,53 @@ PODS: - Flutter (1.0.0) + - fluttertoast (0.0.2): + - Flutter + - Toast + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - package_info_plus (0.4.5): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - Toast (4.0.0) DEPENDENCIES: - Flutter (from `Flutter`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + +SPEC REPOS: + trunk: + - FMDB + - Toast EXTERNAL SOURCES: Flutter: :path: Flutter + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e + path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 PODFILE CHECKSUM: b634fd49380cdd3837626153fb977533b1916433 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 51aaac1..93b743f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -139,6 +139,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 9A8FF4FA508F9F04A7721B23 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -249,6 +250,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + 9A8FF4FA508F9F04A7721B23 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/lib/config/constant.dart b/lib/config/constant.dart new file mode 100644 index 0000000..881b614 --- /dev/null +++ b/lib/config/constant.dart @@ -0,0 +1,28 @@ +// ignore_for_file: constant_identifier_names + +/* +this is constant pages + */ + +import 'package:flutter/material.dart'; + +const String APP_NAME = 'Mobile DR'; + +// color for apps +const Color PRIMARY_COLOR = Color(0xFF07ac12); +const Color ASSENT_COLOR = Color(0xFFe75f3f); + +const Color CHARCOAL = Color(0xFF515151); +const Color BLACK_GREY = Color(0xff777777); +const Color SOFT_GREY = Color(0xFFaaaaaa); +const Color SOFT_BLUE = Color(0xff01aed6); + +const String ERROR_OCCURED = 'Error occured, please try again later'; + +const int LIMIT_PAGE = 8; + +const String GLOBAL_URL = 'https://ijtechnology.net/assets/images/api/devkit'; +//const String GLOBAL_URL = 'http://192.168.0.4/devkit'; +//const String GLOBAL_URL = 'http://192.168.100.9/devkit'; + +const String LOCAL_IMAGES_URL = 'assets/images'; diff --git a/lib/config/global_style.dart b/lib/config/global_style.dart new file mode 100644 index 0000000..afca780 --- /dev/null +++ b/lib/config/global_style.dart @@ -0,0 +1,168 @@ +/* +this is global style +This file is used to styling a whole application + */ + +import 'package:mobdr/config/constant.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class GlobalStyle { + // appBar + static const Color appBarIconThemeColor = Colors.black; + static const double appBarElevation = 0; + static const SystemUiOverlayStyle appBarSystemOverlayStyle = + SystemUiOverlayStyle.dark; + static const Color appBarBackgroundColor = Colors.white; + static const TextStyle appBarTitle = + TextStyle(fontSize: 18, color: Colors.black); + /* + this is used for height at product card using gridDelegate + if you change font size or using custom font such as google font, sometimes there is an error said + "Bottom overflowed by xx pixel" depends on the font height (Every font has a different height) + so you need to change below + */ + static const double gridDelegateRatio = 0.625; // lower is more longer + static const double gridDelegateFlashsaleRatio = + 0.597; // lower is more longer + static const double horizontalProductHeightMultiplication = + 1.90; // higher is more longer + + // styling product card + static const TextStyle productName = TextStyle(fontSize: 12, color: CHARCOAL); + + static const TextStyle productPrice = + TextStyle(fontSize: 13, fontWeight: FontWeight.bold); + + static const TextStyle productPriceDiscounted = TextStyle( + fontSize: 12, + color: SOFT_GREY, + decoration: TextDecoration.lineThrough, + ); + + static const TextStyle productSale = + TextStyle(fontSize: 11, color: SOFT_GREY); + + static const TextStyle productLocation = + TextStyle(fontSize: 11, color: SOFT_GREY); + + static const TextStyle productTotalReview = + TextStyle(fontSize: 11, color: SOFT_GREY); + + // search filter + static const TextStyle filterTitle = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + + // detail product + static const TextStyle detailProductPrice = + TextStyle(fontSize: 20, fontWeight: FontWeight.bold); + + static const TextStyle sectionTitle = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + + static const TextStyle viewAll = TextStyle( + fontSize: 13, fontWeight: FontWeight.bold, color: PRIMARY_COLOR); + + static const TextStyle paymentTotalPrice = + TextStyle(fontSize: 14, fontWeight: FontWeight.bold); + + // delivery + static const TextStyle deliveryPrice = TextStyle(fontSize: 15); + + static const TextStyle deliveryTotalPrice = + TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: ASSENT_COLOR); + + static const TextStyle chooseCourier = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + + static const TextStyle courierTitle = + TextStyle(fontSize: 15, fontWeight: FontWeight.bold); + + static const TextStyle courierType = TextStyle(fontSize: 15, color: CHARCOAL); + + // shopping cart + static const TextStyle shoppingCartTotalPrice = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: ASSENT_COLOR); + + static const TextStyle shoppingCartOtherProduct = TextStyle( + fontSize: 12, + ); + + // account information + static const TextStyle accountInformationLabel = + TextStyle(fontSize: 15, color: BLACK_GREY, fontWeight: FontWeight.normal); + + static const TextStyle accountInformationValue = + TextStyle(fontSize: 16, color: Colors.black); + + static const TextStyle accountInformationEdit = TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: PRIMARY_COLOR, + ); + + // address + static const TextStyle addressTitle = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + + static const TextStyle addressContent = TextStyle(fontSize: 12); + + static const TextStyle addressAction = + TextStyle(fontSize: 14, color: PRIMARY_COLOR); + + // verification + static const TextStyle chooseVerificationTitle = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: CHARCOAL); + + static const TextStyle chooseVerificationMessage = + TextStyle(fontSize: 13, color: BLACK_GREY); + + static const TextStyle methodTitle = + TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: CHARCOAL); + + static const TextStyle methodMessage = + TextStyle(fontSize: 13, color: BLACK_GREY); + + static const TextStyle notReceiveCode = + TextStyle(fontSize: 13, color: SOFT_GREY); + + static const TextStyle resendVerification = + TextStyle(fontSize: 13, color: PRIMARY_COLOR); + + static const Icon iconBack = + Icon(Icons.arrow_back, size: 16, color: PRIMARY_COLOR); + + static const TextStyle back = + TextStyle(color: PRIMARY_COLOR, fontWeight: FontWeight.w700); + + // authentication + static const TextStyle authTitle = TextStyle( + fontSize: 14, fontWeight: FontWeight.bold, color: PRIMARY_COLOR); + + static const TextStyle authNotes = TextStyle(fontSize: 13, color: SOFT_GREY); + + static const TextStyle authSignWith = + TextStyle(fontSize: 13, color: SOFT_GREY); + + static const TextStyle authBottom1 = + TextStyle(fontSize: 13, color: SOFT_GREY); + + static const TextStyle authBottom2 = + TextStyle(fontSize: 13, color: PRIMARY_COLOR); + + static const TextStyle resetPasswordNotes = + TextStyle(fontSize: 14, color: SOFT_GREY); + + // coupon + static const TextStyle couponLimitedOffer = + TextStyle(fontSize: 11, color: Colors.white); + + static const TextStyle couponName = + TextStyle(fontSize: 15, fontWeight: FontWeight.bold); + + static const TextStyle couponExpired = + TextStyle(fontSize: 13, color: SOFT_GREY); + + static const Icon iconTime = + Icon(Icons.access_time, size: 14, color: SOFT_GREY); +} diff --git a/lib/library/flutter_overboard/LICENSE.md b/lib/library/flutter_overboard/LICENSE.md new file mode 100644 index 0000000..8660970 --- /dev/null +++ b/lib/library/flutter_overboard/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Aakash Kumar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/library/flutter_overboard/circular_clipper.dart b/lib/library/flutter_overboard/circular_clipper.dart new file mode 100644 index 0000000..690f15f --- /dev/null +++ b/lib/library/flutter_overboard/circular_clipper.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class CircularClipper extends CustomClipper { + final Offset? center; + final double fraction; + + CircularClipper(this.fraction, [this.center]); + @override + Rect getClip(Size size) { + Rect rect = Rect.fromCircle( + center: center ?? Offset(size.width / 2, size.height / 2), + radius: size.height * this.fraction); + return rect; + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return true; + } +} diff --git a/lib/library/flutter_overboard/overboard.dart b/lib/library/flutter_overboard/overboard.dart new file mode 100644 index 0000000..f028586 --- /dev/null +++ b/lib/library/flutter_overboard/overboard.dart @@ -0,0 +1,396 @@ +import 'dart:math'; + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/library/flutter_overboard/circular_clipper.dart'; +import 'package:mobdr/library/flutter_overboard/overboard_animator.dart'; +import 'package:mobdr/library/flutter_overboard/page_model.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +enum SwipeDirection { LEFT_TO_RIGHT, RIGHT_TO_LEFT, SKIP_TO_LAST } + +class OverBoard extends StatefulWidget { + final List pages; + final Offset? center; + final bool? showBullets; + final VoidCallback finishCallback; + final VoidCallback? skipCallback; + final OverBoardAnimator? animator; + final String? skipText, nextText, finishText; + + OverBoard( + {Key? key, + required this.pages, + this.center, + this.showBullets, + this.skipText, + this.nextText, + this.finishText, + required this.finishCallback, + this.animator, + this.skipCallback}) + : super(key: key); + + @override + _OverBoardState createState() => _OverBoardState(); +} + +class _OverBoardState extends State with TickerProviderStateMixin { + late OverBoardAnimator _animator; + + ScrollController _scrollController = new ScrollController(); + double _bulletPadding = 5.0, _bulletSize = 10.0, _bulletContainerWidth = 0; + + int _counter = 0, _last = 0; + int _total = 0; + double initial = 0, distance = 0; + Random random = new Random(); + SwipeDirection _swipeDirection = SwipeDirection.RIGHT_TO_LEFT; + + @override + void initState() { + super.initState(); + + _animator = new OverBoardAnimator(this); + _total = widget.pages.length; + _animate(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: _getStack(), + ); + } + + _getStack() { + return GestureDetector( + onPanStart: (DragStartDetails details) { + initial = details.globalPosition.dx; + }, + onPanUpdate: (DragUpdateDetails details) { + distance = details.globalPosition.dx - initial; + }, + onPanEnd: (DragEndDetails details) { + initial = 0.0; + setState(() { + _last = _counter; + }); + if (distance > 1 && _counter > 0) { + setState(() { + _counter--; + _swipeDirection = SwipeDirection.LEFT_TO_RIGHT; + }); + _animate(); + } else if (distance < 0 && _counter < _total - 1) { + setState(() { + _counter++; + _swipeDirection = SwipeDirection.RIGHT_TO_LEFT; + }); + _animate(); + } + }, + child: Stack( + children: [ + _getPage(_last), + AnimatedBuilder( + animation: _animator.getAnimator(), + builder: (context, child) { + return ClipOval( + clipper: CircularClipper( + _animator.getAnimator().value, widget.center), + child: _getPage(_counter)); + }, + child: Container(), + ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Opacity( + child: TextButton( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + ), + onPressed: (widget.skipCallback != null + ? widget.skipCallback + : _skip), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18), + child: Text(widget.skipText ?? "SKIP"), + )), + opacity: (_counter < _total - 1) ? 1.0 : 0.0, + ), + Expanded( + child: Center(child: LayoutBuilder( + builder: (context, constraints) { + _bulletContainerWidth = constraints.maxWidth - 40.0; + return Container( + padding: const EdgeInsets.all(20.0), + child: ((widget.showBullets ?? true) + ? SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + scrollDirection: Axis.horizontal, + controller: _scrollController, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (int i = 0; i < _total; i++) + Padding( + padding: EdgeInsets.all(_bulletPadding), + child: AnimatedContainer( + duration: + Duration(milliseconds: 150), + height: _bulletSize, + width: (i == _counter) + ? _bulletSize * 2 + : _bulletSize, + decoration: BoxDecoration( + color: (i == _counter) + ? PRIMARY_COLOR + : Colors.grey[300], + borderRadius: + BorderRadius.circular(10))), + ) + ], + ), + ) + : Container()), + ); + }, + )), + ), + (_counter < _total - 1 + ? TextButton( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + ), + onPressed: _next, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text(widget.nextText ?? "NEXT"), + )) + : TextButton( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + ), + onPressed: widget.finishCallback, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text(widget.finishText ?? "FINISH"), + ))), + ], + ), + ), + ], + ), + ); + } + + _getPage(index) { + PageModel page = widget.pages[index]; + return Container( + width: double.infinity, + height: double.infinity, + color: page.color, + child: page.child != null + ? Center( + child: page.doAnimateChild! + ? AnimatedBoard( + animator: _animator, + child: page.child, + ) + : page.child, + ) + : (kIsWeb) + ? SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: 64), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + page.doAnimateImage! + ? AnimatedBoard( + animator: _animator, + child: new Padding( + padding: new EdgeInsets.only(bottom: 25.0), + child: (page.imageAssetPath != null) + ? new Image.asset(page.imageAssetPath!, + width: 300.0, height: 300.0) + : buildCacheNetworkImage( + width: 300, + height: 300, + url: page.imageFromUrl, + plColor: Colors.transparent), + ), + ) + : new Padding( + padding: new EdgeInsets.only(bottom: 25.0), + child: (page.imageAssetPath != null) + ? new Image.asset(page.imageAssetPath!, + width: 300.0, height: 300.0) + : buildCacheNetworkImage( + width: 300, + height: 300, + url: page.imageFromUrl, + plColor: Colors.transparent), + ), + Padding( + padding: new EdgeInsets.only( + top: 10.0, bottom: 30.0, left: 30.0, right: 30.0), + child: new Text( + page.title!, + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black, + fontSize: 24.0, + fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: new EdgeInsets.only( + bottom: 75.0, left: 30.0, right: 30.0), + child: new Text( + page.body!, + textAlign: TextAlign.center, + style: new TextStyle( + color: SOFT_GREY, + fontSize: 15, + ), + ), + ), + ], + ), + ), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + page.doAnimateImage! + ? AnimatedBoard( + animator: _animator, + child: new Padding( + padding: new EdgeInsets.only(bottom: 25.0), + child: (page.imageAssetPath != null) + ? new Image.asset(page.imageAssetPath!, + width: 300.0, height: 300.0) + : buildCacheNetworkImage( + width: 300, + height: 300, + url: page.imageFromUrl, + plColor: Colors.transparent), + ), + ) + : new Padding( + padding: new EdgeInsets.only(bottom: 25.0), + child: (page.imageAssetPath != null) + ? new Image.asset(page.imageAssetPath!, + width: 300.0, height: 300.0) + : buildCacheNetworkImage( + width: 300, + height: 300, + url: page.imageFromUrl, + plColor: Colors.transparent), + ), + Padding( + padding: new EdgeInsets.only( + top: 10.0, bottom: 30.0, left: 30.0, right: 30.0), + child: new Text( + page.title!, + textAlign: TextAlign.center, + style: new TextStyle( + color: Colors.black, + fontSize: 24.0, + fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: new EdgeInsets.only( + bottom: 75.0, left: 30.0, right: 30.0), + child: new Text( + page.body!, + textAlign: TextAlign.center, + style: new TextStyle( + color: SOFT_GREY, + fontSize: 15, + ), + ), + ), + ], + ), + ); + } + + _next() { + setState(() { + _swipeDirection = SwipeDirection.RIGHT_TO_LEFT; + _last = _counter; + _counter++; + }); + _animate(); + } + + _skip() { + setState(() { + _swipeDirection = SwipeDirection.SKIP_TO_LAST; + _last = _counter; + _counter = _total - 1; + }); + _animate(); + } + + _animate() { + _animator.getController().forward(from: 0.0); + + double _bulletDimension = (_bulletPadding * 2) + (_bulletSize); + double _scroll = _bulletDimension * _counter; + double _maxScroll = _bulletDimension * _total - 1; + if (_scroll > _bulletContainerWidth && + _swipeDirection == SwipeDirection.RIGHT_TO_LEFT) { + double _scrollDistance = + (((_scroll - _bulletContainerWidth) ~/ _bulletDimension) + 1) * + _bulletDimension; + _scrollController.animateTo(_scrollDistance, + curve: Curves.easeIn, duration: Duration(milliseconds: 100)); + } else if (_scroll < (_maxScroll - _bulletContainerWidth) && + _swipeDirection == SwipeDirection.LEFT_TO_RIGHT) { + _scrollController.animateTo(_scroll, + curve: Curves.easeIn, duration: Duration(milliseconds: 100)); + } else if (_swipeDirection == SwipeDirection.SKIP_TO_LAST) { + _scrollController.animateTo(_maxScroll, + curve: Curves.easeIn, duration: Duration(milliseconds: 100)); + } + } +} + +class AnimatedBoard extends StatelessWidget { + final Widget? child; + final OverBoardAnimator? animator; + + const AnimatedBoard({Key? key, this.animator, this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Transform( + transform: new Matrix4.translationValues( + 0.0, 50.0 * (1.0 - animator!.getAnimator().value), 0.0), + child: new Padding( + padding: new EdgeInsets.only(bottom: 25.0), + child: child, + ), + ); + } +} diff --git a/lib/library/flutter_overboard/overboard_animator.dart b/lib/library/flutter_overboard/overboard_animator.dart new file mode 100644 index 0000000..eb6343b --- /dev/null +++ b/lib/library/flutter_overboard/overboard_animator.dart @@ -0,0 +1,27 @@ +import 'package:flutter/widgets.dart'; + +class OverBoardAnimator { + late TickerProvider _vsync; + late AnimationController _controller; + late Animation _animation; + + OverBoardAnimator(vsync) { + this._vsync = vsync; + _controller = AnimationController( + vsync: _vsync, duration: const Duration(milliseconds: 500)); + _animation = CurvedAnimation(parent: _controller, curve: Curves.easeIn); + //print('creating new animator'); + } + + AnimationController getController() { + return _controller; + } + + Animation getAnimator() { + return _animation; + } + + dispose() { + _controller.dispose(); + } +} diff --git a/lib/library/flutter_overboard/page_model.dart b/lib/library/flutter_overboard/page_model.dart new file mode 100644 index 0000000..49778fb --- /dev/null +++ b/lib/library/flutter_overboard/page_model.dart @@ -0,0 +1,25 @@ +import 'package:flutter/cupertino.dart'; + +class PageModel { + Color color; + String? imageAssetPath; + String? imageFromUrl; + String? title; + String? body; + Widget? child; + bool? doAnimateChild; + bool? doAnimateImage; + + PageModel( + {required this.color, + this.imageAssetPath, + this.imageFromUrl, + required this.title, + required this.body, + required this.doAnimateImage}); + + PageModel.withChild( + {required this.child, + required this.color, + required this.doAnimateChild}); +} diff --git a/lib/main.dart b/lib/main.dart index bb9e508..b46fea0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,115 +1,48 @@ +// ignore_for_file: prefer_const_constructors + +import 'dart:ui'; + +import 'package:mobdr/config/constant.dart'; + +import 'package:mobdr/ui/splash_screen.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; void main() { - runApp(const MyApp()); + // this function makes application always run in portrait mode + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .then((_) { + runApp(MyApp()); + }); +} + +class MyCustomScrollBehavior extends MaterialScrollBehavior { + // Override behavior methods and getters like dragDevices + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + // etc. + }; } class MyApp extends StatelessWidget { - const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + scrollBehavior: MyCustomScrollBehavior(), + title: APP_NAME, + debugShowCheckedModeBanner: false, 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, + visualDensity: VisualDensity.adaptivePlatformDensity, + pageTransitionsTheme: PageTransitionsTheme(builders: { + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.android: ZoomPageTransitionsBuilder(), + }), ), - home: const MyHomePage(title: 'Mobile DR'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // 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++; - }); - } - - @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.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + home: SplashScreenPage(), ); } } diff --git a/lib/model/address_model.dart b/lib/model/address_model.dart new file mode 100644 index 0000000..e3588f5 --- /dev/null +++ b/lib/model/address_model.dart @@ -0,0 +1,71 @@ +class AddressModel { + late int id; + late String title; + late String recipientName; + late String phoneNumber; + late String addressLine1; + late String addressLine2; + late String state; + late String postalCode; + late bool defaultAddress; + + AddressModel({required this.id, required this.title, required this.recipientName, required this.phoneNumber, required this.addressLine1, required this.addressLine2, required this.state, required this.postalCode, required this.defaultAddress}); +} + +List addressData = [ + AddressModel( + id: 1, + title: 'Home Address', + recipientName: 'Robert Steven', + phoneNumber: '0811888999', + addressLine1: '6019 Madison St', + addressLine2: 'West New York, NJ', + state: 'USA', + postalCode: '07093', + defaultAddress: true + ), + AddressModel( + id: 2, + title: 'Apartment Address', + recipientName: 'Robert Steven', + phoneNumber: '0811888999', + addressLine1: 'Meridia Park Avenue Apartments', + addressLine2: '6035 Park Ave, West New York, NJ', + state: 'USA', + postalCode: '07093', + defaultAddress: false + ), + AddressModel( + id: 3, + title: 'Office Address', + recipientName: 'Robert Steven', + phoneNumber: '0811888999', + addressLine1: '6015 Van Buren Pl', + addressLine2: 'West New York, NJ', + state: 'USA', + postalCode: '07093', + defaultAddress: false + ), + AddressModel( + id: 4, + title: 'Mom Address', + recipientName: 'Stephanie', + phoneNumber: '0811564855', + addressLine1: '7503 2nd Ave', + addressLine2: 'North Bergen, NJ', + state: 'USA', + postalCode: '07047', + defaultAddress: false + ), + AddressModel( + id: 5, + title: 'Anthony Address', + recipientName: 'Anthony Daniel', + phoneNumber: '0811118997', + addressLine1: '223-201 62nd St', + addressLine2: 'West New York, NJ', + state: 'USA', + postalCode: '07093', + defaultAddress: false + ), +]; \ No newline at end of file diff --git a/lib/model/category_all_product_model.dart b/lib/model/category_all_product_model.dart new file mode 100644 index 0000000..c2c3ddd --- /dev/null +++ b/lib/model/category_all_product_model.dart @@ -0,0 +1,118 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryAllProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late String location; + + CategoryAllProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.location}); +} + +List categoryAllProductData = [ + CategoryAllProductModel( + id: 1, + name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575', + price: 1643, + image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg', + rating: 5, + review: 2, + sale: 3, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 2, + name: 'iPhone SE 2020', + price: 399, + image: GLOBAL_URL + '/apps/ecommerce/product/78.png', + rating: 5, + review: 219, + sale: 23, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 3, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + sale: 69, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 4, + name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver', + price: 866, + image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg', + rating: 5, + review: 22, + sale: 468, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 5, + name: + 'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone', + price: 219, + image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg', + rating: 5, + review: 934, + sale: 1881, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 6, + name: + 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air', + price: 22.66, + image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg', + rating: 5, + review: 131, + sale: 466, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 7, + name: 'iPod Touch 2019 7th Generation - 32GB SpaceGrey MVHW2', + price: 242, + image: GLOBAL_URL + '/apps/ecommerce/product/79.jpg', + rating: 5, + review: 11, + sale: 17, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 8, + name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray', + price: 189, + image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg', + rating: 5, + review: 36, + sale: 112, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 9, + name: 'NEW Original Apple TV 4K 64GB 5th Generation', + price: 261, + image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg', + rating: 5, + review: 98, + sale: 23, + location: 'Brooklyn'), + CategoryAllProductModel( + id: 10, + name: + 'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage', + price: 1099, + image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg', + rating: 5, + review: 78, + sale: 132, + location: 'Brooklyn') +]; diff --git a/lib/model/category_banner_model.dart b/lib/model/category_banner_model.dart new file mode 100644 index 0000000..cc9de6b --- /dev/null +++ b/lib/model/category_banner_model.dart @@ -0,0 +1,17 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryBannerModel { + late int id; + late String image; + + CategoryBannerModel({required this.id, required this.image}); +} + +List categoryBannerData = [ + CategoryBannerModel( + id: 1, image: GLOBAL_URL + '/apps/ecommerce/category_banner/1.jpg'), + CategoryBannerModel( + id: 2, image: GLOBAL_URL + '/apps/ecommerce/category_banner/2.jpg'), + CategoryBannerModel( + id: 3, image: GLOBAL_URL + '/apps/ecommerce/category_banner/3.jpg'), +]; diff --git a/lib/model/category_for_you_model.dart b/lib/model/category_for_you_model.dart new file mode 100644 index 0000000..0a3fdb4 --- /dev/null +++ b/lib/model/category_for_you_model.dart @@ -0,0 +1,23 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryForYouModel { + late int id; + late String image; + + CategoryForYouModel({required this.id, required this.image}); +} + +List categoryForYouData = [ + CategoryForYouModel( + id: 7, + image: GLOBAL_URL + '/apps/ecommerce/category_for_you/1.jpg', + ), + CategoryForYouModel( + id: 3, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/2.jpg'), + CategoryForYouModel( + id: 5, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/3.jpg'), + CategoryForYouModel( + id: 2, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/4.jpg'), + CategoryForYouModel( + id: 8, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/5.jpg'), +]; diff --git a/lib/model/category_model.dart b/lib/model/category_model.dart new file mode 100644 index 0000000..a9d8c15 --- /dev/null +++ b/lib/model/category_model.dart @@ -0,0 +1,57 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryModel { + late int id; + late String name; + late String image; + + CategoryModel({required this.id, required this.name, required this.image}); +} + +/* +Category Data Information +width = 110px +height = 110px + */ +List categoryData = [ + CategoryModel( + id: 1, + name: 'Fashion', + image: GLOBAL_URL + '/apps/ecommerce/category/fashion.png', + ), + CategoryModel( + id: 2, + name: 'Smartphone & Tablets', + image: GLOBAL_URL + '/apps/ecommerce/category/smartphone.png', + ), + CategoryModel( + id: 3, + name: 'Electronic', + image: GLOBAL_URL + '/apps/ecommerce/category/electronic.png', + ), + CategoryModel( + id: 4, + name: 'Otomotif', + image: GLOBAL_URL + '/apps/ecommerce/category/otomotif.png', + ), + CategoryModel( + id: 5, + name: 'Sport', + image: GLOBAL_URL + '/apps/ecommerce/category/sport.png', + ), + CategoryModel( + id: 6, + name: 'Food', + image: GLOBAL_URL + '/apps/ecommerce/category/food.png', + ), + CategoryModel( + id: 7, + name: 'Voucher\nGame', + image: GLOBAL_URL + '/apps/ecommerce/category/voucher_game.png', + ), + CategoryModel( + id: 8, + name: 'Health & Care', + image: GLOBAL_URL + '/apps/ecommerce/category/health.png', + ), +]; diff --git a/lib/model/category_new_product_model.dart b/lib/model/category_new_product_model.dart new file mode 100644 index 0000000..1781bc6 --- /dev/null +++ b/lib/model/category_new_product_model.dart @@ -0,0 +1,80 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryNewProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + + CategoryNewProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review}); +} + +List categoryNewProductData = [ + CategoryNewProductModel( + id: 1, + name: + 'Converter APPLE USB-C To 3.5 mm Headphone Jack Adapter New Ipad Pro', + price: 10, + image: GLOBAL_URL + '/apps/ecommerce/product/71.jpg', + rating: 5, + review: 54), + CategoryNewProductModel( + id: 2, + name: + 'Charger adapter New Macbook Pro Retina 13 inch Apple 2017 2018 61w ori', + price: 46, + image: GLOBAL_URL + '/apps/ecommerce/product/72.jpg', + rating: 5, + review: 4), + CategoryNewProductModel( + id: 3, + name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray', + price: 189, + image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg', + rating: 5, + review: 24), + CategoryNewProductModel( + id: 4, + name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23', + price: 1369, + image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg', + rating: 0, + review: 0), + CategoryNewProductModel( + id: 5, + name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black', + price: 179, + image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg', + rating: 5, + review: 120), + CategoryNewProductModel( + id: 6, + name: 'Magic Mouse 2 - Space Gray', + price: 99, + image: GLOBAL_URL + '/apps/ecommerce/product/76.jpg', + rating: 5, + review: 174), + CategoryNewProductModel( + id: 7, + name: + 'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage', + price: 1099, + image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg', + rating: 5, + review: 78), + CategoryNewProductModel( + id: 8, + name: 'iPhone SE 2020', + price: 399, + image: GLOBAL_URL + '/apps/ecommerce/product/78.png', + rating: 5, + review: 219), +]; diff --git a/lib/model/category_trending_product_model.dart b/lib/model/category_trending_product_model.dart new file mode 100644 index 0000000..4bb6f11 --- /dev/null +++ b/lib/model/category_trending_product_model.dart @@ -0,0 +1,85 @@ +import 'package:mobdr/config/constant.dart'; + +class CategoryTrendingProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + + CategoryTrendingProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review}); +} + +List categoryTrendingProductData = [ + CategoryTrendingProductModel( + id: 1, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + ), + CategoryTrendingProductModel( + id: 2, + name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver', + price: 866, + image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg', + rating: 5, + review: 22, + ), + CategoryTrendingProductModel( + id: 3, + name: 'iphone 7 Plus / 7+ 128GB', + price: 433, + image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg', + rating: 5, + review: 129, + ), + CategoryTrendingProductModel( + id: 4, + name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575', + price: 1643, + image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg', + rating: 5, + review: 2, + ), + CategoryTrendingProductModel( + id: 5, + name: + 'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone', + price: 219, + image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg', + rating: 5, + review: 934, + ), + CategoryTrendingProductModel( + id: 6, + name: 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air', + price: 22.66, + image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg', + rating: 5, + review: 131, + ), + CategoryTrendingProductModel( + id: 7, + name: 'NEW Original Apple TV 4K 64GB 5th Generation', + price: 261, + image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg', + rating: 5, + review: 98, + ), + CategoryTrendingProductModel( + id: 8, + name: 'Apple Pencil 1 - Original Apple Garansi Resmi Inter', + price: 99, + image: GLOBAL_URL + '/apps/ecommerce/product/70.jpg', + rating: 5, + review: 8) +]; diff --git a/lib/model/chat_model.dart b/lib/model/chat_model.dart new file mode 100644 index 0000000..83e4ad5 --- /dev/null +++ b/lib/model/chat_model.dart @@ -0,0 +1,38 @@ +/* +In this model, we give example about using private variable + */ + +class ChatModel { + int _id; + String? _type; + String _isTextImageDate; + String _message; + String? _date; + bool? _read; + + ChatModel(this._id, this._type, this._isTextImageDate, this._message, this._date, this._read); + + int get getId{ + return _id; + } + + String? get getType{ + return _type; + } + + String get getTextImageDate{ + return _isTextImageDate; + } + + String get getMessage{ + return _message; + } + + String? get getDate{ + return _date; + } + + bool? get getRead{ + return _read; + } +} \ No newline at end of file diff --git a/lib/model/coupon_model.dart b/lib/model/coupon_model.dart new file mode 100644 index 0000000..aa3d7ec --- /dev/null +++ b/lib/model/coupon_model.dart @@ -0,0 +1,101 @@ +class CouponModel { + late int id; + late String name; + late String day; + late String term; + + CouponModel({required this.id, required this.name, required this.day, required this.term}); +} + +List couponData =[ + CouponModel( + id: 1, + name: 'FASHION50', + day: '2', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 2, + name: 'EVERY20', + day: '6', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 3, + name: 'COC40', + day: '3', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 4, + name: 'PUBG70', + day: '1', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 5, + name: 'HEADSET10', + day: '1', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 6, + name: 'ENDOFMONTH', + day: '5', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 7, + name: 'LUCKYPRIZED', + day: '7', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 8, + name: 'RANDOMSALE', + day: '14', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 9, + name: 'HEALTH15', + day: '3', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), + CouponModel( + id: 10, + name: 'RIDER35', + day: '4', + term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+ + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+ + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+ + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.' + ), +]; \ No newline at end of file diff --git a/lib/model/flashsale_model.dart b/lib/model/flashsale_model.dart new file mode 100644 index 0000000..623d689 --- /dev/null +++ b/lib/model/flashsale_model.dart @@ -0,0 +1,201 @@ +import 'package:mobdr/config/constant.dart'; + +class FlashsaleModel { + late int id; + late String name; + late double price; + late String image; + late int discount; + late double countItem; + late int sale; + + FlashsaleModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.discount, + required this.countItem, + required this.sale}); +} + +/* +Flashsale Data Information +width = 700px +height = 700px + */ +List flashsaleData = [ + FlashsaleModel( + id: 1, + name: + 'BARDI Smart IP Camera CCTV Wifi IoT HomeAutomation Support iOS Android', + price: 32.3, + image: GLOBAL_URL + '/apps/ecommerce/product/1.jpg', + discount: 35, + countItem: 20, + sale: 17), + FlashsaleModel( + id: 2, + name: 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60', + price: 9, + image: GLOBAL_URL + '/apps/ecommerce/product/2.jpg', + discount: 20, + countItem: 20, + sale: 19), + FlashsaleModel( + id: 3, + name: + 'CAFELE Premium Light Glass Case - iPhone 11 Pro iPhone 11 Pro Max - iP 11 Pro Max', + price: 12.85, + image: GLOBAL_URL + '/apps/ecommerce/product/3.jpg', + discount: 30, + countItem: 60, + sale: 13), + FlashsaleModel( + id: 4, + name: + 'Logitech G502 Hero / Mouse Logitech G 502 Hero Original Garansi Resmi', + price: 68.25, + image: GLOBAL_URL + '/apps/ecommerce/product/4.jpg', + discount: 20, + countItem: 130, + sale: 70), + FlashsaleModel( + id: 5, + name: 'Pioneer SE-C5TW TWS Bluetooth Truly Wireless Earphones - Hitam', + price: 56.85, + image: GLOBAL_URL + '/apps/ecommerce/product/5.jpg', + discount: 15, + countItem: 20, + sale: 10), + FlashsaleModel( + id: 6, + name: + 'Anker SoundCore Life Note Wireless Earbuds Bluetooth Earphones A3908 - Hitam', + price: 76.6, + image: GLOBAL_URL + '/apps/ecommerce/product/6.jpg', + discount: 40, + countItem: 130, + sale: 99), + FlashsaleModel( + id: 7, + name: + 'Audio Technica AT-LP60X Fully Automatic Belt-Drive Stereo Turntable', + price: 215.58, + image: GLOBAL_URL + '/apps/ecommerce/product/7.jpg', + discount: 20, + countItem: 40, + sale: 30), + FlashsaleModel( + id: 8, + name: 'Xiaomi Deerma CM800 UV Anti Mite Vacuum Cleaner Dust Bed Sofa', + price: 44.28, + image: GLOBAL_URL + '/apps/ecommerce/product/8.jpg', + discount: 30, + countItem: 20, + sale: 15), + FlashsaleModel( + id: 9, + name: + 'Sony SRS- XB12 / XB 12 Extra Bass Portable Bluetooth Speaker - Black', + price: 53.33, + image: GLOBAL_URL + '/apps/ecommerce/product/9.jpg', + discount: 10, + countItem: 41, + sale: 2), + FlashsaleModel( + id: 10, + name: + 'Changhong Google certified Android Smart TV 32 inch 32H4 LED TV-L32H4', + price: 240.61, + image: GLOBAL_URL + '/apps/ecommerce/product/10.jpg', + discount: 46, + countItem: 20, + sale: 18), + FlashsaleModel( + id: 11, + name: 'READY DJI Mavic Air Fly More Combo', + price: 776.67, + image: GLOBAL_URL + '/apps/ecommerce/product/11.jpg', + discount: 10, + countItem: 20, + sale: 20), + FlashsaleModel( + id: 12, + name: + 'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black', + price: 391.76, + image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg', + discount: 15, + countItem: 20, + sale: 19), + FlashsaleModel( + id: 13, + name: + 'QZSD Q-202F - Flat Lay Tripod - Transverse Center Column FREE Holder U', + price: 40.74, + image: GLOBAL_URL + '/apps/ecommerce/product/13.jpg', + discount: 10, + countItem: 20, + sale: 7), + FlashsaleModel( + id: 14, + name: + 'WD My Passport 2TB Hardisk Eksternal 2.5" USB 3.0 HDD External Baru - 1TB, Merah', + price: 63.3, + image: GLOBAL_URL + '/apps/ecommerce/product/14.jpg', + discount: 40, + countItem: 14, + sale: 10), + FlashsaleModel( + id: 15, + name: + 'SanDisk 128GB Kartu Memori 100MB/S Ultra Microsd SD With Adapter Card', + price: 12, + image: GLOBAL_URL + '/apps/ecommerce/product/15.jpg', + discount: 40, + countItem: 140, + sale: 96), + FlashsaleModel( + id: 16, + name: 'SanDisk Extreme Pro USB 3.1 Solid State Flash Drive 256GB', + price: 120, + image: GLOBAL_URL + '/apps/ecommerce/product/16.png', + discount: 40, + countItem: 20, + sale: 4), + FlashsaleModel( + id: 17, + name: 'Harley Davidson Skull & Flames Nylon Bomber Jacket', + price: 104.16, + image: GLOBAL_URL + '/apps/ecommerce/product/17.jpg', + discount: 20, + countItem: 60, + sale: 50), + FlashsaleModel( + id: 18, + name: + 'video intercom doorbell Wireless wifi HD Res - Cloud Storage ESCAM V6', + price: 55.825, + image: GLOBAL_URL + '/apps/ecommerce/product/18.jpg', + discount: 20, + countItem: 20, + sale: 17), + FlashsaleModel( + id: 19, + name: + 'DEADBOLT DOOR LOCK SMART DOOR LOCK / SMART LOCK DOOR MEREK SEYVEN - EZ-TTLOCK', + price: 150, + image: GLOBAL_URL + '/apps/ecommerce/product/19.jpg', + discount: 20, + countItem: 20, + sale: 6), + FlashsaleModel( + id: 20, + name: 'BT-RIDER" GPS NAVIGATION ANDROID 6.0, 5 in WATERPROOF', + price: 191.66, + image: GLOBAL_URL + '/apps/ecommerce/product/20.jpg', + discount: 20, + countItem: 20, + sale: 15), +]; diff --git a/lib/model/home_banner_model.dart b/lib/model/home_banner_model.dart new file mode 100644 index 0000000..05e97be --- /dev/null +++ b/lib/model/home_banner_model.dart @@ -0,0 +1,26 @@ +import 'package:mobdr/config/constant.dart'; + +class HomeBannerModel { + late int id; + late String image; + + HomeBannerModel({required this.id, required this.image}); +} + +/* +Banner Data Information +width = 800px +height = 600px + */ +List homeBannerData = [ + HomeBannerModel( + id: 1, image: GLOBAL_URL + '/apps/ecommerce/home_banner/1.jpg'), + HomeBannerModel( + id: 2, image: GLOBAL_URL + '/apps/ecommerce/home_banner/2.jpg'), + HomeBannerModel( + id: 3, image: GLOBAL_URL + '/apps/ecommerce/home_banner/3.jpg'), + HomeBannerModel( + id: 4, image: GLOBAL_URL + '/apps/ecommerce/home_banner/4.jpg'), + HomeBannerModel( + id: 5, image: GLOBAL_URL + '/apps/ecommerce/home_banner/5.jpg'), +]; diff --git a/lib/model/home_trending_model.dart b/lib/model/home_trending_model.dart new file mode 100644 index 0000000..ad53d4a --- /dev/null +++ b/lib/model/home_trending_model.dart @@ -0,0 +1,42 @@ +import 'package:mobdr/config/constant.dart'; + +class HomeTrendingModel { + late int id; + late String name; + late String image; + late String sale; + + HomeTrendingModel( + {required this.id, + required this.name, + required this.image, + required this.sale}); +} + +/* +Home Trending Data Information +width = 700px +height = 700px + */ +List homeTrendingData = [ + HomeTrendingModel( + id: 1, + name: 'Adidas Shirt', + image: GLOBAL_URL + '/apps/ecommerce/product/21.jpg', + sale: '12.7k'), + HomeTrendingModel( + id: 2, + name: 'iPhone SE 2020', + image: GLOBAL_URL + '/apps/ecommerce/product/22.jpg', + sale: '8k'), + HomeTrendingModel( + id: 3, + name: 'Macbook Air 2020', + image: GLOBAL_URL + '/apps/ecommerce/product/23.jpg', + sale: '31.4k'), + HomeTrendingModel( + id: 4, + name: 'Gaming Chair', + image: GLOBAL_URL + '/apps/ecommerce/product/24.jpg', + sale: '11.9k'), +]; diff --git a/lib/model/last_search_model.dart b/lib/model/last_search_model.dart new file mode 100644 index 0000000..a3b6749 --- /dev/null +++ b/lib/model/last_search_model.dart @@ -0,0 +1,219 @@ +import 'package:mobdr/config/constant.dart'; + +class LastSearchModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late String location; + + LastSearchModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.location}); +} + +/* +Last Search Data Information +width = 700px +height = 700px + */ +List lastSearchData = [ + LastSearchModel( + id: 1, + name: 'Delta Boots Import 8 Inch', + price: 18.3, + image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg', + rating: 5, + review: 212, + sale: 735, + location: 'Brooklyn'), + LastSearchModel( + id: 2, + name: 'Fimi X8 SE Black', + price: 567, + image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg', + rating: 5, + review: 63, + sale: 115, + location: 'Brooklyn', + ), + LastSearchModel( + id: 3, + name: 'Guess Collection Watch Ceramic Type GC 6004 ', + price: 52, + image: GLOBAL_URL + '/apps/ecommerce/product/27.jpg', + rating: 5, + review: 7, + sale: 7, + location: 'Brooklyn'), + LastSearchModel( + id: 4, + name: 'Adidas Football Predator 19.3 FG F35594 Original', + price: 9, + image: GLOBAL_URL + '/apps/ecommerce/product/45.jpg', + rating: 5, + review: 30, + sale: 70, + location: 'Brooklyn'), + LastSearchModel( + id: 5, + name: 'NEW Original Apple TV 4K 64GB 5th Generation', + price: 261, + image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg', + rating: 5, + review: 98, + sale: 263, + location: 'Brooklyn'), + LastSearchModel( + id: 6, + name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB', + price: 751, + image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg', + rating: 5, + review: 14, + sale: 17, + location: 'Brooklyn'), + LastSearchModel( + id: 7, + name: + 'Changhong Google certified Android Smart TV 32 inch 32H4 LED TV-L32H4', + price: 129.9, + image: GLOBAL_URL + '/apps/ecommerce/product/31.jpg', + rating: 5, + review: 701, + sale: 1558, + location: 'Brooklyn'), + LastSearchModel( + id: 8, + name: 'Adidas EQT Adv Premium Original', + price: 28.67, + image: GLOBAL_URL + '/apps/ecommerce/product/32.jpg', + rating: 5, + review: 146, + sale: 398, + location: 'Brooklyn'), + LastSearchModel( + id: 9, + name: + 'Xiaomi Air Purifier 3 Mijia OLED Touch Sterilization Air Ionizer - 3', + price: 139, + image: GLOBAL_URL + '/apps/ecommerce/product/33.jpg', + rating: 5, + review: 275, + sale: 1055, + location: 'Brooklyn'), + LastSearchModel( + id: 10, + name: 'Spatula Set Stainless Kitchen Tools', + price: 2.5, + image: GLOBAL_URL + '/apps/ecommerce/product/34.jpg', + rating: 5, + review: 302, + sale: 752, + location: 'Brooklyn'), + LastSearchModel( + id: 11, + name: + 'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M', + price: 3, + image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg', + rating: 5, + review: 636, + sale: 2087, + location: 'Brooklyn'), + LastSearchModel( + id: 12, + name: 'BASEUS QUICK CHARGER HEAD QC3.0/4.0 TYPE-C+USB 30W PD 5A - USB TC', + price: 10.6, + image: GLOBAL_URL + '/apps/ecommerce/product/36.jpg', + rating: 5, + review: 2802, + sale: 7052, + location: 'Brooklyn'), + LastSearchModel( + id: 13, + name: 'Xiaomi Powerbank MI2C 20000mAh Mi Power Bank 20000 mAh PLM06ZM', + price: 19.9, + image: GLOBAL_URL + '/apps/ecommerce/product/37.jpg', + rating: 5, + review: 105, + sale: 227, + location: 'Brooklyn'), + LastSearchModel( + id: 14, + name: + '3D FASHION MASK WITH BREATHING VALVE / MASKER PM 2.5 KARBON / WASHABLE - BLACK NEW MODEL', + price: 2.33, + image: GLOBAL_URL + '/apps/ecommerce/product/38.jpg', + rating: 5, + review: 503, + sale: 3645, + location: 'Brooklyn'), + LastSearchModel( + id: 15, + name: + 'Robot RT-US04 Table Phone Holder Stand Aluminium Alloy Universal - Pink', + price: 5.3, + image: GLOBAL_URL + '/apps/ecommerce/product/39.jpg', + rating: 5, + review: 1095, + sale: 3400, + location: 'Brooklyn'), + LastSearchModel( + id: 16, + name: 'Tactical Pants Blackhawk Helikon ', + price: 10, + image: GLOBAL_URL + '/apps/ecommerce/product/40.jpg', + rating: 5, + review: 63, + sale: 131, + location: 'Brooklyn'), + LastSearchModel( + id: 17, + name: + 'Sony SRS- XB12 / XB 12 Extra Bass Portable Bluetooth Speaker - Black', + price: 48, + image: GLOBAL_URL + '/apps/ecommerce/product/41.jpg', + rating: 5, + review: 182, + sale: 427, + location: 'Brooklyn'), + LastSearchModel( + id: 18, + name: + 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air', + price: 22.66, + image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg', + rating: 5, + review: 131, + sale: 466, + location: 'Brooklyn'), + LastSearchModel( + id: 19, + name: + 'Macbook Pro 2019 TouchBar MV912 15" 16GB 512GB 2.3GHz 8-core i9 Gray', + price: 2212, + image: GLOBAL_URL + '/apps/ecommerce/product/43.jpg', + rating: 5, + review: 16, + sale: 37, + location: 'Brooklyn'), + LastSearchModel( + id: 20, + name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575', + price: 1643, + image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg', + rating: 5, + review: 2, + sale: 3, + location: 'Brooklyn') +]; diff --git a/lib/model/last_seen_model.dart b/lib/model/last_seen_model.dart new file mode 100644 index 0000000..e2ab677 --- /dev/null +++ b/lib/model/last_seen_model.dart @@ -0,0 +1,215 @@ +import 'package:mobdr/config/constant.dart'; + +class LastSeenModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late String location; + + LastSeenModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.location}); +} + +List lastSeenData = [ + LastSeenModel( + id: 1, + name: + 'BARDI Smart IP Camera CCTV Wifi IoT HomeAutomation Support iOS Android', + price: 21, + image: GLOBAL_URL + '/apps/ecommerce/product/1.jpg', + rating: 4, + review: 462, + sale: 1543, + location: 'Brooklyn'), + LastSeenModel( + id: 2, + name: 'iphone 7 Plus / 7+ 128GB', + price: 433, + image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg', + rating: 5, + review: 129, + sale: 310, + location: 'Brooklyn'), + LastSeenModel( + id: 3, + name: + 'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black', + price: 333, + image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg', + rating: 5, + review: 13, + sale: 33, + location: 'Brooklyn'), + LastSeenModel( + id: 4, + name: 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60', + price: 7.2, + image: GLOBAL_URL + '/apps/ecommerce/product/2.jpg', + rating: 5, + review: 1, + sale: 2, + location: 'Brooklyn'), + LastSeenModel( + id: 5, + name: + 'WD My Passport 2TB Hardisk Eksternal 2.5" USB 3.0 HDD External Baru - 1TB, Merah', + price: 9, + image: GLOBAL_URL + '/apps/ecommerce/product/14.jpg', + rating: 4, + review: 71, + sale: 152, + location: 'Brooklyn'), + LastSeenModel( + id: 6, + name: + 'SanDisk 128GB Kartu Memori 100MB/S Ultra Microsd SD With Adapter Card', + price: 7.2, + image: GLOBAL_URL + '/apps/ecommerce/product/15.jpg', + rating: 4, + review: 656, + sale: 1760, + location: 'Brooklyn'), + LastSeenModel( + id: 7, + name: + 'BARDI Smart UNIVERSAL IR REMOTE Wifi Wireless IoT For Home Automation', + price: 11.5, + image: GLOBAL_URL + '/apps/ecommerce/product/60.jpg', + rating: 5, + review: 1438, + sale: 4956, + location: 'Brooklyn'), + LastSeenModel( + id: 8, + name: + 'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone', + price: 219, + image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg', + rating: 5, + review: 934, + sale: 1881, + location: 'Brooklyn'), + LastSeenModel( + id: 9, + name: + 'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M', + price: 3, + image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg', + rating: 5, + review: 636, + sale: 2087, + location: 'Brooklyn'), + LastSeenModel( + id: 10, + name: + 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air', + price: 22.66, + image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg', + rating: 5, + review: 131, + sale: 466, + location: 'Brooklyn'), + LastSeenModel( + id: 11, + name: + 'iPhone XS Max / XS / X / XR Case Spigen Clear Anti Shock Ultra Hybrid - Matte Black, XS Max', + price: 16.6, + image: GLOBAL_URL + '/apps/ecommerce/product/62.jpg', + rating: 5, + review: 2201, + sale: 5154, + location: 'Brooklyn'), + LastSeenModel( + id: 12, + name: 'TORCH WAIST BAG VALLEJO TOSCA', + price: 4.1, + image: GLOBAL_URL + '/apps/ecommerce/product/63.jpg', + rating: 5, + review: 130, + sale: 302, + location: 'Brooklyn'), + LastSeenModel( + id: 13, + name: 'Bushnell Speed Gun Velocity 101911', + price: 180, + image: GLOBAL_URL + '/apps/ecommerce/product/64.jpg', + rating: 5, + review: 4, + sale: 14, + location: 'Brooklyn'), + LastSeenModel( + id: 14, + name: 'Iphone XR - COPPER Tempered Glass Full Glue PREMIUM Glossy', + price: 3.1, + image: GLOBAL_URL + '/apps/ecommerce/product/65.jpg', + rating: 5, + review: 130, + sale: 460, + location: 'Brooklyn'), + LastSeenModel( + id: 15, + name: 'Digital Thermometer infrared Termometer Gun', + price: 9.9, + image: GLOBAL_URL + '/apps/ecommerce/product/66.jpg', + rating: 5, + review: 270, + sale: 1287, + location: 'Brooklyn'), + LastSeenModel( + id: 16, + name: "L'Oreal Paris Fall Resist Hair Mask", + price: 9, + image: GLOBAL_URL + '/apps/ecommerce/product/67.jpg', + rating: 5, + review: 947, + sale: 3750, + location: 'Brooklyn'), + LastSeenModel( + id: 17, + name: + 'Samyang Noodle Wholesaler 1 Box (40 Pcs) Hot Spicy Chicken - Original Jan 21', + price: 26, + image: GLOBAL_URL + '/apps/ecommerce/product/68.jpg', + rating: 5, + review: 4, + sale: 22, + location: 'Brooklyn'), + LastSeenModel( + id: 18, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + sale: 69, + location: 'Brooklyn'), + LastSeenModel( + id: 19, + name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus', + price: 1152, + image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg', + rating: 5, + review: 1, + sale: 2, + location: 'Brooklyn'), + LastSeenModel( + id: 20, + name: 'Nitendo Switch Console New HAC-001(-01) Neon Blue - Neon Red', + price: 349, + image: GLOBAL_URL + '/apps/ecommerce/product/52.jpg', + rating: 5, + review: 30, + sale: 86, + location: 'Brooklyn') +]; diff --git a/lib/model/order_list_model.dart b/lib/model/order_list_model.dart new file mode 100644 index 0000000..bdfc8a6 --- /dev/null +++ b/lib/model/order_list_model.dart @@ -0,0 +1,59 @@ +import 'package:mobdr/config/constant.dart'; + +class OrderListModel { + late int id; + late String invoice; + late String date; + late String status; + late String name; + late String image; + late double payment; + + OrderListModel( + {required this.id, + required this.invoice, + required this.date, + required this.status, + required this.name, + required this.image, + required this.payment}); +} + +List orderListData = [ + OrderListModel( + id: 1, + invoice: 'INV385739475', + date: '12 August 2020', + status: 'On Process', + name: 'Logitech G502 Hero / Mouse Logitech G 502 Hero Original', + image: GLOBAL_URL + '/apps/ecommerce/product/4.jpg', + payment: 80, + ), + OrderListModel( + id: 2, + invoice: 'INV385714262', + date: '3 September 2020', + status: 'Order Completed', + name: 'Delta Boots Import 8 Inch', + image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg', + payment: 36, + ), + OrderListModel( + id: 3, + invoice: 'INV385776588', + date: '9 September 2020', + status: 'On Delivery', + name: 'Spatula Set Stainless Kitchen Tools', + image: GLOBAL_URL + '/apps/ecommerce/product/34.jpg', + payment: 9.6, + ), + OrderListModel( + id: 4, + invoice: 'INV385798021', + date: '13 September 2020', + status: 'Waiting for payment', + name: 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air', + image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg', + payment: 38, + ), +]; diff --git a/lib/model/recomended_product_model.dart b/lib/model/recomended_product_model.dart new file mode 100644 index 0000000..866ac5e --- /dev/null +++ b/lib/model/recomended_product_model.dart @@ -0,0 +1,215 @@ +import 'package:mobdr/config/constant.dart'; + +class RecomendedProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late String location; + + RecomendedProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.location}); +} + +/* +Recomended Product Data Information +width = 700px +height = 700px + */ +List recomendedProductData = [ + RecomendedProductModel( + id: 1, + name: 'iphone 7 Plus / 7+ 128GB', + price: 433, + image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg', + rating: 5, + review: 129, + sale: 310, + location: 'Brooklyn'), + RecomendedProductModel( + id: 2, + name: + 'Macbook Pro 2019 TouchBar MV912 15" 16GB 512GB 2.3GHz 8-core i9 Gray', + price: 2212, + image: GLOBAL_URL + '/apps/ecommerce/product/43.jpg', + rating: 5, + review: 16, + sale: 37, + location: 'Brooklyn'), + RecomendedProductModel( + id: 3, + name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575', + price: 1643, + image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg', + rating: 5, + review: 2, + sale: 3, + location: 'Brooklyn'), + RecomendedProductModel( + id: 4, + name: + 'DEADBOLT DOOR LOCK SMART DOOR LOCK / SMART LOCK DOOR MEREK SEYVEN - EZ-TTLOCK', + price: 120, + image: GLOBAL_URL + '/apps/ecommerce/product/19.jpg', + rating: 5, + review: 4, + sale: 6, + location: 'Brooklyn'), + RecomendedProductModel( + id: 5, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + sale: 69, + location: 'Brooklyn'), + RecomendedProductModel( + id: 6, + name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB', + price: 751, + image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg', + rating: 5, + review: 14, + sale: 17, + location: 'Brooklyn'), + RecomendedProductModel( + id: 7, + name: 'Garmin Instinct Tactical - Black', + price: 290, + image: GLOBAL_URL + '/apps/ecommerce/product/47.jpg', + rating: 5, + review: 13, + sale: 23, + location: 'Brooklyn'), + RecomendedProductModel( + id: 8, + name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus', + price: 1152, + image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg', + rating: 5, + review: 1, + sale: 2, + location: 'Brooklyn'), + RecomendedProductModel( + id: 9, + name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver', + price: 866, + image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg', + rating: 5, + review: 22, + sale: 468, + location: 'Brooklyn'), + RecomendedProductModel( + id: 10, + name: 'Folding Bike 20 GENIO BY United Bike', + price: 173, + image: GLOBAL_URL + '/apps/ecommerce/product/50.jpg', + rating: 5, + review: 9, + sale: 23, + location: 'Brooklyn'), + RecomendedProductModel( + id: 11, + name: 'XBOX 360 SLIM 500 GB RGH FULL GAME & KINECT', + price: 183, + image: GLOBAL_URL + '/apps/ecommerce/product/51.jpg', + rating: 4, + review: 3, + sale: 9, + location: 'Brooklyn'), + RecomendedProductModel( + id: 12, + name: 'Nitendo Switch Console New HAC-001(-01) Neon Blue - Neon Red', + price: 349, + image: GLOBAL_URL + '/apps/ecommerce/product/52.jpg', + rating: 5, + review: 30, + sale: 86, + location: 'Brooklyn'), + RecomendedProductModel( + id: 13, + name: 'TP-Link TL-WR840N (V4.0) : 300Mbps TPLink WiFi Wireless N Router', + price: 11, + image: GLOBAL_URL + '/apps/ecommerce/product/53.jpg', + rating: 5, + review: 1075, + sale: 3247, + location: 'Brooklyn'), + RecomendedProductModel( + id: 14, + name: 'Google Chromecast 3 Chrome Cast 3rd HDMI Streaming', + price: 38, + image: GLOBAL_URL + '/apps/ecommerce/product/54.jpg', + rating: 5, + review: 160, + sale: 574, + location: 'Brooklyn'), + RecomendedProductModel( + id: 15, + name: 'PRINTER CANON PIXMA MG2570S / PRINTER ALL IN ONE MG 2570 S', + price: 35, + image: GLOBAL_URL + '/apps/ecommerce/product/55.jpg', + rating: 5, + review: 126, + sale: 285, + location: 'Brooklyn'), + RecomendedProductModel( + id: 16, + name: 'Air Jordan 1 Mid Chicago Black Toe 554724-069 100% Authentic - 43', + price: 220, + image: GLOBAL_URL + '/apps/ecommerce/product/56.jpg', + rating: 5, + review: 15, + sale: 24, + location: 'Brooklyn'), + RecomendedProductModel( + id: 17, + name: 'ADIDAS ICE DIVE PARFUM ORIGINAL 100ML', + price: 4.9, + image: GLOBAL_URL + '/apps/ecommerce/product/57.jpg', + rating: 5, + review: 251, + sale: 751, + location: 'Brooklyn'), + RecomendedProductModel( + id: 18, + name: + 'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT', + price: 8.6, + image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg', + rating: 5, + review: 354, + sale: 540, + location: 'Brooklyn'), + RecomendedProductModel( + id: 19, + name: + 'Xiaomi Yi Dash Camera Nightscape 1080P 60FPS ADAS Night Vision Dashcam', + price: 46.6, + image: GLOBAL_URL + '/apps/ecommerce/product/59.jpg', + rating: 5, + review: 47, + sale: 146, + location: 'Brooklyn'), + RecomendedProductModel( + id: 20, + name: + 'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black', + price: 333, + image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg', + rating: 5, + review: 13, + sale: 33, + location: 'Brooklyn') +]; diff --git a/lib/model/related_product_model.dart b/lib/model/related_product_model.dart new file mode 100644 index 0000000..97ef7ab --- /dev/null +++ b/lib/model/related_product_model.dart @@ -0,0 +1,80 @@ +import 'package:mobdr/config/constant.dart'; + +class RelatedProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + + RelatedProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review}); +} + +List relatedProductData = [ + RelatedProductModel( + id: 1, + name: + 'Converter APPLE USB-C To 3.5 mm Headphone Jack Adapter New Ipad Pro', + price: 10, + image: GLOBAL_URL + '/apps/ecommerce/product/71.jpg', + rating: 5, + review: 54), + RelatedProductModel( + id: 2, + name: + 'Charger adapter New Macbook Pro Retina 13 inch Apple 2017 2018 61w ori', + price: 46, + image: GLOBAL_URL + '/apps/ecommerce/product/72.jpg', + rating: 5, + review: 4), + RelatedProductModel( + id: 3, + name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray', + price: 189, + image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg', + rating: 5, + review: 24), + RelatedProductModel( + id: 4, + name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23', + price: 1369, + image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg', + rating: 0, + review: 0), + RelatedProductModel( + id: 5, + name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black', + price: 179, + image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg', + rating: 5, + review: 120), + RelatedProductModel( + id: 6, + name: 'Magic Mouse 2 - Space Gray', + price: 99, + image: GLOBAL_URL + '/apps/ecommerce/product/76.jpg', + rating: 5, + review: 174), + RelatedProductModel( + id: 7, + name: + 'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage', + price: 1099, + image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg', + rating: 5, + review: 78), + RelatedProductModel( + id: 8, + name: 'iPhone SE 2020', + price: 399, + image: GLOBAL_URL + '/apps/ecommerce/product/78.png', + rating: 5, + review: 219), +]; diff --git a/lib/model/review_model.dart b/lib/model/review_model.dart new file mode 100644 index 0000000..b190174 --- /dev/null +++ b/lib/model/review_model.dart @@ -0,0 +1,82 @@ +class ReviewModel { + late int id; + late String name; + late String date; + late double rating; + late String review; + + ReviewModel({required this.id, required this.name, required this.date, required this.rating, required this.review}); +} + +List reviewData =[ + ReviewModel( + id: 1, + name: 'A*******i', + date: '11 September 2020', + rating: 5, + review: 'Everything came in time. Very well packed. Quality is excellent. Thank you!' + ), + ReviewModel( + id: 2, + name: 'L***a', + date: '10 September 2020', + rating: 5, + review: 'The goods came very quickly, in perfect condition, everything is packed, nothing is damaged! Excellent, visibility super, I recommend the seller, the store, the goods!!!' + ), + ReviewModel( + id: 3, + name: 'R******i', + date: '8 September 2020', + rating: 5, + review: 'Before Moscow 3 weeks, asked the seller that there would be no pictogram, made without them. The packaging is definitely good' + ), + ReviewModel( + id: 4, + name: 'D***s', + date: '7 September 2020', + rating: 4, + review: 'The goods arrived 21 days before perm. Packing on the photo. the box inside is crumpled. Thank you seller' + ), + ReviewModel( + id: 5, + name: 'S*******a', + date: '7 September 2020', + rating: 5, + review: 'An excellent device, its money costs. Packed perfectly, no damage. To the seller 5 +++.' + ), + ReviewModel( + id: 6, + name: 'B*******g', + date: '6 September 2020', + rating: 5, + review: 'The goods are satisfied' + ), + ReviewModel( + id: 7, + name: 'H*********a', + date: '4 September 2020', + rating: 3, + review: 'Good product, seller recommend' + ), + ReviewModel( + id: 8, + name: 'P***u', + date: '4 September 2020', + rating: 3, + review: 'Not as good as they write but still worth' + ), + ReviewModel( + id: 9, + name: 'O**********a', + date: '3 September 2020', + rating: 4, + review: "Perfect. Immediately turned up in flight. I can't give you 5 stars for the parcel. And arrived very deteriorated, but fortunately the drone works" + ), + ReviewModel( + id: 10, + name: 'D********l', + date: '1 September 2020', + rating: 5, + review: 'All OK. Although well packed, the box crushed' + ), +]; \ No newline at end of file diff --git a/lib/model/search_model.dart b/lib/model/search_model.dart new file mode 100644 index 0000000..9eed178 --- /dev/null +++ b/lib/model/search_model.dart @@ -0,0 +1,29 @@ +class SearchModel { + late int id; + late String words; + + SearchModel({required this.id, required this.words}); +} + +List searchData = [ + SearchModel( + id: 1, + words: 'adidas shirt' + ), + SearchModel( + id: 2, + words: 'led tv' + ), + SearchModel( + id: 3, + words: 'apple mac' + ), + SearchModel( + id: 4, + words: 'iphone' + ), + SearchModel( + id: 5, + words: 'asus' + ), +]; diff --git a/lib/model/search_product_model.dart b/lib/model/search_product_model.dart new file mode 100644 index 0000000..b961f44 --- /dev/null +++ b/lib/model/search_product_model.dart @@ -0,0 +1,212 @@ +import 'package:mobdr/config/constant.dart'; + +class SearchProductModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late String location; + + SearchProductModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.location}); +} + +List searchProductData = [ + SearchProductModel( + id: 1, + name: 'iphone 7 Plus / 7+ 128GB', + price: 433, + image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg', + rating: 5, + review: 129, + sale: 310, + location: 'Brooklyn'), + SearchProductModel( + id: 2, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + sale: 69, + location: 'Brooklyn'), + SearchProductModel( + id: 3, + name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575', + price: 1643, + image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg', + rating: 5, + review: 2, + sale: 3, + location: 'Brooklyn'), + SearchProductModel( + id: 4, + name: + 'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone', + price: 219, + image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg', + rating: 5, + review: 934, + sale: 1881, + location: 'Brooklyn'), + SearchProductModel( + id: 5, + name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray', + price: 189, + image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg', + rating: 5, + review: 36, + sale: 112, + location: 'Brooklyn'), + SearchProductModel( + id: 6, + name: 'NEW Original Apple TV 4K 64GB 5th Generation', + price: 261, + image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg', + rating: 5, + review: 98, + sale: 23, + location: 'Brooklyn'), + SearchProductModel( + id: 7, + name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus', + price: 1152, + image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg', + rating: 5, + review: 1, + sale: 2, + location: 'Brooklyn'), + SearchProductModel( + id: 8, + name: 'Garmin Instinct Tactical - Black', + price: 290, + image: GLOBAL_URL + '/apps/ecommerce/product/47.jpg', + rating: 5, + review: 13, + sale: 23, + location: 'Brooklyn'), + SearchProductModel( + id: 9, + name: 'XBOX 360 SLIM 500 GB RGH FULL GAME & KINECT', + price: 183, + image: GLOBAL_URL + '/apps/ecommerce/product/51.jpg', + rating: 4, + review: 3, + sale: 9, + location: 'Brooklyn'), + SearchProductModel( + id: 10, + name: 'TP-Link TL-WR840N (V4.0) : 300Mbps TPLink WiFi Wireless N Router', + price: 11, + image: GLOBAL_URL + '/apps/ecommerce/product/53.jpg', + rating: 5, + review: 1075, + sale: 3247, + location: 'Brooklyn'), + SearchProductModel( + id: 11, + name: + 'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black', + price: 333, + image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg', + rating: 5, + review: 13, + sale: 33, + location: 'Brooklyn'), + SearchProductModel( + id: 12, + name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black', + price: 179, + image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg', + rating: 5, + review: 120, + sale: 131, + location: 'Brooklyn'), + SearchProductModel( + id: 13, + name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23', + price: 1369, + image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg', + rating: 0, + review: 0, + sale: 2, + location: 'Brooklyn'), + SearchProductModel( + id: 14, + name: 'Fimi X8 SE Black', + price: 567, + image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg', + rating: 5, + review: 63, + sale: 115, + location: 'Brooklyn', + ), + SearchProductModel( + id: 15, + name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB', + price: 751, + image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg', + rating: 5, + review: 14, + sale: 17, + location: 'Brooklyn'), + SearchProductModel( + id: 16, + name: + 'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT', + price: 8.6, + image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg', + rating: 5, + review: 354, + sale: 540, + location: 'Brooklyn'), + SearchProductModel( + id: 17, + name: + 'KABEL DATA TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M', + price: 3, + image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg', + rating: 5, + review: 636, + sale: 2087, + location: 'Brooklyn'), + SearchProductModel( + id: 18, + name: + 'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black', + price: 333, + image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg', + rating: 5, + review: 13, + sale: 33, + location: 'Brooklyn'), + SearchProductModel( + id: 19, + name: + 'Samyang Noodle Wholesaler 1 Box (40 Pcs) Hot Spicy Chicken - Original Jan 21', + price: 26, + image: GLOBAL_URL + '/apps/ecommerce/product/68.jpg', + rating: 5, + review: 4, + sale: 22, + location: 'Brooklyn'), + SearchProductModel( + id: 20, + name: 'SAMSUNG LED TV 32 Inch HD Digital - 32N4003', + price: 116, + image: GLOBAL_URL + '/apps/ecommerce/product/80.jpg', + rating: 5, + review: 380, + sale: 866, + location: 'Brooklyn') +]; diff --git a/lib/model/shopping_cart_model.dart b/lib/model/shopping_cart_model.dart new file mode 100644 index 0000000..d9cba4b --- /dev/null +++ b/lib/model/shopping_cart_model.dart @@ -0,0 +1,50 @@ +import 'package:mobdr/config/constant.dart'; + +class ShoppingCartModel { + late int id; + late String name; + late String image; + late double price; + late int qty; + + ShoppingCartModel( + {required this.id, + required this.image, + required this.name, + required this.price, + required this.qty}); + + void setQty(int i) { + if (i < 1) { + qty = 1; + } else { + qty = i; + } + } +} + +List shoppingCartData = [ + ShoppingCartModel( + id: 1, + name: + 'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT', + image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg', + price: 8.6, + qty: 2, + ), + ShoppingCartModel( + id: 2, + name: + 'BARDI Smart UNIVERSAL IR REMOTE Wifi Wireless IoT For Home Automation', + image: GLOBAL_URL + '/apps/ecommerce/product/60.jpg', + price: 11.5, + qty: 1, + ), + ShoppingCartModel( + id: 3, + name: 'BARDI Smart PLUG WiFi Wireless Colokan - IoT Smart Home', + image: GLOBAL_URL + '/apps/ecommerce/product/69.jpg', + price: 11.46, + qty: 1, + ), +]; diff --git a/lib/model/wishlist_model.dart b/lib/model/wishlist_model.dart new file mode 100644 index 0000000..7b32f47 --- /dev/null +++ b/lib/model/wishlist_model.dart @@ -0,0 +1,99 @@ +import 'package:mobdr/config/constant.dart'; + +class WishlistModel { + late int id; + late String name; + late double price; + late String image; + late double rating; + late int review; + late int sale; + late int stock; + late String location; + + WishlistModel( + {required this.id, + required this.name, + required this.price, + required this.image, + required this.rating, + required this.review, + required this.sale, + required this.stock, + required this.location}); +} + +List wishlistData = [ + WishlistModel( + id: 1, + name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone', + price: 62, + image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg', + rating: 5, + review: 42, + sale: 69, + stock: 5, + location: 'Brooklyn'), + WishlistModel( + id: 2, + name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus', + price: 1152, + image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg', + rating: 5, + review: 1, + sale: 2, + stock: 22, + location: 'Brooklyn'), + WishlistModel( + id: 3, + name: 'BARDI Smart PLUG WiFi Wireless Colokan - IoT Smart Home', + price: 11.46, + image: GLOBAL_URL + '/apps/ecommerce/product/69.jpg', + rating: 5, + review: 1062, + sale: 4797, + stock: 0, + location: 'Brooklyn'), + WishlistModel( + id: 4, + name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver', + price: 866, + image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg', + rating: 5, + review: 22, + sale: 468, + stock: 25, + location: 'Brooklyn'), + WishlistModel( + id: 5, + name: 'Fimi X8 SE Black', + price: 567, + image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg', + rating: 5, + review: 63, + sale: 115, + stock: 0, + location: 'Brooklyn', + ), + WishlistModel( + id: 6, + name: + 'Xiaomi Air Purifier 3 Mijia OLED Touch Sterilization Air Ionizer - 3', + price: 139, + image: GLOBAL_URL + '/apps/ecommerce/product/33.jpg', + rating: 5, + review: 275, + sale: 1055, + stock: 4, + location: 'Brooklyn'), + WishlistModel( + id: 7, + name: 'Delta Boots Import 8 Inch', + price: 18.3, + image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg', + rating: 5, + review: 212, + sale: 735, + stock: 55, + location: 'Brooklyn') +]; diff --git a/lib/ui/account/about.dart b/lib/ui/account/about.dart new file mode 100644 index 0000000..99b9c02 --- /dev/null +++ b/lib/ui/account/about.dart @@ -0,0 +1,74 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class AboutPage extends StatefulWidget { + @override + _AboutPageState createState() => _AboutPageState(); +} + +class _AboutPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + String _version = '1.0.0'; + + Future _getSystemDevice() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + setState(() { + _version = packageInfo.version; + }); + } + + @override + void initState() { + if (!kIsWeb) { + _getSystemDevice(); + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'About', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center(child: Image.asset('assets/images/logo.png', height: 32)), + SizedBox( + height: 50, + ), + Text( + 'App Version', + style: TextStyle(fontSize: 14, color: CHARCOAL), + ), + SizedBox( + height: 5, + ), + Text( + _version, + style: TextStyle(fontSize: 14, color: CHARCOAL), + ), + ], + ), + )); + } +} diff --git a/lib/ui/account/account_information/account_information.dart b/lib/ui/account/account_information/account_information.dart new file mode 100644 index 0000000..d90a5c3 --- /dev/null +++ b/lib/ui/account/account_information/account_information.dart @@ -0,0 +1,269 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/account/account_information/edit_email.dart'; +import 'package:mobdr/ui/account/account_information/edit_name.dart'; +import 'package:mobdr/ui/account/account_information/edit_phone_number.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class AccountInformationPage extends StatefulWidget { + @override + _AccountInformationPageState createState() => _AccountInformationPageState(); +} + +class _AccountInformationPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Account Information', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: Container( + margin: EdgeInsets.fromLTRB(16, 0, 16, 16), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _createProfilePicture(), + SizedBox(height: 40), + Text( + 'Name', + style: GlobalStyle.accountInformationLabel, + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + 'Robert Steven', + style: GlobalStyle.accountInformationValue, + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditNamePage())); + }, + child: Text('Edit', + style: GlobalStyle.accountInformationEdit), + ) + ], + ), + SizedBox( + height: 24, + ), + Row( + children: [ + Text( + 'Email', + style: GlobalStyle.accountInformationLabel, + ), + SizedBox( + width: 8, + ), + _verifiedLabel() + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + 'robert.steven@ijtechnology.net', + style: GlobalStyle.accountInformationValue, + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditEmailPage())); + }, + child: Text('Edit', + style: GlobalStyle.accountInformationEdit), + ) + ], + ), + SizedBox( + height: 24, + ), + Row( + children: [ + Text( + 'Phone Number', + style: GlobalStyle.accountInformationLabel, + ), + SizedBox( + width: 8, + ), + _verifiedLabel() + ], + ), + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + '0811888999', + style: GlobalStyle.accountInformationValue, + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditPhoneNumberPage())); + }, + child: Text('Edit', + style: GlobalStyle.accountInformationEdit), + ) + ], + ), + ], + ), + ), + )); + } + + Widget _createProfilePicture() { + final double profilePictureSize = MediaQuery.of(context).size.width / 3; + return Align( + alignment: Alignment.center, + child: Container( + margin: EdgeInsets.only(top: 40), + width: profilePictureSize, + height: profilePictureSize, + child: GestureDetector( + onTap: () { + _showPopupUpdatePicture(); + }, + child: Stack( + children: [ + CircleAvatar( + backgroundColor: Colors.white, + radius: (profilePictureSize), + child: Hero( + tag: 'profilePicture', + child: ClipOval( + child: buildCacheNetworkImage( + width: profilePictureSize, + height: profilePictureSize, + url: GLOBAL_URL + '/user/avatar.png')), + ), + ), + // create edit icon in the picture + Container( + width: 30, + height: 30, + margin: EdgeInsets.only( + top: 0, left: MediaQuery.of(context).size.width / 4), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 1, + child: Icon(Icons.edit, size: 12, color: CHARCOAL), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _verifiedLabel() { + return Container( + padding: EdgeInsets.fromLTRB(8, 2, 8, 2), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)), + child: Row( + children: [ + Text('verified', style: TextStyle(color: Colors.white, fontSize: 11)), + SizedBox( + width: 4, + ), + Icon(Icons.done, color: Colors.white, size: 11) + ], + ), + ); + } + + void _showPopupUpdatePicture() { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + Navigator.pop(context); + Fluttertoast.showToast( + msg: 'Click edit profile picture', + toastLength: Toast.LENGTH_SHORT); + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Edit Profile Picture', + style: TextStyle(fontSize: 18), + ), + content: Text('Do you want to edit profile picture ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/lib/ui/account/account_information/edit_email.dart b/lib/ui/account/account_information/edit_email.dart new file mode 100644 index 0000000..401323c --- /dev/null +++ b/lib/ui/account/account_information/edit_email.dart @@ -0,0 +1,129 @@ +/* +This is edit email page + +include file in reuseable/global_function.dart to call function from GlobalFunction +include file in reuseable/global_widget.dart to call function from GlobalWidget + */ + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class EditEmailPage extends StatefulWidget { + @override + _EditEmailPageState createState() => _EditEmailPageState(); +} + +class _EditEmailPageState extends State { + // initialize reusable widget and global function + final _reusableWidget = ReusableWidget(); + final _globalFunction = GlobalFunction(); + + TextEditingController _etEmail = TextEditingController(); + bool _buttonDisabled = true; + + @override + void initState() { + _etEmail = TextEditingController(text: 'robert.steven@ijtechnology.net'); + + if (_globalFunction.validateEmail(_etEmail.text)) { + _buttonDisabled = false; + } else { + _buttonDisabled = true; + } + super.initState(); + } + + @override + void dispose() { + _etEmail.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Edit Email', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + TextFormField( + keyboardType: TextInputType.emailAddress, + controller: _etEmail, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() { + if (_globalFunction.validateEmail(textValue)) { + _buttonDisabled = false; + } else { + _buttonDisabled = true; + } + }); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Email', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => + states.contains(MaterialState.disabled) + ? Colors.grey[300]! + : _buttonDisabled + ? Colors.grey[300]! + : PRIMARY_COLOR, + ), + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + if (!_buttonDisabled) { + _reusableWidget.startLoading( + context, 'Edit Email Success', 1); + FocusScope.of(context).unfocus(); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: _buttonDisabled + ? Colors.grey[600] + : Colors.white), + textAlign: TextAlign.center, + ), + ))), + ], + )); + } +} diff --git a/lib/ui/account/account_information/edit_name.dart b/lib/ui/account/account_information/edit_name.dart new file mode 100644 index 0000000..c5f9077 --- /dev/null +++ b/lib/ui/account/account_information/edit_name.dart @@ -0,0 +1,92 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class EditNamePage extends StatefulWidget { + @override + _EditNamePageState createState() => _EditNamePageState(); +} + +class _EditNamePageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + TextEditingController _etName = TextEditingController(); + + @override + void initState() { + _etName = TextEditingController(text: 'Robert Steven'); + super.initState(); + } + + @override + void dispose() { + _etName.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Edit Name', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + TextField( + controller: _etName, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Name', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + _reusableWidget.startLoading( + context, 'Edit Name Success', 1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } +} diff --git a/lib/ui/account/account_information/edit_phone_number.dart b/lib/ui/account/account_information/edit_phone_number.dart new file mode 100644 index 0000000..4af1388 --- /dev/null +++ b/lib/ui/account/account_information/edit_phone_number.dart @@ -0,0 +1,122 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class EditPhoneNumberPage extends StatefulWidget { + @override + _EditPhoneNumberPageState createState() => _EditPhoneNumberPageState(); +} + +class _EditPhoneNumberPageState extends State { + // initialize reusable widget and global function + final _reusableWidget = ReusableWidget(); + final _globalFunction = GlobalFunction(); + + TextEditingController _etPhoneNumber = TextEditingController(); + bool _buttonDisabled = true; + + @override + void initState() { + _etPhoneNumber = TextEditingController(text: '0811888999'); + + if (_globalFunction.validateMobileNumber(_etPhoneNumber.text)) { + _buttonDisabled = false; + } else { + _buttonDisabled = true; + } + super.initState(); + } + + @override + void dispose() { + _etPhoneNumber.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Edit Phone Number', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + TextFormField( + keyboardType: TextInputType.number, + controller: _etPhoneNumber, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() { + if (_globalFunction.validateMobileNumber(textValue)) { + _buttonDisabled = false; + } else { + _buttonDisabled = true; + } + }); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Phone Number', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => + states.contains(MaterialState.disabled) + ? Colors.grey[300]! + : _buttonDisabled + ? Colors.grey[300]! + : PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + if (!_buttonDisabled) { + _reusableWidget.startLoading( + context, 'Edit Phone Number Success', 1); + FocusScope.of(context).unfocus(); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: _buttonDisabled + ? Colors.grey[600] + : Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } +} diff --git a/lib/ui/account/last_seen_product.dart b/lib/ui/account/last_seen_product.dart new file mode 100644 index 0000000..4938501 --- /dev/null +++ b/lib/ui/account/last_seen_product.dart @@ -0,0 +1,157 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/last_seen_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class LastSeenProductPage extends StatefulWidget { + @override + _LastSeenProductPageState createState() => _LastSeenProductPageState(); +} + +class _LastSeenProductPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 4); + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Last Seen Product', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView.builder( + itemCount: lastSeenData.length, + // Add one more item for progress indicator + padding: EdgeInsets.symmetric(vertical: 0), + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return _buildItem(index, boxImageSize); + }, + )); + } + + Widget _buildItem(index, boxImageSize) { + return Column( + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: lastSeenData[index].name, + image: lastSeenData[index].image, + price: lastSeenData[index].price, + rating: lastSeenData[index].rating, + review: lastSeenData[index].review, + sale: lastSeenData[index].sale))); + }, + child: Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 6), + child: Container( + margin: EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(4)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: lastSeenData[index].image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + lastSeenData[index].name, + style: GlobalStyle.productName.copyWith(fontSize: 13), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + lastSeenData[index].price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, + color: SOFT_GREY, size: 12), + Text(' ' + lastSeenData[index].location, + style: GlobalStyle.productLocation) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: lastSeenData[index].rating, size: 12), + Text( + '(' + + lastSeenData[index].review.toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + lastSeenData[index].sale.toString() + ' Sale', + style: GlobalStyle.productSale), + ), + ], + ), + ) + ], + ), + ), + ), + ), + (index == lastSeenData.length - 1) + ? Wrap() + : Divider( + height: 0, + color: Colors.grey[400], + ) + ], + ); + } +} diff --git a/lib/ui/account/notification_setting.dart b/lib/ui/account/notification_setting.dart new file mode 100644 index 0000000..1237d4f --- /dev/null +++ b/lib/ui/account/notification_setting.dart @@ -0,0 +1,108 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class NotificationSettingPage extends StatefulWidget { + @override + _NotificationSettingPageState createState() => + _NotificationSettingPageState(); +} + +class _NotificationSettingPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + bool _valChat = true; + bool _valPromotion = true; + bool _valOrderStatus = true; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Notification Setting', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + children: [ + _buildSwitchPromotion(), + Divider(height: 0, color: Colors.grey[400]), + _buildSwitchChat(), + Divider(height: 0, color: Colors.grey[400]), + _buildSwitchOrderStatus(), + Divider(height: 0, color: Colors.grey[400]), + ], + )); + } + + Widget _buildSwitchPromotion() { + return SwitchListTile( + contentPadding: EdgeInsets.only(left: 16, right: 4), + title: Text( + 'Promotion', + style: TextStyle(fontSize: 15, color: CHARCOAL), + ), + value: _valPromotion, + activeColor: PRIMARY_COLOR, + onChanged: (bool value) { + setState(() { + _valPromotion = value; + }); + }, + ); + } + + Widget _buildSwitchChat() { + return SwitchListTile( + contentPadding: EdgeInsets.only(left: 16, right: 4), + title: Text( + 'Chat', + style: TextStyle(fontSize: 15, color: CHARCOAL), + ), + value: _valChat, + activeColor: PRIMARY_COLOR, + onChanged: (bool value) { + setState(() { + _valChat = value; + }); + }, + ); + } + + Widget _buildSwitchOrderStatus() { + return SwitchListTile( + contentPadding: EdgeInsets.only(left: 16, right: 4), + title: Text( + 'New Order Status', + style: TextStyle(fontSize: 15, color: CHARCOAL), + ), + value: _valOrderStatus, + activeColor: PRIMARY_COLOR, + onChanged: (bool value) { + setState(() { + _valOrderStatus = value; + }); + }, + ); + } +} diff --git a/lib/ui/account/order/order_detail.dart b/lib/ui/account/order/order_detail.dart new file mode 100644 index 0000000..89cb413 --- /dev/null +++ b/lib/ui/account/order/order_detail.dart @@ -0,0 +1,484 @@ +// ignore_for_file: prefer_const_constructors, prefer_interpolation_to_compose_strings, avoid_unnecessary_containers, use_key_in_widget_constructors + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/account/order/order_status.dart'; +import 'package:mobdr/ui/general/chat_us.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class OrderDetailPage extends StatefulWidget { + @override + _OrderDetailPageState createState() => _OrderDetailPageState(); +} + +class _OrderDetailPageState extends State { + // initialize global function + final _globalFunction = GlobalFunction(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 6); + + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: 2, + title: Text( + 'Order Detail', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + ), + body: ListView( + children: [ + _createOrderStatus(), + Container( + margin: EdgeInsets.only(top: 6), + child: Column( + children: [ + _buildItemCard(boxImageSize, 1), + _buildItemCard(boxImageSize, 2), + _buildItemCard(boxImageSize, 3), + ], + ), + ), + _createDeliveryDetail(), + _createPaymentInformation(), + Container( + margin: EdgeInsets.all(32), + child: SizedBox( + width: double.maxFinite, + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatUsPage())); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text( + 'Chat Us', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + ))), + ), + ], + )); + } + + Widget _createOrderStatus() { + return Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 3.0, + ), + ], + ), + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Status', style: TextStyle(color: BLACK_GREY, fontSize: 13)), + SizedBox( + height: 4, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Order Completed', + style: TextStyle( + color: PRIMARY_COLOR, + fontSize: 14, + fontWeight: FontWeight.bold)), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => OrderStatusPage())); + }, + child: Text('View Status', + style: TextStyle(color: PRIMARY_COLOR, fontSize: 13)), + ) + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Order Date', + style: TextStyle(color: BLACK_GREY, fontSize: 13)), + Text('3 September 2019 11:32 UTC', + style: TextStyle( + color: BLACK_GREY, + fontSize: 13, + fontWeight: FontWeight.bold)) + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('INV385714262', + style: TextStyle(color: BLACK_GREY, fontSize: 13)), + ], + ), + ); + } + + Widget _buildItemCard(boxImageSize, id) { + // create local data + String name = 'Delta Boots Import 8 Inch'; + String image = GLOBAL_URL + '/product/25.jpg'; + int countItem = 1; + int weight = 800; + double price = 18.3; + double totalPrice = 18.3; + if (id == 2) { + name = + 'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M'; + image = GLOBAL_URL + '/product/35.jpg'; + countItem = 2; + weight = 100; + price = 3; + totalPrice = 6; + } else if (id == 3) { + name = 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60'; + image = GLOBAL_URL + '/product/2.jpg'; + countItem = 1; + weight = 400; + price = 7.2; + totalPrice = 7.2; + } + return Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: name, + image: image, + price: price, + rating: 5, + review: 23, + sale: 40))); + }, + child: Container( + margin: EdgeInsets.all(12), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: GlobalStyle.productName.copyWith( + fontSize: 14, fontWeight: FontWeight.bold), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + countItem.toString() + + ' item (' + + weight.toString() + + ' gr)', + style: GlobalStyle.shoppingCartOtherProduct + .copyWith(color: Colors.grey[400]))), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + '\$' + + _globalFunction + .removeDecimalZeroFormat(price), + style: GlobalStyle.productPrice), + ) + ], + ), + ) + ], + ), + ), + ), + Divider( + height: 0, + color: Colors.grey[400], + ), + Container( + margin: EdgeInsets.all(12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Total Price', style: TextStyle(fontSize: 12)), + Container( + child: Text( + '\$' + + _globalFunction + .removeDecimalZeroFormat(totalPrice), + style: GlobalStyle.productPrice), + ) + ], + )) + ], + ), + ), + ), + ); + } + + Widget _createDeliveryDetail() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 3.0, + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Delivery Details', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + Table( + children: [ + TableRow(children: [ + Container( + child: Text('Courier Delivery', + style: TextStyle( + color: BLACK_GREY, + fontSize: 13, + )), + ), + Container( + child: Text('DHL Express', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ) + ]), + TableRow(children: [ + Container( + margin: EdgeInsets.only(top: 8), + child: Text('AWB Number', + style: TextStyle( + color: BLACK_GREY, + fontSize: 13, + )), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Text('5614571226', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ) + ]), + TableRow(children: [ + Container( + margin: EdgeInsets.only(top: 8), + child: Text('Delivery Address', + style: TextStyle( + color: BLACK_GREY, + fontSize: 13, + )), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Text('Robert Steven', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ), + Container( + child: Text('0811888999', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ), + Container( + child: Text('6019 Madison St', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ), + Container( + child: Text('West New York, NJ 07093', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ), + Container( + child: Text('USA', + style: TextStyle( + color: CHARCOAL, + fontSize: 14, + fontWeight: FontWeight.bold)), + ), + ], + ), + ) + ]), + ], + ), + ], + )); + } + + Widget _createPaymentInformation() { + return Container( + margin: EdgeInsets.only(top: 16), + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 3.0, + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Payment Information', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Payment Method', + style: TextStyle(color: BLACK_GREY, fontSize: 13)), + Text('Visa card ending in 4392', + style: TextStyle( + color: CHARCOAL, + fontSize: 13, + fontWeight: FontWeight.bold)) + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Total Price (4 item)', + style: TextStyle(color: BLACK_GREY, fontSize: 13)), + Text('\$31.5', style: GlobalStyle.productPrice) + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Total Delivery (1.3 Kg)', + style: TextStyle(color: BLACK_GREY, fontSize: 13)), + Text('\$19', style: GlobalStyle.productPrice) + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Total Payment', + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + Text('\$50.5', style: GlobalStyle.productPrice) + ], + ), + ], + )); + } +} diff --git a/lib/ui/account/order/order_list.dart b/lib/ui/account/order/order_list.dart new file mode 100644 index 0000000..6c5f867 --- /dev/null +++ b/lib/ui/account/order/order_list.dart @@ -0,0 +1,164 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/order_list_model.dart'; +import 'package:mobdr/ui/account/order/order_detail.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class OrderListPage extends StatefulWidget { + @override + _OrderListPageState createState() => _OrderListPageState(); +} + +class _OrderListPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 6); + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Order List', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView.builder( + itemCount: orderListData.length, + // Add one more item for progress indicator + padding: EdgeInsets.symmetric(vertical: 0), + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return _buildOrderListCard(index, boxImageSize); + }, + )); + } + + Widget _buildOrderListCard(index, boxImageSize) { + return Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => OrderDetailPage())); + }, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.center, + padding: EdgeInsets.all(12), + child: Text(orderListData[index].status, + style: TextStyle(color: SOFT_BLUE, fontSize: 12)), + ), + Divider( + height: 0, + color: Colors.grey[400], + ), + Container( + margin: EdgeInsets.fromLTRB(12, 12, 12, 0), + child: Text(orderListData[index].date, + style: TextStyle(fontSize: 12, color: Colors.grey[400])), + ), + Container( + margin: EdgeInsets.fromLTRB(12, 8, 12, 0), + child: Text(orderListData[index].invoice, + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + ), + Container( + margin: EdgeInsets.all(12), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: orderListData[index].image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + orderListData[index].name, + style: GlobalStyle.productName.copyWith( + fontSize: 14, fontWeight: FontWeight.bold), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('+2 other product', + style: GlobalStyle.shoppingCartOtherProduct + .copyWith(color: Colors.grey[400]))) + ], + ), + ) + ], + ), + ), + Divider( + height: 0, + color: Colors.grey[400], + ), + Container( + margin: EdgeInsets.all(12), + alignment: Alignment.topRight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('Total Payment', style: TextStyle(fontSize: 12)), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + '\$' + + _globalFunction.removeDecimalZeroFormat( + orderListData[index].payment), + style: GlobalStyle.productPrice), + ) + ], + )) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/account/order/order_status.dart b/lib/ui/account/order/order_status.dart new file mode 100644 index 0000000..7c92999 --- /dev/null +++ b/lib/ui/account/order/order_status.dart @@ -0,0 +1,231 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class OrderStatusPage extends StatefulWidget { + @override + _OrderStatusPageState createState() => _OrderStatusPageState(); +} + +class _OrderStatusPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Order Status', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: Container( + child: ListView( + padding: EdgeInsets.all(32), + children: [ + _createPathTop('11 Sep 2019 08:40', 'Order Completed', + 'Your order is completed'), + _createPath('11 Sep 2019 08:39', 'Order Arrived', + 'Your order has arrived'), + _createPath('9 Sep 2019 14:12', 'Order Sent', + 'Your order is being shipped by courier'), + _createPath('9 Sep 2019 14:12', 'Ready to Pickup', + 'Your order is ready to be picked up by the courier'), + _createPath('9 Sep 2019 12:12', 'Order Processed', + 'Your order is being processed'), + _createPath('9 Sep 2019 11:52', 'Payment Received', + 'Payment has been received'), + _createPath('9 Sep 2019 11:32', 'Waiting for Payment', + 'We are waiting for your payment'), + _createPathDown('9 Sep 2019 11:32', 'Order Placed', + 'We have received your order'), + ], + ), + )); + } + + Widget _createPathTop( + String date, String orderStatus, String orderDescription) { + return Stack( + children: [ + Container( + margin: EdgeInsets.only(top: 0), + width: 16, + height: 16, + decoration: new BoxDecoration( + shape: BoxShape + .circle, // You can use like this way or like the below line + //borderRadius: new BorderRadius.circular(30.0), + color: PRIMARY_COLOR, + ), + ), + IntrinsicHeight( + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 7.5, right: 7.5), + child: Container( + width: 1, + color: PRIMARY_COLOR, + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(left: 32, right: 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(orderStatus, + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL)), + SizedBox( + height: 4, + ), + Text(date, + style: + TextStyle(color: Colors.grey[400], fontSize: 11)), + SizedBox( + height: 8, + ), + Text(orderDescription, + style: TextStyle(color: BLACK_GREY)), + SizedBox( + height: 24, + ) + ], + ), + ), + ), + ], + ), + ) + ], + ); + } + + Widget _createPath(String date, String orderStatus, String orderDescription) { + return Stack( + children: [ + Container( + margin: EdgeInsets.only(top: 0), + width: 16, + height: 16, + decoration: new BoxDecoration( + shape: BoxShape + .circle, // You can use like this way or like the below line + //borderRadius: new BorderRadius.circular(30.0), + color: Colors.grey[400], + ), + ), + IntrinsicHeight( + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 7.5, right: 7.5), + child: Container( + width: 1, + color: Colors.grey[400], + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(left: 32, right: 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(orderStatus, + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL)), + SizedBox( + height: 4, + ), + Text(date, + style: + TextStyle(color: Colors.grey[400], fontSize: 11)), + SizedBox( + height: 8, + ), + Text(orderDescription, + style: TextStyle(color: BLACK_GREY)), + SizedBox( + height: 24, + ) + ], + ), + ), + ), + ], + ), + ) + ], + ); + } + + Widget _createPathDown( + String date, String orderStatus, String orderDescription) { + return Stack( + children: [ + Container( + margin: EdgeInsets.only(top: 0), + width: 16, + height: 16, + decoration: new BoxDecoration( + shape: BoxShape + .circle, // You can use like this way or like the below line + //borderRadius: new BorderRadius.circular(30.0), + color: Colors.grey[400], + ), + ), + IntrinsicHeight( + child: Row( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.only(left: 48, right: 48), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(orderStatus, + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL)), + SizedBox( + height: 4, + ), + Text(date, + style: + TextStyle(color: Colors.grey[400], fontSize: 11)), + SizedBox( + height: 8, + ), + Text(orderDescription, + style: TextStyle(color: BLACK_GREY)), + ], + ), + ), + ), + ], + ), + ) + ], + ); + } +} diff --git a/lib/ui/account/payment_method/add_payment_method.dart b/lib/ui/account/payment_method/add_payment_method.dart new file mode 100644 index 0000000..38684ee --- /dev/null +++ b/lib/ui/account/payment_method/add_payment_method.dart @@ -0,0 +1,351 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class AddPaymentMethodPage extends StatefulWidget { + @override + _AddPaymentMethodPageState createState() => _AddPaymentMethodPageState(); +} + +class _AddPaymentMethodPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + List _monthList = []; + List _yearList = []; + String? _expiredMonth; + String? _expiredYear; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) async { + _initForLang(); + }); + + super.initState(); + } + + void _initForLang() { + setState(() { + _expiredMonth = 'Month'; + _expiredYear = 'Year'; + // select box data for month of credit card + _monthList.add('Month'); + _monthList.add("01"); + _monthList.add('02'); + _monthList.add('03'); + _monthList.add('04'); + _monthList.add('05'); + _monthList.add('06'); + _monthList.add('07'); + _monthList.add('08'); + _monthList.add('09'); + _monthList.add('10'); + _monthList.add('11'); + _monthList.add('12'); + + // select box data for year of credit card + _yearList.add('Year'); + _yearList.add("2020"); + _yearList.add('2021'); + _yearList.add('2022'); + _yearList.add('2023'); + _yearList.add('2024'); + _yearList.add('2025'); + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Add Payment Method', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + Text('Credit Card Information', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + Container( + margin: EdgeInsets.only(top: 20), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/visa.png', height: 10), + ), + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/mastercard.png', height: 20), + ), + ], + ), + ), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Credit Card Number *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Name of Cardholder *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + Text('Expired Date *', + style: TextStyle(color: BLACK_GREY, fontSize: 12)), + Row( + children: [ + _buildExpiredMonth(), + SizedBox( + width: 16, + ), + _buildExpiredYear(), + ], + ), + SizedBox( + height: 32, + ), + Text('Billing Information', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Full Name *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Company Name', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 1 *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 2', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'City *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'State / Province / Region *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Postal Code *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Recipient\'s Phone Number *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + _reusableWidget.startLoading( + context, 'Add Payment Method Success', 1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } + + // dropdown menu + DropdownButton _buildExpiredMonth() { + return DropdownButton( + value: _expiredMonth, + icon: Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: TextStyle(color: Colors.grey[700], fontSize: 16), + underline: Container( + height: 1, + color: Colors.grey[600], + ), + onChanged: (String? data) { + setState(() { + _expiredMonth = data!; + }); + }, + items: _monthList.map>((String value) { + return DropdownMenuItem( + value: value, + child: Container( + child: Text(value), + alignment: Alignment.center, + ), + ); + }).toList(), + ); + } + + DropdownButton _buildExpiredYear() { + return DropdownButton( + value: _expiredYear, + icon: Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: TextStyle(color: Colors.grey[700], fontSize: 16), + underline: Container( + height: 1, + color: Colors.grey[600], + ), + onChanged: (String? data) { + setState(() { + _expiredYear = data!; + }); + }, + items: _yearList.map>((String value) { + return DropdownMenuItem( + value: value, + child: Container( + child: Text(value), + alignment: Alignment.center, + ), + ); + }).toList(), + ); + } +} diff --git a/lib/ui/account/payment_method/edit_payment_method.dart b/lib/ui/account/payment_method/edit_payment_method.dart new file mode 100644 index 0000000..c6d8b6e --- /dev/null +++ b/lib/ui/account/payment_method/edit_payment_method.dart @@ -0,0 +1,381 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class EditPaymentMethodPage extends StatefulWidget { + @override + _EditPaymentMethodPageState createState() => _EditPaymentMethodPageState(); +} + +class _EditPaymentMethodPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + TextEditingController _etCreditCardNumber = TextEditingController(); + TextEditingController _etCreditCardName = TextEditingController(); + TextEditingController _etFullName = TextEditingController(); + TextEditingController _etAddressLine1 = TextEditingController(); + TextEditingController _etAddressLine2 = TextEditingController(); + TextEditingController _etCity = TextEditingController(); + TextEditingController _etState = TextEditingController(); + TextEditingController _etPostalCode = TextEditingController(); + TextEditingController _etPhoneNumber = TextEditingController(); + + List _monthList = []; + List _yearList = []; + String _expiredMonth = "04"; + String _expiredYear = "2023"; + + @override + void initState() { + _etCreditCardNumber = TextEditingController(text: '4485653755194392'); + _etCreditCardName = TextEditingController(text: 'Robert Steven'); + _etFullName = TextEditingController(text: 'Robert Steven'); + _etAddressLine1 = TextEditingController( + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); + _etAddressLine2 = TextEditingController( + text: + 'Quisque tortor tortor, ultrices id scelerisque a, elementum id elit.'); + _etCity = TextEditingController(text: 'New York City'); + _etState = TextEditingController(text: 'New York'); + _etPostalCode = TextEditingController(text: '10010'); + _etPhoneNumber = TextEditingController(text: '0811888999'); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + _initForLang(); + }); + + super.initState(); + } + + void _initForLang() { + setState(() { + // select box data for month of credit card + _monthList.add('Month'); + _monthList.add("01"); + _monthList.add('02'); + _monthList.add('03'); + _monthList.add('04'); + _monthList.add('05'); + _monthList.add('06'); + _monthList.add('07'); + _monthList.add('08'); + _monthList.add('09'); + _monthList.add('10'); + _monthList.add('11'); + _monthList.add('12'); + + // select box data for year of credit card + _yearList.add('Year'); + _yearList.add("2020"); + _yearList.add('2021'); + _yearList.add('2022'); + _yearList.add('2023'); + _yearList.add('2024'); + _yearList.add('2025'); + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Edit Payment Method', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + Text('Credit Card Information', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + Container( + margin: EdgeInsets.only(top: 20), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/visa.png', height: 10), + ), + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/mastercard.png', height: 20), + ), + ], + ), + ), + TextField( + controller: _etCreditCardNumber, + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Credit Card Number *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etCreditCardName, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Name of Cardholder *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + Text('Expired Date *', + style: TextStyle(color: BLACK_GREY, fontSize: 12)), + Row( + children: [ + _buildExpiredMonth(), + SizedBox( + width: 16, + ), + _buildExpiredYear(), + ], + ), + SizedBox( + height: 32, + ), + Text('Billing Information', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + TextField( + controller: _etFullName, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Full Name *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Company Name', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etAddressLine1, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 1 *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etAddressLine2, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 2', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etCity, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'City *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etState, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'State / Province / Region *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etPostalCode, + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Postal Code *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etPhoneNumber, + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Phone Number *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + _reusableWidget.startLoading( + context, 'Edit Payment Method Success', 1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } + + // dropdown menu + DropdownButton _buildExpiredMonth() { + return DropdownButton( + value: _expiredMonth, + icon: Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: TextStyle(color: Colors.grey[700], fontSize: 16), + underline: Container( + height: 1, + color: Colors.grey[600], + ), + onChanged: (String? data) { + setState(() { + _expiredMonth = data!; + }); + }, + items: _monthList.map>((String value) { + return DropdownMenuItem( + value: value, + child: Container( + child: Text(value), + alignment: Alignment.center, + ), + ); + }).toList(), + ); + } + + DropdownButton _buildExpiredYear() { + return DropdownButton( + value: _expiredYear, + icon: Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: TextStyle(color: Colors.grey[700], fontSize: 16), + underline: Container( + height: 1, + color: Colors.grey[600], + ), + onChanged: (String? data) { + setState(() { + _expiredYear = data!; + }); + }, + items: _yearList.map>((String value) { + return DropdownMenuItem( + value: value, + child: Container( + child: Text(value), + alignment: Alignment.center, + ), + ); + }).toList(), + ); + } +} diff --git a/lib/ui/account/payment_method/payment_method.dart b/lib/ui/account/payment_method/payment_method.dart new file mode 100644 index 0000000..027df7a --- /dev/null +++ b/lib/ui/account/payment_method/payment_method.dart @@ -0,0 +1,470 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/account/payment_method/add_payment_method.dart'; +import 'package:mobdr/ui/account/payment_method/edit_payment_method.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class PaymentMethodPage extends StatefulWidget { + @override + _PaymentMethodPageState createState() => _PaymentMethodPageState(); +} + +class _PaymentMethodPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Payment Method', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + Text('Default Payment Preferences', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: CHARCOAL)), + Container( + margin: EdgeInsets.only(top: 8), + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Text('Payment Method', + style: + TextStyle(fontSize: 14, color: Colors.grey[400])), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/visa.png', height: 10), + ), + Text('Visa card ending in 4392', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CHARCOAL)) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 24), + child: Text('Billing Address', + style: + TextStyle(fontSize: 14, color: Colors.grey[400])), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Text( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit.', + style: TextStyle(fontSize: 14, color: CHARCOAL)), + ), + Container( + margin: EdgeInsets.only(top: 24), + child: Text('Phone Number', + style: + TextStyle(fontSize: 14, color: Colors.grey[400])), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Text('0811888999', + style: TextStyle(fontSize: 14, color: CHARCOAL)), + ) + ], + ), + ), + SizedBox( + height: 32, + ), + Container( + margin: EdgeInsets.only(bottom: 8), + child: Text('List of Payment Method', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: CHARCOAL)), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + ), + child: DataTable( + columns: [ + DataColumn( + label: Text('Credit Card', + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL))), + DataColumn( + label: Text('Name on Card', + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL))), + DataColumn( + label: Text('Expires on', + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL))), + DataColumn( + label: Text('Action', + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL))), + ], + rows: [ + DataRow( + cells: [ + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/visa.png', + height: 10), + ), + Text('Visa card ending in 4392') + ], + )), + DataCell(Text("Robert Steven")), + DataCell(Text("04/2023")), + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.fromLTRB(8, 2, 8, 2), + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.circular(2)), + child: Row( + children: [ + Text('Default', + style: TextStyle( + color: Colors.white, fontSize: 11)), + SizedBox( + width: 4, + ), + Icon(Icons.done, + color: Colors.white, size: 11) + ], + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + EditPaymentMethodPage())); + }, + child: Text('Edit', + style: TextStyle(color: SOFT_BLUE)), + ), + Container( + margin: EdgeInsets.only(left: 8), + child: GestureDetector( + onTap: () { + _showPopupDeletePayment(0); + }, + child: Text('Delete', + style: TextStyle(color: SOFT_BLUE)), + ), + ) + ], + )), + ], + ), + DataRow( + cells: [ + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/mastercard.png', + height: 20), + ), + Text('MasterCard ending in 5309') + ], + )), + DataCell(Text("Robert Steven")), + DataCell(Text("07/2022")), + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + child: GestureDetector( + onTap: () { + showPopupMakeDefault(); + }, + child: Text('Make Default', + style: TextStyle(color: SOFT_BLUE)), + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + EditPaymentMethodPage())); + }, + child: Text('Edit', + style: TextStyle(color: SOFT_BLUE)), + ), + Container( + margin: EdgeInsets.only(left: 8), + child: GestureDetector( + onTap: () { + _showPopupDeletePayment(1); + }, + child: Text('Delete', + style: TextStyle(color: SOFT_BLUE)), + ), + ) + ], + )), + ], + ), + DataRow( + cells: [ + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/visa.png', + height: 10), + ), + Text('Visa card ending in 2285') + ], + )), + DataCell(Text("Robert Steven")), + DataCell(Text("11/2021")), + DataCell(Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + child: GestureDetector( + onTap: () { + showPopupMakeDefault(); + }, + child: Text('Make Default', + style: TextStyle(color: SOFT_BLUE)), + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + EditPaymentMethodPage())); + }, + child: Text('Edit', + style: TextStyle(color: SOFT_BLUE)), + ), + Container( + margin: EdgeInsets.only(left: 8), + child: GestureDetector( + onTap: () { + _showPopupDeletePayment(2); + }, + child: Text('Delete', + style: TextStyle(color: SOFT_BLUE)), + ), + ) + ], + )), + ], + ), + ], + ), + ), + ), + Container( + margin: EdgeInsets.only(top: 32), + child: OutlinedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddPaymentMethodPage())); + }, + style: ButtonStyle( + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + side: MaterialStateProperty.all( + BorderSide(color: SOFT_BLUE, width: 1.0), + )), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text( + 'Add a card', + style: TextStyle( + color: SOFT_BLUE, + fontWeight: FontWeight.bold, + fontSize: 13), + textAlign: TextAlign.center, + ), + )), + ) + ], + )); + } + + void showPopupMakeDefault() { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + Navigator.pop(context); + _reusableWidget.startLoading(context, 'Success', 0); + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Make Default', + style: TextStyle(fontSize: 18), + ), + content: Text('Are you sure to make this card as a default payment ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + void _showPopupDeletePayment(int index) { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + Navigator.pop(context); + if (index == 0) { + Fluttertoast.showToast( + msg: + 'Please change default payment method if you want to delete this payment method', + toastLength: Toast.LENGTH_LONG); + } else { + _reusableWidget.startLoading( + context, 'Delete Payment Method Success', 0); + } + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Delete Payment Method', + style: TextStyle(fontSize: 18), + ), + content: Text('Are you sure to delete this payment method ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/lib/ui/account/privacy_policy.dart b/lib/ui/account/privacy_policy.dart new file mode 100644 index 0000000..72c68ff --- /dev/null +++ b/lib/ui/account/privacy_policy.dart @@ -0,0 +1,64 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; + +class PrivacyPolicyPage extends StatefulWidget { + @override + _PrivacyPolicyPageState createState() => _PrivacyPolicyPageState(); +} + +class _PrivacyPolicyPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Privacy Policy', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(8), + child: MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1), + child: Html( + data: """ +

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.

+

Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.

+

Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.

+

Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.

+

Phasellus pellentesque et magna in aliquam. Etiam vehicula dui vitae lectus ultrices iaculis. Nullam volutpat magna vel volutpat laoreet. Donec accumsan mi augue, nec elementum libero imperdiet eget. Duis in enim facilisis, lobortis tellus id, tincidunt urna. Donec ipsum neque, pharetra id imperdiet eget, varius bibendum sapien. Suspendisse tincidunt justo a purus molestie, sed elementum urna scelerisque. Suspendisse eget erat ultrices, suscipit nunc ut, iaculis lacus. Donec finibus, nisi vitae porta sodales, diam sapien scelerisque tortor, vel aliquet urna ex non urna. Etiam dictum eros ut justo ultrices tincidunt. Nulla et neque velit. Phasellus malesuada, lectus et sodales iaculis, sapien nibh ultrices tellus, ut ultrices magna tellus eget tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non diam ac quam aliquet facilisis sit amet at lectus. Nulla vestibulum libero arcu, eu malesuada ipsum congue feugiat.

+ """, + style: { + "p": Style.fromTextStyle( + TextStyle(fontSize: 16), + ), + }, + ), + ), + )); + } +} diff --git a/lib/ui/account/set_address/add_address.dart b/lib/ui/account/set_address/add_address.dart new file mode 100644 index 0000000..f4df6ba --- /dev/null +++ b/lib/ui/account/set_address/add_address.dart @@ -0,0 +1,167 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class AddAddressPage extends StatefulWidget { + @override + _AddAddressPageState createState() => _AddAddressPageState(); +} + +class _AddAddressPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Add Address', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Title *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Recipient\'s Name *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Recipient\'s Phone Number', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 1 *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 2', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'State / Province / Region *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Postal Code *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + _reusableWidget.startLoading( + context, 'Add Address Success', 1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } +} diff --git a/lib/ui/account/set_address/edit_address.dart b/lib/ui/account/set_address/edit_address.dart new file mode 100644 index 0000000..1fd0a2a --- /dev/null +++ b/lib/ui/account/set_address/edit_address.dart @@ -0,0 +1,191 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class EditAddressPage extends StatefulWidget { + @override + _EditAddressPageState createState() => _EditAddressPageState(); +} + +class _EditAddressPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + // create controller to edit text field + TextEditingController _etAddressTitle = TextEditingController(); + TextEditingController _etRecipientName = TextEditingController(); + TextEditingController _etRecipientPhoneNumber = TextEditingController(); + TextEditingController _etAddressLine1 = TextEditingController(); + TextEditingController _etAddressLine2 = TextEditingController(); + TextEditingController _etPostalCode = TextEditingController(); + TextEditingController _etState = TextEditingController(); + + @override + void initState() { + _etAddressTitle = TextEditingController(text: 'Home Address'); + _etRecipientName = TextEditingController(text: 'Robert Steven'); + _etRecipientPhoneNumber = TextEditingController(text: '0811888999'); + _etAddressLine1 = TextEditingController(text: '6019 Madison St'); + _etAddressLine2 = TextEditingController(text: 'West New York, NJ'); + _etPostalCode = TextEditingController(text: '07093'); + _etState = TextEditingController(text: 'USA'); + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Edit Address', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + TextField( + controller: _etAddressTitle, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Title *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etRecipientName, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Recipient\'s Name *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etRecipientPhoneNumber, + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Recipient\'s Phone Number', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etAddressLine1, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 1 *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etAddressLine2, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Address Line 2', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etState, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'State / Province / Region *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 20, + ), + TextField( + controller: _etPostalCode, + keyboardType: TextInputType.number, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Postal Code *', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + _reusableWidget.startLoading( + context, 'Edit Address Success', 1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Save', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } +} diff --git a/lib/ui/account/set_address/set_address.dart b/lib/ui/account/set_address/set_address.dart new file mode 100644 index 0000000..2a4dc04 --- /dev/null +++ b/lib/ui/account/set_address/set_address.dart @@ -0,0 +1,260 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/address_model.dart'; +import 'package:mobdr/ui/account/set_address/add_address.dart'; +import 'package:mobdr/ui/account/set_address/edit_address.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class SetAddressPage extends StatefulWidget { + @override + _SetAddressPageState createState() => _SetAddressPageState(); +} + +class _SetAddressPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Set Address', + style: GlobalStyle.appBarTitle, + ), + actions: [ + Padding( + padding: EdgeInsets.only(right: 20.0), + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddAddressPage())); + }, + child: Icon( + Icons.add, + size: 26.0, + ), + )), + ], + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView.builder( + itemCount: addressData.length, + // Add one more item for progress indicator + padding: EdgeInsets.symmetric(vertical: 0), + itemBuilder: (BuildContext context, int index) { + return _buildAddressCard(index); + }, + )); + } + + Widget _buildAddressCard(int index) { + return Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // if default address == true, add default label + addressData[index].defaultAddress == true + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(addressData[index].title, + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 16)), + _reusableWidget.createDefaultLabel(context) + ], + ) + : Text(addressData[index].title, + style: GlobalStyle.addressTitle), + Container( + margin: EdgeInsets.only(top: 20), + child: Text(addressData[index].recipientName, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].phoneNumber, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].addressLine1, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + addressData[index].addressLine2 + + ' ' + + addressData[index].postalCode, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].state, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + addressData[index].defaultAddress == false + ? GestureDetector( + onTap: () { + _showPopupMakeDefault(); + }, + child: Text('Make Default', + style: GlobalStyle.addressAction), + ) + : Wrap(), + index != 0 + ? SizedBox( + width: 12, + ) + : Wrap(), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditAddressPage())); + }, + child: Text('Edit', style: GlobalStyle.addressAction), + ), + SizedBox( + width: 12, + ), + GestureDetector( + onTap: () { + _showPopupDeleteAddress(index); + }, + child: Text('Delete', style: GlobalStyle.addressAction), + ), + ], + )) + ], + ), + ), + ), + ); + } + + void _showPopupDeleteAddress(int index) { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + Navigator.pop(context); + if (index == 0) { + Fluttertoast.showToast( + msg: + 'Please change default address if you want to delete this address', + toastLength: Toast.LENGTH_LONG); + } else { + setState(() { + addressData.removeAt(index); + }); + } + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Delete Address', + style: TextStyle(fontSize: 18), + ), + content: Text('Are you sure to delete this address ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + void _showPopupMakeDefault() { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + Navigator.pop(context); + _reusableWidget.startLoading(context, 'Success', 0); + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Make Default', + style: TextStyle(fontSize: 18), + ), + content: Text('Are you sure to make this address as a default address ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/lib/ui/account/tab_account.dart b/lib/ui/account/tab_account.dart new file mode 100644 index 0000000..8654ea4 --- /dev/null +++ b/lib/ui/account/tab_account.dart @@ -0,0 +1,211 @@ +/* +This is account page +we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime + */ + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/account/about.dart'; +import 'package:mobdr/ui/account/account_information/account_information.dart'; +import 'package:mobdr/ui/account/last_seen_product.dart'; +import 'package:mobdr/ui/account/notification_setting.dart'; +import 'package:mobdr/ui/account/order/order_list.dart'; +import 'package:mobdr/ui/account/payment_method/payment_method.dart'; +import 'package:mobdr/ui/account/privacy_policy.dart'; +import 'package:mobdr/ui/account/set_address/set_address.dart'; +import 'package:mobdr/ui/account/terms_conditions.dart'; +import 'package:mobdr/ui/general/chat_us.dart'; +import 'package:mobdr/ui/general/notification.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class TabAccountPage extends StatefulWidget { + @override + _TabAccountPageState createState() => _TabAccountPageState(); +} + +class _TabAccountPageState extends State + with AutomaticKeepAliveClientMixin { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); + super.build(context); + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Account', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ChatUsPage())); + }, + child: Icon(Icons.email, color: BLACK_GREY)), + IconButton( + icon: _reusableWidget.customNotifIcon( + count: 8, notifColor: BLACK_GREY), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationPage())); + }), + ], + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + _createAccountInformation(), + _createListMenu('Set Address for Delivery', SetAddressPage()), + _reusableWidget.divider1(), + _createListMenu('Order List', OrderListPage()), + _reusableWidget.divider1(), + _createListMenu('Payment Method', PaymentMethodPage()), + _reusableWidget.divider1(), + _createListMenu('Last Seen Product', LastSeenProductPage()), + _reusableWidget.divider1(), + _createListMenu('Notification Setting', NotificationSettingPage()), + _reusableWidget.divider1(), + _createListMenu('About', AboutPage()), + _reusableWidget.divider1(), + _createListMenu('Terms and Conditions', TermsConditionsPage()), + _reusableWidget.divider1(), + _createListMenu('Privacy Policy', PrivacyPolicyPage()), + _reusableWidget.divider1(), + Container( + margin: EdgeInsets.fromLTRB(0, 18, 0, 0), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Fluttertoast.showToast( + msg: 'Click Sign Out', toastLength: Toast.LENGTH_LONG); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.power_settings_new, + size: 20, color: ASSENT_COLOR), + SizedBox(width: 8), + Text('Sign Out', + style: TextStyle(fontSize: 15, color: ASSENT_COLOR)), + ], + ), + ), + ), + ], + )); + } + + Widget _createAccountInformation() { + final double profilePictureSize = MediaQuery.of(context).size.width / 4; + return Container( + margin: EdgeInsets.only(bottom: 14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: profilePictureSize, + height: profilePictureSize, + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AccountInformationPage())); + }, + child: CircleAvatar( + backgroundColor: Colors.grey[200], + radius: profilePictureSize, + child: CircleAvatar( + backgroundColor: Colors.white, + radius: profilePictureSize - 4, + child: Hero( + tag: 'profilePicture', + child: ClipOval( + child: buildCacheNetworkImage( + width: profilePictureSize - 4, + height: profilePictureSize - 4, + url: GLOBAL_URL + '/user/avatar.png')), + ), + ), + ), + ), + ), + SizedBox( + width: 16, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Robert Steven', + style: + TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + SizedBox( + height: 8, + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AccountInformationPage())); + }, + child: Row( + children: [ + Text('Account Information', + style: TextStyle(fontSize: 14, color: BLACK_GREY)), + SizedBox( + width: 8, + ), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY) + ], + ), + ) + ], + ), + ) + ], + ), + ); + } + + Widget _createListMenu(String menuTitle, StatefulWidget page) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => page)); + }, + child: Container( + margin: EdgeInsets.fromLTRB(0, 18, 0, 18), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(menuTitle, style: TextStyle(fontSize: 15, color: CHARCOAL)), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), + ], + ), + ), + ); + } +} diff --git a/lib/ui/account/terms_conditions.dart b/lib/ui/account/terms_conditions.dart new file mode 100644 index 0000000..2b4d473 --- /dev/null +++ b/lib/ui/account/terms_conditions.dart @@ -0,0 +1,64 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; + +class TermsConditionsPage extends StatefulWidget { + @override + _TermsConditionsPageState createState() => _TermsConditionsPageState(); +} + +class _TermsConditionsPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Terms and Conditions', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(8), + child: MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1), + child: Html( + data: """ +

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.

+

Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.

+

Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.

+

Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.

+

Phasellus pellentesque et magna in aliquam. Etiam vehicula dui vitae lectus ultrices iaculis. Nullam volutpat magna vel volutpat laoreet. Donec accumsan mi augue, nec elementum libero imperdiet eget. Duis in enim facilisis, lobortis tellus id, tincidunt urna. Donec ipsum neque, pharetra id imperdiet eget, varius bibendum sapien. Suspendisse tincidunt justo a purus molestie, sed elementum urna scelerisque. Suspendisse eget erat ultrices, suscipit nunc ut, iaculis lacus. Donec finibus, nisi vitae porta sodales, diam sapien scelerisque tortor, vel aliquet urna ex non urna. Etiam dictum eros ut justo ultrices tincidunt. Nulla et neque velit. Phasellus malesuada, lectus et sodales iaculis, sapien nibh ultrices tellus, ut ultrices magna tellus eget tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non diam ac quam aliquet facilisis sit amet at lectus. Nulla vestibulum libero arcu, eu malesuada ipsum congue feugiat.

+ """, + style: { + "p": Style.fromTextStyle( + TextStyle(fontSize: 16), + ), + }, + ), + ), + )); + } +} diff --git a/lib/ui/authentication/forgot_password.dart b/lib/ui/authentication/forgot_password.dart new file mode 100644 index 0000000..1430c09 --- /dev/null +++ b/lib/ui/authentication/forgot_password.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; + +class ForgotPasswordPage extends StatefulWidget { + @override + _ForgotPasswordPageState createState() => _ForgotPasswordPageState(); +} + +class _ForgotPasswordPageState extends State { + TextEditingController _etEmail = TextEditingController(); + + @override + void initState() { + _etEmail = TextEditingController(text: 'robert.steven@ijtechnology.net'); + + super.initState(); + } + + @override + void dispose() { + _etEmail.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ListView( + padding: EdgeInsets.fromLTRB(30, 120, 30, 30), + children: [ + Center(child: Image.asset('assets/images/logo.png', height: 32)), + SizedBox( + height: 80, + ), + TextFormField( + keyboardType: TextInputType.emailAddress, + controller: _etEmail, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Email', + labelStyle: TextStyle(color: BLACK_GREY)), + ), + SizedBox( + height: 10, + ), + Text( + 'We will send the instruction to reset your password through the email', + style: GlobalStyle.resetPasswordNotes, + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + Fluttertoast.showToast( + msg: 'Click Reset Password', + toastLength: Toast.LENGTH_LONG); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Reset Password', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + SizedBox( + height: 50, + ), + // create sign in link + Center( + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GlobalStyle.iconBack, + Text( + ' Back to login', + style: GlobalStyle.back, + ) + ], + ), + ), + ) + ], + )); + } +} diff --git a/lib/ui/authentication/signin.dart b/lib/ui/authentication/signin.dart new file mode 100644 index 0000000..7019d2f --- /dev/null +++ b/lib/ui/authentication/signin.dart @@ -0,0 +1,250 @@ +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/authentication/forgot_password.dart'; +import 'package:mobdr/ui/home.dart'; +import 'package:mobdr/ui/authentication/signup.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mobdr/config/constant.dart'; + +class SigninPage extends StatefulWidget { + @override + _SigninPageState createState() => _SigninPageState(); +} + +class _SigninPageState extends State { + TextEditingController _etEmail = TextEditingController(); + bool _obscureText = true; + IconData _iconVisible = Icons.visibility_off; + + void _toggleObscureText() { + setState(() { + _obscureText = !_obscureText; + if (_obscureText == true) { + _iconVisible = Icons.visibility_off; + } else { + _iconVisible = Icons.visibility; + } + }); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _etEmail.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ListView( + padding: EdgeInsets.fromLTRB(30, 120, 30, 30), + children: [ + Center(child: Image.asset('assets/images/logo.png', height: 32)), + SizedBox( + height: 80, + ), + Text('Sign In', style: GlobalStyle.authTitle), + TextFormField( + keyboardType: TextInputType.emailAddress, + controller: _etEmail, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Email', + labelStyle: TextStyle(color: BLACK_GREY), + ), + ), + SizedBox( + height: 20, + ), + TextField( + obscureText: _obscureText, + style: TextStyle(color: CHARCOAL), + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Password', + labelStyle: TextStyle(color: BLACK_GREY), + suffixIcon: IconButton( + icon: Icon(_iconVisible, color: Colors.grey[400], size: 20), + onPressed: () { + _toggleObscureText(); + }), + ), + ), + SizedBox( + height: 20, + ), + Align( + alignment: Alignment.centerRight, + child: Container( + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ForgotPasswordPage())); + FocusScope.of(context).unfocus(); + }, + child: Text( + 'Forgot Password?', + style: TextStyle(color: PRIMARY_COLOR, fontSize: 13), + ), + ), + )), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + Navigator.pushReplacement(context, + MaterialPageRoute(builder: (context) => HomePage())); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Login', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + SizedBox( + height: 40, + ), + Center( + child: Text( + 'Or sign in with', + style: GlobalStyle.authSignWith, + ), + ), + SizedBox( + height: 20, + ), + Container( + margin: EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + Navigator.pushReplacement(context, + MaterialPageRoute(builder: (context) => HomePage())); + Fluttertoast.showToast( + msg: 'Sign in with Google', + toastLength: Toast.LENGTH_LONG); + }, + child: Image( + image: AssetImage("assets/images/google.png"), + width: 40, + ), + ), + GestureDetector( + onTap: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + Navigator.pushReplacement(context, + MaterialPageRoute(builder: (context) => HomePage())); + Fluttertoast.showToast( + msg: 'Sign in with Facebook', + toastLength: Toast.LENGTH_LONG); + }, + child: Image( + image: AssetImage("assets/images/facebook.png"), + width: 40, + ), + ), + GestureDetector( + onTap: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + Navigator.pushReplacement(context, + MaterialPageRoute(builder: (context) => HomePage())); + Fluttertoast.showToast( + msg: 'Sign in with Twitter', + toastLength: Toast.LENGTH_LONG); + }, + child: Image( + image: AssetImage("assets/images/twitter.png"), + width: 40, + ), + ) + ], + ), + ), + SizedBox( + height: 20, + ), + Center( + child: GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SignupPage())); + FocusScope.of(context).unfocus(); + }, + child: Wrap( + children: [ + Text( + 'No account yet? ', + style: GlobalStyle.authBottom1, + ), + Text( + 'Create one', + style: GlobalStyle.authBottom2, + ) + ], + ), + ), + ), + SizedBox( + height: 30, + ), + Center( + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GlobalStyle.iconBack, + Text( + ' Back', + style: GlobalStyle.back, + ) + ], + ), + ), + ), + ], + )); + } +} diff --git a/lib/ui/authentication/signup.dart b/lib/ui/authentication/signup.dart new file mode 100644 index 0000000..dcedf87 --- /dev/null +++ b/lib/ui/authentication/signup.dart @@ -0,0 +1,177 @@ +import 'package:mobdr/ui/home.dart'; +import 'package:flutter/material.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; + +class SignupPage extends StatefulWidget { + final bool fromList; + + const SignupPage({Key? key, this.fromList = false}) : super(key: key); + + @override + _SignupPageState createState() => _SignupPageState(); +} + +class _SignupPageState extends State { + TextEditingController _etEmail = TextEditingController(); + TextEditingController _etName = TextEditingController(); + bool _obscureText = true; + IconData _iconVisible = Icons.visibility_off; + + void _toggleObscureText() { + setState(() { + _obscureText = !_obscureText; + if (_obscureText == true) { + _iconVisible = Icons.visibility_off; + } else { + _iconVisible = Icons.visibility; + } + }); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _etEmail.dispose(); + _etName.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: WillPopScope( + onWillPop: () { + FocusScope.of(context).unfocus(); + return Future.value(true); + }, + child: ListView( + padding: EdgeInsets.fromLTRB(30, 120, 30, 30), + children: [ + Center(child: Image.asset('assets/images/logo.png', height: 32)), + SizedBox( + height: 80, + ), + Text('Sign Up', style: GlobalStyle.authTitle), + TextFormField( + keyboardType: TextInputType.emailAddress, + controller: _etEmail, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Email', + labelStyle: TextStyle(color: BLACK_GREY), + ), + ), + SizedBox( + height: 20, + ), + TextFormField( + keyboardType: TextInputType.text, + controller: _etName, + style: TextStyle(color: CHARCOAL), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Name', + labelStyle: TextStyle(color: BLACK_GREY), + ), + ), + SizedBox( + height: 20, + ), + TextField( + obscureText: _obscureText, + style: TextStyle(color: CHARCOAL), + decoration: InputDecoration( + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Color(0xFFCCCCCC)), + ), + labelText: 'Password', + labelStyle: TextStyle(color: BLACK_GREY), + suffixIcon: IconButton( + icon: Icon(_iconVisible, color: Colors.grey[400], size: 20), + onPressed: () { + _toggleObscureText(); + }), + ), + ), + SizedBox( + height: 40, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + if (!widget.fromList) { + Navigator.pop(context); + } + Navigator.pushReplacement(context, + MaterialPageRoute(builder: (context) => HomePage())); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Register', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + SizedBox( + height: 30, + ), + Center( + child: GestureDetector( + onTap: () { + Navigator.pop(context); + FocusScope.of(context).unfocus(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GlobalStyle.iconBack, + Text( + ' Back to login', + style: GlobalStyle.back, + ) + ], + ), + ), + ), + ], + ), + )); + } +} diff --git a/lib/ui/general/chat_us.dart b/lib/ui/general/chat_us.dart new file mode 100644 index 0000000..d49a616 --- /dev/null +++ b/lib/ui/general/chat_us.dart @@ -0,0 +1,345 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/chat_model.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class ChatUsPage extends StatefulWidget { + @override + _ChatUsPageState createState() => _ChatUsPageState(); +} + +class _ChatUsPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + TextEditingController _etChat = TextEditingController(); + + String _lastDate = '13 Sep 2019'; + + List _chatList = []; + List _chatListReversed = []; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) async { + _initForLang(); + }); + + super.initState(); + } + + void _initForLang() { + setState(() { + // set chat dummy data + _chatList.add(new ChatModel(1, null, 'date', '9 Sep 2019', null, null)); + _chatList.add( + new ChatModel(2, 'buyer', 'text', 'Good morning', '13:59', true)); + _chatList.add(new ChatModel( + 3, 'buyer', 'image', GLOBAL_URL + '/product/80.jpg', null, null)); + _chatList.add(new ChatModel(4, 'buyer', 'text', + 'I want to ask about the samsung tv product', '13:59', true)); + _chatList.add(new ChatModel(5, 'buyer', 'text', + 'Is the Samsung LED TV 32 Inch is still on sale ?', '14:01', true)); + _chatList.add(new ChatModel(6, 'seller', 'text', + 'Hello, thank you for contacting us', '14:18', true)); + _chatList.add(new ChatModel( + 7, + 'seller', + 'text', + 'We are sorry, but the promotion for Samsung LED TV 32 Inch has ended. Don\'t forget to turn on notification setting for promotion so you will get the news about our product', + '14:20', + null)); + _chatList.add(new ChatModel(8, 'buyer', 'text', + 'Ok, thank you for your information.', '14:22', true)); + _chatList.add(new ChatModel(9, null, 'date', '13 Sep 2019', null, null)); + _chatList.add(new ChatModel( + 10, 'buyer', 'image', GLOBAL_URL + '/product/21.jpg', null, null)); + _chatList.add(new ChatModel( + 11, + 'buyer', + 'text', + 'Hi, is Adidas Polo Shirt size L is ready ? For the black color.', + '08:58', + true)); + _chatList.add(new ChatModel( + 12, 'buyer', 'text', 'I want to order for 2 pcs.', '09:00', true)); + _chatList.add(new ChatModel( + 13, + 'buyer', + 'text', + 'And can I change it if the size doesn\'t fit my body?', + '09:00', + true)); + _chatList.add(new ChatModel( + 14, + 'seller', + 'text', + 'Hello, good morning. The product is ready and you can change if the size is not fit your body', + '09:14', + null)); + + // reverse the list + _chatListReversed = _chatList.reversed.toList(); + }); + } + + @override + void dispose() { + _etChat.dispose(); + super.dispose(); + } + + void _addDate(String currentDate) { + _chatListReversed.insert( + 0, new ChatModel(15, null, 'date', currentDate, null, null)); + } + + void _addMessage(String message) { + DateTime now = DateTime.now(); + String _currentTime = DateFormat('kk:mm').format(now); + _chatListReversed.insert( + 0, new ChatModel(16, 'buyer', 'text', message, _currentTime, false)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Chat', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: Column( + children: [ + Flexible( + child: ListView.builder( + reverse: true, + itemCount: _chatListReversed.length, + padding: EdgeInsets.all(16), + itemBuilder: (context, index) { + if (_chatListReversed[index].getTextImageDate == 'date') { + return _buildDate(_chatListReversed[index].getMessage); + } else if (_chatListReversed[index].getTextImageDate == + 'image') { + return _buildImage(_chatListReversed[index].getMessage); + } else { + if (_chatListReversed[index].getType == 'buyer') { + return _buildChatBuyer( + _chatListReversed[index].getMessage, + _chatListReversed[index].getDate!, + _chatListReversed[index].getRead!); + } else { + return _buildChatSeller( + _chatListReversed[index].getMessage, + _chatListReversed[index].getDate!); + } + } + }, + ), + ), + Container( + margin: EdgeInsets.all(16), + child: Row( + children: [ + Flexible( + child: TextFormField( + controller: _etChat, + minLines: 1, + maxLines: 4, + textAlignVertical: TextAlignVertical.bottom, + style: TextStyle(fontSize: 16, color: Colors.grey[600]), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + fillColor: Colors.grey[200], + filled: true, + hintText: 'Write Message', + focusedBorder: UnderlineInputBorder( + borderRadius: + BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!)), + enabledBorder: UnderlineInputBorder( + borderRadius: + BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!), + ), + ), + ), + ), + SizedBox( + width: 10, + ), + Container( + child: GestureDetector( + onTap: () { + if (_etChat.text != '') { + print('send message : ' + _etChat.text); + setState(() { + DateTime now = DateTime.now(); + String currentDate = + DateFormat('d MMM yyyy').format(now); + if (_lastDate != currentDate) { + _lastDate = currentDate; + _addDate(currentDate); + } + _addMessage(_etChat.text); + _etChat.text = ''; + }); + } + }, + child: ClipOval( + child: Container( + color: SOFT_BLUE, + padding: EdgeInsets.all(10), + child: Icon(Icons.send, color: Colors.white)), + ), + ), + ), + ], + ), + ) + ], + ), + )); + } + + Widget _buildDate(String date) { + return Container( + margin: EdgeInsets.all(16), + child: Center( + child: Text(date, style: TextStyle(color: SOFT_GREY, fontSize: 11)), + ), + ); + } + + Widget _buildChatBuyer(String message, String time, bool read) { + final double boxChatSize = MediaQuery.of(context).size.width / 1.3; + return Container( + margin: EdgeInsets.only(top: 4), + child: Wrap( + alignment: WrapAlignment.end, + children: [ + Container( + constraints: BoxConstraints(maxWidth: boxChatSize), + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(width: 1, color: Colors.grey[300]!), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(5), + bottomLeft: Radius.circular(5), + bottomRight: Radius.circular(12), + ), + color: Colors.grey[300]), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text(message, style: TextStyle(color: CHARCOAL)), + ), + Wrap( + children: [ + SizedBox(width: 4), + Icon(Icons.done_all, + color: read == true ? PRIMARY_COLOR : SOFT_GREY, + size: 11), + SizedBox(width: 2), + Text(time, style: TextStyle(color: SOFT_GREY, fontSize: 9)), + ], + ) + ], + ), + ), + ], + ), + ); + } + + Widget _buildChatSeller(String message, String date) { + final double boxChatSize = MediaQuery.of(context).size.width / 1.3; + return Container( + margin: EdgeInsets.only(top: 4), + child: Wrap( + children: [ + Container( + constraints: BoxConstraints(maxWidth: boxChatSize), + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(width: 1, color: Colors.grey[300]!), + borderRadius: BorderRadius.only( + topRight: Radius.circular(5), + bottomLeft: Radius.circular(12), + bottomRight: Radius.circular(5), + )), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text(message, style: TextStyle(color: CHARCOAL)), + ), + Wrap( + children: [ + SizedBox(width: 2), + Text(date, + style: TextStyle(color: SOFT_GREY, fontSize: 9)), + ], + ) + ], + )), + ], + ), + ); + } + + Widget _buildImage(String imageUrl) { + final double boxChatSize = MediaQuery.of(context).size.width / 1.3; + final double boxImageSize = (MediaQuery.of(context).size.width / 6); + return Container( + margin: EdgeInsets.only(top: 4), + child: Wrap( + alignment: WrapAlignment.end, + children: [ + Container( + constraints: BoxConstraints(maxWidth: boxChatSize), + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(width: 1, color: Colors.grey[300]!), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(5), + bottomLeft: Radius.circular(5), + bottomRight: Radius.circular(12), + )), + child: Container( + width: boxImageSize, + height: boxImageSize, + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: imageUrl)), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/general/notification.dart b/lib/ui/general/notification.dart new file mode 100644 index 0000000..75fc07a --- /dev/null +++ b/lib/ui/general/notification.dart @@ -0,0 +1,155 @@ +import 'dart:async'; + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/account/order/order_status.dart'; +import 'package:mobdr/ui/home/flashsale.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class NotificationPage extends StatefulWidget { + @override + _NotificationPageState createState() => _NotificationPageState(); +} + +class _NotificationPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Notification', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: Container( + child: ListView(children: [ + _createItem( + notifDate: '11 Sep 2019 08:40', + notifTitle: 'Order Completed', + notifMessage: 'Your order is completed', + page: OrderStatusPage()), + _createItem( + notifDate: '11 Sep 2019 08:39', + notifTitle: 'Order Arrived', + notifMessage: 'Your order has arrived', + page: OrderStatusPage()), + _createItem( + notifDate: '10 Sep 2019 10:00', + notifTitle: 'Flash Sale', + notifMessage: + 'Hi Robert Steven, Flash Sale is open in 10 minutes. Grab your favorite product on sale', + page: FlashSalePage()), + _createItem( + notifDate: '9 Sep 2019 14:12', + notifTitle: 'Order Sent', + notifMessage: 'Your order is being shipped by courier', + page: OrderStatusPage()), + _createItem( + notifDate: '9 Sep 2019 14:12', + notifTitle: 'Ready to Pickup', + notifMessage: + 'Your order is ready to be picked up by the courier', + page: OrderStatusPage()), + _createItem( + notifDate: '9 Sep 2019 13:00', + notifTitle: 'Trending Product', + notifMessage: + 'Hi Robert Steven, there is a trending product for you, check it out now'), + _createItem( + notifDate: '9 Sep 2019 12:12', + notifTitle: 'Order Processed', + notifMessage: 'Your order is being processed', + page: OrderStatusPage()), + _createItem( + notifDate: '9 Sep 2019 11:52', + notifTitle: 'Payment Received', + notifMessage: 'Payment has been received', + page: OrderStatusPage()), + _createItem( + notifDate: '9 Sep 2019 11:32', + notifTitle: 'Waiting for Payment', + notifMessage: 'We are waiting for your payment', + page: OrderStatusPage()), + _createItem( + notifDate: '9 Sep 2019 11:32', + notifTitle: 'Order Placed', + notifMessage: 'We have received your order', + page: OrderStatusPage()), + ])), + )); + } + + Widget _createItem( + {required String notifDate, + required String notifTitle, + required String notifMessage, + StatefulWidget? page}) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (page != null) { + Navigator.push( + context, MaterialPageRoute(builder: (context) => page)); + } + }, + child: Container( + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(notifTitle, + style: TextStyle( + fontWeight: FontWeight.bold, color: CHARCOAL)), + SizedBox( + height: 4, + ), + Text(notifDate, + style: + TextStyle(color: Colors.grey[400], fontSize: 11)), + SizedBox( + height: 8, + ), + Text(notifMessage, style: TextStyle(color: BLACK_GREY)), + ], + )), + Divider( + height: 1, + color: Colors.grey[400], + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/general/product_detail/delivery_estimated.dart b/lib/ui/general/product_detail/delivery_estimated.dart new file mode 100644 index 0000000..de93e06 --- /dev/null +++ b/lib/ui/general/product_detail/delivery_estimated.dart @@ -0,0 +1,269 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class DeliveryEstimatedPage extends StatefulWidget { + @override + _DeliveryEstimatedPageState createState() => _DeliveryEstimatedPageState(); +} + +class _DeliveryEstimatedPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Delivery Estimated', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + children: [_createLocationInformation(), _createCourierInformation()], + )); + } + + Widget _createLocationInformation() { + return Container( + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Location', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + Text('Delivery from :', + style: TextStyle(color: SOFT_GREY, fontSize: 14)), + SizedBox( + height: 4, + ), + Text('Brooklyn, NY 11204, USA', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + SizedBox( + height: 12, + ), + Text('Delivery to :', + style: TextStyle(color: SOFT_GREY, fontSize: 14)), + SizedBox( + height: 4, + ), + Container( + child: Text('Robert Steven', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('0811888999', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('6019 Madison St', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('West New York, NJ 07093', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('USA', style: TextStyle(color: CHARCOAL, fontSize: 14)), + ) + ], + ), + ); + } + + Widget _createCourierInformation() { + return Container( + margin: EdgeInsets.only(top: 16), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Courier', style: GlobalStyle.chooseCourier), + SizedBox( + height: 8, + ), + Text('Courier price based on weight per 1000gr', + style: TextStyle(color: SOFT_GREY, fontSize: 14)), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('DHL', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$13', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$22', style: GlobalStyle.deliveryPrice), + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('FedEx', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 1', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 2', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 3', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 4', style: GlobalStyle.courierTitle), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + SizedBox( + height: 8, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ], + ), + ); + } +} diff --git a/lib/ui/general/product_detail/product_description.dart b/lib/ui/general/product_detail/product_description.dart new file mode 100644 index 0000000..55b21e1 --- /dev/null +++ b/lib/ui/general/product_detail/product_description.dart @@ -0,0 +1,94 @@ +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/material.dart'; + +class ProductDescriptionPage extends StatefulWidget { + final String name, image; + + const ProductDescriptionPage({Key? key, this.name = '', this.image = ''}) + : super(key: key); + @override + _ProductDescriptionPageState createState() => _ProductDescriptionPageState(); +} + +class _ProductDescriptionPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 4); + + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Product Description', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + children: [ + _createProductImageAndTitle(boxImageSize), + Divider(height: 0, color: Colors.grey[400]), + Container( + padding: EdgeInsets.all(16), + child: Text('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n' + + 'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n' + + 'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n' + + 'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'), + ) + ], + )); + } + + Widget _createProductImageAndTitle(boxImageSize) { + return Container( + margin: EdgeInsets.all(24), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(4)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: widget.image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.name, + style: GlobalStyle.productName + .copyWith(fontSize: 14, fontWeight: FontWeight.bold), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ) + ], + )); + } +} diff --git a/lib/ui/general/product_detail/product_detail.dart b/lib/ui/general/product_detail/product_detail.dart new file mode 100644 index 0000000..3ce69e7 --- /dev/null +++ b/lib/ui/general/product_detail/product_detail.dart @@ -0,0 +1,818 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/related_product_model.dart'; +import 'package:mobdr/model/review_model.dart'; +import 'package:mobdr/ui/general/chat_us.dart'; +import 'package:mobdr/ui/general/notification.dart'; +import 'package:mobdr/ui/general/product_detail/delivery_estimated.dart'; +import 'package:mobdr/ui/general/product_detail/product_description.dart'; +import 'package:mobdr/ui/general/product_detail/product_review.dart'; +import 'package:mobdr/ui/home/product_category.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class ProductDetailPage extends StatefulWidget { + final String name; + final String image; + final double price; + final double rating; + final int review; + final int sale; + + const ProductDetailPage( + {Key? key, + this.name = '', + this.image = '', + this.price = 24, + this.rating = 4, + this.review = 45, + this.sale = 63}) + : super(key: key); + + @override + _ProductDetailPageState createState() => _ProductDetailPageState(); +} + +class _ProductDetailPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + final List _imgProductSlider = []; + int _currentImageSlider = 0; + + // size data + List _sizeList = ['XS', 'S', 'M', 'L', 'XL', 'XXL']; + int _sizeIndex = 0; + + // color data + List _colorList = [ + 'Red', + 'Black', + 'Green', + 'White', + 'Blue', + 'Yellow', + 'Pink' + ]; + int _colorIndex = 0; + + // wishlist + bool _isLove = false; + + // shopping cart count + int _shoppingCartCount = 3; + + @override + void initState() { + // image slider for the product + _imgProductSlider.add(widget.image); + _imgProductSlider.add(widget.image); + _imgProductSlider.add(widget.image); + _imgProductSlider.add(widget.image); + _imgProductSlider.add(widget.image); + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 3); + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + titleSpacing: 0.0, + // create search text field in the app bar + title: Container( + margin: EdgeInsets.only(right: 16), + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => Colors.grey[100]!, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SearchPage())); + }, + child: Row( + children: [ + SizedBox(width: 8), + Icon(Icons.search, color: Colors.grey[500], size: 18), + SizedBox(width: 8), + Text( + 'Search Product', + style: TextStyle( + fontSize: 13, + color: Colors.grey[600], + fontWeight: FontWeight.normal), + ) + ], + )), + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + IconButton( + padding: EdgeInsets.all(0), + constraints: BoxConstraints(), + icon: _customShoppingCart(_shoppingCartCount), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TabShoppingCartPage())); + }), + IconButton( + icon: _reusableWidget.customNotifIcon( + count: 8, notifColor: BLACK_GREY), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationPage())); + }), + ], + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + _createProductSlider(), + _createProductPriceTitleEtc(), + _createProductVariant(), + _createDeliveryEstimated(), + _createProductInformation(), + _createProductDescription(), + _createProductRelated(boxImageSize), + _createProductReview(), + SizedBox(height: 16) + ], + ), + ), + Container( + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 2.0, + ), + ], + ), + child: Row( + children: [ + Container( + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatUsPage())); + }, + child: ClipOval( + child: Container( + color: SOFT_BLUE, + padding: EdgeInsets.all(9), + child: Icon(Icons.chat, + color: Colors.white, size: 16)), + ), + ), + ), + SizedBox( + width: 10, + ), + Expanded( + child: GestureDetector( + onTap: () { + setState(() { + _shoppingCartCount++; + }); + Fluttertoast.showToast( + msg: 'Item has been added to Shopping Cart', + toastLength: Toast.LENGTH_LONG); + }, + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 1, color: SOFT_BLUE), + borderRadius: BorderRadius.all(Radius.circular( + 10) // <--- border radius here + )), + child: Text('Add to Shopping Cart', + style: TextStyle( + color: SOFT_BLUE, + fontWeight: FontWeight.bold)), + ), + ), + ), + ], + ), + ) + ], + ), + )); + } + + Widget _customShoppingCart(int count) { + return Stack( + children: [ + Icon(Icons.shopping_cart, color: BLACK_GREY), + Positioned( + right: 0, + child: Container( + padding: EdgeInsets.all(1), + decoration: BoxDecoration( + color: ASSENT_COLOR, + borderRadius: BorderRadius.circular(10), + ), + constraints: BoxConstraints( + minWidth: 14, + minHeight: 14, + ), + child: Center( + child: Text( + count.toString(), + style: TextStyle( + color: Colors.white, + fontSize: 8, + ), + textAlign: TextAlign.center, + ), + ), + ), + ) + ], + ); + } + + Widget _createProductSlider() { + return Stack( + children: [ + CarouselSlider( + items: _imgProductSlider + .map((item) => Container( + child: + buildCacheNetworkImage(width: 0, height: 0, url: item), + )) + .toList(), + options: CarouselOptions( + aspectRatio: 1, + viewportFraction: 1.0, + autoPlay: false, + enlargeCenterPage: false, + onPageChanged: (index, reason) { + setState(() { + _currentImageSlider = index; + }); + }), + ), + Positioned( + bottom: 16, + left: 16, + child: Container( + padding: EdgeInsets.fromLTRB(8, 4, 8, 4), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(4)), + child: Text( + (_currentImageSlider + 1).toString() + + '/' + + _imgProductSlider.length.toString(), + style: TextStyle(color: Colors.white, fontSize: 11)), + ), + ), + ], + ); + } + + Widget _createProductPriceTitleEtc() { + return Container( + color: Colors.white, + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('\$' + _globalFunction.removeDecimalZeroFormat(widget.price), + style: GlobalStyle.detailProductPrice), + GestureDetector( + onTap: () { + setState(() { + if (_isLove == true) { + _isLove = false; + Fluttertoast.showToast( + msg: 'Item has been deleted from your wishlist', + toastLength: Toast.LENGTH_LONG); + } else { + Fluttertoast.showToast( + msg: 'Item has been added to your wishlist', + toastLength: Toast.LENGTH_LONG); + _isLove = true; + } + }); + }, + child: Icon(Icons.favorite, + color: _isLove == true ? ASSENT_COLOR : BLACK_GREY, + size: 28), + ) + ], + ), + SizedBox(height: 12), + Text(widget.name, + style: TextStyle( + fontSize: 14, + )), + SizedBox(height: 12), + IntrinsicHeight( + child: Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductReviewPage())); + }, + child: Row( + children: [ + Icon(Icons.star, color: Colors.yellow[700], size: 18), + SizedBox( + width: 3, + ), + Text(widget.rating.toString(), + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 14)), + SizedBox( + width: 3, + ), + Text('(' + widget.review.toString() + ')', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + ], + ), + ), + VerticalDivider( + width: 30, + thickness: 1, + color: Colors.grey[300], + ), + Text(widget.sale.toString() + ' Sale', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + VerticalDivider( + width: 30, + thickness: 1, + color: Colors.grey[300], + ), + Icon(Icons.location_on, color: SOFT_GREY, size: 16), + Text('Brooklyn', + style: TextStyle(fontSize: 13, color: SOFT_GREY)) + ], + ), + ), + ], + ), + ); + } + + Widget _createProductVariant() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Variant', style: GlobalStyle.sectionTitle), + SizedBox( + height: 16, + ), + Row( + children: [ + Text('Size : ', + style: TextStyle(color: BLACK_GREY, fontSize: 14)), + Text(_sizeList[_sizeIndex], + style: TextStyle(fontWeight: FontWeight.bold)), + ], + ), + Wrap( + children: List.generate(_sizeList.length, (index) { + return radioSize(_sizeList[index], index); + }), + ), + SizedBox( + height: 16, + ), + Row( + children: [ + Text('Color : ', + style: TextStyle(color: BLACK_GREY, fontSize: 14)), + Text(_colorList[_colorIndex], + style: TextStyle(fontWeight: FontWeight.bold)), + ], + ), + Wrap( + children: List.generate(_colorList.length, (index) { + return radioColor(_colorList[index], index); + }), + ), + ], + )); + } + + Widget radioSize(String txt, int index) { + return GestureDetector( + onTap: () { + setState(() { + _sizeIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _sizeIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _sizeIndex == index ? SOFT_BLUE : Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Text(txt, + style: TextStyle( + color: _sizeIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget radioColor(String txt, int index) { + return GestureDetector( + onTap: () { + setState(() { + _colorIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _colorIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _colorIndex == index ? SOFT_BLUE : Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Text(txt, + style: TextStyle( + color: _colorIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget _createDeliveryEstimated() { + return GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => DeliveryEstimatedPage())); + }, + child: Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Row( + children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Delivery', style: GlobalStyle.sectionTitle), + SizedBox( + height: 16, + ), + RichText( + text: new TextSpan( + // Note: Styles for TextSpans must be explicitly defined. + // Child text spans will inherit styles from parent + style: new TextStyle( + fontSize: 15.5, + color: BLACK_GREY, + ), + children: [ + new TextSpan( + text: + 'Calculate the estimated cost for shipping goods to '), + new TextSpan( + text: 'West New York, NJ', + style: + new TextStyle(fontWeight: FontWeight.bold)), + ], + ), + ), + ], + ), + ), + Icon(Icons.chevron_right, size: 36, color: CHARCOAL) + ], + )), + ); + } + + Widget _createProductInformation() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Information', style: GlobalStyle.sectionTitle), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Weight', style: TextStyle(color: BLACK_GREY)), + Text('300 Gram', style: TextStyle(color: BLACK_GREY)) + ], + ), + SizedBox( + height: 12, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Condition', style: TextStyle(color: BLACK_GREY)), + Text('Second', style: TextStyle(color: BLACK_GREY)) + ], + ), + SizedBox( + height: 12, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Category', style: TextStyle(color: BLACK_GREY)), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: 3, categoryName: 'Electronic'))); + }, + child: Text('Electronic', style: TextStyle(color: SOFT_BLUE)), + ) + ], + ), + ], + )); + } + + Widget _createProductDescription() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Description', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + Text( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nQuisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales.'), + SizedBox( + height: 16, + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDescriptionPage( + name: widget.name, image: widget.image))); + }, + child: Center( + child: Text('Read More', style: TextStyle(color: SOFT_BLUE)), + ), + ), + ], + )); + } + + Widget _createProductRelated(boxImageSize) { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.only(bottom: 16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.all(16), + child: Text('Related Product', style: GlobalStyle.sectionTitle), + ), + Container( + height: boxImageSize * + GlobalStyle.horizontalProductHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + itemCount: relatedProductData.length, + itemBuilder: (BuildContext context, int index) { + return Container( + width: boxImageSize + 10, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: relatedProductData[index].name, + image: relatedProductData[index].image, + price: relatedProductData[index].price, + rating: relatedProductData[index].rating, + review: relatedProductData[index].review, + sale: 36))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize + 10, + height: boxImageSize + 10, + url: relatedProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + relatedProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction + .removeDecimalZeroFormat( + relatedProductData[index] + .price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: relatedProductData[index] + .rating, + size: 12), + Text( + '(' + + relatedProductData[index] + .review + .toString() + + ')', + style: + GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ], + )); + } + + Widget _createProductReview() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Review', style: GlobalStyle.sectionTitle), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductReviewPage())); + }, + child: Text('View All', + style: GlobalStyle.viewAll.copyWith(color: SOFT_BLUE), + textAlign: TextAlign.end), + ) + ], + ), + SizedBox( + height: 8, + ), + Row( + children: [ + _reusableWidget.createRatingBar( + rating: widget.rating, size: 12), + Text('(' + widget.review.toString() + ')', + style: TextStyle(fontSize: 11, color: SOFT_GREY)) + ], + ), + Column( + children: List.generate(reviewData.length, (index) { + return Column( + children: [ + Divider( + height: 32, + color: Colors.grey[400], + ), + Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(reviewData[index].date, + style: TextStyle(fontSize: 13, color: SOFT_GREY)), + SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(reviewData[index].name, + style: TextStyle( + fontSize: 14, fontWeight: FontWeight.bold)), + _reusableWidget.createRatingBar( + rating: reviewData[index].rating, size: 12), + ], + ), + SizedBox(height: 4), + Text(reviewData[index].review) + ], + )) + ], + ); + })), + ], + )); + } +} diff --git a/lib/ui/general/product_detail/product_review.dart b/lib/ui/general/product_detail/product_review.dart new file mode 100644 index 0000000..8264b23 --- /dev/null +++ b/lib/ui/general/product_detail/product_review.dart @@ -0,0 +1,145 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/review_model.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class ProductReviewPage extends StatefulWidget { + @override + _ProductReviewPageState createState() => _ProductReviewPageState(); +} + +class _ProductReviewPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + List starList = ['All Review', '1', '2', '3', '4', '5']; + int starIndex = 0; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Product Review', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.all(16), + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate(starList.length, (index) { + return radioStar(starList[index], index); + }), + ), + ), + SizedBox(height: 16), + Container( + child: ListView.builder( + shrinkWrap: true, + physics: ScrollPhysics(), + itemCount: reviewData.length, + // Add one more item for progress indicator + itemBuilder: (BuildContext context, int index) { + return _buildReviewCard(index); + }, + ), + ), + ], + )); + } + + Widget radioStar(String txt, int index) { + return GestureDetector( + onTap: () { + setState(() { + starIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 10), + decoration: BoxDecoration( + color: starIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: starIndex == index ? SOFT_BLUE : Colors.grey[300]!), + borderRadius: BorderRadius.all(Radius.circular(10))), + child: index == 0 + ? Text(txt, + style: TextStyle( + color: starIndex == index ? Colors.white : CHARCOAL)) + : Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text(txt, + style: TextStyle( + color: + starIndex == index ? Colors.white : CHARCOAL)), + SizedBox(width: 2), + Icon(Icons.star, + color: starIndex == index + ? Colors.white + : Colors.yellow[700], + size: 12), + ], + )), + ); + } + + Widget _buildReviewCard(index) { + return Card( + margin: EdgeInsets.only(top: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(reviewData[index].date, + style: TextStyle(fontSize: 13, color: SOFT_GREY)), + SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(reviewData[index].name, + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + _reusableWidget.createRatingBar( + rating: reviewData[index].rating, size: 12) + ], + ), + SizedBox(height: 4), + Text(reviewData[index].review) + ], + ), + ), + ); + } +} diff --git a/lib/ui/home.dart b/lib/ui/home.dart new file mode 100644 index 0000000..f59f278 --- /dev/null +++ b/lib/ui/home.dart @@ -0,0 +1,94 @@ +import 'package:mobdr/ui/account/tab_account.dart'; +import 'package:mobdr/ui/home/tab_home.dart'; +import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.dart'; +import 'package:mobdr/ui/wishlist/tab_wishlist.dart'; +import 'package:flutter/material.dart'; +import 'package:mobdr/config/constant.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State + with SingleTickerProviderStateMixin { + late PageController _pageController; + int _currentIndex = 0; + + // Pages if you click bottom navigation + final List _contentPages = [ + TabHomePage(), + TabWishlistPage(), + TabShoppingCartPage(), + TabAccountPage(), + ]; + + @override + void initState() { + // set initial pages for navigation to home page + _pageController = PageController(initialPage: 0); + _pageController.addListener(_handleTabSelection); + super.initState(); + } + + void _handleTabSelection() { + setState(() {}); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: PageView( + controller: _pageController, + physics: NeverScrollableScrollPhysics(), + children: _contentPages.map((Widget content) { + return content; + }).toList(), + ), + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: _currentIndex, + onTap: (value) { + _currentIndex = value; + _pageController.jumpToPage(value); + // this unfocus is to prevent show keyboard in the wishlist page when focus on search text field + FocusScope.of(context).unfocus(); + }, + selectedFontSize: 8, + unselectedFontSize: 8, + iconSize: 28, + selectedLabelStyle: TextStyle( + color: _currentIndex == 1 ? ASSENT_COLOR : PRIMARY_COLOR, + fontWeight: FontWeight.bold), + unselectedLabelStyle: + TextStyle(color: CHARCOAL, fontWeight: FontWeight.bold), + selectedItemColor: _currentIndex == 1 ? ASSENT_COLOR : PRIMARY_COLOR, + unselectedItemColor: CHARCOAL, + items: [ + BottomNavigationBarItem( + label: 'Home', + icon: Icon(Icons.home, + color: _currentIndex == 0 ? PRIMARY_COLOR : CHARCOAL)), + BottomNavigationBarItem( + label: 'Wishlist', + icon: Icon(Icons.favorite, + color: _currentIndex == 1 ? ASSENT_COLOR : CHARCOAL)), + BottomNavigationBarItem( + label: 'Cart', + icon: Icon(Icons.shopping_cart, + color: _currentIndex == 2 ? PRIMARY_COLOR : CHARCOAL)), + BottomNavigationBarItem( + label: 'Account', + icon: Icon(Icons.person_outline, + color: _currentIndex == 3 ? PRIMARY_COLOR : CHARCOAL)), + ], + ), + ); + } +} diff --git a/lib/ui/home/coupon.dart b/lib/ui/home/coupon.dart new file mode 100644 index 0000000..7bb34a9 --- /dev/null +++ b/lib/ui/home/coupon.dart @@ -0,0 +1,171 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/coupon_model.dart'; +import 'package:mobdr/ui/home/coupon_detail.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class CouponPage extends StatefulWidget { + @override + _CouponPageState createState() => _CouponPageState(); +} + +class _CouponPageState extends State { + TextEditingController _etSearch = TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _etSearch.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Coupon', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: PreferredSize( + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey[100]!, + width: 1.0, + )), + ), + padding: EdgeInsets.fromLTRB(16, 0, 16, 12), + height: kToolbarHeight, + child: TextFormField( + controller: _etSearch, + textAlignVertical: TextAlignVertical.bottom, + maxLines: 1, + style: TextStyle(fontSize: 16, color: Colors.grey[600]), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + fillColor: Colors.grey[100], + filled: true, + hintText: 'Enter promo code', + prefixIcon: Icon(Icons.search, color: Colors.grey[500]), + suffixIcon: (_etSearch.text == '') + ? null + : GestureDetector( + onTap: () { + setState(() { + _etSearch = TextEditingController(text: ''); + }); + }, + child: Icon(Icons.close, color: Colors.grey[500])), + focusedBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!)), + enabledBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!), + ), + ), + ), + ), + preferredSize: Size.fromHeight(kToolbarHeight), + ), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: ListView.builder( + itemCount: couponData.length, + padding: EdgeInsets.fromLTRB(16, 0, 16, 16), + physics: AlwaysScrollableScrollPhysics(), + // Add one more item for progress indicator + itemBuilder: (BuildContext context, int index) { + return _buildCouponCard(couponData[index]); + }, + ), + )); + } + + Widget _buildCouponCard(CouponModel couponData) { + return Card( + margin: EdgeInsets.only(top: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CouponDetailPage(couponData: couponData))); + }, + child: Container( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 5), + padding: EdgeInsets.fromLTRB(8, 4, 8, 4), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(5)), + child: Text('Limited Offer', + style: GlobalStyle.couponLimitedOffer), + ), + SizedBox(height: 12), + Text(couponData.name, style: GlobalStyle.couponName), + SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + GlobalStyle.iconTime, + SizedBox( + width: 4, + ), + Text('Expiring in ' + couponData.day + ' days', + style: GlobalStyle.couponExpired), + ], + ), + GestureDetector( + onTap: () { + Fluttertoast.showToast( + msg: 'Coupon applied', + toastLength: Toast.LENGTH_LONG); + Navigator.pop(context); + }, + child: Text('Use Now', + style: TextStyle( + fontSize: 14, + color: SOFT_BLUE, + fontWeight: FontWeight.bold)), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/home/coupon_detail.dart b/lib/ui/home/coupon_detail.dart new file mode 100644 index 0000000..097d65e --- /dev/null +++ b/lib/ui/home/coupon_detail.dart @@ -0,0 +1,143 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/coupon_model.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class CouponDetailPage extends StatefulWidget { + final bool fromList; + final CouponModel couponData; + + const CouponDetailPage( + {Key? key, required this.couponData, this.fromList = false}) + : super(key: key); + + @override + _CouponDetailPageState createState() => _CouponDetailPageState(); +} + +class _CouponDetailPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Coupon Detail', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + padding: EdgeInsets.fromLTRB(24, 20, 24, 24), + children: [ + _buildCouponCard(widget.couponData), + Container( + margin: EdgeInsets.only(top: 24), + child: Text('Terms and Conditions', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Text(widget.couponData.term), + ), + Container( + margin: EdgeInsets.only(top: 12), + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + Fluttertoast.showToast( + msg: 'Coupon applied', toastLength: Toast.LENGTH_LONG); + Navigator.pop(context); + if (!widget.fromList) { + Navigator.pop(context); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Use', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ), + ], + )); + } + + Widget _buildCouponCard(CouponModel couponData) { + return Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + elevation: 2, + color: Colors.white, + child: Container( + padding: EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(couponData.name, + style: GlobalStyle.couponName.copyWith(fontSize: 18)), + SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + margin: EdgeInsets.only(top: 5), + padding: EdgeInsets.fromLTRB(8, 4, 8, 4), + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.circular(5)), // + child: Text('Limited Offer', + style: GlobalStyle.couponLimitedOffer), + ), + Row( + children: [ + GlobalStyle.iconTime, + SizedBox( + width: 4, + ), + Text('Expiring in ' + couponData.day + ' days', + style: GlobalStyle.couponExpired), + ], + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/home/flashsale.dart b/lib/ui/home/flashsale.dart new file mode 100644 index 0000000..97cab94 --- /dev/null +++ b/lib/ui/home/flashsale.dart @@ -0,0 +1,344 @@ +import 'dart:async'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/flashsale_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class FlashSalePage extends StatefulWidget { + final int seconds; + + const FlashSalePage({Key? key, this.seconds = 0}) : super(key: key); + @override + _FlashSalePageState createState() => _FlashSalePageState(); +} + +class _FlashSalePageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + Timer? _flashsaleTimer; + late int _flashsaleSecond; + + void _startFlashsaleTimer() { + const period = const Duration(seconds: 1); + _flashsaleTimer = Timer.periodic(period, (timer) { + setState(() { + _flashsaleSecond--; + }); + if (_flashsaleSecond == 0) { + _cancelFlashsaleTimer(); + } + }); + } + + void _cancelFlashsaleTimer() { + if (_flashsaleTimer != null) { + _flashsaleTimer?.cancel(); + _flashsaleTimer = null; + } + } + + @override + void initState() { + _flashsaleSecond = widget.seconds; + if (_flashsaleSecond != 0) { + _startFlashsaleTimer(); + } + + super.initState(); + } + + @override + void dispose() { + _cancelFlashsaleTimer(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Flash Sale', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + IconButton( + icon: Icon(Icons.search, color: BLACK_GREY), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SearchPage())); + }), + ], + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: ListView( + physics: AlwaysScrollableScrollPhysics(), + children: [ + buildCacheNetworkImage( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width / 2, + url: GLOBAL_URL + '/flashsale/1.jpg'), + Container( + margin: EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Flash sale end in :', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: CHARCOAL)), + _buildFlashsaleTime(), + ], + ), + ), + CustomScrollView( + shrinkWrap: true, + primary: false, + slivers: [ + SliverPadding( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: + GlobalStyle.gridDelegateFlashsaleRatio, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildFlashsaleCard(index); + }, + childCount: flashsaleData.length, + ), + ), + ), + ]) + ], + ), + )); + } + + Widget _buildFlashsaleTime() { + int hour = _flashsaleSecond ~/ 3600; + int minute = _flashsaleSecond % 3600 ~/ 60; + int second = _flashsaleSecond % 60; + + return Row( + children: [ + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(hour), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ), + Text(' : ', + style: TextStyle( + color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)), + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(minute), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ), + Text(' : ', + style: TextStyle( + color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)), + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(second), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ) + ], + ); + } + + Widget _buildFlashsaleCard(index) { + final double boxImageSize = + ((MediaQuery.of(context).size.width) - 24) / 2 - 12; + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: flashsaleData[index].name, + image: flashsaleData[index].image, + price: flashsaleData[index].price, + rating: 4, + review: 45, + sale: flashsaleData[index].sale))); + }, + child: Column( + children: [ + Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: flashsaleData[index].image)), + Positioned( + right: 0, + top: 10, + child: Container( + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6), + bottomLeft: Radius.circular(6))), + padding: EdgeInsets.fromLTRB(12, 6, 12, 6), + child: Text( + flashsaleData[index].discount.toString() + '%', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 13)), + ), + ), + ], + ), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + flashsaleData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + (100 - flashsaleData[index].discount) * + flashsaleData[index].price / + 100), + style: GlobalStyle.productPrice), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + flashsaleData[index].price), + style: GlobalStyle.productPriceDiscounted, + textAlign: TextAlign.right, + ), + ) + ], + ), + ), + _createAvailableBar(flashsaleData[index].sale.toDouble(), + flashsaleData[index].countItem), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + flashsaleData[index].countItem - + flashsaleData[index].sale == + 0 + ? 'Sold out' + : 'Available', + style: TextStyle( + color: CHARCOAL, + fontSize: 12, + fontWeight: FontWeight.bold)), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget _createAvailableBar(double sale, double total) { + final double availableWidth = + ((MediaQuery.of(context).size.width) - 24) / 2 - 28; + return Container( + margin: EdgeInsets.only(top: 10), + child: Container( + child: Row( + children: [ + Container( + width: sale / total * (availableWidth), + height: 5, + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10), + topRight: Radius.circular(sale == total ? 10 : 0), + bottomRight: Radius.circular(sale == total ? 10 : 0)), + ), + ), + Container( + width: (total - sale) / total * (availableWidth), + height: 5, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.only( + topLeft: Radius.circular(sale == 0 ? 10 : 0), + bottomLeft: Radius.circular(sale == 0 ? 10 : 0), + topRight: Radius.circular(10), + bottomRight: Radius.circular(10)), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/home/last_search.dart b/lib/ui/home/last_search.dart new file mode 100644 index 0000000..ad0a8b7 --- /dev/null +++ b/lib/ui/home/last_search.dart @@ -0,0 +1,180 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/last_search_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class LastSearchPage extends StatefulWidget { + @override + _LastSearchPageState createState() => _LastSearchPageState(); +} + +class _LastSearchPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Last Search', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + IconButton( + icon: Icon(Icons.search, color: BLACK_GREY), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SearchPage())); + }), + ], + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: CustomScrollView( + shrinkWrap: true, + primary: false, + physics: AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: GlobalStyle.gridDelegateRatio, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildLastSearchCard(index); + }, + childCount: lastSearchData.length, + ), + ), + ), + ]), + )); + } + + Widget _buildLastSearchCard(index) { + final double boxImageSize = + ((MediaQuery.of(context).size.width) - 24) / 2 - 12; + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: lastSearchData[index].name, + image: lastSearchData[index].image, + price: lastSearchData[index].price, + rating: lastSearchData[index].rating, + review: lastSearchData[index].review, + sale: lastSearchData[index].sale))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: lastSearchData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + lastSearchData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + lastSearchData[index].price), + style: GlobalStyle.productPrice), + Text(lastSearchData[index].sale.toString() + ' Sale', + style: GlobalStyle.productSale) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, color: SOFT_GREY, size: 12), + Text(' ' + lastSearchData[index].location, + style: GlobalStyle.productLocation) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: lastSearchData[index].rating, size: 12), + Text( + '(' + + lastSearchData[index].review.toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/home/product_category.dart b/lib/ui/home/product_category.dart new file mode 100644 index 0000000..bc5be94 --- /dev/null +++ b/lib/ui/home/product_category.dart @@ -0,0 +1,453 @@ +import 'dart:async'; + +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/category_all_product_model.dart'; +import 'package:mobdr/model/category_banner_model.dart'; +import 'package:mobdr/model/category_new_product_model.dart'; +import 'package:mobdr/model/category_trending_product_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class ProductCategoryPage extends StatefulWidget { + final int categoryId; + final String categoryName; + + const ProductCategoryPage( + {Key? key, this.categoryId = 0, required this.categoryName}) + : super(key: key); + @override + _ProductCategoryPageState createState() => _ProductCategoryPageState(); +} + +class _ProductCategoryPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + int _currentImageSlider = 0; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 3); + + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + widget.categoryName.replaceAll('\n', ' '), + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + IconButton( + icon: Icon(Icons.search, color: BLACK_GREY), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SearchPage())); + }), + ], + bottom: _reusableWidget.bottomAppBar(), + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: ListView( + physics: AlwaysScrollableScrollPhysics(), + children: [ + _createCategorySlider(), + Container( + margin: EdgeInsets.only(top: 20, left: 16, right: 16), + child: + Text('Trending Product', style: GlobalStyle.sectionTitle), + ), + Container( + margin: EdgeInsets.only(top: 8), + height: boxImageSize * + GlobalStyle.horizontalProductHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + itemCount: categoryTrendingProductData.length, + itemBuilder: (BuildContext context, int index) { + return _buildTrendingProductCard(index, boxImageSize); + }, + )), + Container( + margin: EdgeInsets.only(top: 20, left: 16, right: 16), + child: Text('New Product', style: GlobalStyle.sectionTitle), + ), + Container( + margin: EdgeInsets.only(top: 8), + height: boxImageSize * + GlobalStyle.horizontalProductHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + itemCount: categoryNewProductData.length, + itemBuilder: (BuildContext context, int index) { + return _buildNewProductCard(index, boxImageSize); + }, + ), + ), + Container( + margin: EdgeInsets.only(top: 20, left: 16, right: 16), + child: Text('All Product', style: GlobalStyle.sectionTitle), + ), + CustomScrollView( + shrinkWrap: true, + primary: false, + slivers: [ + SliverPadding( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: GlobalStyle.gridDelegateRatio, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildAllProductCard(index); + }, + childCount: categoryAllProductData.length, + ), + ), + ), + ]), + ], + ), + )); + } + + Widget _createCategorySlider() { + return Stack( + children: [ + CarouselSlider( + items: categoryBannerData + .map((item) => Container( + child: buildCacheNetworkImage( + width: 0, height: 0, url: item.image), + )) + .toList(), + options: CarouselOptions( + aspectRatio: 2, + viewportFraction: 1.0, + autoPlay: true, + autoPlayInterval: Duration(seconds: 6), + autoPlayAnimationDuration: Duration(milliseconds: 300), + enlargeCenterPage: false, + onPageChanged: (index, reason) { + setState(() { + _currentImageSlider = index; + }); + }), + ), + Positioned.fill( + child: Align( + alignment: Alignment.bottomCenter, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: categoryBannerData.map((item) { + int index = categoryBannerData.indexOf(item); + return Container( + width: 8.0, + height: 8.0, + margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _currentImageSlider == index + ? SOFT_BLUE + : Colors.grey[300], + ), + ); + }).toList(), + ), + ), + ), + ], + ); + } + + Widget _buildTrendingProductCard(index, boxImageSize) { + return Container( + width: boxImageSize + 10, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: categoryTrendingProductData[index].name, + image: categoryTrendingProductData[index].image, + price: categoryTrendingProductData[index].price, + rating: categoryTrendingProductData[index].rating, + review: categoryTrendingProductData[index].review, + sale: 44))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize + 10, + height: boxImageSize + 10, + url: categoryTrendingProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + categoryTrendingProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + categoryTrendingProductData[index].price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: categoryTrendingProductData[index].rating, + size: 12), + Text( + '(' + + categoryTrendingProductData[index] + .review + .toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildNewProductCard(index, boxImageSize) { + return Container( + width: boxImageSize + 10, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: categoryNewProductData[index].name, + image: categoryNewProductData[index].image, + price: categoryNewProductData[index].price, + rating: categoryNewProductData[index].rating, + review: categoryNewProductData[index].review, + sale: 36))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize + 10, + height: boxImageSize + 10, + url: categoryNewProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + categoryNewProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + categoryNewProductData[index].price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: categoryNewProductData[index].rating, + size: 12), + Text( + '(' + + categoryNewProductData[index] + .review + .toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildAllProductCard(index) { + final double boxImageSize = + ((MediaQuery.of(context).size.width) - 24) / 2 - 12; + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: categoryAllProductData[index].name, + image: categoryAllProductData[index].image, + price: categoryAllProductData[index].price, + rating: categoryAllProductData[index].rating, + review: categoryAllProductData[index].review, + sale: categoryAllProductData[index].sale))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: categoryAllProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + categoryAllProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + categoryAllProductData[index].price), + style: GlobalStyle.productPrice), + Text( + categoryAllProductData[index].sale.toString() + + ' Sale', + style: GlobalStyle.productSale) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, color: SOFT_GREY, size: 12), + Text(' ' + categoryAllProductData[index].location, + style: GlobalStyle.productLocation) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: categoryAllProductData[index].rating, + size: 12), + Text( + '(' + + categoryAllProductData[index] + .review + .toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/home/search.dart b/lib/ui/home/search.dart new file mode 100644 index 0000000..c1928d0 --- /dev/null +++ b/lib/ui/home/search.dart @@ -0,0 +1,252 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/last_seen_model.dart'; +import 'package:mobdr/model/search_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/search_product.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:flutter/material.dart'; + +class SearchPage extends StatefulWidget { + @override + _SearchPageState createState() => _SearchPageState(); +} + +class _SearchPageState extends State { + TextEditingController _etSearch = TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _etSearch.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 7); + return Scaffold( + appBar: AppBar( + titleSpacing: 0.0, + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + // create search text field in the app bar + title: Container( + margin: EdgeInsets.only(right: 16), + height: kToolbarHeight - 20, + child: TextField( + controller: _etSearch, + autofocus: true, + textInputAction: TextInputAction.search, + onChanged: (textValue) { + setState(() {}); + }, + maxLines: 1, + style: TextStyle(fontSize: 13, color: Colors.grey[600]), + decoration: InputDecoration( + prefixIcon: + Icon(Icons.search, color: Colors.grey[500], size: 18), + suffixIcon: (_etSearch.text == '') + ? null + : GestureDetector( + onTap: () { + setState(() { + _etSearch = TextEditingController(text: ''); + }); + }, + child: Icon(Icons.close, + color: Colors.grey[500], size: 18)), + contentPadding: EdgeInsets.all(0), + isDense: true, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(5), + ), + ), + filled: true, + fillColor: Colors.grey[200], + hintText: 'Search product', + ), + ), + )), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + FocusScope.of(context).unfocus(); + return Future.value(true); + }, + // if search field is empty, show history search + // if search field not empty, show search text + child: _etSearch.text == '' + ? _showHistorySearch(boxImageSize) + : _showSearchText(), + )); + } + + Widget _showHistorySearch(boxImageSize) { + return ListView( + padding: EdgeInsets.all(16), + children: [ + Text('Last Seen Product', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), + Container( + margin: EdgeInsets.only(top: 8), + height: boxImageSize, + child: ListView.builder( + padding: EdgeInsets.only(right: 12), + scrollDirection: Axis.horizontal, + itemCount: lastSeenData.length, + itemBuilder: (BuildContext context, int index) { + return _buildLastSeenCard(index, boxImageSize); + }, + ), + ), + Container( + margin: EdgeInsets.only(top: 32), + child: Text('Last Search', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + shrinkWrap: true, + itemCount: searchData.length, + // Add one more item for progress indicator + padding: EdgeInsets.symmetric(vertical: 0), + itemBuilder: (BuildContext context, int index) { + return _buildLastSearchList(index); + }, + ), + ], + ) + ], + ); + } + + Widget _showSearchText() { + return ListView( + padding: EdgeInsets.all(16), + children: [ + Container( + margin: EdgeInsets.only(top: 16), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + int idx = searchData + .indexWhere((data) => data.words == _etSearch.text); + if (idx == -1) { + if (searchData.length == 5) { + searchData.removeLast(); + } + searchData.insert( + 0, SearchModel(id: 1, words: _etSearch.text)); + } else { + searchData.removeAt(idx); + searchData.insert( + 0, SearchModel(id: 1, words: _etSearch.text)); + } + }); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + SearchProductPage(words: _etSearch.text))); + }, + child: Row( + children: [ + Icon(Icons.access_time, color: SOFT_GREY, size: 16), + SizedBox(width: 12), + Text(_etSearch.text, style: TextStyle(color: CHARCOAL)), + ], + ), + ), + ), + ], + ); + } + + Widget _buildLastSeenCard(index, boxImageSize) { + return Container( + margin: EdgeInsets.only(left: index == 0 ? 0 : 12), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: lastSeenData[index].name, + image: lastSeenData[index].image, + price: lastSeenData[index].price, + rating: lastSeenData[index].rating, + review: lastSeenData[index].review, + sale: lastSeenData[index].sale))); + }, + child: ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: lastSeenData[index].image)), + ), + ); + } + + Widget _buildLastSearchList(index) { + return Container( + margin: EdgeInsets.only(top: 16), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + SearchProductPage(words: searchData[index].words))); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Container( + child: Row( + children: [ + Icon(Icons.access_time, color: SOFT_GREY, size: 16), + SizedBox(width: 12), + Flexible( + child: Text(searchData[index].words, + style: TextStyle(color: CHARCOAL), + overflow: TextOverflow.ellipsis, + maxLines: 1), + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + setState(() { + searchData.removeAt(index); + }); + }, + child: Icon(Icons.close, color: BLACK_GREY, size: 18)), + ], + ), + ), + ); + } +} diff --git a/lib/ui/home/search_product.dart b/lib/ui/home/search_product.dart new file mode 100644 index 0000000..26d321f --- /dev/null +++ b/lib/ui/home/search_product.dart @@ -0,0 +1,458 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/search_product_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class SearchProductPage extends StatefulWidget { + final String words; + + const SearchProductPage({Key? key, this.words = ''}) : super(key: key); + + @override + _SearchProductPageState createState() => _SearchProductPageState(); +} + +class _SearchProductPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + TextEditingController _etSearch = TextEditingController(); + + // create sort filter data + late List _sortList; + int _sortIndex = 0; + + // create other filter 1 data + late List _otherFilterOneList; + int _otherFilterOneIndex = 0; + + // create other filter 2 data + late List _otherFilterTwoList; + int _otherFilterTwoIndex = 0; + + // create other filter 3 data + late List _otherFilterThreeList; + int _otherFilterThreeIndex = 0; + + @override + void initState() { + _etSearch.text = widget.words; + + WidgetsBinding.instance.addPostFrameCallback((_) async { + _initForLang(); + }); + + super.initState(); + } + + void _initForLang() { + setState(() { + _sortList = [ + 'Relevant Product', + 'Review', + 'Newest Product', + 'Highest Price', + 'Lowest Price' + ]; + _otherFilterOneList = ['Filter 1', 'Filter 2', 'Filter 3', 'Filter 4']; + _otherFilterTwoList = [ + 'Filter 1', + 'Filter 2', + 'Filter 3', + 'Filter 4', + 'Filter 5', + 'Filter 6', + 'Filter 7' + ]; + _otherFilterThreeList = [ + 'Filter 1', + 'Filter 2', + 'Filter 3', + 'Filter 4', + 'Filter 5', + 'Filter 6', + 'Filter 7', + 'Filter 8', + 'Filter 9', + 'Filter 10' + ]; + }); + } + + @override + void dispose() { + _etSearch.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + titleSpacing: 0.0, + iconTheme: IconThemeData( + color: Colors.black, //change your color here + ), + backgroundColor: Colors.white, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + elevation: 0, + title: Container( + margin: EdgeInsets.only(right: 16), + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => Colors.grey[100]!, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => SearchPage())); + }, + child: Row( + children: [ + SizedBox(width: 8), + Icon(Icons.search, color: Colors.grey[500], size: 18), + SizedBox(width: 8), + Text( + widget.words, + style: TextStyle( + fontSize: 13, + color: Colors.grey[600], + fontWeight: FontWeight.normal), + ) + ], + )), + ), + actions: [ + GestureDetector( + onTap: () { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return _showFilterPopup(); + }, + ); + }, + child: Container( + margin: EdgeInsets.only(right: 16), + child: Icon(Icons.filter_list, color: BLACK_GREY))), + ], + ), + body: WillPopScope( + onWillPop: () { + Navigator.pop(context); + return Future.value(true); + }, + child: CustomScrollView( + shrinkWrap: true, + primary: false, + physics: AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: GlobalStyle.gridDelegateRatio, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildSearchProductCard(index); + }, + childCount: searchProductData.length, + ), + ), + ), + ]), + )); + } + + Widget _showFilterPopup() { + // must use StateSetter to update data between main screen and popup. + // if use default setState, the data will not update + return StatefulBuilder( + builder: (BuildContext context, StateSetter mystate) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + margin: EdgeInsets.only(top: 12, bottom: 12), + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[500], + borderRadius: BorderRadius.circular(10)), + ), + ), + Container( + margin: EdgeInsets.fromLTRB(16, 8, 16, 8), + child: Text('Filter', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + Flexible( + child: ListView( + padding: EdgeInsets.all(16), + children: [ + Text('Sort', style: GlobalStyle.filterTitle), + Wrap( + children: List.generate(_sortList.length, (index) { + return _radioSort(_sortList[index], index, mystate); + }), + ), + SizedBox( + height: 24, + ), + Text('Other Filter 1', style: GlobalStyle.filterTitle), + Wrap( + children: List.generate(_otherFilterOneList.length, (index) { + return _otherFilterOneSort( + _otherFilterOneList[index], index, mystate); + }), + ), + SizedBox( + height: 24, + ), + Text('Other Filter 2', style: GlobalStyle.filterTitle), + Wrap( + children: List.generate(_otherFilterTwoList.length, (index) { + return _otherFilterTwoSort( + _otherFilterTwoList[index], index, mystate); + }), + ), + SizedBox( + height: 24, + ), + Text('Other Filter 3', style: GlobalStyle.filterTitle), + Wrap( + children: + List.generate(_otherFilterThreeList.length, (index) { + return _otherFilterThreeSort( + _otherFilterThreeList[index], index, mystate); + }), + ), + ], + ), + ), + ], + ); + }); + } + + Widget _radioSort(String txt, int index, mystate) { + return GestureDetector( + onTap: () { + mystate(() { + _sortIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _sortIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _sortIndex == index ? SOFT_BLUE : Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Text(txt, + style: TextStyle( + color: _sortIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget _otherFilterOneSort(String txt, int index, mystate) { + return GestureDetector( + onTap: () { + mystate(() { + _otherFilterOneIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _otherFilterOneIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _otherFilterOneIndex == index + ? SOFT_BLUE + : Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Text(txt, + style: TextStyle( + color: + _otherFilterOneIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget _otherFilterTwoSort(String txt, int index, mystate) { + return GestureDetector( + onTap: () { + mystate(() { + _otherFilterTwoIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _otherFilterTwoIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _otherFilterTwoIndex == index + ? SOFT_BLUE + : Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Text(txt, + style: TextStyle( + color: + _otherFilterTwoIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget _otherFilterThreeSort(String txt, int index, mystate) { + return GestureDetector( + onTap: () { + mystate(() { + _otherFilterThreeIndex = index; + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8, top: 8), + decoration: BoxDecoration( + color: _otherFilterThreeIndex == index ? SOFT_BLUE : Colors.white, + border: Border.all( + width: 1, + color: _otherFilterThreeIndex == index + ? SOFT_BLUE + : Colors.grey[300]!), + borderRadius: BorderRadius.all(Radius.circular(10))), + child: Text(txt, + style: TextStyle( + color: + _otherFilterThreeIndex == index ? Colors.white : CHARCOAL)), + ), + ); + } + + Widget _buildSearchProductCard(index) { + final double boxImageSize = + ((MediaQuery.of(context).size.width) - 24) / 2 - 12; + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: searchProductData[index].name, + image: searchProductData[index].image, + price: searchProductData[index].price, + rating: searchProductData[index].rating, + review: searchProductData[index].review, + sale: searchProductData[index].sale))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: searchProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + searchProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + searchProductData[index].price), + style: GlobalStyle.productPrice), + Text( + searchProductData[index].sale.toString() + + ' Sale', + style: GlobalStyle.productSale) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, color: SOFT_GREY, size: 12), + Text(' ' + searchProductData[index].location, + style: GlobalStyle.productLocation) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: searchProductData[index].rating, + size: 12), + Text( + '(' + + searchProductData[index].review.toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart new file mode 100644 index 0000000..c4c9ac6 --- /dev/null +++ b/lib/ui/home/tab_home.dart @@ -0,0 +1,970 @@ +/* +For this homepage, appBar is created at the bottom after CustomScrollView +we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime + */ + +import 'dart:async'; + +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:mobdr/model/category_for_you_model.dart'; +import 'package:mobdr/model/category_model.dart'; +import 'package:mobdr/model/flashsale_model.dart'; +import 'package:mobdr/model/home_banner_model.dart'; +import 'package:mobdr/model/home_trending_model.dart'; +import 'package:mobdr/model/last_search_model.dart'; +import 'package:mobdr/model/recomended_product_model.dart'; +import 'package:mobdr/ui/general/chat_us.dart'; +import 'package:mobdr/ui/general/notification.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/coupon.dart'; +import 'package:mobdr/ui/home/flashsale.dart'; +import 'package:mobdr/ui/home/last_search.dart'; +import 'package:mobdr/ui/home/product_category.dart'; +import 'package:mobdr/ui/home/search.dart'; +import 'package:mobdr/ui/home/search_product.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; + +class TabHomePage extends StatefulWidget { + @override + _TabHomePageState createState() => _TabHomePageState(); +} + +class _TabHomePageState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + int _currentImageSlider = 0; + + late ScrollController _scrollController; + Color _topIconColor = Colors.white; + Color _topSearchColor = Colors.white; + late AnimationController _topColorAnimationController; + late Animation _appBarColor; + SystemUiOverlayStyle _appBarSystemOverlayStyle = SystemUiOverlayStyle.light; + + Timer? _flashsaleTimer; + late int _flashsaleSecond; + + void _startFlashsaleTimer() { + const period = const Duration(seconds: 1); + _flashsaleTimer = Timer.periodic(period, (timer) { + setState(() { + _flashsaleSecond--; + }); + if (_flashsaleSecond == 0) { + _cancelFlashsaleTimer(); + Fluttertoast.showToast( + msg: 'Flash sale is over', toastLength: Toast.LENGTH_LONG); + } + }); + } + + void _cancelFlashsaleTimer() { + if (_flashsaleTimer != null) { + _flashsaleTimer?.cancel(); + _flashsaleTimer = null; + } + } + + // keep the state to do not refresh when switch navbar + @override + bool get wantKeepAlive => true; + + @override + void initState() { + _setupAnimateAppbar(); + + // set how many times left for flashsale + var timeNow = DateTime.now(); + + // 8000 second = 2 hours 13 minutes 20 second for flashsale timer + var flashsaleTime = + timeNow.add(Duration(seconds: 8000)).difference(timeNow); + _flashsaleSecond = flashsaleTime.inSeconds; + _startFlashsaleTimer(); + + super.initState(); + } + + @override + void dispose() { + _scrollController.dispose(); + _topColorAnimationController.dispose(); + + _cancelFlashsaleTimer(); + super.dispose(); + } + + void _setupAnimateAppbar() { + // use this function and paramater to animate top bar + _topColorAnimationController = + AnimationController(vsync: this, duration: Duration(seconds: 0)); + _appBarColor = ColorTween(begin: Colors.transparent, end: Colors.white) + .animate(_topColorAnimationController); + _scrollController = ScrollController() + ..addListener(() { + _topColorAnimationController.animateTo(_scrollController.offset / 120); + // if scroll for above 150, then change app bar color to white, search button to dark, and top icon color to dark + // if scroll for below 150, then change app bar color to transparent, search button to white and top icon color to light + if (_scrollController.hasClients && + _scrollController.offset > (150 - kToolbarHeight)) { + if (_topIconColor != BLACK_GREY) { + _topIconColor = BLACK_GREY; + _topSearchColor = Colors.grey[100]!; + _appBarSystemOverlayStyle = SystemUiOverlayStyle.dark; + } + } else { + if (_topIconColor != Colors.white) { + _topIconColor = Colors.white; + _topSearchColor = Colors.white; + _appBarSystemOverlayStyle = SystemUiOverlayStyle.light; + } + } + }); + } + + @override + Widget build(BuildContext context) { + // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); + super.build(context); + + final double boxImageSize = (MediaQuery.of(context).size.width / 3); + final double categoryForYouHeightShort = boxImageSize; + final double categoryForYouHeightLong = (boxImageSize * 2); + + return Scaffold( + body: Stack( + children: [ + CustomScrollView( + controller: _scrollController, + slivers: [ + SliverList( + delegate: SliverChildListDelegate([ + _createHomeBannerSlider(), + _createCoupon(), + _createGridCategory(), + Container( + margin: EdgeInsets.only(top: 10, left: 16, right: 16), + child: Text('Flash Sale', style: GlobalStyle.sectionTitle), + ), + Container( + margin: EdgeInsets.only(top: 4, left: 16, right: 16), + child: Row( + children: [ + Text('Flash sale end in ', + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 13, + color: CHARCOAL)), + _buildFlashsaleTime(), + Expanded( + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => FlashSalePage( + seconds: _flashsaleSecond))); + }, + child: Text('View All', + style: GlobalStyle.viewAll, + textAlign: TextAlign.end), + ), + ) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 16), + height: boxImageSize * + GlobalStyle.horizontalProductHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + itemCount: flashsaleData.length, + itemBuilder: (BuildContext context, int index) { + return _buildFlashsaleCard(index, boxImageSize); + }, + ), + ), + Container( + margin: EdgeInsets.only(top: 30, left: 16, right: 16), + child: + Text('Trending Product', style: GlobalStyle.sectionTitle), + ), + Container( + margin: EdgeInsets.fromLTRB(12, 0, 12, 0), + child: GridView.count( + padding: EdgeInsets.fromLTRB(0, 8, 0, 0), + primary: false, + childAspectRatio: 4 / 1.6, + shrinkWrap: true, + crossAxisSpacing: 2, + mainAxisSpacing: 2, + crossAxisCount: 2, + children: List.generate(homeTrendingData.length, (index) { + return _buildTrendingProductCard(index); + }), + ), + ), + Container( + margin: EdgeInsets.only(top: 30, left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Last Search', style: GlobalStyle.sectionTitle), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LastSearchPage())); + }, + child: Text('View All', + style: GlobalStyle.viewAll, + textAlign: TextAlign.end), + ) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 16), + height: boxImageSize * + GlobalStyle.horizontalProductHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + itemCount: lastSearchData.length, + itemBuilder: (BuildContext context, int index) { + return _buildLastSearchCard(index, boxImageSize); + }, + ), + ), + Container( + margin: EdgeInsets.only(top: 30, left: 16, right: 16), + child: + Text('Category For You', style: GlobalStyle.sectionTitle), + ), + _createCategoryForYou(boxImageSize, categoryForYouHeightShort, + categoryForYouHeightLong), + Container( + margin: EdgeInsets.only(top: 30, left: 16, right: 16), + child: Text('Recomended Product', + style: GlobalStyle.sectionTitle), + ), + CustomScrollView( + shrinkWrap: true, + primary: false, + slivers: [ + SliverPadding( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + sliver: SliverGrid( + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: GlobalStyle.gridDelegateRatio, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return _buildRecomendedProductCard(index); + }, + childCount: recomendedProductData.length, + ), + ), + ), + ]), + ])), + ], + ), + // Create AppBar with Animation + Container( + height: AppBar().preferredSize.height + + MediaQuery.of(context).padding.top - + 20 + + 22, + child: AnimatedBuilder( + animation: _topColorAnimationController, + builder: (context, child) => AppBar( + automaticallyImplyLeading: false, + backgroundColor: _appBarColor.value, + systemOverlayStyle: _appBarSystemOverlayStyle, + elevation: GlobalStyle.appBarElevation, + title: Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) => _topSearchColor, + ), + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchPage())); + }, + child: Row( + children: [ + SizedBox(width: 8), + Icon( + Icons.search, + color: Colors.grey[500], + size: 18, + ), + SizedBox(width: 8), + Text( + 'Search Product', + style: TextStyle( + color: Colors.grey[500], + fontWeight: FontWeight.normal), + ) + ], + )), + ), + actions: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatUsPage())); + }, + child: Icon(Icons.email, color: _topIconColor)), + IconButton( + icon: _reusableWidget.customNotifIcon( + count: 8, notifColor: _topIconColor), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationPage())); + }), + ], + ), + ), + ) + ], + ), + ); + } + + Widget _createHomeBannerSlider() { + return Column( + children: [ + CarouselSlider( + items: homeBannerData + .map((item) => Container( + child: buildCacheNetworkImage( + width: 0, height: 0, url: item.image), + )) + .toList(), + options: CarouselOptions( + aspectRatio: 8 / 6, + viewportFraction: 1.0, + autoPlay: true, + autoPlayInterval: Duration(seconds: 6), + autoPlayAnimationDuration: Duration(milliseconds: 300), + enlargeCenterPage: false, + onPageChanged: (index, reason) { + setState(() { + _currentImageSlider = index; + }); + }), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: homeBannerData.map((item) { + int index = homeBannerData.indexOf(item); + return Container( + width: 8.0, + height: 8.0, + margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _currentImageSlider == index + ? PRIMARY_COLOR + : Colors.grey[300], + ), + ); + }).toList(), + ), + ], + ); + } + + Widget _createCoupon() { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, MaterialPageRoute(builder: (context) => CouponPage())); + }, + child: Container( + padding: EdgeInsets.all(12), + margin: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(5)), + child: Row( + children: [ + Expanded( + child: Container( + child: Text( + 'There are 10 coupon waiting', + style: TextStyle( + fontSize: 14, + color: Color(0xffffffff), + fontWeight: FontWeight.bold), + ), + ), + ), + Icon(Icons.local_offer, color: Colors.white) + ], + ), + ), + ); + } + + Widget _buildFlashsaleTime() { + int hour = _flashsaleSecond ~/ 3600; + int minute = _flashsaleSecond % 3600 ~/ 60; + int second = _flashsaleSecond % 60; + + return Row( + children: [ + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(hour), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ), + Text(' : ', + style: TextStyle( + color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)), + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(minute), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ), + Text(' : ', + style: TextStyle( + color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)), + Container( + padding: EdgeInsets.fromLTRB(3, 4, 3, 4), + decoration: BoxDecoration( + color: Colors.red, borderRadius: BorderRadius.circular(5)), // + child: Text(_globalFunction.formatTime(second), + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold)), + ) + ], + ); + } + + Widget _createGridCategory() { + return GridView.count( + padding: EdgeInsets.fromLTRB(16, 16, 16, 0), + primary: false, + childAspectRatio: 1.1, + shrinkWrap: true, + crossAxisSpacing: 0, + mainAxisSpacing: 0, + crossAxisCount: 4, + children: List.generate(categoryData.length, (index) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryData[index].id, + categoryName: categoryData[index].name))); + }, + child: Column(children: [ + buildCacheNetworkImage( + width: 40, + height: 40, + url: categoryData[index].image, + plColor: Colors.transparent), + Flexible( + child: Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 0), + child: Text( + categoryData[index].name, + style: TextStyle( + color: CHARCOAL, + fontWeight: FontWeight.normal, + fontSize: 12, + ), + textAlign: TextAlign.center, + ), + ), + ) + ])); + }), + ); + } + + Widget _buildFlashsaleCard(index, boxImageSize) { + return Container( + width: boxImageSize + 10, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: flashsaleData[index].name, + image: flashsaleData[index].image, + price: flashsaleData[index].price, + rating: 4, + review: 45, + sale: flashsaleData[index].sale))); + }, + child: Column( + children: [ + Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize + 10, + height: boxImageSize + 10, + url: flashsaleData[index].image)), + Positioned( + right: 0, + top: 10, + child: Container( + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6), + bottomLeft: Radius.circular(6))), + padding: EdgeInsets.fromLTRB(8, 4, 8, 4), + child: Text( + flashsaleData[index].discount.toString() + '%', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 12)), + ), + ) + ], + ), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + flashsaleData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + flashsaleData[index].price), + style: GlobalStyle.productPriceDiscounted), + ), + Container( + margin: EdgeInsets.only(top: 2), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + ((100 - flashsaleData[index].discount) * + flashsaleData[index].price / + 100)), + style: GlobalStyle.productPrice), + ) + ], + ), + ) + ], + ), + ), + ), + ); + } + + Widget _buildTrendingProductCard(index) { + return GestureDetector( + onTap: () { + StatefulWidget menuPage = + SearchProductPage(words: homeTrendingData[index].name); + Navigator.push( + context, MaterialPageRoute(builder: (context) => menuPage)); + }, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10)), + child: buildCacheNetworkImage( + width: + (MediaQuery.of(context).size.width / 2) * (1.6 / 4) - + 12 - + 1, + height: + (MediaQuery.of(context).size.width / 2) * (1.6 / 4) - + 12 - + 1, + url: homeTrendingData[index].image)), + Expanded( + child: Container( + margin: EdgeInsets.all(10), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(homeTrendingData[index].name, + style: TextStyle( + fontSize: 11, fontWeight: FontWeight.bold)), + SizedBox(height: 4), + Text(homeTrendingData[index].sale + ' Product', + style: TextStyle(fontSize: 9, color: BLACK_GREY)) + ], + ), + ), + ) + ], + )), + ); + } + + Widget _buildLastSearchCard(index, boxImageSize) { + return Container( + width: boxImageSize + 10, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: lastSearchData[index].name, + image: lastSearchData[index].image, + price: lastSearchData[index].price, + rating: lastSearchData[index].rating, + review: lastSearchData[index].review, + sale: lastSearchData[index].sale))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize + 10, + height: boxImageSize + 10, + url: lastSearchData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + lastSearchData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + lastSearchData[index].price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: lastSearchData[index].rating, size: 12), + Text( + '(' + + lastSearchData[index].review.toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget _createCategoryForYou( + boxImageSize, categoryForYouHeightShort, categoryForYouHeightLong) { + return Container( + margin: EdgeInsets.only(top: 8), + width: MediaQuery.of(context).size.width, + height: categoryForYouHeightLong, + child: Row( + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryForYouData[0].id, + categoryName: categoryData[0].name))); + }, + child: Container( + width: boxImageSize, + height: categoryForYouHeightLong, + child: buildCacheNetworkImage( + width: 0, height: 0, url: categoryForYouData[0].image), + ), + ), + Column( + children: [ + Row( + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryForYouData[1].id, + categoryName: categoryData[1].name))); + }, + child: Container( + width: boxImageSize, + height: categoryForYouHeightShort, + child: buildCacheNetworkImage( + width: 0, + height: 0, + url: categoryForYouData[1].image), + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryForYouData[2].id, + categoryName: categoryData[2].name))); + }, + child: Container( + width: boxImageSize, + height: categoryForYouHeightShort, + child: buildCacheNetworkImage( + width: 0, + height: 0, + url: categoryForYouData[2].image), + ), + ) + ], + ), + Row( + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryForYouData[3].id, + categoryName: categoryData[3].name))); + }, + child: Container( + width: boxImageSize, + height: categoryForYouHeightShort, + child: buildCacheNetworkImage( + width: 0, + height: 0, + url: categoryForYouData[3].image), + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductCategoryPage( + categoryId: categoryForYouData[4].id, + categoryName: categoryData[4].name))); + }, + child: Container( + width: boxImageSize, + height: categoryForYouHeightShort, + child: buildCacheNetworkImage( + width: 0, + height: 0, + url: categoryForYouData[4].image), + ), + ) + ], + ) + ], + ) + ], + ), + ); + } + + Widget _buildRecomendedProductCard(index) { + final double boxImageSize = + ((MediaQuery.of(context).size.width) - 24) / 2 - 12; + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: recomendedProductData[index].name, + image: recomendedProductData[index].image, + price: recomendedProductData[index].price, + rating: recomendedProductData[index].rating, + review: recomendedProductData[index].review, + sale: recomendedProductData[index].sale))); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: recomendedProductData[index].image)), + Container( + margin: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + recomendedProductData[index].name, + style: GlobalStyle.productName, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + recomendedProductData[index].price), + style: GlobalStyle.productPrice), + Text( + recomendedProductData[index].sale.toString() + + ' Sale', + style: TextStyle(fontSize: 11, color: SOFT_GREY)) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, color: SOFT_GREY, size: 12), + Text(' ' + recomendedProductData[index].location, + style: GlobalStyle.productSale) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: recomendedProductData[index].rating, + size: 12), + Text( + '(' + + recomendedProductData[index] + .review + .toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ) + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/onboarding.dart b/lib/ui/onboarding.dart new file mode 100644 index 0000000..b75a08b --- /dev/null +++ b/lib/ui/onboarding.dart @@ -0,0 +1,70 @@ +import 'package:mobdr/ui/authentication/signin.dart'; +import 'package:flutter/material.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/library/flutter_overboard/overboard.dart'; +import 'package:mobdr/library/flutter_overboard/page_model.dart'; + +class OnBoardingPage extends StatefulWidget { + @override + _OnBoardingPageState createState() => _OnBoardingPageState(); +} + +class _OnBoardingPageState extends State { + // create each page of onBoard here + final _pageList = [ + PageModel( + color: Colors.white, + imageAssetPath: LOCAL_IMAGES_URL + '/onboarding/search_product.gif', + title: 'Choose Product', + body: 'Search and browse the product you want to buy at iJShop', + doAnimateImage: true), + PageModel( + color: Colors.white, + imageFromUrl: GLOBAL_URL + '/apps/ecommerce/onboarding/cart.png', + title: 'Add to Cart and Pay', + body: + 'Add the product to shopping cart, choose delivery and then pay with your preferences payment', + doAnimateImage: true), + PageModel( + color: Colors.white, + imageFromUrl: GLOBAL_URL + '/apps/ecommerce/onboarding/delivery.png', + title: 'Delivery', + body: + 'Wait until the product that has been purchased comes to the house', + doAnimateImage: true), + ]; + + @override + void initState() { + super.initState(); + } + + // this function used if user click finish, you won't see this page again until you clear your data of this apps in your phone setting + /*void _finishOnBoarding() async { + final SharedPreferences _pref = await SharedPreferences.getInstance(); + await _pref.setBool('onBoarding', false); + }*/ + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: OverBoard( + pages: _pageList, + showBullets: true, + finishCallback: () { + //_finishOnBoarding(); + + // after you click finish, direct to signin page + // Adjust with your need using push replacement or push remove until + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => SigninPage()), (Route route) => false); + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => SigninPage())); + }, + )); + } +} diff --git a/lib/ui/reusable/cache_image_network.dart b/lib/ui/reusable/cache_image_network.dart new file mode 100644 index 0000000..b58bde6 --- /dev/null +++ b/lib/ui/reusable/cache_image_network.dart @@ -0,0 +1,55 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +CachedNetworkImage buildCacheNetworkImage({double? width, double? height, url, plColor, imageColor}){ + if(width == 0 && height == 0){ + return CachedNetworkImage( + placeholder: (context, url) { + return Container( + color: plColor==null?Colors.grey[200]:plColor, + ); + }, + errorWidget: (context, url, error) { + return Container( + color: Colors.grey[200], + ); + }, + imageUrl: url, + fit: BoxFit.cover, + color: imageColor==null?null:imageColor, + ); + } else if(height == 0){ + return CachedNetworkImage( + placeholder: (context, url) { + return Container( + width: width, + color: plColor==null?Colors.grey[200]:plColor, + ); + }, + errorWidget: (context, url, error) { + return Container( + width: width, + color: Colors.grey[200], + ); + }, + imageUrl: url, + fit: BoxFit.cover, + width: width, + color: imageColor==null?null:imageColor, + ); + } else { + return CachedNetworkImage( + placeholder: (context, url) { + return SizedBox.shrink(); + }, + errorWidget: (context, url, error) { + return SizedBox.shrink(); + }, + imageUrl: url, + fit: BoxFit.cover, + width: width, + height: height, + color: imageColor==null?null:imageColor, + ); + } +} \ No newline at end of file diff --git a/lib/ui/reusable/global_function.dart b/lib/ui/reusable/global_function.dart new file mode 100644 index 0000000..09d9836 --- /dev/null +++ b/lib/ui/reusable/global_function.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class GlobalFunction{ + bool validateMobileNumber(String value) { + String patttern = r'(^(?:[+0]9)?[0-9]{10,15}$)'; + RegExp regExp = new RegExp(patttern); + if (value.length < 8) { + return false; + } else if (!regExp.hasMatch(value)) { + return false; + } else { + return true; + } + } + + bool validateEmail(String value) { + String pattern = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; + RegExp regex = new RegExp(pattern); + if (!regex.hasMatch(value)) { + return false; + } else { + return true; + } + } + + String removeDecimalZeroFormat(double v) { + NumberFormat formatter = NumberFormat(); + formatter.minimumFractionDigits = 0; + formatter.maximumFractionDigits = 2; + return formatter.format(v); + } + + String formatTime(int timeNum) { + return timeNum < 10 ? "0" + timeNum.toString() : timeNum.toString(); + } + + Future showProgressDialog(BuildContext context) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Center( + child: CircularProgressIndicator(), + ), + ); + }); + } +} \ No newline at end of file diff --git a/lib/ui/reusable/reusable_widget.dart b/lib/ui/reusable/reusable_widget.dart new file mode 100644 index 0000000..489566c --- /dev/null +++ b/lib/ui/reusable/reusable_widget.dart @@ -0,0 +1,227 @@ +import 'dart:async'; + +import 'package:mobdr/config/constant.dart'; +import 'package:flutter/material.dart'; + +class ReusableWidget { + PreferredSizeWidget bottomAppBar() { + return PreferredSize( + child: Container( + color: Colors.grey[100], + height: 1.0, + ), + preferredSize: Size.fromHeight(1.0)); + } + + Widget createRatingBar({double rating = 5, double size = 24}) { + if (rating < 0) { + rating = 0; + } else if (rating > 5) { + rating = 5; + } + + bool _absolute = false; + int _fullStar = 0; + int _emptyStar = 0; + + if (rating == 0 || + rating == 1 || + rating == 2 || + rating == 3 || + rating == 4 || + rating == 5) { + _absolute = true; + } else { + double _dec = (rating - int.parse(rating.toString().substring(0, 1))); + if (_dec > 0 && _dec < 1) { + if (_dec >= 0.25 && _dec <= 0.75) { + _absolute = false; + } else { + _absolute = true; + if (_dec < 0.25) { + _emptyStar = 1; + } else if (_dec > 0.75) { + _fullStar = 1; + } + } + } + } + return Row( + children: [ + for (int i = 1; i <= rating + _fullStar; i++) + Icon(Icons.star, color: Colors.yellow[700], size: size), + !_absolute + ? Icon(Icons.star_half, color: Colors.yellow[700], size: size) + : SizedBox.shrink(), + for (int i = 1; i <= (5 - rating + _emptyStar); i++) + Icon(Icons.star_border, color: Colors.yellow[700], size: size), + ], + ); + } + + Widget customNotifIcon( + {int count = 0, + Color notifColor = Colors.grey, + Color labelColor = Colors.pinkAccent, + double notifSize = 24, + double labelSize = 14, + String position = 'right'}) { + double? posLeft; + double? posRight = 0; + if (position == 'left') { + posLeft = 0; + posRight = null; + } + return Stack( + children: [ + Icon(Icons.notifications, color: notifColor, size: notifSize), + Positioned( + left: posLeft, + right: posRight, + child: Container( + padding: EdgeInsets.all(1), + decoration: BoxDecoration( + color: labelColor, + borderRadius: BorderRadius.circular(labelSize), + ), + constraints: BoxConstraints( + minWidth: labelSize, + minHeight: labelSize, + ), + child: Center( + child: Text( + count.toString(), + style: TextStyle( + color: Colors.white, + fontSize: 8, + ), + textAlign: TextAlign.center, + ), + ), + ), + ) + ], + ); + } + + Widget divider1() { + return Divider(height: 0, color: Colors.grey[400]); + } + + Future _showProgressDialog(BuildContext context) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Center( + child: CircularProgressIndicator(), + ), + ); + }); + } + + // dummy loading + void startLoading(context, String textMessage, int backToPreviousPageStack) { + _showProgressDialog(context); + Timer(Duration(seconds: 2), () { + Navigator.pop(context); + _buildShowDialog(context, textMessage, backToPreviousPageStack); + }); + } + + Future _buildShowDialog( + BuildContext context, String textMessage, int backToPreviousPageStack) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0)), //this right here + child: Container( + padding: EdgeInsets.all(20), + margin: EdgeInsets.fromLTRB(40, 20, 40, 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + textMessage, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 14, color: BLACK_GREY), + ), + SizedBox( + height: 20, + ), + Container( + child: TextButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: () { + Navigator.pop(context); + + if (backToPreviousPageStack > 0) { + FocusScope.of(context) + .unfocus(); // hide keyboard when press button + for (int i = 1; + i <= backToPreviousPageStack; + i++) { + Navigator.pop(context); + } + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'OK', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ) + ], + ), + ), + ), + ); + }); + } + // end dummy loading + + Widget createDefaultLabel(context) { + return Container( + padding: EdgeInsets.fromLTRB(8, 2, 8, 2), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)), + child: Row( + children: [ + Text('Default', style: TextStyle(color: Colors.white, fontSize: 13)), + SizedBox( + width: 4, + ), + Icon(Icons.done, color: Colors.white, size: 11) + ], + ), + ); + } +} diff --git a/lib/ui/shopping_cart/change_address.dart b/lib/ui/shopping_cart/change_address.dart new file mode 100644 index 0000000..530391c --- /dev/null +++ b/lib/ui/shopping_cart/change_address.dart @@ -0,0 +1,149 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/address_model.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class ChangeAddressPage extends StatefulWidget { + @override + _ChangeAddressPageState createState() => _ChangeAddressPageState(); +} + +class _ChangeAddressPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Change Address', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView.builder( + itemCount: addressData.length, + // Add one more item for progress indicator + padding: EdgeInsets.symmetric(vertical: 0), + itemBuilder: (BuildContext context, int index) { + return _buildAddressCard(index); + }, + )); + } + + Widget _buildAddressCard(int index) { + return Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + addressData[index].defaultAddress == true + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(addressData[index].title, + style: GlobalStyle.addressTitle), + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.fromLTRB(8, 2, 8, 2), + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.circular(2)), + child: Row( + children: [ + Text('Default', + style: TextStyle( + color: Colors.white, fontSize: 13)), + SizedBox( + width: 4, + ), + Icon(Icons.done, color: Colors.white, size: 11) + ], + ), + ) + ], + ) + : Text(addressData[index].title, + style: GlobalStyle.addressTitle), + Container( + margin: EdgeInsets.only(top: 20), + child: Text(addressData[index].recipientName, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].phoneNumber, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].addressLine1, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + addressData[index].addressLine2 + + ' ' + + addressData[index].postalCode, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text(addressData[index].state, + style: GlobalStyle.addressContent), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + addressData[index].defaultAddress == false + ? GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Text('Use this', + style: GlobalStyle.addressAction), + ) + : Wrap(), + index != 0 + ? SizedBox( + width: 12, + ) + : Wrap(), + ], + )) + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/shopping_cart/delivery.dart b/lib/ui/shopping_cart/delivery.dart new file mode 100644 index 0000000..0e8b70f --- /dev/null +++ b/lib/ui/shopping_cart/delivery.dart @@ -0,0 +1,662 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/shopping_cart_model.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/shopping_cart/change_address.dart'; +import 'package:mobdr/ui/shopping_cart/payment.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; + +class DeliveryPage extends StatefulWidget { + final List shoppingCartData; + + const DeliveryPage({Key? key, required this.shoppingCartData}) + : super(key: key); + + @override + _DeliveryPageState createState() => _DeliveryPageState(); +} + +class _DeliveryPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + double _subTotal = 0; + String _delivery = ''; + double _deliveryPrice = 0; + + @override + void initState() { + shoppingCartData = widget.shoppingCartData; + countSubTotal(); + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + void countSubTotal() { + _subTotal = 0; + for (int i = 0; i < shoppingCartData.length; i++) { + _subTotal += shoppingCartData[i].price * shoppingCartData[i].qty; + } + _subTotal += _deliveryPrice; + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 6); + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Delivery', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + children: [ + _createDeliveryInformation(), + _createOrderListInformation(boxImageSize), + _createChooseDeliveryInformation(), + _createSubTotalInformation() + ], + )); + } + + Widget _createDeliveryInformation() { + return Container( + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Flexible( + child: Text('Home Address', + style: + TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + SizedBox( + width: 8, + ), + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.fromLTRB(8, 2, 8, 2), + decoration: BoxDecoration( + color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)), + child: Row( + children: [ + Text('Default', + style: TextStyle(color: Colors.white, fontSize: 13)), + SizedBox( + width: 4, + ), + Icon(Icons.done, color: Colors.white, size: 11) + ], + ), + ) + ], + ), + SizedBox( + height: 16, + ), + Container( + child: Text('Robert Steven', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('0811888999', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('6019 Madison St', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('West New York, NJ 07093', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text('USA', style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + SizedBox(height: 4), + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ChangeAddressPage())); + }, + child: Container( + alignment: Alignment.topRight, + child: Text('Change Address', + style: TextStyle(color: PRIMARY_COLOR, fontSize: 14)), + ), + ), + ], + ), + ); + } + + Widget _createOrderListInformation(boxImageSize) { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Order List', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + Column( + children: List.generate(shoppingCartData.length, (index) { + int quantity = shoppingCartData[index].qty; + return GestureDetector( + onTap: () { + //Navigator.push(context, MaterialPageRoute(builder: (context) => ProductDetailPage(name: shoppingCartData[index].name, image: shoppingCartData[index].image, price: shoppingCartData[index].price, rating: 4, review: 23, sale: 36))); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: shoppingCartData[index].image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + shoppingCartData[index].name, + style: GlobalStyle.productName.copyWith( + fontSize: 14, fontWeight: FontWeight.bold), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + quantity.toString() + ' item (150 gr)', + style: GlobalStyle.shoppingCartOtherProduct + .copyWith(color: Colors.grey[400]))), + Container( + margin: EdgeInsets.only(top: 4), + child: Text( + '\$' + + _globalFunction.removeDecimalZeroFormat( + quantity * + shoppingCartData[index].price), + style: GlobalStyle.productPrice), + ) + ], + ), + ) + ], + ), + ), + ); + }), + ), + ], + ), + ); + } + + Widget _createChooseDeliveryInformation() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Delivery', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return _showDeliveryPopup(); + }, + ); + }, + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 1, color: Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: _delivery == '' + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon(Icons.local_shipping, color: SOFT_BLUE), + SizedBox(width: 12), + Text('Choose Delivery', + style: TextStyle( + color: CHARCOAL, + fontWeight: FontWeight.bold)), + ], + ), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(_delivery, + style: TextStyle( + color: CHARCOAL, + fontWeight: FontWeight.bold)), + SizedBox( + height: 5, + ), + Text('\$' + _deliveryPrice.toString(), + style: GlobalStyle.deliveryTotalPrice), + ], + ), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), + ], + ), + ), + ), + ], + )); + } + + Widget _showDeliveryPopup() { + return StatefulBuilder( + builder: (BuildContext context, StateSetter mystate) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + margin: EdgeInsets.only(top: 12, bottom: 12), + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[500], + borderRadius: BorderRadius.circular(10)), + ), + ), + Container( + margin: EdgeInsets.fromLTRB(16, 8, 16, 8), + child: Text('Choose Courier', style: GlobalStyle.chooseCourier), + ), + Flexible( + child: ListView( + padding: EdgeInsets.all(16), + children: [ + Text('DHL', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'DHL Regular'; + _deliveryPrice = 13; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$13', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'DHL Express'; + _deliveryPrice = 22; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$22', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('FedEx', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'FedEx Regular'; + _deliveryPrice = 9; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'FedEx Express'; + _deliveryPrice = 17; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 1', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 1 Regular'; + _deliveryPrice = 9; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 1 Express'; + _deliveryPrice = 17; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 2', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 2 Regular'; + _deliveryPrice = 9; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 2 Express'; + _deliveryPrice = 17; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 3', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 3 Regular'; + _deliveryPrice = 9; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 3 Express'; + _deliveryPrice = 17; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + Text('Other 4', style: GlobalStyle.courierTitle), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 4 Regular'; + _deliveryPrice = 9; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Regular', style: GlobalStyle.courierType), + Text('\$9', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _delivery = 'Other 4 Express'; + _deliveryPrice = 17; + }); + countSubTotal(); + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Express', style: GlobalStyle.courierType), + Text('\$17', style: GlobalStyle.deliveryPrice), + ], + ), + ), + ), + ], + ), + ), + ], + ); + }); + } + + Widget _createSubTotalInformation() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Sub Total ', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + SizedBox(height: 5), + Text('\$' + _globalFunction.removeDecimalZeroFormat(_subTotal), + style: GlobalStyle.shoppingCartTotalPrice), + ], + ), + TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4.0), + )), + ), + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => PaymentPage())); + }, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Text( + 'Pay', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )) + ], + ), + ); + } +} diff --git a/lib/ui/shopping_cart/payment.dart b/lib/ui/shopping_cart/payment.dart new file mode 100644 index 0000000..adb4491 --- /dev/null +++ b/lib/ui/shopping_cart/payment.dart @@ -0,0 +1,371 @@ +import 'dart:async'; + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/ui/home.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:flutter/material.dart'; + +class PaymentPage extends StatefulWidget { + final bool fromList; + + const PaymentPage({Key? key, this.fromList = false}) : super(key: key); + + @override + _PaymentPageState createState() => _PaymentPageState(); +} + +class _PaymentPageState extends State { + // initialize reusable widget + final _reusableWidget = ReusableWidget(); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Payment', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + children: [ + Container( + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Summary', + style: + TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text('Total Payment', + style: TextStyle(color: CHARCOAL, fontSize: 14)), + ), + Container( + child: + Text('\$74', style: GlobalStyle.paymentTotalPrice), + ) + ], + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Payment Method', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + GestureDetector( + onTap: () { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return _createChoosePayment(); + }, + ); + }, + child: Text('Change', + style: + TextStyle(color: PRIMARY_COLOR, fontSize: 14)), + ), + ], + ), + Container( + margin: EdgeInsets.only(top: 16), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/visa.png', height: 10), + ), + Text('Visa card ending in 4392', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CHARCOAL)) + ], + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.all(32), + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () { + showLoading( + 'Your payment is success, we will prepare your order as soon as possible'); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Pay', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ) + ], + )); + } + + Widget _createChoosePayment() { + return StatefulBuilder( + builder: (BuildContext context, StateSetter mystate) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + margin: EdgeInsets.only(top: 12, bottom: 12), + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[500], + borderRadius: BorderRadius.circular(10)), + ), + ), + Container( + margin: EdgeInsets.fromLTRB(16, 8, 16, 8), + child: Text('Payment Method', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + Flexible( + child: ListView( + padding: EdgeInsets.all(16), + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context); + }, + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/visa.png', height: 10), + ), + Text('Visa card ending in 4392') + ], + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context); + }, + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: Image.asset('assets/images/mastercard.png', + height: 20), + ), + Text('MasterCard ending in 5309') + ], + ), + ), + Divider( + height: 32, + color: Colors.grey[400], + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Navigator.pop(context); + }, + child: Row( + children: [ + Container( + margin: EdgeInsets.only(right: 8), + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xffcccccc), + width: 1.0, + ), + ), + child: + Image.asset('assets/images/visa.png', height: 10), + ), + Text('Visa card ending in 2285') + ], + ), + ) + ], + ), + ), + ], + ); + }); + } + + void showLoading(String textMessage) { + _progressDialog(context); + Timer(Duration(seconds: 2), () { + Navigator.pop(context); + _buildShowDialog(context, textMessage); + }); + } + + Future _progressDialog(BuildContext context) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Center( + child: CircularProgressIndicator(), + ), + ); + }); + } + + Future _buildShowDialog(BuildContext context, String textMessage) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0)), //this right here + child: Container( + padding: EdgeInsets.all(20), + margin: EdgeInsets.fromLTRB(40, 20, 40, 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + textMessage, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 14, color: BLACK_GREY), + ), + SizedBox( + height: 20, + ), + Container( + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: + MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: () { + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); + Navigator.pop(context); + Navigator.pop(context); + if (!widget.fromList) { + Navigator.pop(context); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => HomePage())); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 16), + child: Text( + 'OK', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )), + ) + ], + ), + ), + ), + ); + }); + } +} diff --git a/lib/ui/shopping_cart/tab_shopping_cart.dart b/lib/ui/shopping_cart/tab_shopping_cart.dart new file mode 100644 index 0000000..ec05413 --- /dev/null +++ b/lib/ui/shopping_cart/tab_shopping_cart.dart @@ -0,0 +1,369 @@ +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/shopping_cart_model.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/shopping_cart/delivery.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class TabShoppingCartPage extends StatefulWidget { + @override + _TabShoppingCartPageState createState() => _TabShoppingCartPageState(); +} + +class _TabShoppingCartPageState extends State { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + double _totalPrice = 0; + + @override + void initState() { + _countTotalPrice(); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + void _countTotalPrice() { + _totalPrice = 0; + for (int i = 0; i < shoppingCartData.length; i++) { + _totalPrice += shoppingCartData[i].price * shoppingCartData[i].qty; + } + } + + @override + Widget build(BuildContext context) { + final double boxImageSize = (MediaQuery.of(context).size.width / 5); + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Shopping Cart', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + bottom: _reusableWidget.bottomAppBar(), + ), + body: ListView( + physics: AlwaysScrollableScrollPhysics(), + children: [ + Container( + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + children: List.generate(shoppingCartData.length, (index) { + return _buildItem(index, boxImageSize); + }), + ), + ), + Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + children: [ + _createUseCoupon(), + _createTotalPrice(), + ], + ), + ) + ], + )); + } + + Widget _createUseCoupon() { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + //Navigator.push(context, MaterialPageRoute(builder: (context) => CouponPage())); + }, + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 1, color: Colors.grey[300]!), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + )), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon(Icons.local_offer, color: SOFT_BLUE), + SizedBox(width: 12), + Text('Use coupons', + style: TextStyle( + color: CHARCOAL, fontWeight: FontWeight.bold)), + ], + ), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), + ], + ), + ), + ); + } + + Widget _createTotalPrice() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Total', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + SizedBox(height: 5), + Text('\$' + _globalFunction.removeDecimalZeroFormat(_totalPrice), + style: GlobalStyle.shoppingCartTotalPrice), + ], + ), + TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => PRIMARY_COLOR, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4.0), + )), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + DeliveryPage(shoppingCartData: shoppingCartData))); + }, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Text( + 'Next', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.white), + textAlign: TextAlign.center, + ), + )) + ], + ); + } + + Column _buildItem(index, boxImageSize) { + int quantity = shoppingCartData[index].qty; + return Column( + children: [ + Container( + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: shoppingCartData[index].name, + image: shoppingCartData[index].image, + price: shoppingCartData[index].price, + rating: 4, + review: 23, + sale: 36))); + }, + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(4)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: shoppingCartData[index].image)), + ), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: shoppingCartData[index].name, + image: shoppingCartData[index].image, + price: shoppingCartData[index].price, + rating: 4, + review: 23, + sale: 36))); + }, + child: Text( + shoppingCartData[index].name, + style: GlobalStyle.productName.copyWith(fontSize: 14), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + shoppingCartData[index].price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + showPopupDelete(index, boxImageSize); + }, + child: Container( + padding: EdgeInsets.fromLTRB(5, 0, 5, 0), + height: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all( + width: 1, color: Colors.grey[300]!)), + child: Icon(Icons.delete, + color: BLACK_GREY, size: 20), + ), + ), + Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + quantity--; + shoppingCartData[index].setQty(quantity); + _countTotalPrice(); + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(5, 0, 5, 0), + height: 28, + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.circular(8)), + child: Icon(Icons.remove, + color: Colors.white, size: 20), + ), + ), + SizedBox(width: 10), + Container( + child: Text(quantity.toString(), + style: TextStyle()), + ), + SizedBox(width: 10), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + quantity++; + shoppingCartData[index].setQty(quantity); + _countTotalPrice(); + }); + }, + child: Container( + padding: EdgeInsets.fromLTRB(5, 0, 5, 0), + height: 28, + decoration: BoxDecoration( + color: SOFT_BLUE, + borderRadius: BorderRadius.circular(8)), + child: Icon(Icons.add, + color: Colors.white, size: 20), + ), + ) + ], + ), + ], + ), + ) + ], + ), + ) + ], + ), + ), + ), + (index == shoppingCartData.length - 1) + ? Wrap() + : Divider( + height: 32, + color: Colors.grey[400], + ) + ], + ); + } + + void showPopupDelete(index, boxImageSize) { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + setState(() { + shoppingCartData.removeAt(index); + }); + _countTotalPrice(); + Navigator.pop(context); + Fluttertoast.showToast( + msg: 'Item has been deleted from your Shopping Cart', + toastLength: Toast.LENGTH_LONG); + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Delete from Shopping Cart', + style: TextStyle(fontSize: 18), + ), + content: Text( + 'Are you sure to delete this item from your Shopping Cart ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/lib/ui/splash_screen.dart b/lib/ui/splash_screen.dart new file mode 100644 index 0000000..76191bd --- /dev/null +++ b/lib/ui/splash_screen.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/ui/onboarding.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class SplashScreenPage extends StatefulWidget { + @override + _SplashScreenPageState createState() => _SplashScreenPageState(); +} + +class _SplashScreenPageState extends State { + Timer? _timer; + int _second = 3; // set timer for 3 second and then direct to next page + + void _startTimer() { + const period = const Duration(seconds: 1); + _timer = Timer.periodic(period, (timer) { + setState(() { + _second--; + }); + if (_second == 0) { + _cancelFlashsaleTimer(); + // for this example we will use pushReplacement because we want to go back to the list + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => OnBoardingPage())); + + // if you use this splash screen on the very first time when you open the page, use below code + //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => OnBoardingPage()), (Route route) => false); + } + }); + } + + void _cancelFlashsaleTimer() { + if (_timer != null) { + _timer?.cancel(); + _timer = null; + } + } + + @override + void initState() { + // set status bar color to transparent and navigation bottom color to black21 + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + ), + ); + + if (_second != 0) { + _startTimer(); + } + super.initState(); + } + + @override + void dispose() { + _cancelFlashsaleTimer(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: WillPopScope( + onWillPop: () { + return Future.value(false); + }, + child: Container( + child: Center( + child: Image.asset(LOCAL_IMAGES_URL + '/logo.png', + width: MediaQuery.of(context).size.width / 2), + ), + ), + )); + } +} diff --git a/lib/ui/wishlist/tab_wishlist.dart b/lib/ui/wishlist/tab_wishlist.dart new file mode 100644 index 0000000..1fb464c --- /dev/null +++ b/lib/ui/wishlist/tab_wishlist.dart @@ -0,0 +1,378 @@ +/* +This is wishlist page +we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime + */ + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/model/wishlist_model.dart'; +import 'package:mobdr/ui/general/chat_us.dart'; +import 'package:mobdr/ui/general/notification.dart'; +import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; +import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class TabWishlistPage extends StatefulWidget { + @override + _TabWishlistPageState createState() => _TabWishlistPageState(); +} + +class _TabWishlistPageState extends State + with AutomaticKeepAliveClientMixin { + // initialize global function and reusable widget + final _globalFunction = GlobalFunction(); + final _reusableWidget = ReusableWidget(); + + // _listKey is used for AnimatedList + final GlobalKey _listKey = GlobalKey(); + + TextEditingController _etSearch = TextEditingController(); + + // keep the state to do not refresh when switch navbar + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _etSearch.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); + super.build(context); + final double boxImageSize = (MediaQuery.of(context).size.width / 4); + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Wishlist', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, + actions: [ + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ChatUsPage())); + }, + child: Icon(Icons.email, color: BLACK_GREY)), + IconButton( + icon: _reusableWidget.customNotifIcon( + count: 8, notifColor: BLACK_GREY), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationPage())); + }), + ], + // create search text field in the app bar + bottom: PreferredSize( + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey[100]!, + width: 1.0, + )), + ), + padding: EdgeInsets.fromLTRB(16, 0, 16, 12), + height: kToolbarHeight, + child: TextFormField( + controller: _etSearch, + textAlignVertical: TextAlignVertical.bottom, + maxLines: 1, + style: TextStyle(fontSize: 16, color: Colors.grey[600]), + onChanged: (textValue) { + setState(() {}); + }, + decoration: InputDecoration( + fillColor: Colors.grey[100], + filled: true, + hintText: 'Search Wishlist', + prefixIcon: Icon(Icons.search, color: Colors.grey[500]), + suffixIcon: (_etSearch.text == '') + ? null + : GestureDetector( + onTap: () { + setState(() { + _etSearch = TextEditingController(text: ''); + }); + }, + child: Icon(Icons.close, color: Colors.grey[500])), + focusedBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!)), + enabledBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + borderSide: BorderSide(color: Colors.grey[200]!), + ), + ), + ), + ), + preferredSize: Size.fromHeight(kToolbarHeight), + ), + ), + body: AnimatedList( + key: _listKey, + initialItemCount: wishlistData.length, + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index, animation) { + return _buildWishlistCard( + wishlistData[index], boxImageSize, animation, index); + }, + )); + } + + Widget _buildWishlistCard( + WishlistModel wishlistData, boxImageSize, animation, index) { + return SizeTransition( + sizeFactor: animation, + child: Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + margin: EdgeInsets.all(8), + child: Column( + children: [ + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductDetailPage( + name: wishlistData.name, + image: wishlistData.image, + price: wishlistData.price, + rating: wishlistData.rating, + review: wishlistData.review, + sale: wishlistData.sale))); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: wishlistData.image)), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + wishlistData.name, + style: GlobalStyle.productName + .copyWith(fontSize: 13), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '\$ ' + + _globalFunction.removeDecimalZeroFormat( + wishlistData.price), + style: GlobalStyle.productPrice), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon(Icons.location_on, + color: SOFT_GREY, size: 12), + Text(' ' + wishlistData.location, + style: GlobalStyle.productLocation) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + _reusableWidget.createRatingBar( + rating: wishlistData.rating, size: 12), + Text( + '(' + + wishlistData.review.toString() + + ')', + style: GlobalStyle.productTotalReview) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + wishlistData.sale.toString() + ' Sale', + style: GlobalStyle.productSale), + ), + ], + ), + ) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + showPopupDeleteTabWishlist(index, boxImageSize); + }, + child: Container( + padding: EdgeInsets.fromLTRB(5, 0, 5, 0), + height: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all( + width: 1, color: Colors.grey[300]!)), + child: + Icon(Icons.delete, color: BLACK_GREY, size: 20), + ), + ), + SizedBox( + width: 8, + ), + Expanded( + child: (wishlistData.stock == 0) + ? TextButton( + style: ButtonStyle( + minimumSize: + MaterialStateProperty.all(Size(0, 30)), + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) => + Colors.grey[300]!, + ), + overlayColor: MaterialStateProperty.all( + Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: null, + child: Text( + 'Out of Stock', + style: TextStyle( + color: Colors.grey[600], + fontWeight: FontWeight.bold, + fontSize: 13), + textAlign: TextAlign.center, + )) + : OutlinedButton( + onPressed: () { + Fluttertoast.showToast( + msg: + 'Item has been added to Shopping Cart'); + }, + style: ButtonStyle( + minimumSize: + MaterialStateProperty.all(Size(0, 30)), + overlayColor: MaterialStateProperty.all( + Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + side: MaterialStateProperty.all( + BorderSide(color: SOFT_BLUE, width: 1.0), + )), + child: Text( + 'Add to Shopping Cart', + style: TextStyle( + color: SOFT_BLUE, + fontWeight: FontWeight.bold, + fontSize: 13), + textAlign: TextAlign.center, + )), + ), + ], + ), + ) + ], + ), + ), + ), + ), + ); + } + + void showPopupDeleteTabWishlist(index, boxImageSize) { + // set up the buttons + Widget cancelButton = TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('No', style: TextStyle(color: SOFT_BLUE))); + Widget continueButton = TextButton( + onPressed: () { + int removeIndex = index; + var removedItem = wishlistData.removeAt(removeIndex); + // This builder is just so that the animation has something + // to work with before it disappears from view since the original + // has already been deleted. + AnimatedRemovedItemBuilder builder = (context, animation) { + // A method to build the Card widget. + return _buildWishlistCard( + removedItem, boxImageSize, animation, removeIndex); + }; + _listKey.currentState!.removeItem(removeIndex, builder); + + Navigator.pop(context); + Fluttertoast.showToast( + msg: 'Item has been deleted from your wishlist', + toastLength: Toast.LENGTH_LONG); + }, + child: Text('Yes', style: TextStyle(color: SOFT_BLUE))); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + title: Text( + 'Delete Wishlist', + style: TextStyle(fontSize: 18), + ), + content: Text('Are you sure to delete this item from your Wishlist ?', + style: TextStyle(fontSize: 13, color: BLACK_GREY)), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..4d98e47 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,12 @@ import FlutterMacOS import Foundation +import package_info_plus +import path_provider_foundation +import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..049abe2 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/pubspec.lock b/pubspec.lock index 459da2c..5fce04a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + url: "https://pub.dev" + source: hosted + version: "3.2.3" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + carousel_slider: + dependency: "direct main" + description: + name: carousel_slider + sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + url: "https://pub.dev" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -41,6 +73,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 + url: "https://pub.dev" + source: hosted + version: "0.17.2" cupertino_icons: dependency: "direct main" description: @@ -57,24 +105,101 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_lints: - dependency: "direct dev" + flutter_blurhash: + dependency: transitive description: - name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + name: flutter_blurhash + sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "0.7.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + url: "https://pub.dev" + source: hosted + version: "3.3.0" + flutter_html: + dependency: "direct main" + description: + name: flutter_html + sha256: "342c7908f0a67bcec62b6e0f7cf23e23bafe7f64693665dd35be98d5e783bdfd" + url: "https://pub.dev" + source: hosted + version: "3.0.0-alpha.6" 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" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "774fa28b07f3a82c93596bc137be33189fec578ed3447a93a5a11c93435de394" + url: "https://pub.dev" + source: hosted + version: "8.1.3" + html: + dependency: transitive + description: + name: html + sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269 + url: "https://pub.dev" + source: hosted + version: "0.15.1" + http: + dependency: transitive + description: + name: http + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" + source: hosted + version: "0.13.5" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" js: dependency: transitive description: @@ -83,14 +208,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.5" - lints: - dependency: transitive - description: - name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" - url: "https://pub.dev" - source: hosted - version: "2.0.1" matcher: dependency: transitive description: @@ -115,6 +232,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + numerus: + dependency: transitive + description: + name: numerus + sha256: "436759d84f233b40107d0cc31cfa92d24e0960afeb2e506be70926d4cddffd9e" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "8df5ab0a481d7dc20c0e63809e90a588e496d276ba53358afc4c4443d0a00697" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: transitive description: @@ -123,6 +272,94 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9" + url: "https://pub.dev" + source: hosted + version: "2.0.13" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e" + url: "https://pub.dev" + source: hosted + version: "2.0.23" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a" + url: "https://pub.dev" + source: hosted + version: "2.1.9" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pedantic: + dependency: transitive + description: + name: pedantic + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" sky_engine: dependency: transitive description: flutter @@ -136,6 +373,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f" + url: "https://pub.dev" + source: hosted + version: "2.2.4+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f + url: "https://pub.dev" + source: hosted + version: "2.4.2+2" stack_trace: dependency: transitive description: @@ -160,6 +413,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + url: "https://pub.dev" + source: hosted + version: "3.0.1" term_glyph: dependency: transitive description: @@ -176,6 +437,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.16" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: @@ -184,5 +461,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" + source: hosted + version: "3.1.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" sdks: dart: ">=2.19.0 <4.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 39af00d..d5cdc33 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,7 +34,13 @@ 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 + cupertino_icons: 1.0.5 + fluttertoast: 8.1.3 + package_info_plus: 3.0.3 + flutter_html: 3.0.0-alpha.6 + intl: 0.17.0 + carousel_slider: 4.2.1 + cached_network_image: 3.2.3 dev_dependencies: flutter_test: @@ -45,7 +51,8 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + #FBE nolint + #flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -88,3 +95,14 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages + + assets: + - assets/images/google.png + - assets/images/facebook.png + - assets/images/twitter.png + - assets/images/whatsapp.png + - assets/images/placeholder.jpg + - assets/images/visa.png + - assets/images/mastercard.png + - assets/images/logo.png + - assets/images/onboarding/search_product.gif \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart index d0ee948..14c5365 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,7 +1,7 @@ // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll +// utility that Flutter provides. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. @@ -13,7 +13,7 @@ import 'package:mobdr/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);