From c64aedaeb3fed908722b8872b71e288ff87bc761 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 3 Jun 2022 14:21:40 -0700 Subject: [PATCH] feat: Aligned text in the TextBoxComponent (#1620) - Added option align in the TextBoxComponent which controls the alignment of text. - Added option for the TextBoxComponent to have a fixed size (before the only mode was for the textbox to automatically expand/shrink to fit the text). --- doc/flame/rendering/text.md | 24 ++++-- .../lib/stories/rendering/text_example.dart | 52 +++++++----- .../src/components/text_box_component.dart | 75 ++++++++++++----- .../_goldens/text_box_component_test_1.png | Bin 0 -> 36900 bytes .../components/text_box_component_test.dart | 78 +++++++++++++++++- packages/flame_test/lib/src/test_golden.dart | 33 ++++---- packages/flame_test/test/golden_test.dart | 7 ++ 7 files changed, 204 insertions(+), 65 deletions(-) create mode 100644 packages/flame/test/_goldens/text_box_component_test_1.png diff --git a/doc/flame/rendering/text.md b/doc/flame/rendering/text.md index bb67acacc..596d13e7f 100644 --- a/doc/flame/rendering/text.md +++ b/doc/flame/rendering/text.md @@ -98,7 +98,12 @@ class MyGame extends FlameGame { text inside a bounding box, creating line breaks according to the provided box size. You can decide if the box should grow as the text is written or if it should be static by the -`growingBox` variable in the `TextBoxConfig`. +`growingBox` variable in the `TextBoxConfig`. A static box could either have a fixed size (setting +the `size` property of the `TextBoxComponent`), or to automatically shrink to fit the text content. + +In addition, the `align` property allows you to control the the horizontal and vertical alignment +of the text content. For example, setting `align` to `Anchor.center` will center the text within +its bounding box both vertically and horizontally. If you want to change the margins of the box use the `margins` variable in the `TextBoxConfig`. @@ -106,17 +111,18 @@ Example usage: ```dart class MyTextBox extends TextBoxComponent { - MyTextBox(String text) : super(text: text, textRenderer: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); + MyTextBox(String text) + : super(text: text, textRenderer: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); + + final bgPaint = Paint()..color = Color(0xFFFF00FF); + final borderPaint = Paint()..color = Color(0xFF000000)..style = PaintingStyle.stroke; @override - void drawBackground(Canvas c) { + void render(Canvas canvas) { Rect rect = Rect.fromLTWH(0, 0, width, height); - c.drawRect(rect, Paint()..color = Color(0xFFFF00FF)); - c.drawRect( - rect.deflate(boxConfig.margin), - BasicPalette.black.Paint() - ..style = PaintingStyle.stroke, - ); + canvas.drawRect(rect, bgPaint); + canvas.drawRect(rect.deflate(boxConfig.margin), borderPaint); + super.render(canvas); } } ``` diff --git a/examples/lib/stories/rendering/text_example.dart b/examples/lib/stories/rendering/text_example.dart index 030170580..39ac706d5 100644 --- a/examples/lib/stories/rendering/text_example.dart +++ b/examples/lib/stories/rendering/text_example.dart @@ -10,32 +10,20 @@ class TextExample extends FlameGame { @override Future onLoad() async { - add( + addAll([ TextComponent(text: 'Hello, Flame', textRenderer: _regular) ..anchor = Anchor.topCenter ..x = size.x / 2 ..y = 32.0, - ); - - add( TextComponent(text: 'Text with shade', textRenderer: _shaded) ..anchor = Anchor.topRight ..position = size - Vector2.all(100), - ); - - add( TextComponent(text: 'center', textRenderer: _tiny) ..anchor = Anchor.center ..position.setFrom(size / 2), - ); - - add( TextComponent(text: 'bottomRight', textRenderer: _tiny) ..anchor = Anchor.bottomRight ..position.setFrom(size), - ); - - add( MyTextBox( '"This is our world now. The world of the electron and the switch; ' 'the beauty of the baud. We exist without nationality, skin color, ' @@ -45,7 +33,23 @@ class TextExample extends FlameGame { ) ..anchor = Anchor.bottomLeft ..y = size.y, - ); + MyTextBox( + 'Let A be a finitely generated torsion-free abelian group. Then ' + 'A is free.', + align: Anchor.center, + size: Vector2(300, 200), + timePerChar: 0, + margins: 10, + )..position = Vector2(10, 50), + MyTextBox( + 'Let A be a torsion abelian group. Then A is the direct sum of its ' + 'subgroups A(p) for all primes p such that A(p) ≠ 0.', + align: Anchor.bottomRight, + size: Vector2(300, 200), + timePerChar: 0, + margins: 10, + )..position = Vector2(10, 260), + ]); } } @@ -72,21 +76,29 @@ final _shaded = TextPaint( ); class MyTextBox extends TextBoxComponent { - MyTextBox(String text) - : super( + MyTextBox( + String text, { + Anchor? align, + Vector2? size, + double? timePerChar, + double? margins, + }) : super( text: text, textRenderer: _box, + align: align, + size: size, boxConfig: TextBoxConfig( maxWidth: 400, - timePerChar: 0.05, + timePerChar: timePerChar ?? 0.05, growingBox: true, - margins: const EdgeInsets.all(25), + margins: EdgeInsets.all(margins ?? 25), ), ); @override - void drawBackground(Canvas c) { + void render(Canvas canvas) { final rect = Rect.fromLTWH(0, 0, width, height); - c.drawRect(rect, Paint()..color = Colors.white10); + canvas.drawRect(rect, Paint()..color = Colors.white10); + super.render(canvas); } } diff --git a/packages/flame/lib/src/components/text_box_component.dart b/packages/flame/lib/src/components/text_box_component.dart index 0d079ab72..5bc44c931 100644 --- a/packages/flame/lib/src/components/text_box_component.dart +++ b/packages/flame/lib/src/components/text_box_component.dart @@ -68,26 +68,52 @@ class TextBoxComponent extends TextComponent { String? text, T? textRenderer, TextBoxConfig? boxConfig, + Anchor? align, double? pixelRatio, Vector2? position, + Vector2? size, Vector2? scale, double? angle, Anchor? anchor, Iterable? children, int? priority, }) : _boxConfig = boxConfig ?? TextBoxConfig(), + _fixedSize = size != null, + align = align ?? Anchor.topLeft, pixelRatio = pixelRatio ?? window.devicePixelRatio, super( text: text, textRenderer: textRenderer, position: position, scale: scale, + size: size, angle: angle, anchor: anchor, children: children, priority: priority, ); + /// Alignment of the text within its bounding box. + /// + /// This property combines both the horizontal and vertical alignment. For + /// example, setting this property to `Align.center` will make the text + /// centered inside its box. Similarly, `Align.bottomRight` will render the + /// text that's aligned to the right and to the bottom of the box. + /// + /// Custom alignment anchors are supported too. For example, if this property + /// is set to `Anchor(0.1, 0)`, then the text would be positioned such that + /// its every line will have 10% of whitespace on the left, and 90% on the + /// right. You can use an `AnchorEffect` to make the text gradually transition + /// between different alignment values. + Anchor align; + + /// If true, the size of the component will remain fixed. If false, the size + /// will expand or shrink to the fit the text. + /// + /// This property is set to true if the user has explicitly specified [size] + /// in the constructor. + final bool _fixedSize; + @override set text(String value) { if (text != value) { @@ -99,9 +125,8 @@ class TextBoxComponent extends TextComponent { @override @mustCallSuper - Future onLoad() async { - await super.onLoad(); - await redraw(); + Future onLoad() { + return redraw(); } @override @@ -117,12 +142,13 @@ class TextBoxComponent extends TextComponent { void updateBounds() { _lines.clear(); double? lineHeight; + final maxBoxWidth = _fixedSize ? width : _boxConfig.maxWidth; text.split(' ').forEach((word) { final possibleLine = _lines.isEmpty ? word : '${_lines.last} $word'; lineHeight ??= textRenderer.measureTextHeight(possibleLine); final textWidth = textRenderer.measureTextWidth(possibleLine); - if (textWidth <= _boxConfig.maxWidth - _boxConfig.margins.horizontal) { + if (textWidth <= maxBoxWidth - _boxConfig.margins.horizontal) { if (_lines.isNotEmpty) { _lines.last = possibleLine; } else { @@ -176,7 +202,9 @@ class TextBoxComponent extends TextComponent { } Vector2 _recomputeSize() { - if (_boxConfig.growingBox) { + if (_fixedSize) { + return size; + } else if (_boxConfig.growingBox) { var i = 0; var totalCharCount = 0; final _currentChar = currentChar; @@ -225,23 +253,32 @@ class TextBoxComponent extends TextComponent { /// Override this method to provide a custom background to the text box. void drawBackground(Canvas c) {} - void _fullRender(Canvas c) { - drawBackground(c); + void _fullRender(Canvas canvas) { + drawBackground(canvas); - final _currentLine = currentLine; + final nLines = currentLine + 1; + final boxWidth = size.x - boxConfig.margins.horizontal; + final boxHeight = size.y - boxConfig.margins.vertical; var charCount = 0; - var dy = _boxConfig.margins.top; - for (var line = 0; line < _currentLine; line++) { - charCount += _lines[line].length; - _drawLine(c, _lines[line], dy); - dy += _lineHeight; + for (var i = 0; i < nLines; i++) { + var line = _lines[i]; + if (i == nLines - 1) { + final nChars = math.min(currentChar - charCount, line.length); + line = line.substring(0, nChars); + } + textRenderer.render( + canvas, + line, + Vector2( + boxConfig.margins.left + + (boxWidth - textRenderer.measureTextWidth(line)) * align.x, + boxConfig.margins.top + + (boxHeight - nLines * _lineHeight) * align.y + + i * _lineHeight, + ), + ); + charCount += _lines[i].length; } - final max = math.min(currentChar - charCount, _lines[_currentLine].length); - _drawLine(c, _lines[_currentLine].substring(0, max), dy); - } - - void _drawLine(Canvas c, String line, double dy) { - textRenderer.render(c, line, Vector2(_boxConfig.margins.left, dy)); } Future redraw() async { diff --git a/packages/flame/test/_goldens/text_box_component_test_1.png b/packages/flame/test/_goldens/text_box_component_test_1.png new file mode 100644 index 0000000000000000000000000000000000000000..800c7f40abcb24c239527c1983ae8edd48406b28 GIT binary patch literal 36900 zcmeHw2Ut_tx^@s19Sfq4pkiAjQKh)9Wm5IRT;7$AfYAPEWo+B;b0-h1wI&wrkK?%Z?D!=uU0&f06Q^{sb(?fv$x zGlu$WzT5I00)beAI-&Oy02N&iPgR)gdy_^{OTN7Tv?KD~c%;$NnD)zLt#$W(%?eN;v- zdzK|j;%pTGw~2j}DZAB|UvX#frzlbGR_dK?WZys&(nL_EFJBxQhA#5)_~6HxLB$xa zOv)|Ih$sav@{&F&y5b&PEgL$a4L{-Xn71#a=LA<5jUTjlbsjpJo_A-Ej8dG|ezTxn zfuNbjPkyxM4S~MN@yM2E(^b#5u1h*O50F(t@T8;2~E2JI&!!?LN)EFD@*;y1)M*Vpu<(&T5^Y|*B%A6Soj5suoky5Eic7)5FR1@7XQ5~+ENH(xbiU)1FEAXC#R z;83^JAwTWw99(bwIa#e!w)Imy)%iB~TMpNkGt?;(f&?2E$boEFvADsrK^# zecbLCo@4dqV^7Acu7e@9qvlsH{B7vU#EFCWndbNEQCj6M%qB*Zh}Sw_t+$4n)hca` z4(gbybVQ-cZfq{=(b!o9&DcoqlLGuNte@gfIR09l&ZA9eM9=uHt3ooqhgyriCR2kc z3ZnbP2S4eak=k2qZRFvy45JIaLv%N!wx_qFI(r%vKPnMBYFM%utD)`CH>(@)%~jWn zI)z#ps%dWHgDlw#<@2+`;2Ss01&5hyK6%MAArE~}x!rYoVh1) zwNLMLX4?<5R={Pyv4@9DW5AgQ(EQq^J;P|K=CO=IHYkDW22?pL{(RWOR&OmimNsn6nkayW?e z@#%EOJxS1LU>`aL=J&X`Jm5VZ@(F||`(<+xW;!Is@CO)`Y#ykYyY$xMwioz9!=2+h zor@_5X%YDXN8Dfj0o;)b7R^bh9&iVL$5YPsrmSw7neizqF3z`5GDg!Arg7czX*9<-PQffx~JA_uj(a^O+ea}OJar;iBH13o)Ong%l zEw6$+w8BT;H)Wn3)Wej0c{#WYUiaO8S`EX)b~@a#{H_ZBe7ZZlj*%TzYjPry+VN@0 z0nh0BmXA|)7yS8?p}LoIO{cZr_T458NNTt}pzlNUJv2x=N4~f>Ie4bMf>FKIpUNTufA5f4)-nP`*w#3{@`w4urm33LspZ5@9|MT*_GsdFTgW#7ta*x~r!4VzQa~%_E5!4hvPx7ku$vm ztD7<}6i-!ebsbc~u*1bs6Ce0Yo8u{Wd?$T#fU?3bV9qgZEUd@jKhbK?v_Omby04mE zJM|+a8A~pNGyD=qy*|qLnU^dOCm3_h^#h`~7f^SWO}`#cncXtGI4Hcf_rqsP77)RV zIpJ~=YZ>wP6@SCbjII)CGl)!DYNKSX7;C1m3BLWi&)IxWu1D#Zd(_2v!?d`1yNN@4 zfx-sBEYCtw0BLR()&eX89cIqM&^+eNRU>K!OkYh7`1rJngusUS3{*z|bY6G^V71yd z)EM0hlqk=^H+O##;`>X_F@*ST|#ta)_IOq8MS%Urk_F|)!9RTma zU+qxSO6fxq5y!#J8Twvx2R*VVar=Q@!HndR*Zzk$!7;Yn+KCuD285~DrTcA=j_*lP zt>Zwtid}mr0#^%wb!Ic(5%NaBuYacGe{&vuQ>k?_)^dNO7m-1bA=+ zyfeFSffl&BjndlMs>L580^|vyIof9_A#Umk{7|cVJWA_=bjVI>wSHqZ3u=(E6{ljq zA5R7ldj_oXWHiS?sp*9xXsMRN39$0}@N1o2eido+-I~i$(eg%eD3{)zXf})~f zMrjBwJ&Eeu77UhQ`0GbN9db}hxT1r%FSP)wov^@iMJ#s(D&N#ltv@ZZct32Kxq%K& z)v1sNUyEDUg)3E!9K!|zHS@IGhS_axJDNvTl`1BGM9-G@`p=h=uByHTe`$DhJXv?4 zE4*#i6!a|{%}Sr2iY07pEN}}hWX`WGcLW#t*pgoAG4z`{xHq+gA#lm$PWC;4RV-2Q z`qCXE9k#4Vw$*sx-6NWQlQvfVLor&jU7kQiv*{f`7(E#E+I{1yJFPXdf;&;}&#YJX z1n;4nFDF|Q=3Xl;HQfg7QULT6ttv*?=`WqsHrO!se%i6-=E8jd1m(Ba)kq+Oy=M1u z5?reoOJ=mSFhZ+isY-qt1B+7!&0kyk*SDjEC#Om?>p} z4DB`o9Cy}CvI7Gg7+6j}3yT17${+xscXV~ty$%=m9PcQsqcTB<6JLgkQs3N>VSh}| zo?=m~XF-FepY7%{1G(s+=|=tbuCB4bjf)K-i9m;T=_#X)?M{5o)f{R>i8@SW5zhUD4 zX-rHP&B-e4LI*uL2g!u87j18`YlasTL_mxP>;aqmk%e7Vg-yGlV-K1?3TG6p$?JU_ zd=B!}VC8FbRk_CwiNS`F@^;XiT9kz=yC=?gE#yBV+7ym&u*BiEL;JnW8Q`+pQ6sXq zm{25WPU2Zn@S~#O9pFpE2;}~Ia<3C7f3W!N5$K3SqNl^mEuW^MY*<^+9tGY@uEdLl zlX+Dy%-|CuS-?Ads1zw!=^@QV*cdOo7YnTiyWfoB!Ph@JCnl~1Cle7YaU08%Qx|Jz zBcHT89m+3*ZPby*I92Wul6lp_#Y8luiV?Si^LEsiZz5b}_vFfA45oad2KFRCK1Qsu zX0qJ^HQND~T@}_0yRjuQP}vyn@v1;y+k|}ID#dPg*}cJh;NH~P26YhEnJc?+Pp(2U zTI7%r?{s{yEOb=j0a`dimc6~^lta&G+t3m9LFj0kuGPtW41A};Ep+{LJ>EL?cIKTV zF=!koFSDiV(T(gX5!hmNdATAd%1&kc=fxW>Q)(L9a7*B}7rwax4D#`)0TjP&c01Ja zbU)5vCRB}sgd0cJK;A66{Wa?{eS=uW#8E~>O@YJ=p`9wyuk|@ z0yd|{tM6weKJS4DtbURDVtmGYZ)`1qMX-+nKR^`;6$RWG{%5z1)L+Vdb18Qu@V0CX z*bl+32(;?ODeR@>#@Y;y9Bj*kArarRR*>xGW+--&PcZVI<3_1J;O@Y&m~UM1Jtl?Jh= zw%Z;2clO9-$kh_Jy1z7BL zBPFm~HtS(u_ke5BdT@LQQWl?6pH0haFK@_1ipze;iH_AC;;>APOaQk4~fyE z6SnWvdxKQ&i|2&)_t-2$-UG@&FUIfZETy*2>R?g9+<<>`O#U@3Z=i>bcxlzRMkS&= z#u_LA*&85vK2vz-GOdB~L{>_!w3Vg@jl$%co11NTIyI$Xnb60XXgZyhSjr?v$YKmV zF0uxogTw*l4BU3Dm_bZbB5n@C#XeL{vYpaMvufZ`=!6{7YI$P7p&G7(j?X&q86aXn zeC5Mp?&4}Q1W~A5)dWX-!2xVfl0m+SR|fqE^y9wgAQaRcku9kNO$*U7P5K42+HJ`9 zr#Zz72kIG9mRa(pUR1|DHPtSnFg#_M8f`F6DrK^wx$|Sz(TP^iEufYrS8xsq(Z;>- zLEhRmr%;jd2V)0A!eB(0$%{}8)S@#bHV#ynK|`$Tju^usoMLn==;gVm-2-&PL5f1ZBrI3+sq!q9f;gw=1` zRRi@7Idq~L4*X%4!^Fr)?)bRcLy!);g#+L%`g z^sPImA1@a$|MZ+OR;{$)TH`mVkA4FT$lg+eW6gji?ww_?riRdy?wLMXHJQn(t0*r;cZxn`FOg? z`V!wVXV~%r+|e6ZK)Xw1&f!cnVf+1@8r{6H!toSzPiNk>EI0L~YAM12qQ0&Q$79T_ zz0jB)K4P-fhKDS9s8042bj8imMFM9ZfR-pn;A0$SRI4Bl6UXkz5c<$ z$I}wgLvwZNtB;P+>7;UBdjIrv!L&xSEsw25&VDV42Y>7ga=002kLkFNWUoVmVaTm> zWlo7Z)CQMMz{M(ASHFFGa@1We{n>8J!z%26N-u-qUJ=0BZACykZUn6Zp`^EyTx`Rg9W#Kf3dS{6QE9t*6EW1U4hFC5m=M9=)j^4~{Ou|R&h zbp1(DL~ndJ|M0F>DXc@D4t$S@Sd;*}p^^RlD~IUcJIbJ)=F+e@mARkFQuCZ-qFjF!kGN<(vLI!^y-yJ<{Cb5S{X{e_*kY0PLbPJ`yL(xN_+=<&m~Q|QtH){ zRNvGi0(r4mwP(+s+_EywTWi!1shQ55HG1pv+Z8&Bl3wXQUh{IDh=_>0pC4FcVPU3L zR*~c5<9_pVxd_L|D*8Zuf_iR#{st0>^n`RPEPsXLcS7fXp@ep6ozukRw%u)@pAHd7 z(0wiOLZQCS`%R?whye*pN@@AqB~;SL$jH_GLajgh1+O69Jw5O;34$8kL5k zM0d#)I(MW@(JtSiYcMm*F#+8r;cZC*t|s7HKHfVE1QWD@!RYo>2AXJT}UkKL2Oq z?+uzyUvj?b`a$b3hC4GZE1dx_#MRYxeLj3kcefM!t3fx`u3g(dIhixPQ%o$kqC&E> zvy(B$uoYUWe!R0Nx*byW8;hKOHZ~5}2)2i=yRG(pQ`5mxu00jEl9G}%baWK84fPU_ znF{vKzM}Ou?lK0W0~%mFvuJYevm$*{W*7vv)(0J_tEm}(S^xxY3DCy!>E&km*Oioi zWGgZlT?CmvxpbN)5UA$n=BAX>C!1BzQoKj^$z?x#)+B>)6QZdQb_i6JtYzZG*Q3CO za#7+WZXe3qGW_Z$AOLD=YL^@x_mpMr(QbIGYOsMuqoFaI{&=<!*5o|xj< zcNYP$;@Pu)AcFhAAmf&F{RlRT4<9Z9r9&SQ(YU*1Spy(O%@}Ev5v!MU?yPQYJ+Fn~ zYgM}|fYxET(d#UFq92z5nrTQn^kd2@0Gf9%2M?bUOtQ@sm|RgQow*UY2Vnr2L#5DR zpJbuov4WBk3B=U3{3Z`$3lDb|Nz43XXk;|N(vsiB`NF-?Zc>6dvq;N;xkbHEnV#!0)mC~uQ@o78jhGO{8$#5>Hw?DJENOa z4c>cO1}sL&uv-nV%VM2?2yu1FvclWz45*4f85`>*ZcE?w2hF^|BjUaHHn3cBOG`Hg z_r@aSrKLY>;{f|Jva`9t!(Y%?1LMy0Lt9E&QjYb`M#2b1)LW6$7X_4)z`(%V^75_D zo%fLT9?zcr1~mBNdap=0sc={67Vm9fLEHur-;P&Ravenx;!0Mpz`A1e;ysOx4KCe# z_Uw_v7DrL_YUS0r1cH=nZ`EGpEpWAFa5W`M%IlsUE5-txIfqCTvw6N_>sP=W?S=LY z@qD7*b}u&8HZbrIQWxzc%-!P9dH(X!;={eOjN$r(w)Xa00|Nt(@)z6GwWKU!s@#v* zWhOQ!rKm414QL#vc&l*`aw9m2oi$M-(0NMKq$ljvTe)=8@Sczn+8Fa~@Waxvab^5bsC7#qs-HrXa0ujL0Y$ff} z37YFm@jXG~>Y(R7gf5Cp)4=JsQ&S#JuCAmI5mBeZitmat4bVTqovymg3RR$ygIcAk zT1Q76DliLHlC_vcGn#T^RcBBH(1&Pc8k(AULhM^i+ku12fe{Q$P7%dexF@|gbl3G4 zcV%yd#~b|fSFr2;H8qW|JQshl^?41}^Q@m`(XPhJ0Y%^xZvQS|QCm`;kOX2;5(9vS zurJaUXq5m6ozx-{mbQ93pWC**q4gz;#cJ#7x?M#-h1(z|2CP_j509S$ZiA*;DU>9o ztT>==OWXvCwVocu8<@_Jhei++Eb)Px{hPY<)3sdzK=%n3R|1-q9gGvJ(XL<#H4i51uNaI!HsI46_OYaSI_7kEjZDR8KYyB|KT^Dx+$BPon#fv{gwf!u7ax-Zk(4JCKWQ1Df z8*mW^;@-+B)w@*go1-rq$cuxw!aRnft|f4^Qe zUmS?BbVKC#3%VqG;IRJu2c#8RjRFs<3qoiU@t6Nhfp)%xdBHHN`|`)G__Dy@C_7>j z+9fuqtv>o0bmd0pCw5bvCKm{>=Um`F2mdNHdnYZrlHn`ATJzGzQ-ItEgxt0B0iVPf zfu()LpZj0v#vZ*UoUx!nWpF=*SBmyg8Tf2LUc#I-6h+xjbr&d{)QJZF`GFWi*qF9{ zT-tCJn^gg^tLY?k1O6t{V9f52dav2hGC#4*(&{tsH&v;=YQ`L7h^jWm_s z8&q*K;NVeS4L0r3vwh|zAl5_*n~PnAT!;;uYmPC5f518|zVT8&Wa{BC^R^X1 z?EUA@p9gz^nXxf@6@S$iFNcuJQ&?CXr{%##S0!7A`v+@$6;avylPyQiO`Cd+j%A(F zo0k+L5c&d@2F9`_x=%tGLVZ4ETkR=fic_8z=%8hkE)bgm-u~}$5G6hxMc;3JzJoC@ z?I?(_^h;K~8+(iCC)E4Tqw5xE$Dy9PROyo14?GI5D^Fdjr`NU-Oc3@)mJX3~Dv|fP z|Crj*U!c&L+g9GehO9M~gH#Q09`w5nUM!*Wdav-Cg6QDZt=)ZU{h`AyMdj{3dq>Kb zE!Z^{%0fqWW}L>d&gsp2Kc7{X>30=lgr;wX7AAHHf1N!AJKW9Q-9NUa$;darkF&|ooj$oPL##2=-im>0y0C@;5x#? z5T;Rnewys=GY-?#^a& zc33$9&8zBzL&SqTNqecibNi&xxAXfx#y*0>;zW}KJFNFa>|C~lz*GjU*j!3w ziTwh#2frv`i=YEO{U5e-fXj1JnUvVVMzI7j_gPAIgkS)FXU&Gq0U5%HG3a89Q;oNv zG3!e=P$6=T>do&Gux8b3T(enDd`>Sx{}tgiCdYT7%sp`y8A?d8JejwBdqqgj`PFgq zzjmAuUnlv@@ZuJ?U40`{}Yv{L$!+A~r^vb?14*uL{S+-DI~v`7YV8$)hw) zqS0NVA&ieo+#E#~R#>w7)?}!7;*I(vo z^rm%hg(KPL>@10Ws%LEt=EX?2_e-(}v|YrPHw7 zq(En8JM^ua*1&R85k}0w5Cpy&ZYt6+^V%?i{f90up@397>mN7RSWa?@N|V=yjTiY# zD$YL_vL+bJ*&w<)%4LnRIMh%OmSIRN!}FuFt>oC!AbF`Ah13s%;UQkzpq%8XKAub# zcbLVNb)7B`6`+KgA=X=^#)nK+6m7=rhc{LGW}h;n7ag~? zWuRu{9On=;f@T)sw1@ATBhZoicgG6?_4G$O1i1@$)@-$LejH5dKZ_QZIs^@tTk?2n zw~XdnyD}qV7?;GrDcxfw{QWf{Kfr@+6NP-t-I$S8xdUoYqA^FP^*o8C9|CV?#Jk?( z-+Pb4nFGYIn`?4f4`m6+sv7SKP2Lw4!{Z>cMUBYuC$Ypo{b=0BVxj#3oX$tgT@Cu= zWg!ly3c2+;kXZFc(x6LWZ8xiZNg>cU3RI9!uP^lZ;@8HfKji`-{!1y-s#1XnY&1w) zZdR>tvpwB(t?(^KY`#{wCGc*|6jxaOC6mnc=RbVQW1%OMG&Uz=po?nImzomeyoTP! zbd`DU0eQgN0`H;#rGhuJ+#qX?T za6PpQnl(N=2!&frpyCJIp%%g4Hr&5!5d~Ulh}mOOL2Ui-;@4FmADbzrwQZhHtYOSI zlbNDYFL*6v-ZFU-6LzsuyMN6ULCm!|ahN~M^AWa;d0}}H4^9~&qQ27d|K3^#jX`6u zzJr)kPH))_{I`IjQR?=b>w*JLq`kg5o-ExTKLrN>;=GiX>YIbD3uJ{sPX-x z2&Hv_83!5ZQ}b+Qu72F(kmfaO{jQH&|J#0iYGnZQe2Id_BD;>bImbe=QUgLJnG)rh zPcn%>q{V#MguZcu!MOaF*5g+REZ-_;fI<83mYl0PkjN$~fy@skTMNdm{ky=s{C20Y zeHueu*7}~9=QWP;c4K0YXxpeRQRu5P%RJxS*;xzL<*%#Oat?tUey~8T&ZoDurJRoJ z#m^q{bFZh98}0Jr#Myx$TNXs*hXJ<^tQ&xeQ0sU!=V`Ch+b-*Fz<@wA!mVXdJypSn zrc|2;>Ecq49O5l11H8xf39e`m^}$MeoP+9-T^XbY`bn4{Qer0b&dUK`A!rW;C?gm? z6VDv1iB^cM%_YTcBC)`F>&Q%p_&Ta5SvzRmETu6% z71gZD=5y)HS;9g$0kf7d8!pNM{hepvFmeaRzu&xI*0J1vsJ7D~^bc+>nD+ z0sNF<5Wn^3&$Ge3cU;Py>>m)D=T9G+Z`^Oyw_p;hX!$njkgvyh#{nf9h;OoC^YfJe ztOEh0^Jj_)L$qu{18__N4Et%G*MD@JPN%k{oA8~X-I!PkBGC^+mCx$R!48o354dF?k2&eQ0SX1AW86l?12(PFTf2NSHFb4Qpav>z z3Wop+Lzp{b(ERBV2f!t~K)PQLdngv-S$VLhhQL`FsSbJOnbkX#(}yWo6F&43LIMeH*g}Kt*3y8$fky{#0xbk45ok4%fGVnB~Mh;`o6C9Hv3RL2UZ~x^tNAOB-#?#IOS~StjiSC>P%~$s4cItN?{*0^!pRWvZIorO zebJoX;~wn$<%_KP(oBIp(x*{2<0Lp=IZA&UWx?|u%s0}A%+gNC(oWtWNO;By;|^uH z%C;jtVCyu%IjuYY;7JOSyi!0wB3>6qC3^>rr_0iW8(?2uV{7w7i{y}5hjy}%AUe=T z;=gkbmZ{+hOt8Kav(3RAV@glMN`XR5#A%>{LL-yTJGXve2$ohmO@Q5HBH zC4Gvij-9BgTR0hL!X%mkI<(`)lj)>=uGYBK zX-bU=syvqIci(+&0*+}5UBafnDB4MuZA_x5Hz6Gs%4Kmq+I*#&<|C=l=A%)r7Co=v z>{b}#Bb7=n9@X7U`$cdg4{FbM_!^>Hnc7<&aK%!iVURRnJ;%*BM%7!e0?HLuu^i=+ z8Y~Xmm(5eo9hC{)?xc0Fz#hhjte|~p3jx1EOooKU=>^3IsP27!MbTX+12bSo{D9v* zADms_*b)9>z8Hz6QDYaSB>f5+Ux}vtg24R=4zpL~F2lMHuzEjg`OUVS2z<{wAI0*; zk&h=r26mr<-@TcXWxml`C%0NuN_aoA5{%Rz0e3Lfuy7=v(zyZ8D%79MlK(&o zRJ=gv&pJfu%KB@}YtRQ|RzL#RSK=ba?v)jlh6{d4G}!_9r8iGBo?dkHMGl~&7Es(= zSTb(gs*EG+dATBNd4c#(n;DfqE!GxzfaYxujVIT0+Cl`9^Puxc(1Bdrap)>1%I#II zuW;0vz`^$TO0L{^?o6M5Evue%3W$e$0m9ULsE$6R1xE<>5binTu=W@zs{0tX6 zvDV4k0;4(tJI^HbDai(3z;5zLeCp=FUvT!717)yrY!d)L6#DI(pf)KHQoea+UU6|O z;8(8__qw5fu&v7~^Sq4FeuF>x1ArY~Y^nfGv=5H#>kXw-`5)C2>i5GNdBfvW2M^ZA zmR!=DAG*h#_gdgqa+56IM!~3!Y+Ry{>QHK72hspQn7Fp1qXX=4hRv#G)~=rQ-QAW- zdD||1-q8iVh_8JHcWW5*m*|OXG|(2*UAo?|Xq027ZcBizA&(F8@G*SeF;XJn(A-Z> z1G|ez(vR5Js(>^IB@mJEx>CSRq*?KFd|46Zi{c=-4D3|MkeQpks5@&yI2Efn1hPFr zU5t3ds!zM^P$?{yo**yz^4{1}N;*prlfnx+kFOP)U&%EMJKDDtSP$O}fq!acnt#9{ z_Dxd<=<&BS^?%~v2s|JNx2W^!#(*`GXR6@w;l}I_+nzYY%Z{5f=ov-?07#XU+B8VH zfo*cIA)UUU7z?=)LEd0qQIXn=Ms%~P0my@jmMt$YmySQ0LFNJ~?bz7ZrKKDMS?=5NtE2%_vp^iNUS}!G(bL?mS%)fo8vp*0JYfhkybJ1v9iK zN~-Q$4qS|fJO=TQV;~-~`27LY`4IlAG?dPveonaTf|z~{H06mVG*lJ2QK&UvHbJ=V zUE#Ub)sy*9 z6jGLbi6|%o(90{3()GLh(2#IajuD9h=ONHHR*q3uW0stc>KbQv_(ZujArC?xd18A} zvX9aqd#X-yW}~1J;qVYHT5F8WIUhxj4$=|m4Y&K41%ZbzX!kOe$F1N%{OR`^Qb+vy z+@q{3sex9qK#gHw9CWK-&Tq55N)5Sbxyc2TS`U-gD_82@badPP>oPR>##TZ%=7KFu zR(rv5Dp(0!_5LG!hk^9&>zIk9YGcrWIryXo{dLGTgYr8wGo@99Hu3<>*#fz3-O+K9#5Ea4sm0mmP~2l$0Ow^-o@`t;8? zOW{8Jy|I=52DF3}`RR@mQ|Yo7P_4b%YWi?-VeGRsluxfVatB+jCj6{C1VUKRR#~$M z?NS4Z>`H_i3@=_DuUBn&Q$cS#27gUxO9?Xa|5&I-q+B0kW8pzEMhch}_b zktPK8M{3wyrxM0+pnnpsMbc@ntBBrCXDeZX1&F_=0vaWZ78a2(O_xI1+?|jA4k>=;bf6#libFE-eE|Jk7@7e%r1Ss~P zTd0JAeuPU)_I*fkv?-kFm(Jr9U&k<26?<+Le)p%_0C_38VvO=+0@rah0nQ)6vY22T z1IfKS39*H;M}Ho@h?d}V>mYI1z(JtL%S-(BaHP2(>Y(L`Z8Ya?&FLY%e%O9RcVWt# z09P64qZNpYyOwk8p7&;c+-oW`cMC?nD&mR;&-2{n8NILj=XOi?<9>imyt(+K>erha zrMZm@=e?mj-g&i9sFe;(B~QH|!rSZ6A?1_3q}OJR(!utpppR$XffOvNw)H~iNqrc2 z@3WG=XC*Dt*a1&{CZKAO;GSQV|EMY-_AtQhs)Jwk#zexiyzkIBn?uO(g6$W#y~cWV zCNtffs&j>TZ~(+@>s?KK54i4%TR9LC`$E?*QE5||uN>~*QXIjg{39ujU-|R@f4J=T zrk6Q<&IN!31Z)p~FO)&m1U-3h6Ik(uY3ZtBNN>8Kh+DlhFOR~H@YvU8 z?DI0R)6J|en^~vZCN0^~M$NXvESHd?WT%&+g?3~x`3l4v7p?&_I$0-p3($C>-vYwC zI~r{jAdvz%VUki(B;5rz`F_3KlTa1SR!U1o_QhfQ`|MGB4AKr$70+rRbGFz8Th?=HeRCHg~k(HUvWR~N7+Ls238@Tz1RW1+f>+Ag( z{l}nc3n1P9l&F2dvuEbD9y7&mK0cc;oMwDK8N<&HU)a$~VVPPlHpTBE^MkZoO>}7> z^THc`8{WFwIkzkQkIy*UO=$jJ{ zt|ykTB`@!zC_O0f*4AHd^L+uRWC0lc5#8P0O-DCp0$9<|)m75I-3U^mky?Iz!(jzw zS+<~(0AS}s@Ts4SV*+0a(Ku(SuK}pZPyz^GUd0YV zf(n#?fEuwzsla-WsiTG`ufknU)FN4C(#l54vZj@f(*QS|0b+Rf4buRI?rw0n$Q~o} zEP6xPl8^B;@iR@Dc$t+s-+=;)w;>v9x=HpWP_%K{4N!4*i`WZtr(a#(ze{_g%hQ_6 z>p{^D##pv2f!DYxaP&KMham3r#0$USUq z>^?hTDWw^07tG49D%nMIWW(%}6hG#0L_T&IAUIZ5RsyKPylYqeQV`Mb0-Qdd;}h-f7DyfpZHH5~#v zRDtYn;<@%6c|q!dEVx0g3Zl5ZELn!lgS1A$>+70D-{2UG$u~F#gYf^OyDkdU|8jlx zr*nYcqkJ4L&;-@L5WVuVppc8GaH}~e;}nW;tmWJo%B?z|;;_ZDUL34n?bc014G5{z zEQzdb*iI))JYlKnub}8607y{##L3A|tX&!}-W{Z)q!r~3*x&UB6i}dG6X5X!>;q?v zez*;4dNBH)3v)#kP{zifOuz-8k``u(Udg!0I#9>ym&?d*fjHM-&BxP(zG$rj2Pdx0 z?RZ3`y4%f&(91~=+u9;grsNB zKtUHJX=%W#J!TsFgpd^B-!9d3{uxUUbOcIfVU>ww! zV!d4T6~g^T>h09(Vk|)5b`q!52n%*QMMFcw{<%3*8!s>hP$ejcx&HFyQOevYi(a9U z%vLu06xR3z#hU?c1R`8&-#uGa(bi?U&;be}Kuny8UV;+w#tu;K5@`j>JJ#DVKD-o} z)ma5bd~S}BVtw#1tf}{>LWA}R!xgRvXW#*AHcNoE5uh*?slD9{4E1u!{FO=IUkn6i~pri1Z!RbE^Z-;6kTAinK2U>N?V)X%?`>JL#tK|ag^C`<%0 zXJLbT|84=TADH(hltNY^mi5~8A*iBp6gWv^w9*uY4=#h&RWeX5`!>assr!-)!P;zV zo0Kj;rsLYdWLHd{sIHk`y=Z}})XiIr|5;H5G;UC(Lb56f5E7vhihv^xRmjpvxfJYv z4%E{C6|+V!W*nd*Og!-i>*iNGcB-W|KV^^RW=Ta7CHxFWDXIoR-0@;DG011&r#kR6 zrI4!O^7xZpTmv=+SWny3^&_Z6(Aapk!k;Mz>dFI(i#Ar6sZBO}1m0o}A_a<-4#Kgo`3`VZh z(#lt$*XEFRDd;s+R}!iAq76o!=EcUaGoC8!-KlaoWE%dwws3CSzRHU`( z@_sQz;(CxW4N=P-x$6&#NDS+E-e;F47dZ!)5QsrKIH1&WTCV2kC7Z0K|2tENK;2puko{-@PgGGYC^9pig%cS=NTGMYQ5Y zhhxbH5odn0-}vAz*?tFJCwP6BMZe^&guE(_nRQMOEg*8R^7$w1MSO#uZ?Gc}hW~{e z?SG_v+~0tFNX0#D1!~9{8;@Q@APCn5wToUNup7y#V4#Xm!DS7>f(EXI@2Lgwf|C8u ze5Y5?Hz7wP@RS1Q<0g>q3%MXZAnyVGx4>iYG|Q1I>R@c_ueQwe99YV!%?<_5fUu2z zSS^j$)dFb#tNwmNxB-A?r_;_c^Rd9Co9r113S$9+2c(EXb+a1&&`A6BGg<*P7%DjG z$NFdtDiFmY8?G7~4?TCnLcRFZAu~{f4pe)AK=>ii{Im9{0eS#w-9Q5c6b!%VkvlJs zL3NQ1`A%ev8!0Ubs{gDjN7%>w$)S)Dcni9~C*VC2zif>BQj2Qkygv@Ax&eslb$n0A zr;z2X16(!F_ZC)GZUf9OXf{f2{(J57i&9IsiND+Kv`I=Q$nC0V&P~t*pkTkQsR3QT z-83W`VX+ER5A7F}=Aul5qY#&=OfIFU#GTeVPJElmJO}~>E)TskjtC)a;s8Yj3^0U@ zHkPo5;9W=y$g>9K3o&Z$Km%gnGG>B$P~4`gl_goDv~2k&u>4CKe&Gv_^tC=IB~3bO z7iwFovAr+Q~cx#*F7$DcZ1AXvNo)`hv} zxp2L?xyaea*B5%MkC&zTv_?ulZy7z^VN2jvkg;FGIgRwIQ|MFyA%oVZb=UYg*frXpwP@sXgz+;!^>uXD*B6S{6?5YYs5YMzN- zm_`mr(3Si^D}zl>P8mK2p3377>0F?$Q-LzyLrZ?(5D&m!9<^3fbUa$pHu!wLPP!Ss zm{z6;_6h2gC~KLiFhq7yw60PhE@_1xWC~c+Ko{}~3u6GxLyt>ZvO;6o zgl=u=0&%pntE+(VT^jV{B=9UDweN)x*t4J{B=E7b79kz!SmdJEX?EG^2-{lNEJ0-kK$keBLk6A2~HAePGzx4Z$2 zHZM&Big$aP+q!mCq?#+0$tFhMj%JxIBnry6*wjeixN zLj#=Qwb~6Mss_=Z^1N&fh%UVuk)Y+9cNUfUGwlGuZQ|(I_hnhNUYaaAc^?dvCF$>RGg*r|~(N}1f@jU?A;5VXaHvbM3 zJ-jgd{%|FLjQ}DML=nJDnCgGMif#Z6?A+CrIm-tbdTyFPtt`vRXN#`xA3;t}4V@Bd z-8Vr-xS{Lq@*fK5a?hPs!-OWNZwR86V3y_q;uz5@UTG= zK$<-!SwMY+7TD_Jpcc!a;9h$W_J^24!^6Yx25tkU5O_qUVHEHza(-U`XcW)#FVCXo z5eWDyI)FE?F1!hoTlfW|6kjZnhy#qI(WV^&%I#z$V2n@=duRPQ(+&WsZX&PTjl}Hi z7v}52x|c6hFpsu$f}GCgnLC3@!@d(;HJ~5>V57!$b?$Wo$?gBAhto>L_ia94DI5k)(Ma*=0WiGcZ_IbGulGfswiPB`jaRa9%SXUSMh ze4zojUgBBj&fl`{X=o-(yS82E)ojt;t4D0LC0ob2mFp=)a( z#!ESE8)+qAI}_vH&GOg>Etg^4xw~q38?xvXrD(^>0chp9)$tT`c^}if*XAuUVXN&& zf*)T=v)K_&T8A!AJqVpOm(MlRLQl#HC!%9yx6&|h`nNw0$zyeuyXjC3hPk#0eM)0h9>(Oac=h_f&ZGmSZl~b zxHwv$uju~YK7UbOnlxCe5LpN(-%F75+EPljr?+EFU9l|u4vKH9;5lrzMdgDWi5F=k zv$|R5>V5F=llsjz1@1358*9A%=5l8Cj8@G;l3OsfeirJSO<)a6Oj3TK+`1=d{IdId zp4BE#SNYO?f|7~U=lKdVH`YdLy_GK6;(no^2jSQ**KT#(Bo)#=m4C0?K_P9l+2)_D zdkN+&>zt$oFa40RgF-N;!71;z+w&?PT?vF!>Xjd`mYdcZmUf^#0+T|Ziz}Zj&TreJ zd27Ras5yy;ay3Dj*>iMRb;ARADwr)pf5Ff@punMHN8B4ItBeyLI(Jmu2} ztU=jQLcpS@(er!4WvEa$GIVXRS5prY>)N6EDr`{Hp+>>UVjC;i$nu*o?k)V#VCCgQr=%@ zL=U=y$)4(Ma6tv=1LAgD^*eZq;7^Ih|1+vV widget is GameWidget), - matchesGoldenFile(goldenFile), - ); - }); + await expectLater( + find.byWidgetPredicate((widget) => widget is GameWidget), + matchesGoldenFile(goldenFile), + ); + }, + skip: skip, + ); } typedef PrepareGameFunction = Future Function(FlameGame game); diff --git a/packages/flame_test/test/golden_test.dart b/packages/flame_test/test/golden_test.dart index 2898560b2..2b10e5874 100644 --- a/packages/flame_test/test/golden_test.dart +++ b/packages/flame_test/test/golden_test.dart @@ -37,5 +37,12 @@ void main() { }, goldenFile: 'golden_test.png', ); + + testGolden( + 'skipped test', + (game) async {}, + goldenFile: 'golden_test.png', + skip: true, + ); }); }