From 2e33f8324e97048e93425d9f94afc9abc32ff10e Mon Sep 17 00:00:00 2001 From: jwinterm Date: Fri, 7 Aug 2026 03:02:37 -0400 Subject: [PATCH] feat(send): animated hash-bag on the "Transaction Sent" screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the static success illustration with HashBagSuccessAnimation: the money sack sits at the bottom and #/$/₿ glyphs erupt from its mouth, rising and fading on a loop. Glyphs are PNG assets (the app bundles no font with a ₿ glyph). InfoPage gains an overridable buildIllustration() hook (default unchanged: static HashImageWidget); only TransactionSuccessPage overrides it, so every other InfoPage is untouched. NOTE: written without a local Flutter build (Dart pub can't resolve here) — verify on the next CI/device build. Self-contained and revertible; the static hash_bag.png remains the fallback via imageLightPath/imageDarkPath. --- assets/images/hash_bag_base.png | Bin 0 -> 9551 bytes assets/images/hashbag_sym_btc.png | Bin 0 -> 3436 bytes assets/images/hashbag_sym_dollar.png | Bin 0 -> 1893 bytes assets/images/hashbag_sym_hash.png | Bin 0 -> 1062 bytes lib/src/screens/Info_page.dart | 8 +- .../send/transaction_success_info_page.dart | 5 + .../widgets/hash_bag_success_animation.dart | 124 ++++++++++++++++++ 7 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 assets/images/hash_bag_base.png create mode 100644 assets/images/hashbag_sym_btc.png create mode 100644 assets/images/hashbag_sym_dollar.png create mode 100644 assets/images/hashbag_sym_hash.png create mode 100644 lib/src/widgets/hash_bag_success_animation.dart diff --git a/assets/images/hash_bag_base.png b/assets/images/hash_bag_base.png new file mode 100644 index 0000000000000000000000000000000000000000..d8f5868608c62977d615f12cd80ac406feab2980 GIT binary patch literal 9551 zcmV-VC9v9wP) zd3;?}x%Yq1+WVZ$>D;90+_XvCw6x6NaP4IhWKfX^dYw_m3knw%(R&RSy&%4#GKk)f z@}gdSk%9$5%AlC5APAH~+O%n!wrSF4Y|}Prk~8eRp7)P)PMSG;So@rvlk@$2)`xPk z*ILis=Xaj9p0(Cqi!cS*@K9xbNaU`QkolU>_P=)Rqjh6lTk&{hu<&qB1xOSzBV@2> zee3#1>OtUf>!{kGS65XV6WMb1!=ITi1(pgH`7o*50PlVC#z))6%X9iepDU(-EDUIR z>#J8k(lb#V=>VnLfMjy&&pxwAGx;unJ`Nyz(4QKK@uS1$w4XNI{6H8CI^*HbehA>} z0GtCLcUVy!U~JN)c<8m89^D22XWv(uV`OE2N)UaVfinOU0tf+khY3$h5&ij=YyaON zATCacYnku~0Lw=)wVuF_vx!IExarsXVzys1O~}yo#P|Y$6^RQOT&7$;5Gm`7RomO`Ze$r+r3NT=X46w9O zsm*#u=9g<-Lj~X?=33V2T7mnt+T*h=7wH7N_m^nYK4J1fqb|zT$A$kTI_)$Z~%(5lk_x_fY`= zngAjOq+#02cY=VEOp^g;Zf24pCA5JKqs_#o1Q7Ffmh&aX!EDw=A{IbN!*paTm}x`R zhO~4(X?at_VbC$_{mY@4hKywu!d5UfM7I_pEmjAc(y6&dH8x0~+EHddGc|;2L_3}K zQ4>u+31O>`8dIFjJ}Sk$wv3wgCJGYu0-dO;lD0EW0;YseqmZ2eb~fcB4Kvn|6FeP9 z1rQqo10YT5u#ZX|4l+#&rbHjL?i-uBz--v=^GS{kzB7X~*z2PXA5687MBJke1aNi} zWM^uhw0BGip@P87d@$kRtR~Yi2Z~@4mh%mbrEJuh2&PQaUcN+<+?tBVn+&Ez(+>d5 zv<=|Jw(`USDG?Wt-C)Ybcz=v|z{UnU3Z{s-#(u8J!PF6|DQk#uiLE>XW2hQnYZcVc z?t@hb@upu&KuS1RO7*VY43mYaBUE6UT{k7;O+VQg?8k|q%Wg207?@yYNF&}awt)u9 z2z9b!!gxnxdtLHG^i4J*eqd0)6!!AP9O$6=eql?gEj4#Om5KT%JM~r1>;iL(ohX`( z_>iO!HY4zOr4AzZgjbcO-N+w*PRWB!5>v*b0$`SS3BYG9XE5VS{D{DIhSB*%vXu`2 zkpNgi8WO_J%stD^_G+43OeLWLl3!%9F4Td6cL#mcA;VR6h}XsT7N(da^%w0`G`W~k zLN(ZUNrNU~vCG3|ESM4k_QshK*Rsug7-X4gYi#R{y1O?WwpY>QV#@SU0ifaT&7G3@ zua*bU<6>bVdjs$B0o24YKRXE&>kpf8Z*D0M@Vit$Td$c?9u)uxc$AqpSh-f3GCt35 zx9N|^Qx2Mb5^+atT5kFy67sjUDw%{#DUS*O+0p0|EYZU<7U+zd27)OI*A7aEy}c3> ztc`rJz$>OyHrQew`a{Fro6~lVKf#m}YR&(7u@{n`m~gmf(C`qE-QlT0!&G+j#QVci zS{t5{Z2sR=KhtC|<(hr~kb-_fQht8CYuGg7&XmA3Xl&F1@m{L4sGs6hSzGE?zjuGZ z3-&6QoJ={73IKcWeeNxmcyYV{MVco(oRgJIv54J4zW6IIY$cci--C@C%?WQf4VaQb z1pp@b-3gn1oLF^lyU7QfHT{WoD9o{iDPv#ToReSpn^ZizR4^r*{u$?;I<335{Y$xv zX0V0yy#G86$7FwKV?^$-A3G<2V zzi;1P(9?7lfHR1A35az_YGF`y&z8Nfq}pCpOpb5IA*cTA(p)2)cNG|40_aQt)U&$- zUGE-*mY+SaW1e1+h2Zq;@eA_^W{$rFV8Qb;p|`#bxh2yv{p5w{-rkCyJ)MS(8e_GL zf9yW0eGCl^livHLrBK)Z_>(J`<<(5|MFM6G{u>(-Et|I?+8qMrWI<#HV;i8Q5@wh^ z{g5GRu~AORbcEZxV{29?ESf*pv!(HuZcR)z>C;E8suJsF)O{EbH!}Df5RJOSDCq*l z=dVV`Uw0$a-VNCo0_m|nB?Wbm?r0lTcMw4<%!SCwg6s=HhNI(N-rsW6a?!%tSpmK$ zErfdV{hvHfqW*heI>ky8s$~Z-?}F9nJ8%SDZ|z64Cj>JZKWT>u6wp9=EJ29ap%Gs6 zVdLYh(l7xjPpm;rjNQ6~0>UggcWw08Ta7f)aYKG*QaAl4-~Y+;Ov!f>;WWpEFr&Y% z8^`x{qPOV?*cg4HhX}ESsY1ji0Yp3zop^{ug9hv|AhyIxD#%#k#9+Q0dygs72sy>m zk+*mz%!mQAFM{5tBQTDKEZr7qgt`Azj@$*)0bm<3-Sy6wfBVE_73`!G>PZiL;vAB6 z55T9ZESC)v{cSx6wI4&My&I9PK1buAP9P!#XXYZis1Vsj(~whKFrpjM^4vCr5BE-3 zmY$!DIqzGJ>^UR$^?d`N+u!~2Z~vms^5ih7gj(@XWw9O!+zaAQAqlp#DG@!^kI1nQ zBFFj=?e2%{kHF|RAp0Yhf|Qge_38y#C@hF|p_rj4`u2Ara-^X%9&djs^ zjS`Ll{O$62^1+QC_y58c-wXAmA6$7B1Re%xeX4CqQ{EQD*S+(V-@Tb?L&n3mLaltf zQmbp$ZX@Emkj7hn`ve0_-z{Hg-sM;C>Pw-XbpO9BK-BmR2>+C7Tc#2WR13&Yy?g7% zgQ>PDjurL7k%_Q5I&S@TQUscrJ%u;TW;TYEY*gjT&U~r ztDMi0o@HQts%^GJKjZ~S92D^2Rs_BG$DJU=7SNly^OPp-Z9 zV@VyMNt#DpeeV_TC&`64}l{a|@X?Kd{PoQ%qnBGfhC``BqLQH_j9(~VvM@qumM z@P0JFbD^&K?&b4@pf`{if=X9fHNj`peEs*Hu5I;HsAu0-nbRFIs{x$hWjz^E3juFu zMWXNDQuR!qS9N$7OfjfCWIlq7NYfq$P6-79zXE&b_&h7P;=3RH4uihwRrwiw$(=jC z`THs_3id>(tM2;9M~LY6UX`E0w=h9|xb|C5E1wWhE~GWzy{t&GRs&$J>asKV9|Afh z(Yl@A{Jqj6+0|}9!65k^@d&^fkxo$fV!MIT69s)`|>dNnY^j2d2fyy#7Oil>6 zdB?v$^?<_i)Uf(LFRfsRw*bmak7fXXu$1zQT~&XmQBjx@p;lB?2Bj9+0>&B@zf6X^id^6Zh_2*G{Z0$C-t^<+zh~VchI#*U0!wh0v+Z~03$X! znoOKqekQ_31iL$$(B9*}^KZ{ywhq~P5cM7VaqwtIs?D)OkJQao_1}8d)hpPpJ!$~P zeLTrU3IMmB_h~Gf9b1mj6Yj(Aj{RupXu_V(X4JOs!Oph5=!;m}NzV(edEezYYw0=w z!0~V&_II^nPiGTqTlQdgXA^d{HKMC8gxZv;Fe`7`g!VOb#%*h9 zi2b{x8DS&!r~EMSxx1>Kal9nZu}~|jDuWWCcbI9F(=zUpJj^4j|xCe?(y&#rb-=9r>3HQfXS;ynf4n zI}5BFH~oegx&ee$whjsPDdj~=9Oumr1W{2^ii(m_)VDX{Z@b@CXIPx_xl0`94QM)6 z7B9uh;-%>9>B8piDLW`-b#HO&bpRZVtvXsgwWMl8Hh`}pahS&>Pi1o#J9!Kv*wxnP zjeSLG?5l6zpVH^dj5`+GQt9YS0Y^g3Hu7!+xFqEYQf(U8>=t74`oEh#il@~9y`xr)W z==c#FK7P!T=TKJU-ab>B*xPv^#X1E5+c=ESXh;EIsJjzFlqJ*yrfGX8E-en_ktHP(wle8ZggQ-5lsbN4B077pSu`h=G96 zkz=zQRe@A}d!skazR&G*n2HM`{3Sc~+Yu@;=z2SaTOmsiBB<%gmT?ggvh*MVnzdd{ zS@9Aj_3mnGR8r1x{BEC97Y(1D_+@R_<6JC!QQF16I8SrRI&pjicC9?J`xRSlhaWuJ<~lD`0v^@ynRqAEl!S0bS@xI|O$$0$Kp33=i0#B9H=roh^Gkxi3`_e_g9L_9-Kg0b@a9!&@PXZMJDe0QD9PL1NUvJ8v z`?yKs;)<#bQa`XFNi8MQ=b$jh z{dTguTD`GPF}b1R*_Qx#Y^)2$Z~B?9RN+ni27$0@-ZCX+#Rk%$Je3!#v9Hb}`;@mJ z#N#&qV+wW2S2j#%;CyDlX%Zwrq&d#-Whz%B)Vh{^p4g{s>a?{n<N4S-mrFNEf^#X2WguIL@4x|Y43++*q7MbIrSQyh;(|9KKIW7&eE zC+`K`8vch-pN1ysd^h)}Yx=0o&9^VV0q3oGkBgGkpkg5k0DSiSAHgTjzF0|Fg9++t zMoKPP_kNtS`b;Hd4d?lqSMbfpf8;^QbUv&KmBHt`sY(}#MJGea%gV*{+yXa6`-;4* z93acpFtexR1hbJ7^kx^({OlZf()ndRAHdziKOWIXE&tkk3&1$bX*WY70gUx_XomE` zt8Oar-lE$s7!g2>5Nb4>{Q(97PU{gu8^?wW>4JwLRWK)FzJGXqBbt6@{IeTb>G3y6 zN58v_T+<4VzE2{Y1K|0=pN$-zB6i&S)`S2Q%%5kN;xI;NaHvo#D>X=3@6>0DiB?Eu zNH4r=|4$h{o;sxIUsiNvwPbcY60Qj`?gHJ6R?`TEmy5CU7ZrDvHvu~m9}MxRCYh%@ zEt!Zw0Axq9bib)ubu3)L_wtF{i6&ShOs{Jj{%QLkn5P8iH(S?Im?Vg zO)otDKBdIE#J>lGDuMM*Ppyu5(~JtI9^QWEgi(YF##*P<5-2l`KgD2J2ua+tHt`P~ z07|aeFdNK{SM(FCHT|#Fz3tgMMia4SQ5n`Oa=*Rn|JJ^NrbDd?EE>v+d5dvY*?QOI zywOmDT?hBMH)sNC8tanLh|XSg{rhG#J^acM9RLK3Qimt`07#6XPHgq2*I&S<*DY;_ zshl5Q{qIVI`X7(|DE7jfV(z@^W+g)X!PCFM>-F!rF2^@Su?)Z#9RM_lQa5fqHZ-Ng zEYBz}alav2UpR!Oj#f|Zb>;k0CFSfou-EtBg)K;wA|X`CSmyTb*vC&NCzy=|v*)=k zXIIN!m~1MrXe8zH-M=2)dZZIwy}mqQ+A#!_0t3O6TmY|!rc%g?;>Aj)cy}JyirucAklMlqv95K)hM?fK>|=HTwtmC@IHRFwX_z z9+jmma_(?ADdQAwC@Wc_q@20~dpx<u62TO+?bCh-V1GCLbEnP1jQnY?%h~Bsv#)D9mKQH_UC!>7{fK&W)>Vqh1pt96 z2mr3Om-I8`%KCra1S-qtEk;1W7Cv?97)Zs)1pvKh&jmTqdYy;vK3^1FrmBD1?C<_* zU32;kO~uid-*i!yuG7(MX6HM{Ny)w*>@SD6A^t_sTv$VD$9o2nuNF zd3SMC%=j3lfreIZj=)*9aJiCl>O30b^#k3}eyQtbmW*=PMKS&jrZk~pnSwy2q1C%U zMa5wM(cW&f9&T4)jXw#9ep7Tu`=y&g{M_s>D_*2zuhF_@ZvvIV+aZw}>MtsvrOX4I!~#E-a8V~bKGjxn&%X-QJb z_?GDYK~9#oOqlYzrRx$Dw1S&h5yzy4q!7MB(ay-Z~Kwa}5n9|h@xbLB;xf6&B zwY}`kMlkx^Y`iK>)9a00Ehkk==XN$Vcw%4q{AEg7pMf=~>6?06Hg0JKaAHtu-j!!; z1)bQB(k?`V+^q4>-MsTNx8VF!MjXnfoZPG&K%w)u$vh`NCr?Qi#|hIFY?xtzQI79n`Ig(0EX;|T{|sq_JFQqc8HME=iP{E) zDk1*i)^sVo*_SE9)I@>?G&#`p7jz%qdc0sp7%tvOHB5=%c&}e&ylUFOfY+m9;ryN> zHHrTj@Td)&8^Qo;M$KXoW`i|A;H^mO#&FXSo&qR;|ZoTCb%X;`rzHT6a#-A zT+`5#`y^g+TZdtov1M@?jixu=X%t{U051)$ZirBG^7CE=;CgYRkqN3YxT80(=g5%Y zeuz*TAFU1n^f%XDHELu$>Qulxk6Jyre+rE9LLW)_lw12HOPCq2n!zDV*fjjJ5kh4l zp9FXemuNj1W^|O#Go%-uxKo2*{$Y4cBZS(vacd_Suefgh4KwzNN=B>ciw8Po#>;IR zw{{M%YV;*MgpDfdjTq64M$;HC{3*asj8@mELe(kw7#JbtUNuAd;7O~G{;W{cx=;^n ze60&SvP0{HNP6yqoWTPK*x2;PZAV7?+t{mjHTeq_KM_-!g957z>4SQAN|3)8tF|$h zkhDLs_2r_=PiX+d^~*hxXcU^z@LKH_^oIJ~7B?zumzHDo5(S6yzWX{3bhat5XecWd zEX5fs*SRj|&AN9{e_)>ii`=nw_jaYehd}kMo8JE0*nb~~;*Xs2uS|GAg(148AuHhB zQ2{^ux4UuPsqb@L&gs`(isp_M1r`nEo@;K$=Ps;tUCw29d;xFNy{o_?f5G7EJ2q|o z=~#as_r)Dy(jx#4sW8MaO+-ChSyjHs{p-ia`}=U9Q~eR=0I+JYdvDLo*wvht`%HBN z_3U4ccfq(qZFy!(A2C0yYQ=UW5{@mdR=g5CFFOYdW*57DT08dFgQZk%9|2gY;D>ae zvlYjVcPqCq)rpxOJh1V#ar=Z5z6!}?J_w-K^;3=$ii9Cq?JK_J3rp4PGqvBF*Q4jp zF2=OH0@vkuWnXHco&W`YGSO8N3blPxO$UJcRRwn}8KH0(%&eq5Fi^fw(d*HT-n<^Y zO2K9m0I;KRwE-e;_+AFORM{t)(H{=OWT~V)75!5H*wI+; z$@j5pNtqh^_U}%$u9PMKM?%Cuv~us3gnIO;caAdfeJj^mMY2RF5>~RxN=5&VnPWTS zdBEz$YCLMXHT?|!ucJ@BbJWVUmQO+pNO8X*%!)MlcY>via_R+q;_g!Z6ckjW3b>@q%?%(zkWKH`rOygXD z#@}elA*PftBViB`G$9~_20;Xhr`WkxvF(GTG*PO+TLEz)cz}v&T>#kWk$owkpTIZR z&18E^xsIo{>;dDOcJtXKwnu=8NHl^_B#i!W2z{Y`^oK(TMZ$KZ1 zBL$>SAjP2D+n=e~XE&dty$?H1d-q2S{!6N7G{CGrQ1PLC0I*|UeafG;g7k9dsaqd( za{taY>8ye@4gL%PM;zr>L_iNHY5MD%_aSO{vkqakf-%&t?xW~9qGZWM>L47`r1(Po z06yC|-c7b^bIk!}+?>iE=!ykP5m0a_?~cZLPwbmfFdaoRXS=qqcHb@^I74?yGTqqv z+*Ze1@VnafxZ{uS{tv(t&eM9u>ZOWid>Xy!|5f$; zIC>HQ_lrSoqHM;eu^vcF>M2B7py!c&NrCWsXO8)AZi{ihk^Gh|QB&d*V6-QDJZ_bu zp`Tcw;%ECN?HFpC1YLoRwXR>BQ*z!;*XEi740#F2-6_>}QPK1t={bt_E^h)A6@io~ zC3bnV2Ek+c1mmNJpMBTW8D`&%>s6LH7RmyZecsIRFD+S! zTm>hycx9iL5OC9>=W9l6%i{*+N9`SYuIA^=e5=<#*;PwbPEC4X-x5`UlsEQymYBcM zvAO19B{x3^PtJrq^!&CTGUHY+pNFcc$sK$B9Z1!BWS=JqxV`iFZTBd-@nN`s(P$iI zU%2)b0QY%O_QIkPtY1-q)k{}k)shvcSXzcf#q%M^`fpFN#L~;pMxO_J4HVCsgOkhF z4A{16$qFo+zu5MPeP>*K3HG%#DYs8COiZ_QzEJaHuPX3Vr~okggKMq>=s^IQ*L4N; z0G7>Pgmo)cqij(rRxU2Z`mzeloiY0C(B6YhIP>aDyf$lqfUcu-{^9}K)~#5L@R{$vu;yw89suBeR6Ij6=p&{ZJ73!N zXeugCnNR_s=)ASdOp?Du3RYfau*E;1X`emxe9f*@8IaVbe>m-b+P3#lbk09Ba65p^ z*3|wZ3Zgp?O<(=qREiWxu}2-uobxNzOU6$DWCATeK=`{Ra6`unwZ7Pl)i*+oL$l7S z_%veM2l-+cYYK9hiSFup@Ae0rzTe~peiJGHVA{oNXXs)1p8&1};Qpnk3|5GM_@`zx z`0bn(bI(~*YLdJejH?0UrP`iJN*JKWnQ%|n zpKEud+8C=$DxoGQICtf&AjGv`+z6l~)%Hvh4uJ8KXpnx^{X%V5s*SPBq!j7^RS>gY zS#=I1J_F!m0CH1plFtYOc!@#3>XLIep}N|;V`)5Qatn13g&(Y#83@Zy5aB8Y&PEc} zzxouDfmey}m=Vz*JNC-xM>bA2(nhF*DL8lKte~We8Mp+%MF8?sZIj1@0K5Xmvl_(j zI;(5dA3d02(o(2{DZ1dKyhzyiCjs8Tzy=Ub#pL(427oODR5L_%c0u-EV>^PUFKI8- z;mo{fc_D`a?}y~~5aASnP6kkvYP%+&9l+bnycL4}u0`Zu552mpC)GwJlPM?E(U^5k z*?cL*$pXAiGL;dq9Kdo=(x-9T!Po=BZUXO?B;L_ax4uYNlU_Hxkevzvtq7Nb9|hWO(aLIM9ZY1RFh+3C=AI> zDkNefLOGXEvfujs{r%(pemx$q_v`UGp0CII^>{tg&<^LIBJv^t004@zv2x}~;@=}A zz`HxmF81^EhA6AE|He>Oii2~!kS2IeWQuf$_Cr2aAvwseAgmT;2Mur(LJFZvl{!}WrC2M zK$T=4;sRaw>5{Vmw*CrGS0GE<{chX3!mFs_y}GX zo^s!+w|E3)hBOK4E1NW5@xK2>gLQ>EZ~>>|oPA!NI2sONNw7fJy1dbi_E07-x9xsR zc|qSH-izFNAQnB*91|dnZ$>YW=+eQCxC1;-!up~+(x2x%eX|wt?>j!xrAg^>#8J8m z{uJK7_nA~z1T%;_;K>Wn%*!U4e;@SSsX>>&j?ctLn-fVsf5ju0fCzU-`hTdb9&cRAsHk!3o;{PvGU+wBN4kY(~1~|2+kptYh6C4-nf%Q zj})8?S2m89z-jg(~sfq3%2=`#-(qM>^N_kfjsE@fO?BGy7&Y5pnz)6ZriW4G_* znSeivE&6h%t*8TC7UbN)-jy9H0R}3)wm8)i6a(_GE&5KFFkA11{U}S8) z;*tD`MNho0SWigo;q`iifA_jos9V&Jvn^6BRq3s4L3XCv>W}A9JHE(0Q>sQ?!(iCK zb#Io#OcLkUO`n20Qznm(hp6Ver~Y0TpWNR9Vb&*fSxcthk_!T)e!RwE`d=mplH+{A zRx|r*t@BY9mf)ad^g^J?_77FBs`l5Pj5P+iD~E&#lZ>|rYhpx1j(hfTCZ2}N{^{8e zaO?5y@iJFM9M7alsIj}kyt)cYn>S~<%!Er`!dGN6gmKD0;Zq?^N_b=Zk(!KayD&M; zeDXKjduuS3T_42o4im;SqRqRsra*AhvVC4joXd)+Jzc8M^~DSK7^%{@udTNueDVhc zu<_J^JjAJ54`l(bRL)tct`Mvsbs$8ggOCMKfBL3Va62)f%1NDuTRU2Xp$nmMJ+5?Z>y1E_lc+IP`;R`<_a7w~62d3He{Dh+0Ps(Ch#7M zN&nm=ygQh)Teo&qK;MvU8G?dE!-dBIBl&LmK*BK^K;;3m7}2wiyxb8L%_c0Tg5!;Y zUdrt}Krf^u(_2a=qAda}ITrzwaXIXavBS5M4YJwJ?`u7--k9wA4kZZ7%F$)ql4;)h z8tK|0Hu>J=&X+$UCUMelix05CtZTts8|^x&d-5&xaNRB$M&GNCKWYV@+V zT2b+Bx{Z^qw~HdPvc(@Bk#6zmUo?{cIouAH$Z$9@^AC-<>JLEeaT7?xU`Sn-j8pS( zxv83&AT_dV+L&fBdyb96bbwVr!&*TnVbPecYkO&QdUp@esE# zJl!qNv<-5$&Ku%}!Y~Q=G@UtODsdju55x2yLw#Foz~l`3U;4zm z%KrWuPq}SAS)XN?47}QSwL9Dq^{E!U5H>YIIAmR8#6wR2?cW@E+C$-ZjmP;Zvo`UU zK!zJUkT<(+-Zh~Yjrk-TshF&&d9@1ts!&6{t{(25q-A=mt+S)Bj_o!s29-KAg(6YcI^yC|^Wc!>8&aVWdMh#;u-;25rW$oXA)K)ryrjY}O}fPE zeN`Kd35RjbY-`h9n0tqzWwfj5ri$?+-3ckJNfv=Id6M0VRJ%Nv>w*4HYYol%_E89kwM!>I>-`^?xV2gPxttM773SXE&~wwsM+#LHo>9}mwf1n2kNDjc2~KRcvIvHiF` zvM%98NzhC-s3T1WE$d+~&Iih}zuZ{;Cp>RKnkM zBn!I>{@8Xot`5y*50^Fak(VqEUF&UZEsL$HPz1*qud+Sij(q#j0ekt0qMM8Cne(+1 zPUF9&7XBNSUm5=b59Kmr8uV2eOnoS%M z44Y)MpX|x%U8wrVpDKm~h?|FA?`NVGz6R37xpQ4JWKf0STBXkBIg_ zt!YJEM!6rfxckzlt@5c~Z7BClowl`NgqZFS_|-TgTkYz;is%Q-$TAjS5{-8;C^Ge< zpablclOha>J=v@O^-FYyP{5B1KC!v#dCw)E_1IAdLSu!8Ptm@DSaO@TYHQ$Ee)W_z z2sBZIkwZ(UaU5&9QN1jH4I5{Dv91anA1R%W+$m-A9-v+CpnsUtkiyi4$?;%9+s-{wfBf zz2qE79q^x;w{_9hbuv~UMI;-fc**+Oy2~Pf*m{J#?3Pa7hcc_Nxto7F+IB@*!G3CEWl)3W z%oh#(?aF3bBCUx(-QeA`mv;MFACI2UA#wUCYjaO1KxSgg&HI-idS840ow}rlIucL) z_Z{Fw9jAj}o&1POy*L}0DjLkzY6es=Kaws(Zn{fP`?hR9r>|(9hb@l5q?DK3y zmhIch$d;t?NCof{jXvl{*}}%A_#dCdU0(-#nqrQ7X6OjjOEFkp$(~(p0EEitqwiK0 v8w7HapBU@}Fq;wdRPg_%COp2PoyG;6yh5;%BxdLESQG$-bg-(jz$X14==^F> literal 0 HcmV?d00001 diff --git a/assets/images/hashbag_sym_dollar.png b/assets/images/hashbag_sym_dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..70ff3d00df0c567721ea2cf3cb38a3547c16c86c GIT binary patch literal 1893 zcmZ{ldpy(s9>+i5*)VdMNMj`-LVinyLWEg_T<2bM9<3=wXg`<2unJoj$E|V={WNp? zNz5(#O|9Q?i<9fmFdU8}hF=UxJL_@&JCAezc)#C&ydRI(KkwK3^-Onn!z;*X$N>Np zoX$IVN+$J3$x2Il=h={M0A!4v9L{;i7A#N1EgiJSd{~P)N*T7Ol%@=#eGlNUGZ~J` z_>@Yc8kKj?(saJ744hAs9XNFOo!>aX#4gr&v~ml!@POz2WY>V1K-| zYrW?p%ppL9nwte03g9Jky5N{T6nYwI+wBAb++h7tDpavKFR(qcsik%$SG1he7uc?F z3xh?Ds{6g^{v{U_OUY9oCpRTGQMbF;FHz2}m9;QI^(D2ZcI=f7a_>ZfOb=8fUF6M6 z@QO})vp@&2EMJ%#qDIQh)t&1ppa@Wcb0j_)d40iI1g#Px5PbQiyp~+aiX>>jq+m@c zkWVb@F=u0C0g4v=NXtvz)3MPPnp}KM{SZ3br?r0HRQS(9Tz2LhiW6v!X4B`t z)2xeIsP*-huvk_MBz)0qsU>J0L}cgqfVLqWr2bu2MGZ-|L#0Nx{)rEuMkfb*sUK&Y z?qcgP=>|smK-E01#wi$&9K(FqE(1UxXudQlrQL7?+O;fdmQKU3v{i)!xh>_b?A82T z`RVegQ$$yyah-PEsXB#eGFgeZj$QuAlh;dkCx5(x0udVq0zpQvm103HW9NEWhrgX{ z=g9jGv1NgJS&F3^1><>l*zf92)!W@CPJoKMZS!2_R+Fi4$q=EcleD5<9GKyL&|&M^ z8EL<~oUkt z>i#o|$RjVP1~yzb7;XwLexAlPc$y_@Dx(K?|bmQc4Ah2*4Uj9-LLZV-yS_8g_#nLx36cAB4| z81SWiNc|3LB)$Kt==nGA-lVo6BdIBspZ{Xq0llW8IrJ`ea@tL@4jo905Fu^CWNza) z_8b>dklDpA@1A&pMlkOoo+WBQpOSa7BW;HGzENXvzzIyy!&c)bb{(Ha-^w=?wjbOW zi*7~`bwi@&`>ciWW>{DBX4K=I>R*(!IlhLjU9mA#FuIqcAEk4etF_qc_G?L$-)D@ znaI^>$?Y!W%z5$>kQmt@d1E(ko+5`exmdP$Tu1gIVE) z{+N-kM?h-ZP^(nl-eH-=4bDe^^+%htKJ5Nny#mAj9%!w|R<@8DEqM?uR$R*J?k#(( zz&RALVaV~I9l}Ug@l(U65Tc%tzlKi9lw>u<8)MNH$kEn$p@}bV!Y^7Y=ZzsKY#W+I z>NC7vnXL%K-vA@xtLO1*1zY`y4LH#-1Xh_bx{l~#kLxt57s5+Yo8l?lll82$=m%@A zB>pATAIyhv;_0@bvgCZK^Im6UnDiqL-mZIlW&D){{W_B_c5zOmSMn#>5z&MO^wh#< z$R37;llXfoZSFTF&^)i}K>H=%M<9mZJJQJTc0Q&H=3Hv)ZdQ`_IcI`wFVaLYDKGPy zD$@4!l+UOrFVtty7UteJDmQK>Qc*UQr#8i3kF@C+5lOtcIZ*lPoY{{Mkmn=s&Q}*r zSD!7x@76#C{x8y-Go6_@w-Y3p7_NapRiGNNVJCpNVF9H!S-w3^1&WN?{+_VRE+%eY zQwsgS8lvRE*6VvT_48Kpex#rroSwuK2)=FXd|jY?cI3DZvn;$B11XXh3YDe<vWe6b5I0Le-}Ts+o~aPqXQfZqCEFGO$J344d|f$c;#aPS%e|PiMk;k zY3Zj1J%_>c`+X0Gx3V}hRpPWk%I)b8693yEZcoJ;bcpf$TvWM6!+!Q*3X1d(WvXEw zks0)o5%{zv%~T^nO;A0VXYpBTqU$h7%@}!i5$2}P*YqmHEHBM($>)- zn|kIV*i`y5!>qcZNhQUxMs(YT!amHLI5>(KWeAVNS=n#>@=>o%;oGJ~j@gANIMLue zr=OFHru&8r%N?sUP54P!hx5;^d;%w&n?sEq GG3DR7b!T$` literal 0 HcmV?d00001 diff --git a/assets/images/hashbag_sym_hash.png b/assets/images/hashbag_sym_hash.png new file mode 100644 index 0000000000000000000000000000000000000000..1591fed09211afe608924b506cfc678e089e9b5d GIT binary patch literal 1062 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV1DcA;uumf=k49SeRo|Xj(t>D zWL0odTJj-qB8SU_11aVOq9Ip`d8b^6@DlfJbUCTZqbgc{@VMG!m!`C47LiPafCKM@ zRF5v}nzH$`i`|)?=l@s7@85Lod{6D0jp0l{YcPPVWoR|~Hq~ceb}upKI{p4kaK-zU zI&qb|#$^v*S8^R(vHnrXTiF|JGcIRq+5edICD!q5`?I`13D?3mo?J$`&j(xF z2oWf*^1O80?R=iw{wi3%D7oYcU*~d`8m?34! zz&%P_Lyg6OTHUH|pPW82EYzH~97E#r>J z4QvN$H~*+!yv;I}-QhU5!+Ghiw7Cx4;`(`q;@xULUz2ID@qhSm zS<~@&@%JsOHZJ|h(F|8T3~q<=YhV!YK522 zTdV(>_& + HashImageWidget(imageUrl: image); + @override bool get gradientBackground => true; @@ -56,7 +62,7 @@ abstract class InfoPage extends BasePage { ), child: AspectRatio( aspectRatio: 0.9, - child: HashImageWidget(imageUrl: image), + child: buildIllustration(context, image), ), ), ), diff --git a/lib/src/screens/send/transaction_success_info_page.dart b/lib/src/screens/send/transaction_success_info_page.dart index e057b7f01..f3f0123e0 100644 --- a/lib/src/screens/send/transaction_success_info_page.dart +++ b/lib/src/screens/send/transaction_success_info_page.dart @@ -1,5 +1,6 @@ import 'package:hash_wallet/generated/i18n.dart'; import 'package:hash_wallet/src/screens/Info_page.dart'; +import 'package:hash_wallet/src/widgets/hash_bag_success_animation.dart'; import 'package:flutter/cupertino.dart'; class TransactionSuccessPage extends InfoPage { @@ -11,6 +12,10 @@ class TransactionSuccessPage extends InfoPage { final String content; + @override + Widget buildIllustration(BuildContext context, String image) => + const HashBagSuccessAnimation(); + @override bool get onWillPop => false; diff --git a/lib/src/widgets/hash_bag_success_animation.dart b/lib/src/widgets/hash_bag_success_animation.dart new file mode 100644 index 000000000..f7f8d08bd --- /dev/null +++ b/lib/src/widgets/hash_bag_success_animation.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; + +/// "Transaction sent" celebration: the Hash Bags money sack sits at the bottom +/// and money glyphs (#, $, ₿) erupt from its mouth, rising and fading on a loop. +/// +/// The glyphs are image assets (not text) because the app doesn't bundle a font +/// containing a bitcoin glyph — rendering them as PNGs keeps them consistent +/// across platforms. +class HashBagSuccessAnimation extends StatefulWidget { + const HashBagSuccessAnimation({super.key}); + + @override + State createState() => _HashBagSuccessAnimationState(); +} + +class _Particle { + final String asset; + final double dx; // horizontal fan, fraction of width + final double phase; // stagger, 0..1 + final double size; // fraction of width + const _Particle(this.asset, this.dx, this.phase, this.size); +} + +class _HashBagSuccessAnimationState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _controller; + + static const String _hash = 'assets/images/hashbag_sym_hash.png'; + static const String _dollar = 'assets/images/hashbag_sym_dollar.png'; + static const String _btc = 'assets/images/hashbag_sym_btc.png'; + + static const List<_Particle> _particles = <_Particle>[ + _Particle(_btc, 0.00, 0.00, 0.20), + _Particle(_hash, -0.18, 0.13, 0.16), + _Particle(_dollar, 0.19, 0.24, 0.16), + _Particle(_hash, 0.11, 0.40, 0.12), + _Particle(_dollar, -0.13, 0.54, 0.11), + _Particle(_btc, 0.06, 0.68, 0.12), + _Particle(_hash, -0.07, 0.83, 0.10), + ]; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 2800), + )..repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final double w = constraints.maxWidth; + final double h = constraints.maxHeight; + // Sack anchored at the bottom (~62% of the height); mouth ≈ its top. + final double bagH = h * 0.62; + final double bagW = bagH * 0.825; // hash_bag_base.png aspect (w/h) + final double bagLeft = (w - bagW) / 2; + final double bagTop = h - bagH; + final double mouthX = w / 2; + final double mouthY = bagTop + bagH * 0.06; + + return AnimatedBuilder( + animation: _controller, + builder: (BuildContext context, Widget? _) { + final List children = [ + Positioned( + left: bagLeft, + top: bagTop, + width: bagW, + height: bagH, + child: Image.asset( + 'assets/images/hash_bag_base.png', + fit: BoxFit.contain, + ), + ), + ]; + + for (final _Particle p in _particles) { + final double t = (_controller.value + p.phase) % 1.0; + final double sz = w * p.size; + final double rise = h * 0.52 * t; + final double spread = w * p.dx * (0.35 + t); + final double x = mouthX + spread - sz / 2; + final double y = mouthY - rise - sz / 2; + final double opacity = (t < 0.15 + ? t / 0.15 + : (t > 0.78 ? (1.0 - t) / 0.22 : 1.0)) + .clamp(0.0, 1.0) + .toDouble(); + final double scale = 0.55 + 0.45 * (t < 0.3 ? t / 0.3 : 1.0); + + children.add( + Positioned( + left: x, + top: y, + width: sz, + height: sz, + child: Opacity( + opacity: opacity, + child: Transform.scale( + scale: scale, + child: Image.asset(p.asset, fit: BoxFit.contain), + ), + ), + ), + ); + } + + return Stack(clipBehavior: Clip.none, children: children); + }, + ); + }, + ); + } +}