PackageTrack
Sign in Get early access

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 2026
2023 2024 2025 2026
Release Pre-release

Releases

latest 30
  1. 0.20.0 11 Aug 2026
    Release notes

    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, getDiagnosticProp and withDiagnosticProp now cache debugFillProperties. ~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 failing act.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 #150

      final 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, TapCoveredReason and TapUnknownReason. Also add TapInspection, TapFailureReason, TapWidgetInfo, TapHitTestInfo, TapHitSample, TapSamples and TapBlocker.

      TapInspection.samples reports 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 samples instead of up front. An inspection describes the tree of the frame it was created in, so reading samples after a pump throws instead of reporting what a different tree does.

    • New: act.tap() throws a TapFailure that carries the TapInspection explaining the failure, so the reason can be asserted without matching on the message. TapFailure extends TestFailure, existing expectations keep working. #150

      await expectLater(
        () => act.tap(spot<ElevatedButton>()),
        throwsA(
          isA<TapFailure>().having(
            (it) => it.inspection.tapFailure?.reason,
            'reason',
            isA<TapCoveredReason>(),
          ),
        ),
      );
    • Fix: act.tap() now finds an AbsorbPointer anywhere above the target. It previously only looked directly below the widget #150

    • Fix: act.tap() now reports the outermost AbsorbPointer or IgnorePointer above 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 pumpAndSettle could have been a pump. 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 adds Timeline.renderedFrameCount and TimelineEvent.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 expect or 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 a Scrollable as dragStart, so keyed or otherwise untyped scrollable selectors drag from that scrollable directly. #133 (thx @trejdych)

    Selectors and queries

    • New: spotAtPosition and WidgetSelector.atPosition to query widgets on the hit-test path for a global screen position. #28
    • New: WidgetSelector.isPresent() and isAbsent() return bool without failing the test, and countWidgets() returns the number of matching widgets. Use them to branch test logic on the presence, absence or quantity of a widget. #30
      if (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 on WidgetSelector, alongside the existing getWidgetProp, getElementProp, getStateProp and getRenderObjectProp readers. #30
      final message = spot<Tooltip>().getDiagnosticProp<String>('message');
    • New: WidgetSelector<AnyText>.whereIsEditable() and whereIsNotEditable() filter text matches by whether they come from an editable text input.
      spotText('username').whereIsEditable().existsOnce();
      spotText('Username').whereIsNotEditable().existsOnce();
    • New: WidgetSnapshot.queryStats reports how much work the query engine performed to evaluate a selector, useful to debug slow queries. #148
    • Fix: A WidgetMatcher now 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-op WidgetTypeFilter<Widget> at the root.

    Text matching

    • New: Text matching ignores invisible and special whitespace, so tests can use regular characters. spotText, spotTextWhere, whereText, withText and hasText strip 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, the U+FFFC WidgetSpan placeholder) and line breaks are kept. #138 (thx @MichaelTamm)
      spotText('foobar').existsOnce();  // matches Text('foo\u{200B}bar')
      spotText('foo bar').existsOnce(); // matches Text('foo\u{00A0}bar')
      To match exact characters, pass raw: true to spotText/spotTextWhere, or use whereRawText/withRawText/hasRawText on WidgetSelector<AnyText>. Also exposes AnyText.normalizeVisibleText, AnyText.extractText, and AnyTextContent (raw/normalized).
    • Deprecated: spotText(text, exact: true) is now spotText(text, whole: true) — the flag controls whole-string vs. substring matching, not character handling. exact still works. #138

    Screenshots

    • New: ScreenshotAnnotator.cacheKey (default => null) allows caching of annotations #160
    • Fix: Export ScreenshotAnnotator, which has already been a parameter of takeScreenshot(annotators: ...)

    Fonts

    • Fix: loadAppFonts() now also registers a package's own fonts under packages/<self>/MyFont, so fonts referenced via package: '<self>' render instead of falling back to Ahem. #141
    Open source →
    Release notes

    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, getDiagnosticProp and withDiagnosticProp now cache debugFillProperties. ~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 failing act.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 #150

      final 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, TapCoveredReason and TapUnknownReason. Also add TapInspection, TapFailureReason, TapWidgetInfo, TapHitTestInfo, TapHitSample, TapSamples and TapBlocker.

      TapInspection.samples reports 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 samples instead of up front. An inspection describes the tree of the frame it was created in, so reading samples after a pump throws instead of reporting what a different tree does.

    • New: act.tap() throws a TapFailure that carries the TapInspection explaining the failure, so the reason can be asserted without matching on the message. TapFailure extends TestFailure, existing expectations keep working. #150

      await expectLater(
        () => act.tap(spot<ElevatedButton>()),
        throwsA(
          isA<TapFailure>().having(
            (it) => it.inspection.tapFailure?.reason,
            'reason',
            isA<TapCoveredReason>(),
          ),
        ),
      );
      
    • Fix: act.tap() now finds an AbsorbPointer anywhere above the target. It previously only looked directly below the widget #150

    • Fix: act.tap() now reports the outermost AbsorbPointer or IgnorePointer above 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 a Scrollable as dragStart, so keyed or otherwise untyped scrollable selectors drag from that scrollable directly. #133 (thx @trejdych)

    Selectors and queries

    • New: spotAtPosition and WidgetSelector.atPosition to query widgets on the hit-test path for a global screen position. #28
    • New: WidgetSelector.isPresent() and isAbsent() return bool without failing the test, and countWidgets() returns the number of matching widgets. Use them to branch test logic on the presence, absence or quantity of a widget. #30
      if (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 on WidgetSelector, alongside the existing getWidgetProp, getElementProp, getStateProp and getRenderObjectProp readers. #30
      final message = spot<Tooltip>().getDiagnosticProp<String>('message');
      
    • New: WidgetSelector<AnyText>.whereIsEditable() and whereIsNotEditable() filter text matches by whether they come from an editable text input.
      spotText('username').whereIsEditable().existsOnce();
      spotText('Username').whereIsNotEditable().existsOnce();
      
    • New: WidgetSnapshot.queryStats reports how much work the query engine performed to evaluate a selector, useful to debug slow queries. #148
    • Fix: A WidgetMatcher now 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-op WidgetTypeFilter<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, withText and hasText strip 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, the U+FFFC WidgetSpan placeholder) and line breaks are kept. #138 (thx @MichaelTamm)
      spotText('foobar').existsOnce();  // matches Text('foo\u{200B}bar')
      spotText('foo bar').existsOnce(); // matches Text('foo\u{00A0}bar')
      
      To match exact characters, pass raw: true to spotText/spotTextWhere, or use whereRawText/withRawText/hasRawText on WidgetSelector<AnyText>. Also exposes AnyText.normalizeVisibleText, AnyText.extractText, and AnyTextContent (raw/normalized).
    • Deprecated: spotText(text, exact: true) is now spotText(text, whole: true) — the flag controls whole-string vs. substring matching, not character handling. exact still works. #138

    Screenshots

    • New: ScreenshotAnnotator.cacheKey (default => null) allows caching of annotations #160
    • Fix: Export ScreenshotAnnotator, which has already been a parameter of takeScreenshot(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 under packages/<self>/MyFont, so fonts referenced via package: '<self>' render instead of falling back to Ahem. #141
    • Fix: Handle whitespaces in dependency font family names #130
    Open source →
  2. 0.18.0 28 May 2025
    Release notes
    • Breaking: Add act.dragUntilVisible() now moves the target in the center of the viewport (one additional drag). parameter moveStep is now optional, default to half the scrollable size. The direction can be controlled with bool 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 for GenericWidget<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.tap when 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: file property. Still returns File but signature now returns dynamic for web support. Use createTempPngFile() 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
    Open source →
    Release notes
    • Breaking: Add act.dragUntilVisible() now moves the target in the center of the viewport (one additional drag). parameter moveStep is now optional, default to half the scrollable size. The direction can be controlled with bool 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 for GenericWidget<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.tap when 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: file property. Still returns File but signature now returns dynamic for web support. Use createTempPngFile() 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
    Open source →
  3. 0.17.0 03 Jan 2025
    Release notes
    • Timeline is now generated with Jaspr #76
    • New: act.tapAt() #80
    • New Timeline.addEvent() now returns the TimelineEventId id
    • New Timeline.updateEvent(id) and Timeline.removeEvent(id)
    • Fix: Added events to timeline while being off #88
    • Fix: Export stateProp #93
    • Improved screenshot detail page in timeline #91, #92
    Open source →
    Release notes
    • Timeline is now generated with Jaspr #76
    • New: act.tapAt() #80
    • New Timeline.addEvent() now returns the TimelineEventId id
    • New Timeline.updateEvent(id) and Timeline.removeEvent(id)
    • Fix: Added events to timeline while being off #88
    • Fix: Export stateProp #93
    • Improved screenshot detail page in timeline #91, #92
    Open source →
  4. 0.16.0 26 Nov 2024
    Release notes
    • Add snapshotState<S>()
      final state = spot<MyContainer>().snapshotState<MyContainerState>()
    • Add snapshotRenderBox()
    • Export WidgetPresence
    • Add @useResult to .atMost(N), .atLeast(N), .amount(N) and .existsAtMostNTimes(N) to prevent missing assertions
    • Fix existsAtLeastNTimes dumping the widget tree to console
    • Fix image rendering with TimelineMode.always
    • Add Timeline to /README.md
    • Add act to /README.md
    Open source →
    Release notes
    • Add snapshotState<S>() final state = spot<MyContainer>().snapshotState<MyContainerState>()
    • Add snapshotRenderBox()
    • Export WidgetPresence
    • Add @useResult to .atMost(N), .atLeast(N), .amount(N) and .existsAtMostNTimes(N) to prevent missing assertions
    • Fix existsAtLeastNTimes dumping the widget tree to console
    • Fix image rendering with TimelineMode.always
    • Add Timeline to /README.md
    • Add act to /README.md
    Open source →
  5. 0.15.0 21 Nov 2024
    Release notes
    • 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 #71
      • spot<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 getStateProp and stateProp to access state properties #71
      spot<_MyContainer>().existsOnce().getStateProp(stateProp('innerValue', (_MyContainerState s) => s.innerValue));
    • New timeline mode TimelineMode.always to always print a timeline after each test #68
    • Deprecate TimelineMode.record in favor of TimelineMode.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() and whereRenderObjectProp() #67
    Open source →
    Release notes
    • 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 #71
      • spot<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 getStateProp and stateProp to access state properties #71 spot<_MyContainer>().existsOnce().getStateProp(stateProp('innerValue', (_MyContainerState s) => s.innerValue));
    • New timeline mode TimelineMode.always to always print a timeline after each test #68
    • Deprecate TimelineMode.record in favor of TimelineMode.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() and whereRenderObjectProp() #67
    Open source →
  6. 0.14.0 07 Nov 2024
    Release notes
    • New: Timeline! Failing tests now print a timeline with screenshots of all interactions (actions and assertions) as HTML report #57
    • act.tap now checks for multiple tappable position when the center is not tappable for some reason #60
    • act.tap now 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
    Open source →
    Release notes
    • New: Timeline! Failing tests now print a timeline with screenshots of all interactions (actions and assertions) as HTML report #57
    • act.tap now checks for multiple tappable position when the center is not tappable for some reason #60
    • act.tap now 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
    Open source →
  7. 0.13.0 19 Jun 2024
    Release notes
    • Add act.dragUntilVisible() #59
    Open source →
    Release notes
    • Add act.dragUntilVisible() #59
    Open source →
  8. 0.12.1 16 May 2024
    Release notes
    • Support for Flutter 3.22
    • Remove unused dependencies #55
    Open source →
    Release notes
    • Support for Flutter 3.22
    • Remove unused dependencies #55
    Open source →
  9. 0.12.0 22 Mar 2024
    Release notes
    • Breaking Offstage support. By default Offstage widgets are not found by spot<W>(). Use spotOffstage().spot<W>() to find them. spotAllWidgets() returns onstage and offstage widgets. Use .overrideWidgetPresence(WidgetPresence.offstage) to modify a WidgetSelector to search for offstage, onstage or combined #45
    • New: act.enterText(spot<TextField>(), 'Hello World!') allows to enter text into a EditableText #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
    Open source →
  10. 0.11.0 19 Feb 2024
    Release notes
    • Add support for Flutter 3.20
    • Update checks to 0.3.0 #48
    • Remove deprecated property selector from withProp() and hasProp(). Use elementSelector instead
    • Widen test_api version range to include 0.7.X
    Open source →
  11. 0.10.0 05 Feb 2024
    Release notes

    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. Use spot<W>() instead, or spot<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: allWidgets in favor of spotAllWidgets() 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 spot or advanced usages, but should not affect most users.

    • Breaking WidgetSelector now has List<ElementFilter> stages, replacing the previous props, parents, children and elementFilters.
    • Breaking WidgetSelector constructor and copyWith signature changed, reflecting the new properties. createElementFilters(), createCandidateGenerator() and toStringWithoutParents() have been removed.
    • WidgetSelector now has a quantityConstraint property (deprecates expectedQuantity) that allows setting the min and max number of expected widgets.
    • WidgetSelector replaces SingleWidgetSelector and MultiWidgetSelector
    • Breaking Quantity assertions like .doesNotExist() or .existsOnce() now return WidgetMatcher/MultiWidgetMatcher instead of WidgetSnapshot. To get the WidgetSnapshot use snapshot() instead.
    • Breaking Remove WidgetSelector.cast because it lost information and was untested
    • Breaking PropFilter has been renamed to PredicateFilter
    • Breaking PredicateWithDescription has been removed
    • Breaking CandidateGenerator has been removed
    • Explicitly export all classes/extensions/functions to prevent accidental leaks of internal APIs
    Open source →
  12. 0.10.0-beta.3 03 Feb 2024 pre-release
    Release notes

    This release contains breaking changes to the "internal" WidgetSelector API. Unless you are using the WidgetSelector directly, you should not be affected by this.

    The end-user spot API is not affected.

    • Breaking WidgetSelector now has List<ElementFilter> stages, replacing the previous props, parents, children and elementFilters.
    • Breaking WidgetSelector constructor and copyWith signature changed, reflecting the new properties. createElementFilters(), createCandidateGenerator() and toStringWithoutParents() have been removed.
    • Breaking PropFilter has been renamed to PredicateFilter
    • Breaking PredicateWithDescription has been removed
    • Breaking CandidateGenerator has 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.
    Open source →
  13. 0.10.0-beta.2 27 Jan 2024 pre-release
    Release notes
    • 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
    Open source →
  14. 0.10.0-beta.1 08 Jan 2024 pre-release
    Release notes

    Eventually Breaking, but only the class names. The end user API stays the same.

    • spotSingle<W>() is now deprecated. Use spot<W>() instead, or spot<W>().atMost(1) to indicate that only a single widget is expected.
    • WidgetSelector replaces SingleWidgetSelector and MultiWidgetSelector
    • WidgetSelector now has a quantityConstraint property (deprecates expectedQuantity) that allows setting the min and max number 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 return WidgetMatcher/MultiWidgetMatcher instead of WidgetSnapshot. To get the WidgetSnapshot use snapshot() instead.
    • spotText('a') can now return multiple widgets
    • Breaking remove WidgetSelector.cast because it lost information and was untested
    Open source →
  15. 0.7.0 25 Dec 2023
    Release notes
    • New prop API with hasWidgetProp() makes it easy to filter and assert properties of Widgets. This replaces the old hasProp() 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 Element and RenderObject. <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 a WidgetSelector are now annotated with @useResult. This will cause a lint warning when you only define a WidgetSelector without 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 SingleWidgetSelector with snapshotWidget(). It also works for the associated Element and RenderObject. Use snapshotElement() and snapshotRenderObject().

      -final checkbox = spotSingle<Checkbox>().snapshot().widget;
      +final checkbox = spotSingle<Checkbox>().snapshotWidget();
      print(checkbox.checkColor);
      
    Open source →
  16. 0.6.0 15 Sep 2023
    Release notes
    • 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 any WidgetSelector that returns a single widget #23
    Open source →
  17. 0.5.0 30 Aug 2023
    Release notes
    • Breaking act.tap is now async, use await act.tap() #17
    • New: spotText('foo') finds any text on screen using "contains". The new AnyText widget combines Text, SelectableText, RichText and EditableText #18
    • New: spotTextWhere((text) => ) allows to match text with custom logic #18
    • Deprecated: spotSingleText and spotTexts are deprecated in favor of spotText and the basic spot<Text>(), spot<SelectableText>(), ... #18
    • Fix: hasProp matcher can now check for null values with (it) => it.isNull() #18
    • Improvement: withDiagnosticProp now falls back to the default value of a DiagnosticNode #18
    Open source →
  18. 0.4.3 16 May 2024
    Release notes
    • Remove unused dependencies. Fixes incompatibility with latest test_api versions #55
    Open source →
    Release notes
    • Remove unused dependencies. Fixes incompatibility with latest test_api versions #55
    Open source →
  19. 0.4.2 29 Dec 2023
    Release notes
    • Switch to renderView.size to get the window size
    Open source →
  20. 0.4.1 17 Aug 2023
    Release notes
    • 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.dart which are required to use hasProp
    • Update for Flutter 3.13
    Open source →
  21. 0.4.0 26 Jun 2023
    Release notes
    • Added act.tap(button) to tap widgets #9
    • Raise min Flutter version to 3.10.0
    • Switch to the official checks package #12
    • Rename SingleWidgetSnapshot.discoveredElements -> SingleWidgetSnapshot.discoveredElement #11
    Open source →
  22. 0.3.3 16 May 2024
    Release notes
    • Widen test_api range to support Flutter 3.22
    Open source →
    Release notes
    • Widen test_api range to support Flutter 3.22
    Open source →
  23. 0.3.2 17 Aug 2023
    Release notes
    • Export all types from checks.dart which are required to use hasProp
    Open source →
  24. 0.3.1 25 May 2023
    Release notes
    • Fix compilation error with Flutter 3.0.0
    Open source →
  25. 0.3.0 25 May 2023
    Release notes
    • spotTexts now matches EditableText and SelectableText #5
    • spotTexts now has generic type <W> instead of static Text. This changes the return type from MultiWidgetSelector<Text> -> MultiWidgetSelector<W> #5
    • Changed signature of SingleWidgetSelector.withProp and MultiWidgetSelector.withProp.
    • New matchers for EditableText, ListTile, SelectableText
    Open source →
  26. 0.2.2 29 Apr 2023
    Release notes
    • Support for Flutter 3.0.0 / Dart 2.17
    Open source →
  27. 0.2.1 28 Apr 2023
    Release notes
    • Fix WidgetSelector with parents that have parents #4
    • Require all children selector to match, not just one #4
    Open source →
  28. 0.2.0 28 Apr 2023
    Release notes
    • Reworked spot API #3
    • Allow defining WidgetSelector with children
    • Allow defining WidgetSelector with parents
    • Interop with Finder API
    • 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
    Open source →
  29. 0.1.0-preview.2 03 Nov 2022 pre-release
    Release notes
    • Update package description
    • Add issue_tracker link
    • Add example folder
    Open source →
  30. 0.1.0-preview.1 02 Sep 2022 pre-release
    Release notes
    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive