From 285c4ca3f25828a068389e1e2e0ce7a8043eebbc Mon Sep 17 00:00:00 2001 From: Vitalij Date: Tue, 17 Jun 2025 22:27:22 +0300 Subject: [PATCH] =?UTF-8?q?feature(training):=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=B8,=20=D0=BF=D1=83=D1=81=D1=82=D0=BE=D1=82=D1=8B=20=D0=B8?= =?UTF-8?q?=20=D0=BD=D0=B0=D0=BB=D0=B8=D1=87=D0=B8=D0=B5=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BD=D0=B0=20=D1=8D=D0=BA=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=20=D1=82=D1=80=D0=B5=D0=BD=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes .dart_tool/package_config.json | 2 +- .flutter-plugins-dependencies | 2 +- assets/.DS_Store | Bin 10244 -> 10244 bytes assets/images/training_empty.png | Bin 0 -> 5784 bytes lib/gen/assets.gen.dart | 5 + lib/i18n/strings.g.dart | 2 +- lib/router.dart | 2 + lib/router.gr.dart | 135 +++-- lib/screens/home/home_screen.dart | 6 +- .../training/cubit/training_cubit.dart | 25 + .../cubit/training_cubit.freezed.dart | 560 ++++++++++++++++++ .../training/cubit/training_state.dart | 9 + lib/screens/training/states/empty.dart | 71 +++ lib/screens/training/states/loading.dart | 35 ++ lib/screens/training/training_screen.dart | 185 ++++++ .../training/widgets/training_ticket.dart | 124 ++++ 17 files changed, 1098 insertions(+), 65 deletions(-) create mode 100644 assets/images/training_empty.png create mode 100644 lib/screens/training/cubit/training_cubit.dart create mode 100644 lib/screens/training/cubit/training_cubit.freezed.dart create mode 100644 lib/screens/training/cubit/training_state.dart create mode 100644 lib/screens/training/states/empty.dart create mode 100644 lib/screens/training/states/loading.dart create mode 100644 lib/screens/training/training_screen.dart create mode 100644 lib/screens/training/widgets/training_ticket.dart diff --git a/.DS_Store b/.DS_Store index 23b201288d0c4d87deda326eccd34866c30c40b6..e730df1b98ceace501f3eb727dc8af5c0c38c325 100644 GIT binary patch delta 69 zcmZoMXffEJ&d9i9vIb+JxZ e+`Rm*$$JIl8M`;TOYCRg%&ySE4$;jF)C~aLK^VCJ delta 25 fcmZn(XbITRFFsjSVE1Nki9PI_*%Uh2fxJ=xfME#% diff --git a/assets/images/training_empty.png b/assets/images/training_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..25dd25ff9ac9f5994966967275b8eae645493818 GIT binary patch literal 5784 zcmZ8lcT^M2*WQGLDn)uHNK>i^3epmaLZnLXy?2q`yOe+e(xrm}lF*SJM3gQ9igb`7 zRhmGMP`O&Y6-E6( z(4PfuU-R{#{=`<&%I>Q%ULDFh}^`rixj9R(bChI>|3jLB~`>$(Y~q#-YxZ%&>sXQ0|X z8%qSOU;kw7fK3(Rmw(!JWM5-vp6lf;RBf?!L{strQQR)AXvy=832i6IZz$fP29utd ztmb=Lmii)OAKQyH%x{~Tc-O9(AlF62{ihy`^kOXN8l!hu1p#!3z;kUpWV=<|QBlXl zt!D_^%nII13ky?r6p$S|FmT>B4rhdF33>ig3vB>@gx zdw948Yk^Fv5N%``51_vCz3MXydT%~(h7&pIzb+^nA!E2^T+sUpVCMia-$5S)w_soP z%mH?(ZVs((!7G*e%phqQFHohOPDAI#$7KC%E%Tkfepy~MOU4C}EL>JOdDS?3)Yaul z7L(I_@~NWKGFEI_*qyQ!JB-QyrLfqP;#6(SK=^ovClGGm7OTn6m!`edF7|$yPZX=x z)n&X8nCjO!aZc26j?eRKDHP89po{EC#4lAhh401)jt#`@g%p(x4Hw~OWM*G|NfU%} zgQVN3!8%r`?A;-aTgdk}8WTiAJ4|#eEoI+=T=I}orw+95Hoks%lrFy~>peEXNMHIy zQ4L7l#9=#pFE0KS>U*M)%1;B>%THrPGo4elM;K>psg=eIMoC;eR(0cGynyQQ+_gzo z1w~j@%#UX<3wT!dcKFPoEPdmt;RI2vrAf}G(L?ny4NVV`1!GB&bg2wvagA00Kkkqyuqa{v2#^q#o*v(YhbAj;3X05SQ1GpU)+Gs&sT zQjFr`y}2fr4B((=NW6&WG1*>$fWRVcTNBgD#o!$CCFT$?4Z=oLEbU(veIH)y6$O*% zz)Yjl=Xdq>;%z5WW_;xR+ z+1q^26}3>{DrfE4cu2+bWqBaC4p`3i*h$Zk<5ymhv2}>C+7+4dM>7oqw_uVbMMVHteqmM@i+$ z?(N`E!^e3u5_rP1mHac2hQQpVe2O*_*han_W_tN}#`Ig-%^LG3W?hyoC`V_ct^MTL zNn;+`8_|4UZVWvhC^w>Mt^N^24yvwVziaX-y-A}m9+dZ>i>G1=9WFL@i zuV1tk3R`TdbM)zes64JR8#Cn7T3|+|ca}ma3~U#KI}Xg!&vS~*Ts1Y9rWvTCQJ~W= zmtHm?9c`hM?hnn8ibGI4hawKw$pz=rh~`XrKtFrS%nQU^4sJ@E;Q^MTZcfr_`%oh| zt|y;gLV;TuA;7&W5}4t?4?RL-2P{8@i!WBcF&FB2du&QueeK8JBI!aGm83q=#13+|4tq+A34HpI-9tM&CcCOZsM@*u@~!T@iMT6#%YL z;{ALi+*uzx`G7i5n2RqjNgIi^YYncO0}-})w$#)w0lMyFmhdK`=ffv zM_=cLk4SKbE4KS~MU5W)t?vG-n1KDffiG73zGFQnyouM)Z$$sBl7n0-gjVgMUB`D+ zm%O8J$zOAY&lcfJZ0ERdcRPpUmN(`;3|j5ArBI;j`15D&(lcOEj0JgSZvrB8)Neii zE27yJ*3bX(+)vy_+Q=FE@l7gf4nx*vGC5Yr;D;u7i>Crm`$dG z_WKM6kyi}X{PMrZXR?Z4H_e9Zku1!M75sd1H21=4Od))Ec+TOPMm+ORTv^NfzWR?Xfy>gNMz zzlgQ!ZsyI{!R{CPuP&`T$P_RQBHmyB)RNzW%bw&WnzZy~#{2snt-R*U?>fXd%X7v-^<{kb~v$y#C=*=YuDPyGJ z2$^D2H|~#AL;vkz6Qv1P9f2o>$(2fR5$$I|8^>9xYr@9BsRj?uhp}utkqUh^q+({P zRM%TG>UU%ZGX}N|8JDtI?N%i(d|R4Pl8}=f)sth*zb<63P5x$Hooc(VA@*>h^AkNX zLoI>YF02tbYvhBT8mcMP_wO2BEJcQkIk^3L=7-T62_wws3^>N*3BS1W=4+`T+I52L z%`HavPqn*i8(WEGv}y;>O;OXF5%)|Sc<-itwc24K~LuDazUH-JP5`H*w(!=WS(@qdYmxH+Z#kZTInDOtQ2z3DF>QzV<>oRxtZF#G)*G0EvXb;rkQxP05APy3BU`6JN> zQDPq1{oT(##Fj2?Xy-79X5OlX6VQ9-8va-(Rhn30r2#%M)?g#U(4wX1`LVw9P?&*r z5dG83sujvq+53e8(rK{Jg2YG(*}9JY$vbk2p5LD{%QWI+504ThbXCRl z?MniFFc@4`^@6>(yU`RH@oTkI5?G_ZOYazSZ>E~3{U{PV?j8E=BQ_AfAv0+e?=SrzScc0E>)ot)A!Mj|Y2S(y_ z>`Zp+xy;pY1|;;hFzQsJzk8ajJ_-9G&_0_ZEcjO553Z+><%@aUVdfz7WlEy!h;lFD zc>(ss0nBI84)~Vf`2>7zk;OE6)BXiZXb@iKU7P!oc=zv!>y%Hpp}S3fC(SSZT|TPr zVb1Ae&nbe31iHGq(XZ0RkrW$7$Zv7c2cCSLW2+4$e%F2`Gqjy-HLGyEv7;>Jwp96O zc3IjucUL5Z!A*zH6hVS?atZyvK_n>W);S^FwebK*P5DG}%^^PLc#NVlAv z1lw}r!cFg=TWxMxD%M3v zZX_uDye*)8FrzsfYqLp3wTDO7v^e&x-|vaLBMhYEIH6%;M)`>xS@BbKA1fNaXmg&Q zp=w_aqMmxpeU!HK)hM-#;<%&3EpkxR_Blp4WROYh?4|83zQ--{@Cbiha(y*`HH|3R ze2b!clV#qRCOmC|PkeY(pd9g$=v6(|yG0M`SQ*dbs8OPBOI``A=3G|A$nNLT)L{5j z%FP*!*V1!}L6tjKvD;$ImS^w%|Ke!wyEzom^Ev|!J3KXFknUtD!_J$1e~zw}m9EmF zp|B2x-pP|dzlg(#lF@v`u{m4l)&!-;9Yh^aelGNQVGa(iV^9Cic#nm)0qRK+J%>Bs z&^0mzN!-w(ee9Uz91{b9Q4}TXZ9F*pi#fLwu}9K>Cnh07XoZMQUDdXfr=G$mXkV); zuPKrVnJK&5MR8T^ZbMv=P1wT;!?SOC5r(;m;7f%mnP{%{*GV4gu*W{df4sax zcWYGkew*d_;8Qhqje=Ka34Uy}a9FUT^D;!Dx$s0&PsjypDzz)wpN>KO=C&JY7b_Zc zXllh)jWeov(QrCYD)xFAr048D6&GO{)rsl(*VMybaw(G75KsDxgzJcv$erSe9Sy%j zF7voD)zv?%eSwQQn?)iW+*QL_Td1T)H+~ggR)h>YW(yd$k&60c3O^$#(UAD<+7aKx zkq_NmR&G^4s)&6Je%>u*zB04sL0xCQ!BpY1Bd(KcScTgsCg4`@6)3UQXbY9YPHm0? zg|@({OH74vsue*M@02ut8oR7x+og#EQ7SATqnrDwVpH$%jLl4#G$P@^U2sl2c`*LRGx0SO@R5UM2-D*AY_wu+> zC@lQ%_fq%Q@r!%IFIpP1nYe=}5q9iIH@l%fzuSjhXW`9*Qzxm=*KY4hs9TTgK8nQ}p# z!rhjkW8ncEkQza?wGi@>=0^ayQiLGDJ)SW>T>cnw(I9f0OOP8Ft%JIt}&=LI;@aj*XawYR=;q(&@+ zs%HQ-EVZE4=fUb5$Ah1t*-uO<`vOZiDtgl^%D8ZGhK`3aTq?cocwGJ8GvZ=K*Ni%)xivV#pMMwBbWT=@1nF zX4?iOM8AgnRER#GE~aTyn|lyMlMiK&#U!0-6&Tbfcr7n56|ULW$%}})JU0>!J6R{x zZyFwc^KXU8VztLaT!9v7g7FT9t>WPWQ@y^Z$}9H+#P_Z*IbVa}4+%2ASRkdgP8Nc2 zkA_(Ln@V;l11kJ#Ky|^S8I=S#7{>n*taIYN%57=`)RTr^{MjNTmaXs;S+I&DPX=rR zjBosCvRuNiNdE8|pOUdr)Do|(cQZQ$0xP|_Bz%y`$`pbk0pB(!ltBJFJPzunTNa?m zXnMo_or#=o$y3^BAW3&Kl7ySISS{}5*?%VO=hsXGlXQIKA6+z{lsAev$`yt8|TedcS${T~3z5Mld7gk5AihTk?A(&3q5-gv|N+uuOsUgdz+VqZ?;{ z)!~5Z=h8saaklz?^G*3MhfRy&s-KKb=Gc1~%zv$2|7dVVsR4#c_la~|48Fa}{PZ}T zDEi1-1Vhnn_hsO4w_Tr_IO=wKOA6Az!@eMm@)jUe(hidLcJ(|g4w0cR73obd-)ys% z@#K^LEp&`=2w&7X&9b;{Dq%<4*q`J<$(97M=F^7*B&59qp|_8xSbtLd`BYLFwQ0$W z(QT5j9m)H2n}GN(@E-ykR@s%peLQ7HgEG0DOgD-=)Q{;s7a(FS0?7^OoTmF;lzUi~9Rfo!>$BKX<7$p4) z&E0evHZ`!SAE*2+Zt%-UJ2H0T^Yo0MLC{a7{}8QN?lZCVBnuRH#ohyQ(_ehZ{iTr^8sCJI$|FzgFI-i=m z<*IBs3+3nR!FG8y9s50J=h55Di;vrY&|dla$;$cEc3>BS%OLBy?;Mwt)0COyV&(P6 z_=azYNrQ~zhh@vYT2^ESrNjeu2NH8OP^(rZzSFhLC2Q!=CF0}auDAt@UKPvBm-qHFh6J~W* llaMEMp#Lq(0zgmCHPMSZPC7>^0vZ3kMLyC}!6;cr{~y+>)l&cf literal 0 HcmV?d00001 diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 68c3ffb..c0dc899 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -410,6 +410,10 @@ class $AssetsImagesGen { /// File path: assets/images/quote.png AssetGenImage get quote => const AssetGenImage('assets/images/quote.png'); + /// File path: assets/images/training_empty.png + AssetGenImage get trainingEmpty => + const AssetGenImage('assets/images/training_empty.png'); + /// List of all assets List get values => [ aGitkeep, @@ -427,6 +431,7 @@ class $AssetsImagesGen { logo, noData, quote, + trainingEmpty, ]; } diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index bd6bd7c..ba4cd71 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 20 (10 per locale) /// -/// Built on 2025-06-17 at 17:49 UTC +/// Built on 2025-06-17 at 18:40 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/lib/router.dart b/lib/router.dart index 677e01a..e2c30c2 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -22,6 +22,8 @@ class AppRouter extends RootStackRouter { ], ), + AutoRoute(path: '/training', page: TrainingRoute.page), + AutoRoute(path: '/crud_collection', page: CrudCollectionRoute.page), AutoRoute(path: '/collection_search', page: CollectionSearchRoute.page), AutoRoute(path: '/crudFullField', page: CrudCollectionFullscreenField.page), diff --git a/lib/router.gr.dart b/lib/router.gr.dart index 189f9f3..dfa4f2a 100644 --- a/lib/router.gr.dart +++ b/lib/router.gr.dart @@ -9,10 +9,10 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i13; -import 'package:flutter/cupertino.dart' as _i14; -import 'package:flutter/material.dart' as _i16; -import 'package:remever/database/database.dart' as _i15; +import 'package:auto_route/auto_route.dart' as _i14; +import 'package:flutter/cupertino.dart' as _i15; +import 'package:flutter/material.dart' as _i17; +import 'package:remever/database/database.dart' as _i16; import 'package:remever/screens/auth/auth_screen.dart' as _i1; import 'package:remever/screens/collections/collection_detail_screen.dart' as _i2; @@ -28,16 +28,17 @@ import 'package:remever/screens/sandbox/sandbox_screen.dart' as _i9; import 'package:remever/screens/settings/settings_screen.dart' as _i10; import 'package:remever/screens/splash/splash_screen.dart' as _i11; import 'package:remever/screens/statistick/statistick_screen.dart' as _i12; +import 'package:remever/screens/training/training_screen.dart' as _i13; /// generated route for /// [_i1.AuthScreen] -class AuthRoute extends _i13.PageRouteInfo { - const AuthRoute({List<_i13.PageRouteInfo>? children}) +class AuthRoute extends _i14.PageRouteInfo { + const AuthRoute({List<_i14.PageRouteInfo>? children}) : super(AuthRoute.name, initialChildren: children); static const String name = 'AuthRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i1.AuthScreen(); @@ -48,11 +49,11 @@ class AuthRoute extends _i13.PageRouteInfo { /// generated route for /// [_i2.CollectionDetailScreen] class CollectionDetailRoute - extends _i13.PageRouteInfo { + extends _i14.PageRouteInfo { CollectionDetailRoute({ - _i14.Key? key, - required _i15.Collection collection, - List<_i13.PageRouteInfo>? children, + _i15.Key? key, + required _i16.Collection collection, + List<_i14.PageRouteInfo>? children, }) : super( CollectionDetailRoute.name, args: CollectionDetailRouteArgs(key: key, collection: collection), @@ -61,7 +62,7 @@ class CollectionDetailRoute static const String name = 'CollectionDetailRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs(); @@ -76,9 +77,9 @@ class CollectionDetailRoute class CollectionDetailRouteArgs { const CollectionDetailRouteArgs({this.key, required this.collection}); - final _i14.Key? key; + final _i15.Key? key; - final _i15.Collection collection; + final _i16.Collection collection; @override String toString() { @@ -88,13 +89,13 @@ class CollectionDetailRouteArgs { /// generated route for /// [_i3.CollectionScreen] -class CollectionRoute extends _i13.PageRouteInfo { - const CollectionRoute({List<_i13.PageRouteInfo>? children}) +class CollectionRoute extends _i14.PageRouteInfo { + const CollectionRoute({List<_i14.PageRouteInfo>? children}) : super(CollectionRoute.name, initialChildren: children); static const String name = 'CollectionRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i3.CollectionScreen(); @@ -105,11 +106,11 @@ class CollectionRoute extends _i13.PageRouteInfo { /// generated route for /// [_i4.CollectionSearchScreen] class CollectionSearchRoute - extends _i13.PageRouteInfo { + extends _i14.PageRouteInfo { CollectionSearchRoute({ - _i14.Key? key, - required void Function(_i15.Collection) onCollectionSelect, - List<_i13.PageRouteInfo>? children, + _i15.Key? key, + required void Function(_i16.Collection) onCollectionSelect, + List<_i14.PageRouteInfo>? children, }) : super( CollectionSearchRoute.name, args: CollectionSearchRouteArgs( @@ -121,7 +122,7 @@ class CollectionSearchRoute static const String name = 'CollectionSearchRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs(); @@ -136,9 +137,9 @@ class CollectionSearchRoute class CollectionSearchRouteArgs { const CollectionSearchRouteArgs({this.key, required this.onCollectionSelect}); - final _i14.Key? key; + final _i15.Key? key; - final void Function(_i15.Collection) onCollectionSelect; + final void Function(_i16.Collection) onCollectionSelect; @override String toString() { @@ -148,11 +149,11 @@ class CollectionSearchRouteArgs { /// generated route for /// [_i5.CreateScreen] -class CreateRoute extends _i13.PageRouteInfo { +class CreateRoute extends _i14.PageRouteInfo { CreateRoute({ - _i14.Key? key, - _i15.Collection? collection, - List<_i13.PageRouteInfo>? children, + _i15.Key? key, + _i16.Collection? collection, + List<_i14.PageRouteInfo>? children, }) : super( CreateRoute.name, args: CreateRouteArgs(key: key, collection: collection), @@ -161,7 +162,7 @@ class CreateRoute extends _i13.PageRouteInfo { static const String name = 'CreateRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -175,9 +176,9 @@ class CreateRoute extends _i13.PageRouteInfo { class CreateRouteArgs { const CreateRouteArgs({this.key, this.collection}); - final _i14.Key? key; + final _i15.Key? key; - final _i15.Collection? collection; + final _i16.Collection? collection; @override String toString() { @@ -188,15 +189,15 @@ class CreateRouteArgs { /// generated route for /// [_i6.CrudCollectionFullscreenField] class CrudCollectionFullscreenField - extends _i13.PageRouteInfo { + extends _i14.PageRouteInfo { CrudCollectionFullscreenField({ - _i14.Key? key, + _i15.Key? key, String title = '', String? hint, String? content, double height = 92, required void Function(String?) onEditingComplete, - List<_i13.PageRouteInfo>? children, + List<_i14.PageRouteInfo>? children, }) : super( CrudCollectionFullscreenField.name, args: CrudCollectionFullscreenFieldArgs( @@ -212,7 +213,7 @@ class CrudCollectionFullscreenField static const String name = 'CrudCollectionFullscreenField'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs(); @@ -238,7 +239,7 @@ class CrudCollectionFullscreenFieldArgs { required this.onEditingComplete, }); - final _i14.Key? key; + final _i15.Key? key; final String title; @@ -258,11 +259,11 @@ class CrudCollectionFullscreenFieldArgs { /// generated route for /// [_i7.CrudCollectionScreen] -class CrudCollectionRoute extends _i13.PageRouteInfo { +class CrudCollectionRoute extends _i14.PageRouteInfo { CrudCollectionRoute({ - _i14.Key? key, - _i15.Collection? editedCollection, - List<_i13.PageRouteInfo>? children, + _i15.Key? key, + _i16.Collection? editedCollection, + List<_i14.PageRouteInfo>? children, }) : super( CrudCollectionRoute.name, args: CrudCollectionRouteArgs( @@ -274,7 +275,7 @@ class CrudCollectionRoute extends _i13.PageRouteInfo { static const String name = 'CrudCollectionRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -291,9 +292,9 @@ class CrudCollectionRoute extends _i13.PageRouteInfo { class CrudCollectionRouteArgs { const CrudCollectionRouteArgs({this.key, this.editedCollection}); - final _i14.Key? key; + final _i15.Key? key; - final _i15.Collection? editedCollection; + final _i16.Collection? editedCollection; @override String toString() { @@ -303,13 +304,13 @@ class CrudCollectionRouteArgs { /// generated route for /// [_i8.HomeScreen] -class HomeRoute extends _i13.PageRouteInfo { - const HomeRoute({List<_i13.PageRouteInfo>? children}) +class HomeRoute extends _i14.PageRouteInfo { + const HomeRoute({List<_i14.PageRouteInfo>? children}) : super(HomeRoute.name, initialChildren: children); static const String name = 'HomeRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i8.HomeScreen(); @@ -319,13 +320,13 @@ class HomeRoute extends _i13.PageRouteInfo { /// generated route for /// [_i9.SandboxScreen] -class SandboxRoute extends _i13.PageRouteInfo { - const SandboxRoute({List<_i13.PageRouteInfo>? children}) +class SandboxRoute extends _i14.PageRouteInfo { + const SandboxRoute({List<_i14.PageRouteInfo>? children}) : super(SandboxRoute.name, initialChildren: children); static const String name = 'SandboxRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i9.SandboxScreen(); @@ -335,13 +336,13 @@ class SandboxRoute extends _i13.PageRouteInfo { /// generated route for /// [_i10.SettingsScreen] -class SettingsRoute extends _i13.PageRouteInfo { - const SettingsRoute({List<_i13.PageRouteInfo>? children}) +class SettingsRoute extends _i14.PageRouteInfo { + const SettingsRoute({List<_i14.PageRouteInfo>? children}) : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i10.SettingsScreen(); @@ -351,13 +352,13 @@ class SettingsRoute extends _i13.PageRouteInfo { /// generated route for /// [_i11.SplashScreen] -class SplashRoute extends _i13.PageRouteInfo { - const SplashRoute({List<_i13.PageRouteInfo>? children}) +class SplashRoute extends _i14.PageRouteInfo { + const SplashRoute({List<_i14.PageRouteInfo>? children}) : super(SplashRoute.name, initialChildren: children); static const String name = 'SplashRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { return const _i11.SplashScreen(); @@ -367,8 +368,8 @@ class SplashRoute extends _i13.PageRouteInfo { /// generated route for /// [_i12.StatistickScreen] -class StatistickRoute extends _i13.PageRouteInfo { - StatistickRoute({_i16.Key? key, List<_i13.PageRouteInfo>? children}) +class StatistickRoute extends _i14.PageRouteInfo { + StatistickRoute({_i17.Key? key, List<_i14.PageRouteInfo>? children}) : super( StatistickRoute.name, args: StatistickRouteArgs(key: key), @@ -377,7 +378,7 @@ class StatistickRoute extends _i13.PageRouteInfo { static const String name = 'StatistickRoute'; - static _i13.PageInfo page = _i13.PageInfo( + static _i14.PageInfo page = _i14.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -391,10 +392,26 @@ class StatistickRoute extends _i13.PageRouteInfo { class StatistickRouteArgs { const StatistickRouteArgs({this.key}); - final _i16.Key? key; + final _i17.Key? key; @override String toString() { return 'StatistickRouteArgs{key: $key}'; } } + +/// generated route for +/// [_i13.TrainingScreen] +class TrainingRoute extends _i14.PageRouteInfo { + const TrainingRoute({List<_i14.PageRouteInfo>? children}) + : super(TrainingRoute.name, initialChildren: children); + + static const String name = 'TrainingRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i13.TrainingScreen(); + }, + ); +} diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 13744de..36a0dea 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -37,7 +37,7 @@ class HomeScreen extends StatelessWidget { }, ), ), - _buildCentralButton(), + _buildCentralButton(context), ], ); } @@ -80,12 +80,12 @@ class HomeScreen extends StatelessWidget { } /// Построение центральной кнопки - Widget _buildCentralButton() { + Widget _buildCentralButton(BuildContext context) { return Align( alignment: Alignment(0, 0.91), child: GestureDetector( onTap: () { - // Логика нажатия на центральную кнопку + context.pushRoute(TrainingRoute()); }, child: SizedBox.square( dimension: 60.r, diff --git a/lib/screens/training/cubit/training_cubit.dart b/lib/screens/training/cubit/training_cubit.dart new file mode 100644 index 0000000..f2e6e5b --- /dev/null +++ b/lib/screens/training/cubit/training_cubit.dart @@ -0,0 +1,25 @@ +import 'package:bloc/bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'training_state.dart'; +part 'training_cubit.freezed.dart'; + +class TrainingCubit extends Cubit { + TrainingCubit() : super(TrainingState.data()); + + Future toLoading() async { + emit(TrainingState.loading()); + } + + Future toEmptyState() async { + emit(TrainingState.empty()); + } + + Future toDataState() async { + emit(TrainingState.data()); + } + + Future toResultState() async { + emit(TrainingState.result()); + } +} diff --git a/lib/screens/training/cubit/training_cubit.freezed.dart b/lib/screens/training/cubit/training_cubit.freezed.dart new file mode 100644 index 0000000..c2cf45e --- /dev/null +++ b/lib/screens/training/cubit/training_cubit.freezed.dart @@ -0,0 +1,560 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'training_cubit.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$TrainingState { + @optionalTypeArgs + TResult when({ + required TResult Function() loading, + required TResult Function() empty, + required TResult Function() data, + required TResult Function() result, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? loading, + TResult? Function()? empty, + TResult? Function()? data, + TResult? Function()? result, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? loading, + TResult Function()? empty, + TResult Function()? data, + TResult Function()? result, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Loading value) loading, + required TResult Function(_Empty value) empty, + required TResult Function(_Data value) data, + required TResult Function(_Result value) result, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Loading value)? loading, + TResult? Function(_Empty value)? empty, + TResult? Function(_Data value)? data, + TResult? Function(_Result value)? result, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Loading value)? loading, + TResult Function(_Empty value)? empty, + TResult Function(_Data value)? data, + TResult Function(_Result value)? result, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TrainingStateCopyWith<$Res> { + factory $TrainingStateCopyWith( + TrainingState value, + $Res Function(TrainingState) then, + ) = _$TrainingStateCopyWithImpl<$Res, TrainingState>; +} + +/// @nodoc +class _$TrainingStateCopyWithImpl<$Res, $Val extends TrainingState> + implements $TrainingStateCopyWith<$Res> { + _$TrainingStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TrainingState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$LoadingImplCopyWith<$Res> { + factory _$$LoadingImplCopyWith( + _$LoadingImpl value, + $Res Function(_$LoadingImpl) then, + ) = __$$LoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadingImplCopyWithImpl<$Res> + extends _$TrainingStateCopyWithImpl<$Res, _$LoadingImpl> + implements _$$LoadingImplCopyWith<$Res> { + __$$LoadingImplCopyWithImpl( + _$LoadingImpl _value, + $Res Function(_$LoadingImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TrainingState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$LoadingImpl implements _Loading { + const _$LoadingImpl(); + + @override + String toString() { + return 'TrainingState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$LoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() loading, + required TResult Function() empty, + required TResult Function() data, + required TResult Function() result, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? loading, + TResult? Function()? empty, + TResult? Function()? data, + TResult? Function()? result, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? loading, + TResult Function()? empty, + TResult Function()? data, + TResult Function()? result, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Loading value) loading, + required TResult Function(_Empty value) empty, + required TResult Function(_Data value) data, + required TResult Function(_Result value) result, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Loading value)? loading, + TResult? Function(_Empty value)? empty, + TResult? Function(_Data value)? data, + TResult? Function(_Result value)? result, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Loading value)? loading, + TResult Function(_Empty value)? empty, + TResult Function(_Data value)? data, + TResult Function(_Result value)? result, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class _Loading implements TrainingState { + const factory _Loading() = _$LoadingImpl; +} + +/// @nodoc +abstract class _$$EmptyImplCopyWith<$Res> { + factory _$$EmptyImplCopyWith( + _$EmptyImpl value, + $Res Function(_$EmptyImpl) then, + ) = __$$EmptyImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$EmptyImplCopyWithImpl<$Res> + extends _$TrainingStateCopyWithImpl<$Res, _$EmptyImpl> + implements _$$EmptyImplCopyWith<$Res> { + __$$EmptyImplCopyWithImpl( + _$EmptyImpl _value, + $Res Function(_$EmptyImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TrainingState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$EmptyImpl implements _Empty { + const _$EmptyImpl(); + + @override + String toString() { + return 'TrainingState.empty()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$EmptyImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() loading, + required TResult Function() empty, + required TResult Function() data, + required TResult Function() result, + }) { + return empty(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? loading, + TResult? Function()? empty, + TResult? Function()? data, + TResult? Function()? result, + }) { + return empty?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? loading, + TResult Function()? empty, + TResult Function()? data, + TResult Function()? result, + required TResult orElse(), + }) { + if (empty != null) { + return empty(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Loading value) loading, + required TResult Function(_Empty value) empty, + required TResult Function(_Data value) data, + required TResult Function(_Result value) result, + }) { + return empty(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Loading value)? loading, + TResult? Function(_Empty value)? empty, + TResult? Function(_Data value)? data, + TResult? Function(_Result value)? result, + }) { + return empty?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Loading value)? loading, + TResult Function(_Empty value)? empty, + TResult Function(_Data value)? data, + TResult Function(_Result value)? result, + required TResult orElse(), + }) { + if (empty != null) { + return empty(this); + } + return orElse(); + } +} + +abstract class _Empty implements TrainingState { + const factory _Empty() = _$EmptyImpl; +} + +/// @nodoc +abstract class _$$DataImplCopyWith<$Res> { + factory _$$DataImplCopyWith( + _$DataImpl value, + $Res Function(_$DataImpl) then, + ) = __$$DataImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$DataImplCopyWithImpl<$Res> + extends _$TrainingStateCopyWithImpl<$Res, _$DataImpl> + implements _$$DataImplCopyWith<$Res> { + __$$DataImplCopyWithImpl(_$DataImpl _value, $Res Function(_$DataImpl) _then) + : super(_value, _then); + + /// Create a copy of TrainingState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$DataImpl implements _Data { + const _$DataImpl(); + + @override + String toString() { + return 'TrainingState.data()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$DataImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() loading, + required TResult Function() empty, + required TResult Function() data, + required TResult Function() result, + }) { + return data(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? loading, + TResult? Function()? empty, + TResult? Function()? data, + TResult? Function()? result, + }) { + return data?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? loading, + TResult Function()? empty, + TResult Function()? data, + TResult Function()? result, + required TResult orElse(), + }) { + if (data != null) { + return data(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Loading value) loading, + required TResult Function(_Empty value) empty, + required TResult Function(_Data value) data, + required TResult Function(_Result value) result, + }) { + return data(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Loading value)? loading, + TResult? Function(_Empty value)? empty, + TResult? Function(_Data value)? data, + TResult? Function(_Result value)? result, + }) { + return data?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Loading value)? loading, + TResult Function(_Empty value)? empty, + TResult Function(_Data value)? data, + TResult Function(_Result value)? result, + required TResult orElse(), + }) { + if (data != null) { + return data(this); + } + return orElse(); + } +} + +abstract class _Data implements TrainingState { + const factory _Data() = _$DataImpl; +} + +/// @nodoc +abstract class _$$ResultImplCopyWith<$Res> { + factory _$$ResultImplCopyWith( + _$ResultImpl value, + $Res Function(_$ResultImpl) then, + ) = __$$ResultImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ResultImplCopyWithImpl<$Res> + extends _$TrainingStateCopyWithImpl<$Res, _$ResultImpl> + implements _$$ResultImplCopyWith<$Res> { + __$$ResultImplCopyWithImpl( + _$ResultImpl _value, + $Res Function(_$ResultImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TrainingState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$ResultImpl implements _Result { + const _$ResultImpl(); + + @override + String toString() { + return 'TrainingState.result()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$ResultImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() loading, + required TResult Function() empty, + required TResult Function() data, + required TResult Function() result, + }) { + return result(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? loading, + TResult? Function()? empty, + TResult? Function()? data, + TResult? Function()? result, + }) { + return result?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? loading, + TResult Function()? empty, + TResult Function()? data, + TResult Function()? result, + required TResult orElse(), + }) { + if (result != null) { + return result(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Loading value) loading, + required TResult Function(_Empty value) empty, + required TResult Function(_Data value) data, + required TResult Function(_Result value) result, + }) { + return result(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Loading value)? loading, + TResult? Function(_Empty value)? empty, + TResult? Function(_Data value)? data, + TResult? Function(_Result value)? result, + }) { + return result?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Loading value)? loading, + TResult Function(_Empty value)? empty, + TResult Function(_Data value)? data, + TResult Function(_Result value)? result, + required TResult orElse(), + }) { + if (result != null) { + return result(this); + } + return orElse(); + } +} + +abstract class _Result implements TrainingState { + const factory _Result() = _$ResultImpl; +} diff --git a/lib/screens/training/cubit/training_state.dart b/lib/screens/training/cubit/training_state.dart new file mode 100644 index 0000000..4021db4 --- /dev/null +++ b/lib/screens/training/cubit/training_state.dart @@ -0,0 +1,9 @@ +part of 'training_cubit.dart'; + +@freezed +class TrainingState with _$TrainingState { + const factory TrainingState.loading() = _Loading; + const factory TrainingState.empty() = _Empty; + const factory TrainingState.data() = _Data; + const factory TrainingState.result() = _Result; +} diff --git a/lib/screens/training/states/empty.dart b/lib/screens/training/states/empty.dart new file mode 100644 index 0000000..d8b7cde --- /dev/null +++ b/lib/screens/training/states/empty.dart @@ -0,0 +1,71 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:remever/common/resources.dart'; +import 'package:remever/common/widgets/typography.dart'; +import 'package:remever/common/widgets/wspace.dart'; +import 'package:remever/components/extensions/context.dart'; +import 'package:remever/gen/assets.gen.dart'; +import 'package:remever/router.gr.dart'; +import 'package:remever/widgets/primary_button.dart'; + +class TrainingEmpty extends StatelessWidget { + const TrainingEmpty({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bg, + appBar: _buildAppBar(context), + body: _buildMain(context), + ); + } + + /// Построение шапки + AppBar _buildAppBar(BuildContext context) { + return AppBar( + backgroundColor: AppColors.white, + shadowColor: Colors.transparent, + leading: GestureDetector( + onTap: () => context.back(), + child: const Icon(Icons.close, color: Colors.black), + ), + centerTitle: true, + title: AppTypography( + 'Тренировка', + type: SemiBold20px(), + color: AppColors.body_text, + ), + ); + } + + Widget _buildMain(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16).r, + child: Column( + children: [ + HSpace(90), + Assets.images.trainingEmpty.image(height: 127.h, width: 160.w), + HSpace(20), + AppTypography( + 'К сожалению, у вас нет карточек для изучения', + type: SemiBold20px(), + maxLines: 3, + textAlign: TextAlign.center, + ), + Spacer(), + PrimaryButton( + onTap: () { + context.router.replaceAll([CreateRoute()]); + }, + child: AppTypography( + 'Создать карточку', + type: Medium14px(), + color: Colors.white, + ), + ), + HSpace(90), + ], + ), + ); + } +} diff --git a/lib/screens/training/states/loading.dart b/lib/screens/training/states/loading.dart new file mode 100644 index 0000000..3f9eb68 --- /dev/null +++ b/lib/screens/training/states/loading.dart @@ -0,0 +1,35 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:remever/common/resources.dart'; +import 'package:remever/common/widgets/typography.dart'; + +class TrainingLoading extends StatelessWidget { + const TrainingLoading({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bg, + appBar: _buildAppBar(context), + body: Center(child: CircularProgressIndicator(color: AppColors.primary)), + ); + } + + /// Построение шапки + AppBar _buildAppBar(BuildContext context) { + return AppBar( + backgroundColor: AppColors.white, + shadowColor: Colors.transparent, + leading: GestureDetector( + onTap: () => context.back(), + child: const Icon(Icons.close, color: Colors.black), + ), + centerTitle: true, + title: AppTypography( + 'Тренировка', + type: SemiBold20px(), + color: AppColors.body_text, + ), + ); + } +} diff --git a/lib/screens/training/training_screen.dart b/lib/screens/training/training_screen.dart new file mode 100644 index 0000000..21d44f4 --- /dev/null +++ b/lib/screens/training/training_screen.dart @@ -0,0 +1,185 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:remever/common/resources.dart'; +import 'package:remever/common/typography.dart'; +import 'package:remever/common/widgets/typography.dart'; +import 'package:remever/common/widgets/w_if.dart'; +import 'package:remever/common/widgets/wspace.dart'; +import 'package:remever/components/extensions/context.dart'; +import 'package:remever/components/extensions/state.dart'; +import 'package:remever/gen/assets.gen.dart'; +import 'package:remever/router.gr.dart'; +import 'package:remever/screens/training/cubit/training_cubit.dart'; +import 'package:remever/screens/training/states/empty.dart'; +import 'package:remever/screens/training/states/loading.dart'; +import 'package:remever/screens/training/widgets/training_ticket.dart'; +import 'package:remever/widgets/primary_button.dart'; + +@RoutePage() +class TrainingScreen extends StatelessWidget { + const TrainingScreen({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (BuildContext context) => TrainingCubit(), + child: _buildMain(), + ); + } + + /// + /// Построение основного блока + /// + Widget _buildMain() { + return PopScope( + canPop: false, + child: BlocBuilder( + builder: (BuildContext context, state) { + return state.when( + loading: () => TrainingLoading(), + empty: () => TrainingEmpty(), + data: () => TrainingData(), + result: () => Placeholder(), + ); + }, + ), + ); + } +} + +class TrainingData extends StatefulWidget { + const TrainingData({super.key}); + + @override + State createState() => _TrainingDataState(); +} + +class _TrainingDataState extends State { + bool _showAnswer = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bg, + appBar: _buildAppBar(context), + body: _buildMain(), + ); + } + + /// Построение шапки + AppBar _buildAppBar(BuildContext context) { + return AppBar( + backgroundColor: AppColors.white, + shadowColor: Colors.transparent, + leading: GestureDetector( + onTap: () => context.back(), + child: const Icon(Icons.close, color: Colors.black), + ), + centerTitle: true, + title: AppTypography( + 'Тренировка', + type: SemiBold20px(), + color: AppColors.body_text, + ), + actions: [_buildCounter(), WSpace(16)], + ); + } + + Widget _buildCounter() { + return Center( + child: Container( + height: 24.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(100)).r, + color: AppColors.bg, + ), + child: Padding( + padding: const EdgeInsets.all(3).r, + child: Center(child: AppTypography('1 из 9', type: Regular14px())), + ), + ), + ); + } + + Widget _buildMain() { + return Column( + children: [_buildTickets(), _buildShowAnswerButton(), _buildActions()], + ); + } + + Widget _buildActions() { + return Padding( + padding: const EdgeInsets.all(16).r, + child: Row( + spacing: 8.r, + children: [ + Flexible( + child: PrimaryButton( + onTap: () {}, + color: AppColors.danger, + child: AppTypography( + 'Не помню', + type: Medium14px(), + color: Colors.white, + ), + ), + ), + Flexible( + child: PrimaryButton( + child: AppTypography( + 'Помню', + type: Medium14px(), + color: Colors.white, + ), + onTap: () {}, + ), + ), + ], + ), + ); + } + + Widget _buildShowAnswerButton() { + return Wif( + condition: !_showAnswer, + builder: + (context) => PrimaryButton( + onTap: () { + safeSetState(() { + _showAnswer = true; + }); + }, + color: AppColors.secondary, + width: 170, + child: AppTypography( + 'Показать ответ', + type: Medium14px(), + color: AppColors.primary, + ), + ), + ); + } + + Widget _buildTickets() { + return Expanded( + child: SingleChildScrollView( + physics: BouncingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(16).r, + child: Column( + spacing: 8.r, + children: [ + TrainingTicket(), + Wif( + condition: _showAnswer, + builder: (context) => TrainingTicket(isAnswer: true), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/training/widgets/training_ticket.dart b/lib/screens/training/widgets/training_ticket.dart new file mode 100644 index 0000000..f126042 --- /dev/null +++ b/lib/screens/training/widgets/training_ticket.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:remever/common/resources.dart'; +import 'package:remever/common/widgets/typography.dart'; +import 'package:remever/common/widgets/w_if.dart'; +import 'package:remever/common/widgets/wspace.dart'; +import 'package:remever/components/extensions/context.dart'; + +class TrainingTicket extends StatelessWidget { + const TrainingTicket({super.key, this.isAnswer = false}); + + final bool isAnswer; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12).r, + color: Colors.white, + ), + constraints: BoxConstraints(minHeight: 50.h), + child: Stack( + children: [ + SizedBox( + height: 90.h, + width: double.infinity, + child: DecoratedBox(decoration: getDecoration()), + ), + Padding( + padding: const EdgeInsets.all(12).r, + child: Column( + children: [ + _buildCollectionInfo(), + _buildImage(), + HSpace(4), + _buildText(context), + ], + ), + ), + ], + ), + ); + } + + Widget _buildCollectionInfo() { + return Wif( + condition: !isAnswer, + builder: (context) { + return Padding( + padding: const EdgeInsets.only(bottom: 8).r, + child: Row( + children: [ + SizedBox( + height: 24.h, + width: 24.w, + child: ClipOval( + child: Image.network( + 'https://avatars.mds.yandex.net/i?id=56429b65e9098a58fcd538387d43bcbb_l-5384017-images-thumbs&n=13', + fit: BoxFit.cover, + ), + ), + ), + WSpace(4), + AppTypography( + 'Астрология и астрофизика', + type: Regular14px(), + color: AppColors.disabled, + ), + ], + ), + ); + }, + ); + } + + /// Декорирование контейнера + BoxDecoration getDecoration() { + return BoxDecoration( + borderRadius: BorderRadius.circular(12).r, + gradient: LinearGradient( + colors: [ + isAnswer ? AppColors.answer : AppColors.question, + Colors.white, + ], + begin: Alignment.topLeft, + end: const Alignment(-0.5, 1), + stops: const [0.25, 0.25], + ), + ); + } + + Widget _buildText(BuildContext context) { + return AppTypography( + 'Родился 19 февраля 1473 года в Торуне в семье купца. После смерти отца воспитывался у дяди, епископа Вармийской епархии. Коперник изложил свои идеи в сочинении «Commentariolus» («Малый комментарий»), в котором сформулировал основные положения гелиоцентрической системы мира в виде 6 аксиом. Их смысл состоит в том, что Земля, как и другие планеты,' + 'Родился 19 февраля 1473 года в Торуне в семье купца. После смерти отца воспитывался у дяди, епископа Вармийской епархии. Коперник изложил свои идеи в сочинении «Commentariolus» («Малый комментарий»), в котором сформулировал основные положения гелиоцентрической системы мира в виде 6 аксиом. Их смысл состоит в том, что Земля, как и другие планеты,', + maxLines: 99, + type: Regular14px(), + ); + } + + Widget _buildImage() { + // final imageBytes = + // isAnswer ? ticket.answerImage : ticket.questionImage; + + return Wif( + condition: true, // imageBytes != null, + builder: + (context) => Padding( + padding: const EdgeInsets.only(right: 8).r, + child: SizedBox.square( + dimension: 100.r, + child: ClipRRect( + borderRadius: BorderRadius.circular(8).r, + // child: Image.memory(imageBytes!, fit: BoxFit.cover), + child: Image.network( + 'https://avatars.mds.yandex.net/i?id=56429b65e9098a58fcd538387d43bcbb_l-5384017-images-thumbs&n=13', + fit: BoxFit.cover, + ), + ), + ), + ), + ); + } +}