spot
Flutter widget test toolkit - spot, act, validate. Better selectors, automatic screenshots, chainable.
0.20.0
6.3K downloads/mo
#3531 most downloaded on pub.dev
passsy/spot
What this package is like to depend on
Last release 13 days ago
11 Aug 2026
Ships unpredictably
gaps range from 9 days to 1.2 years
Nearly every release is documented
notes for 25 of 25 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
30 releases · first in 2022
1 release in the last 12 months
see the full history below
Release timeline
30 releases · Sep 2022 to Aug 2026Releases
latest 30-
0.20.011 Aug 2026Release notes
Open source →Performance
This release is mostly about speed. Two changes carry it:
- Improvement: Relative queries (
withParent,withChild, chained selectors) are up to 200x faster on big widget trees. Relationships are now resolved by walking up the tree instead of searching the subtree of every parent. #148 - Improvement: A frame's screenshot is rasterized once and reused by every assertion in that frame, instead of photographing the same unchanged screen over and over. Annotations are reused the same way, per test. On one app's suite, capture time went from 9.7s to 1.9s over 261 screenshots. #160
The rest:
- Improvement:
hasDiagnosticProp,getDiagnosticPropandwithDiagnosticPropnow cachedebugFillProperties. ~1.5x faster #159 - Improvement: The source location of a widget is resolved once per widget instead of on every lookup, which speeds up
act.tapAt()timeline events and the diagnostics behind failingact.tap()calls. #154 - Fix: Assertions like
spotKey(key).existsOnce()were extremely slow (tens of seconds) when no match was found in a large widget tree. The error output is now limited and match-all selectors are no longer suggested as "less specific" matches. #119
Tap
-
New:
act.inspectTap()reports whether a widget can be tapped and why not, as a value instead of a thrown error #150final inspection = act.inspectTap(spot<ElevatedButton>()); expect(inspection.canTap, isFalse); // the button is behind a full-screen overlay expect( inspection.tapFailure?.tapCoveredReason.primaryCover?.widget, isA<ColoredBox>(), );
Available reasons:
TapNotFoundReason,TapMultipleWidgetsFoundReason,TapNoRenderObjectReason,TapNonRenderBoxReason,TapOutsideViewportReason,TapAbsorbedReason,TapIgnoredReason,TapOffstageReason,TapZeroSizeReason,TapCoveredReasonandTapUnknownReason. Also addTapInspection,TapFailureReason,TapWidgetInfo,TapHitTestInfo,TapHitSample,TapSamplesandTapBlocker.TapInspection.samplesreports how much of the widget reacts to pointer events and what is in the way, for tappable widgets too. A widget that is tappable but only partially reachable has no failure to assert on, so assert on the samples.final samples = act.inspectTap(spot<ElevatedButton>()).samples!; print('${samples.hittablePercent}% of the button reacts to taps'); for (final blocker in samples.blockers) { print('${blocker.receiver.widgetName} covers ${blocker.percent}%'); }
Sampling hit tests the whole widget on a grid, which costs far more than the rest of the inspection, so it happens on the first read of
samplesinstead of up front. An inspection describes the tree of the frame it was created in, so readingsamplesafter a pump throws instead of reporting what a different tree does. -
New:
act.tap()throws aTapFailurethat carries theTapInspectionexplaining the failure, so the reason can be asserted without matching on the message.TapFailureextendsTestFailure, existing expectations keep working. #150await expectLater( () => act.tap(spot<ElevatedButton>()), throwsA( isA<TapFailure>().having( (it) => it.inspection.tapFailure?.reason, 'reason', isA<TapCoveredReason>(), ), ), );
-
Fix:
act.tap()now finds anAbsorbPointeranywhere above the target. It previously only looked directly below the widget #150 -
Fix:
act.tap()now reports the outermostAbsorbPointerorIgnorePointerabove the target instead of the closest one #150 -
New:
act.tap()explains offstage widgets instead of reporting an unknown reason #150
Timeline
- New: The timeline counts every frame the test rendered, not just the ones something was recorded in, and the report shows the total. Fewer frames is a faster test, so it is worth seeing which
pumpAndSettlecould have been apump. Frames are labelled with their real number, and the stretches between recorded frames appear as a gap showing how many frames went by and how long they took on both clocks. Gaps hold nothing to select, so the arrow keys step straight over them. Also addsTimeline.renderedFrameCountandTimelineEvent.renderedFrameNumber. - New: Whatever failed the test is now the last event of the timeline, in a frame of its own, and the HTML report opens on it. Previously only spot's own assertions reported their failure, so a plain
expector an exception from the widget under test left the report ending at the last thing that worked. The event carries the real error message, a stack trace with the test framework folded out, a capture of the screen as the test left it, and the line that threw. - Fix: A run that reports nothing now deletes the report an earlier run of the same test wrote. The old report used to stay on disk, so the link printed by the earlier run kept opening it and showed the source, events and captures of a run that no longer existed, which reads as the timeline being stale rather than absent.
- Fix: Restore screenshots and interactivity in the hot-restart timeline.
Scrolling
- Improvement:
act.dragUntilVisible()can now use any selector that resolves to aScrollableasdragStart, so keyed or otherwise untyped scrollable selectors drag from that scrollable directly. #133 (thx @trejdych)
Selectors and queries
- New:
spotAtPositionandWidgetSelector.atPositionto query widgets on the hit-test path for a global screen position. #28 - New:
WidgetSelector.isPresent()andisAbsent()returnboolwithout failing the test, andcountWidgets()returns the number of matching widgets. Use them to branch test logic on the presence, absence or quantity of a widget. #30if (spot<Tooltip>().withMessage('Open navigation menu').isPresent()) { // ... } if (spot<Tooltip>().withMessage('Close menu').isAbsent()) { // ... } final buttonCount = spot<ElevatedButton>().countWidgets(); final hasTwoButtons = spot<ElevatedButton>().countWidgets() == 2; final hasAtLeastTwoButtons = spot<ElevatedButton>().countWidgets() >= 2;
- New:
getDiagnosticProp<T>('name')is now also available onWidgetSelector, alongside the existinggetWidgetProp,getElementProp,getStatePropandgetRenderObjectPropreaders. #30final message = spot<Tooltip>().getDiagnosticProp<String>('message');
- New:
WidgetSelector<AnyText>.whereIsEditable()andwhereIsNotEditable()filter text matches by whether they come from an editable text input.spotText('username').whereIsEditable().existsOnce(); spotText('Username').whereIsNotEditable().existsOnce();
- New:
WidgetSnapshot.queryStatsreports how much work the query engine performed to evaluate a selector, useful to debug slow queries. #148 - Fix: A
WidgetMatchernow always reports the widget of the frame it matched, not the current widget in the tree #159 - Improvement: Untyped selectors (
spot,spotKey,spotWidget,spotElement,spotTexts) no longer add a no-opWidgetTypeFilter<Widget>at the root.
Text matching
- New: Text matching ignores invisible and special whitespace, so tests can use regular characters.
spotText,spotTextWhere,whereText,withTextandhasTextstrip invisible characters (zero width space, soft hyphen, word joiner, BOM) and fold every Unicode space separator (Zs, e.g. non-breaking space) to a regular space. Meaningful characters (zero width joiner, bidi controls, theU+FFFCWidgetSpan placeholder) and line breaks are kept. #138 (thx @MichaelTamm)To match exact characters, passspotText('foobar').existsOnce(); // matches Text('foo\u{200B}bar') spotText('foo bar').existsOnce(); // matches Text('foo\u{00A0}bar')
raw: truetospotText/spotTextWhere, or usewhereRawText/withRawText/hasRawTextonWidgetSelector<AnyText>. Also exposesAnyText.normalizeVisibleText,AnyText.extractText, andAnyTextContent(raw/normalized). - Deprecated:
spotText(text, exact: true)is nowspotText(text, whole: true)— the flag controls whole-string vs. substring matching, not character handling.exactstill works. #138
Screenshots
- New:
ScreenshotAnnotator.cacheKey(default =>null) allows caching of annotations #160 - Fix: Export
ScreenshotAnnotator, which has already been a parameter oftakeScreenshot(annotators: ...)
Fonts
- Fix:
loadAppFonts()now also registers a package's own fonts underpackages/<self>/MyFont, so fonts referenced viapackage: '<self>'render instead of falling back to Ahem. #141
Release notes
Open source →Performance
This release is mostly about speed. Two changes carry it:
- Improvement: Relative queries (
withParent,withChild, chained selectors) are up to 200x faster on big widget trees. Relationships are now resolved by walking up the tree instead of searching the subtree of every parent. #148 - Improvement: A frame's screenshot is rasterized once and reused by every assertion in that frame, instead of photographing the same unchanged screen over and over. Annotations are reused the same way, per test. On one app's suite, capture time went from 9.7s to 1.9s over 261 screenshots. #160
The rest:
- Improvement:
hasDiagnosticProp,getDiagnosticPropandwithDiagnosticPropnow cachedebugFillProperties. ~1.5x faster #159 - Improvement: The source location of a widget is resolved once per widget instead of on every lookup, which speeds up
act.tapAt()timeline events and the diagnostics behind failingact.tap()calls. #154 - Fix: Assertions like
spotKey(key).existsOnce()were extremely slow (tens of seconds) when no match was found in a large widget tree. The error output is now limited and match-all selectors are no longer suggested as "less specific" matches. #119
Tap
-
New:
act.inspectTap()reports whether a widget can be tapped and why not, as a value instead of a thrown error #150final inspection = act.inspectTap(spot<ElevatedButton>()); expect(inspection.canTap, isFalse); // the button is behind a full-screen overlay expect( inspection.tapFailure?.tapCoveredReason.primaryCover?.widget, isA<ColoredBox>(), );Available reasons:
TapNotFoundReason,TapMultipleWidgetsFoundReason,TapNoRenderObjectReason,TapNonRenderBoxReason,TapOutsideViewportReason,TapAbsorbedReason,TapIgnoredReason,TapOffstageReason,TapZeroSizeReason,TapCoveredReasonandTapUnknownReason. Also addTapInspection,TapFailureReason,TapWidgetInfo,TapHitTestInfo,TapHitSample,TapSamplesandTapBlocker.TapInspection.samplesreports how much of the widget reacts to pointer events and what is in the way, for tappable widgets too. A widget that is tappable but only partially reachable has no failure to assert on, so assert on the samples.final samples = act.inspectTap(spot<ElevatedButton>()).samples!; print('${samples.hittablePercent}% of the button reacts to taps'); for (final blocker in samples.blockers) { print('${blocker.receiver.widgetName} covers ${blocker.percent}%'); }Sampling hit tests the whole widget on a grid, which costs far more than the rest of the inspection, so it happens on the first read of
samplesinstead of up front. An inspection describes the tree of the frame it was created in, so readingsamplesafter a pump throws instead of reporting what a different tree does. -
New:
act.tap()throws aTapFailurethat carries theTapInspectionexplaining the failure, so the reason can be asserted without matching on the message.TapFailureextendsTestFailure, existing expectations keep working. #150await expectLater( () => act.tap(spot<ElevatedButton>()), throwsA( isA<TapFailure>().having( (it) => it.inspection.tapFailure?.reason, 'reason', isA<TapCoveredReason>(), ), ), ); -
Fix:
act.tap()now finds anAbsorbPointeranywhere above the target. It previously only looked directly below the widget #150 -
Fix:
act.tap()now reports the outermostAbsorbPointerorIgnorePointerabove the target instead of the closest one #150 -
New:
act.tap()explains offstage widgets instead of reporting an unknown reason #150
Scrolling
- Improvement:
act.dragUntilVisible()can now use any selector that resolves to aScrollableasdragStart, so keyed or otherwise untyped scrollable selectors drag from that scrollable directly. #133 (thx @trejdych)
Selectors and queries
- New:
spotAtPositionandWidgetSelector.atPositionto query widgets on the hit-test path for a global screen position. #28 - New:
WidgetSelector.isPresent()andisAbsent()returnboolwithout failing the test, andcountWidgets()returns the number of matching widgets. Use them to branch test logic on the presence, absence or quantity of a widget. #30if (spot<Tooltip>().withMessage('Open navigation menu').isPresent()) { // ... } if (spot<Tooltip>().withMessage('Close menu').isAbsent()) { // ... } final buttonCount = spot<ElevatedButton>().countWidgets(); final hasTwoButtons = spot<ElevatedButton>().countWidgets() == 2; final hasAtLeastTwoButtons = spot<ElevatedButton>().countWidgets() >= 2; - New:
getDiagnosticProp<T>('name')is now also available onWidgetSelector, alongside the existinggetWidgetProp,getElementProp,getStatePropandgetRenderObjectPropreaders. #30final message = spot<Tooltip>().getDiagnosticProp<String>('message'); - New:
WidgetSelector<AnyText>.whereIsEditable()andwhereIsNotEditable()filter text matches by whether they come from an editable text input.spotText('username').whereIsEditable().existsOnce(); spotText('Username').whereIsNotEditable().existsOnce(); - New:
WidgetSnapshot.queryStatsreports how much work the query engine performed to evaluate a selector, useful to debug slow queries. #148 - Fix: A
WidgetMatchernow always reports the widget of the frame it matched, not the current widget in the tree #159 - Improvement: Untyped selectors (
spot,spotKey,spotWidget,spotElement,spotTexts) no longer add a no-opWidgetTypeFilter<Widget>at the root. - Fix:
WidgetSnapshot.toString()formatting #116
Text matching
- New: Text matching ignores invisible and special whitespace, so tests can use regular characters.
spotText,spotTextWhere,whereText,withTextandhasTextstrip invisible characters (zero width space, soft hyphen, word joiner, BOM) and fold every Unicode space separator (Zs, e.g. non-breaking space) to a regular space. Meaningful characters (zero width joiner, bidi controls, theU+FFFCWidgetSpan placeholder) and line breaks are kept. #138 (thx @MichaelTamm)
To match exact characters, passspotText('foobar').existsOnce(); // matches Text('foo\u{200B}bar') spotText('foo bar').existsOnce(); // matches Text('foo\u{00A0}bar')raw: truetospotText/spotTextWhere, or usewhereRawText/withRawText/hasRawTextonWidgetSelector<AnyText>. Also exposesAnyText.normalizeVisibleText,AnyText.extractText, andAnyTextContent(raw/normalized). - Deprecated:
spotText(text, exact: true)is nowspotText(text, whole: true)— the flag controls whole-string vs. substring matching, not character handling.exactstill works. #138
Screenshots
- New:
ScreenshotAnnotator.cacheKey(default =>null) allows caching of annotations #160 - Fix: Export
ScreenshotAnnotator, which has already been a parameter oftakeScreenshot(annotators: ...) - Fix: Shorten screenshot filenames to avoid issues on some filesystems #124
- Fix: Replace colons in screenshot filenames for Windows portability #114
Fonts
- Fix:
loadAppFonts()now also registers a package's own fonts underpackages/<self>/MyFont, so fonts referenced viapackage: '<self>'render instead of falling back to Ahem. #141 - Fix: Handle whitespaces in dependency font family names #130
- Improvement: Relative queries (
-
0.18.028 May 2025Release notes
Open source →- Breaking: Add
act.dragUntilVisible()now moves the target in the center of the viewport (one additional drag). parametermoveStepis now optional, default to half the scrollable size. The direction can be controlled withbool toStart. - Fix: Restore support for integration_tests - don't generate the timeline HTML
- New: Add support for
flutter test --platform chrome- don't generate the timeline HTML and screenshot paths - New: Added warning when
spot<GenericWidget>()can't find a widget because it is actually looking forGenericWidget<dynamic> - Improvement: Moved timeline screenshots into
build/timeline/<test_name>/screenshots/for easier browser image resolution. Fixes issues with Firefox. - Improvement: Improve error message of
act.tapwhen multiple or no widgets are found - Fix:
existsAtLeastNTimes(0)now reports a correct error message
Changes for
WidgetSelector- Improved:
.snapshotWidget(),.snapshotState(),.snapshotElement(),.snapshotRenderBox()and.snapshotRenderObject()now add a single consistent entry each to the timeline with consistent messages.
Changes for
WidgetSnapshot- New:
discoveredRenderObject - New:
discoveredRenderObjects - New:
discoveredRenderBox - New:
discoveredRenderBoxes - New:
removeQuantityConstraints()
Changes for class
Screenshot(big breaking update!)- Fix screenshot filenames on windows (remove colons)
- New:
width,height,pixelRatio,name - New:
readBytes(),readPngBytes(),readPngBytesSync()gives access to raw bytes - Deprecated:
fileproperty. Still returnsFilebut signature now returnsdynamicfor web support. UsecreateTempPngFile()or raw byte APIs instead - New:
createTempPngFile()writes the screenshot to a temporary file and returns the absolute file path - New:
List<ScreenshotAnnotation> annotations,addAnnotation(),removeAnnotation()each layer is now separately available - New:
flattenedImage()merges all layers into a single image
Release notes
Open source →- Breaking: Add
act.dragUntilVisible()now moves the target in the center of the viewport (one additional drag). parametermoveStepis now optional, default to half the scrollable size. The direction can be controlled withbool toStart. - Fix: Restore support for integration_tests - don't generate the timeline HTML
- New: Add support for
flutter test --platform chrome- don't generate the timeline HTML and screenshot paths - New: Added warning when
spot<GenericWidget>()can't find a widget because it is actually looking forGenericWidget<dynamic> - Improvement: Moved timeline screenshots into
build/timeline/<test_name>/screenshots/for easier browser image resolution. Fixes issues with Firefox. - Improvement: Improve error message of
act.tapwhen multiple or no widgets are found - Fix:
existsAtLeastNTimes(0)now reports a correct error message
Changes for
WidgetSelector- Improved:
.snapshotWidget(),.snapshotState(),.snapshotElement(),.snapshotRenderBox()and.snapshotRenderObject()now add a single consistent entry each to the timeline with consistent messages.
Changes for
WidgetSnapshot- New:
discoveredRenderObject - New:
discoveredRenderObjects - New:
discoveredRenderBox - New:
discoveredRenderBoxes - New:
removeQuantityConstraints()
Changes for class
Screenshot(big breaking update!)- Fix screenshot filenames on windows (remove colons)
- New:
width,height,pixelRatio,name - New:
readBytes(),readPngBytes(),readPngBytesSync()gives access to raw bytes - Deprecated:
fileproperty. Still returnsFilebut signature now returnsdynamicfor web support. UsecreateTempPngFile()or raw byte APIs instead - New:
createTempPngFile()writes the screenshot to a temporary file and returns the absolute file path - New:
List<ScreenshotAnnotation> annotations,addAnnotation(),removeAnnotation()each layer is now separately available - New:
flattenedImage()merges all layers into a single image
- Breaking: Add
-
0.17.003 Jan 2025Release notes
Open source →- Timeline is now generated with Jaspr #76
- New:
act.tapAt()#80 - New
Timeline.addEvent()now returns theTimelineEventId id - New
Timeline.updateEvent(id)andTimeline.removeEvent(id) - Fix: Added events to timeline while being
off#88 - Fix: Export
stateProp#93 - Improved screenshot detail page in timeline #91, #92
Release notes
Open source →- Timeline is now generated with Jaspr #76
- New:
act.tapAt()#80 - New
Timeline.addEvent()now returns theTimelineEventId id - New
Timeline.updateEvent(id)andTimeline.removeEvent(id) - Fix: Added events to timeline while being
off#88 - Fix: Export
stateProp#93 - Improved screenshot detail page in timeline #91, #92
-
0.16.026 Nov 2024Release notes
Open source →- Add
snapshotState<S>()
final state = spot<MyContainer>().snapshotState<MyContainerState>() - Add
snapshotRenderBox() - Export
WidgetPresence - Add
@useResultto.atMost(N),.atLeast(N),.amount(N)and.existsAtMostNTimes(N)to prevent missing assertions - Fix
existsAtLeastNTimesdumping the widget tree to console - Fix image rendering with
TimelineMode.always - Add Timeline to
/README.md - Add
actto/README.md
Release notes
Open source →- Add
snapshotState<S>()final state = spot<MyContainer>().snapshotState<MyContainerState>() - Add
snapshotRenderBox() - Export
WidgetPresence - Add
@useResultto.atMost(N),.atLeast(N),.amount(N)and.existsAtMostNTimes(N)to prevent missing assertions - Fix
existsAtLeastNTimesdumping the widget tree to console - Fix image rendering with
TimelineMode.always - Add Timeline to
/README.md - Add
actto/README.md
- Add
-
0.15.021 Nov 2024Release notes
Open source →- Add
loadAppFonts()to display your app fonts on screenshots #66 - Add
loadFont()to load a fonts from a file. Useful when your app depends on preinstalled system fonts (loadFont('Comic Sans', [r'C:\Windows\Fonts\comic.ttf'])) #66 - New direct access to properties from
WidgetSelector#71spot<MyWidget>().getWidgetProp(widgetProp('color', (widget) => widget.color));spot<_MyContainer>().getStateProp(stateProp<String, _MyContainerState>('innerValue', (s) => s.innerValue));spot<_MyContainer>().getRenderObjectProp(renderObjectProp<Size, RenderBox>('size', (r) => r.size));
- New
getStatePropandstatePropto access state properties #71
spot<_MyContainer>().existsOnce().getStateProp(stateProp('innerValue', (_MyContainerState s) => s.innerValue)); - New
timelinemodeTimelineMode.alwaysto always print a timeline after each test #68 - Deprecate
TimelineMode.recordin favor ofTimelineMode.reportOnError(which is the default) #68 - Timeline now shows partial tap warnings #69
- Never show big widget tree dumps in console, only in Timeline HTML report #70
act.tap()now shows a Crosshair on the screenshot- Fix code samples of
whereWidgetProp(),whereElementProp()andwhereRenderObjectProp()#67
Release notes
Open source →- Add
loadAppFonts()to display your app fonts on screenshots #66 - Add
loadFont()to load a fonts from a file. Useful when your app depends on preinstalled system fonts (loadFont('Comic Sans', [r'C:\Windows\Fonts\comic.ttf'])) #66 - New direct access to properties from
WidgetSelector#71spot<MyWidget>().getWidgetProp(widgetProp('color', (widget) => widget.color));spot<_MyContainer>().getStateProp(stateProp<String, _MyContainerState>('innerValue', (s) => s.innerValue));spot<_MyContainer>().getRenderObjectProp(renderObjectProp<Size, RenderBox>('size', (r) => r.size));
- New
getStatePropandstatePropto access state properties #71spot<_MyContainer>().existsOnce().getStateProp(stateProp('innerValue', (_MyContainerState s) => s.innerValue)); - New
timelinemodeTimelineMode.alwaysto always print a timeline after each test #68 - Deprecate
TimelineMode.recordin favor ofTimelineMode.reportOnError(which is the default) #68 - Timeline now shows partial tap warnings #69
- Never show big widget tree dumps in console, only in Timeline HTML report #70
act.tap()now shows a Crosshair on the screenshot- Fix code samples of
whereWidgetProp(),whereElementProp()andwhereRenderObjectProp()#67
- Add
-
0.14.007 Nov 2024Release notes
Open source →- New: Timeline! Failing tests now print a timeline with screenshots of all interactions (actions and assertions) as HTML report #57
act.tapnow checks for multiple tappable position when the center is not tappable for some reason #60act.tapnow reports a useful error when the widget is 0px/0px or invisible #61- Become Compatible with Flutter 3.27 and add nightly tests against
master
Release notes
Open source →- New: Timeline! Failing tests now print a timeline with screenshots of all interactions (actions and assertions) as HTML report #57
act.tapnow checks for multiple tappable position when the center is not tappable for some reason #60act.tapnow reports a useful error when the widget is 0px/0px or invisible #61- Become Compatible with Flutter 3.27 and add nightly tests against
master
-
0.13.019 Jun 2024 -
0.12.116 May 2024 -
0.12.022 Mar 2024Release notes
Open source →- Breaking
Offstagesupport. By defaultOffstagewidgets are not found byspot<W>(). UsespotOffstage().spot<W>()to find them.spotAllWidgets()returns onstage and offstage widgets. Use.overrideWidgetPresence(WidgetPresence.offstage)to modify aWidgetSelectorto search foroffstage,onstageorcombined#45 - New:
act.enterText(spot<TextField>(), 'Hello World!')allows to enter text into aEditableText#51 - Negating parents is not yet supported (
spot<ListView>().withParent(spot<Scaffold>().atMost(0))). It now throws to prevent unexpected behavior. #50 act.tap(spot<ElevatedButton>())now pumps automatically after the tap #52
- Breaking
-
0.11.019 Feb 2024Release notes
Open source →- Add support for Flutter 3.20
- Update
checksto 0.3.0 #48 - Remove deprecated property
selectorfromwithProp()andhasProp(). UseelementSelectorinstead - Widen
test_apiversion range to include0.7.X
-
0.10.005 Feb 2024Release notes
Open source →High-level API changes
- Breaking
spotText('dash')can now return multiple widgets - New:
.atLeast(n)and.atMost(n)and.amount(n)to force the number of expected widgets..atMost(0)can be used to test that a widget does not exist! - Deprecated:
spotSingle<W>()is now deprecated. Usespot<W>()instead, orspot<W>().atMost(1)to indicate that only a single widget is expected. - Fix:
.first()and.last() - New:
.atIndex(n)allows to get the widget at a specific index (when multiple are found) - Deprecate:
allWidgetsin favor ofspotAllWidgets()to avoid conflicts with local variables - New:
getDiagnosticProp<T>('name')for easy access to the values of a diagnostic property #40 - New:
hasEffectiveTextStyle,withEffectiveTextStyleMatching(),withEffectiveTextStyle()#36, #38 - Improve:
WidgetSelector.toString()has been improved, has now separators for stages and adds braces. Example:Center with child SizedBox ❯ with parent (Scaffold ᗕ Row) - Added tons of documentation and examples
Advanced API changes
Those changes can be breaking for packages that depend on
spotor advanced usages, but should not affect most users.- Breaking
WidgetSelectornow hasList<ElementFilter> stages, replacing the previousprops,parents,childrenandelementFilters. - Breaking
WidgetSelectorconstructor andcopyWithsignature changed, reflecting the new properties.createElementFilters(),createCandidateGenerator()andtoStringWithoutParents()have been removed. WidgetSelectornow has aquantityConstraintproperty (deprecatesexpectedQuantity) that allows setting theminandmaxnumber of expected widgets.WidgetSelectorreplacesSingleWidgetSelectorandMultiWidgetSelector- Breaking Quantity assertions like
.doesNotExist()or.existsOnce()now returnWidgetMatcher/MultiWidgetMatcherinstead ofWidgetSnapshot. To get theWidgetSnapshotusesnapshot()instead. - Breaking Remove
WidgetSelector.castbecause it lost information and was untested - Breaking
PropFilterhas been renamed toPredicateFilter - Breaking
PredicateWithDescriptionhas been removed - Breaking
CandidateGeneratorhas been removed - Explicitly export all classes/extensions/functions to prevent accidental leaks of internal APIs
- Breaking
-
0.10.0-beta.303 Feb 2024 pre-releaseRelease notes
Open source →This release contains breaking changes to the "internal"
WidgetSelectorAPI. Unless you are using theWidgetSelectordirectly, you should not be affected by this.The end-user
spotAPI is not affected.- Breaking
WidgetSelectornow hasList<ElementFilter> stages, replacing the previousprops,parents,childrenandelementFilters. - Breaking
WidgetSelectorconstructor andcopyWithsignature changed, reflecting the new properties.createElementFilters(),createCandidateGenerator()andtoStringWithoutParents()have been removed. - Breaking
PropFilterhas been renamed toPredicateFilter - Breaking
PredicateWithDescriptionhas been removed - Breaking
CandidateGeneratorhas been removed WidgetSelector.toString()has been improved, has now separators for stages and adds braces. Example:Center with child SizedBox ❯ with parent (Scaffold ᗕ Row)- Fix
.atIndex(n)to be executed at the right time, not after all other filters.
- Breaking
-
0.10.0-beta.227 Jan 2024 pre-releaseRelease notes
Open source →- New
getDiagnosticProp<T>('name')for easy access to the values of a diagnostic property #40 - New
hasEffectiveTextStyle,withEffectiveTextStyleMatching(),withEffectiveTextStyle()#36, #38 - Tons of documentation and examples #37, #39
- Restructure of internal files
- New
-
0.10.0-beta.108 Jan 2024 pre-releaseRelease notes
Open source →Eventually Breaking, but only the class names. The end user API stays the same.
spotSingle<W>()is now deprecated. Usespot<W>()instead, orspot<W>().atMost(1)to indicate that only a single widget is expected.WidgetSelectorreplacesSingleWidgetSelectorandMultiWidgetSelectorWidgetSelectornow has aquantityConstraintproperty (deprecatesexpectedQuantity) that allows setting theminandmaxnumber of expected widgets.- New:
.atIndex(n)allows to get the widget at a specific index (when multiple are found) - Fix:
.first()and.last()now work after calling.copyWith() - Breaking Quantity assertions like
.doesNotExist()or.existsOnce()now returnWidgetMatcher/MultiWidgetMatcherinstead ofWidgetSnapshot. To get theWidgetSnapshotusesnapshot()instead. spotText('a')can now return multiple widgets- Breaking remove
WidgetSelector.castbecause it lost information and was untested
-
0.7.025 Dec 2023Release notes
Open source →-
New prop API with
hasWidgetProp()makes it easy to filter and assert properties of Widgets. This replaces the oldhasProp()method which was based on way to complicated package:checks context.// Old ⛈️ spotSingle<Checkbox>().existsOnce().hasProp( selector: (e) => e.context.nest( () => ['Checkbox', 'value'], (e) => Extracted.value((e.widget as Checkbox).value), ), match: (it) => it.equals(true), );// New ✨ spotSingle<Checkbox>().existsOnce().hasWidgetProp( prop: widgetProp('value', (widget) => widget.value), match: (value) => value.isTrue(), );The prop API is also available for
ElementandRenderObject. <summary> <details>├── Interface "NamedWidgetProp" added ├── Interface "NamedElementProp" added ├── Interface "NamedRenderObjectProp" added ├── Function "widgetProp" added ├── Function "elementProp" added ├── Function "renderObjectProp" added ├─┬ Class SelectorQueries │ ├── Method "whereWidgetProp" added │ ├── Method "whereElementProp" added │ └── Method "whereRenderObjectProp" added └─┬ Class WidgetMatcherExtensions ├── Method "getWidgetProp" added ├── Method "hasWidgetProp" added ├── Method "getElementProp" added ├── Method "hasElementProp" added ├── Method "getRenderObjectProp" added └── Method "hasRenderObjectProp" added</details> </summary>
-
Never miss asserting your
WidgetSelector. All methods returning aWidgetSelectorare now annotated with@useResult. This will cause a lint warning when you only define aWidgetSelectorwithout asserting it.spot<FloatingActionButton>().withChild(spotIcon(Icons.add)); // warning, no assertion final plusFab = spot<FloatingActionButton>().withChild(spotIcon(Icons.add)); // ok, assigned spot<FloatingActionButton>().withChild(spotIcon(Icons.add)).existsOnce(); // ok, asserted -
It is now easy to directly access the Widget of a
SingleWidgetSelectorwithsnapshotWidget(). It also works for the associatedElementandRenderObject. UsesnapshotElement()andsnapshotRenderObject().-final checkbox = spotSingle<Checkbox>().snapshot().widget; +final checkbox = spotSingle<Checkbox>().snapshotWidget(); print(checkbox.checkColor);
-
-
0.6.015 Sep 2023Release notes
Open source →- Add matchers
.existsAtMostOnce()and.existsAtMostNTimes(x)#19 - Add selector
.withParent(parent)/.withParents([...])#21 - Add selector
.withChild(child)/.withChildren([...])#21 - Child selectors now only match children #22
- You can call
act.tap()now with anyWidgetSelectorthat returns a single widget #23
- Add matchers
-
0.5.030 Aug 2023Release notes
Open source →- Breaking
act.tapis now async, useawait act.tap()#17 - New:
spotText('foo')finds any text on screen using "contains". The newAnyTextwidget combinesText,SelectableText,RichTextandEditableText#18 - New:
spotTextWhere((text) => )allows to match text with custom logic #18 - Deprecated:
spotSingleTextandspotTextsare deprecated in favor ofspotTextand the basicspot<Text>(),spot<SelectableText>(), ... #18 - Fix:
hasPropmatcher can now check for null values with(it) => it.isNull()#18 - Improvement:
withDiagnosticPropnow falls back to the default value of aDiagnosticNode#18
- Breaking
-
0.4.316 May 2024Release notes
Open source →- Remove unused dependencies. Fixes incompatibility with latest test_api versions #55
Release notes
Open source →- Remove unused dependencies. Fixes incompatibility with latest test_api versions #55
-
0.4.229 Dec 2023 -
0.4.117 Aug 2023Release notes
Open source →- Added screenshot methods #14
/// Takes a screenshot of the entire window await takeScreenshot(); /// Takes a screenshot of a single Screen/Widget final homePage = spotSingle<HomePage>(); await takeScreenshot(selector: homePage); /// Use it as extension await spotSingle<HomePage>().takeScreenshot(); - Export all types from
checks.dartwhich are required to usehasProp - Update for Flutter 3.13
- Added screenshot methods #14
-
0.4.026 Jun 2023Release notes
Open source →- Added
act.tap(button)to tap widgets #9 - Raise min Flutter version to 3.10.0
- Switch to the official
checkspackage #12 - Rename
SingleWidgetSnapshot.discoveredElements->SingleWidgetSnapshot.discoveredElement#11
- Added
-
0.3.316 May 2024 -
0.3.217 Aug 2023 -
0.3.125 May 2023 -
0.3.025 May 2023Release notes
Open source →spotTextsnow matchesEditableTextandSelectableText#5spotTextsnow has generic type<W>instead of staticText. This changes the return type fromMultiWidgetSelector<Text>->MultiWidgetSelector<W>#5- Changed signature of
SingleWidgetSelector.withPropandMultiWidgetSelector.withProp. - New matchers for
EditableText,ListTile,SelectableText
-
0.2.229 Apr 2023 -
0.2.128 Apr 2023Release notes
Open source →- Fix
WidgetSelectorwith parents that have parents #4 - Require all children selector to match, not just one #4
- Fix
-
0.2.028 Apr 2023Release notes
Open source →- Reworked spot API #3
- Allow defining
WidgetSelectorwith children - Allow defining
WidgetSelectorwith parents - Interop with
FinderAPI - Match properties of widgets (via
DiagnosticsNode) - Allow matching of nested properties (with checks API)
- Generate code for custom properties for Flutter widgets
- Allow generating code for properties of 3rd party widgets
-
0.1.0-preview.203 Nov 2022 pre-release -
0.1.0-preview.102 Sep 2022 pre-release