Skip to content

Quickstart

View raw .md

A three-step walkthrough from zero to driving the bundled example/ Flutter app with a snapshot, a tap and a screenshot through the tapir CLI.

On this page

Prerequisites: Flutter 3.47 or newer (Dart 3.13, the package and example floor) and a clone of the repository. The example/ app already calls TapirPlugin.install() inside !kReleaseMode and resolves tapir from the parent directory. To drive your own app instead, follow Installation first.


1. Start the example app and capture a snapshot#

Start the app through dowel, so the session (the VM Service URI every tapir:* command dials) is recorded. The --cdp-port makes web screenshots reliable in step 3; on a native target (--device=macos, for example) leave it out:

bash
cd example
flutter pub get
dart run tapir start --device=chrome --cdp-port=9333

Once the app is up, run tapir:snap in the same directory to walk the live Semantics tree. The example has many widgets, so narrow the walk with --grep to the counter button:

bash
dart run tapir tapir:snap --grep=Counter

The command connects to the running app via the VM Service and prints the snapshot to stdout. An illustrative output (ancestor lines trimmed):

yaml
- button "Counter: 0" [ref=e1]

Each interactive node carries a [ref=eN] token (here e1). Tokens are tied to the snapshot that issued them: a node that appears again in a later tapir:snap may keep its token, a node that was rebuilt gets a new one, so read the refs from the latest snapshot. They are what you pass to action commands in the next step.


2. Tap a widget using its ref token#

Pass the ref token from the snapshot to tapir:tap:

bash
dart run tapir tapir:tap --ref=e1

The extension looks up e1 in the snapshot registry, runs the actionability gate (see Reference: Actionability gate: the widget must be enabled, have a non-zero rect, be on-viewport, stable, and receive pointer events), then synthesises a pointer-down and pointer-up pair at the widget's centre. The app responds as if a real user tapped the button.

A successful tap prints one line:

text
✓ Tapped e1

When nothing observable changed, the line reads Tapped e1 (no observable change); read it before concluding the tap worked. Add --json for the full envelope, which carries the effect block.

If the widget fails the actionability gate, the command exits with an error message:

text
Widget ref=e1 is not actionable: not enabled

Re-snap after a tap to confirm the UI updated. The refreshed snapshot shows the button as Counter: 1; read its ref again, because it may or may not match the earlier one:

bash
dart run tapir tapir:snap --grep=Counter

3. Capture a screenshot#

Capture an image of the current viewport to verify the app state visually or to save a baseline:

bash
dart run tapir tapir:screenshot --output=counter_after_tap.png

On web with --cdp-port the command captures through Chrome DevTools Protocol; on a native target it asks the app's RepaintBoundary for a frame. The file lands relative to your current working directory, and the format follows the --output extension.

Expected output:

text
✓ Wrote 48213 bytes (47.1 KB, png) to counter_after_tap.png

Open the file to confirm the button reads Counter: 1. You now have the full snap, tap and screenshot loop working end to end. Stop the app with dart run tapir stop.


Gotchas#

Mistake Fix
Running flutter run by hand, then tapir:snap finds no app. Start the app with dart run tapir start, which records the session the tapir:* commands dial.
Tapping an eN ref from an earlier snapshot after the screen changed. Re-snap and use the ref from that snapshot, or mint a qN handle with tapir:find that survives rebuilds.
The web screenshot hangs or returns an error. Start with --cdp-port=<port> so the CLI captures through Chrome DevTools Protocol.
The tap printed no observable change. Nothing changed in the target's own subtree or the route; that does not prove the handler did not fire. Re-snap and check the intended result before retrying.
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.