Flutter test reporting
Flutter results reach Qualflare with no package at all:
qf collect reads the JSON that
flutter test already writes, for widget tests on
the host and integration_test runs on Android and
iOS. The optional qualflare_flutter package adds what
only the test author knows — labels, steps and screenshots. This guide covers both, a CI pattern,
the limits, and the
hosted, historical analysis
that sits on top.
Flutter’s own JSON test output, explained
flutter test can write a machine-readable record of
the run alongside its normal console output. One flag, and the file is there when the run ends:
# Flutter's own JSON test output, written to a file.
# The console output stays as it is.
flutter test --file-reporter json:flutter-results.json --file-reporter json: keeps the console output you
already read, which is why it is the one to use in CI. If your pipeline already captures
--machine output, that works too. Either way the
Qualflare CLI reads the file as it is — there is nothing to add to
pubspec.yaml to get results in.
What arrives without the package
- Statuses and durations. Passed, failed, error and skipped, each with how long it took, and the test’s own file and line.
- The real reason a widget test failed. When an
expectfails, Flutter itself reports only “Test failed. See exception logs above.” Qualflare shows the test as failed with the actual assertion message and stack trace. - Retries, attempt by attempt. A retried test arrives with the history of each attempt, so a test that failed and then passed is flagged as flaky rather than counted as a plain pass.
- Output. What the test printed with
printstays attached to it. - Tests that never ran. A test file that fails to compile, or a
setUpAllthat throws, shows up as an error instead of quietly shrinking the suite.
Integration tests on Android and iOS
integration_test runs on an Android emulator or an
iOS simulator write the same JSON and upload the same way. The one difference: Flutter’s output never
names the device, so tell the CLI which platform the run was on with
--platform android or
--platform ios:
# integration_test on an Android emulator or an iOS simulator
flutter test integration_test --file-reporter json:flutter-results.json
# Flutter's output never names the device, so say which platform it was
qf myapp collect flutter-results.json --platform android Send Flutter results to Qualflare
Run the suite with the file reporter, then point the CLI at the file. Reading Flutter’s output needs Qualflare CLI 0.2.0 or later:
# Flutter's own JSON test output, written to a file.
# The console output stays as it is.
flutter test --file-reporter json:flutter-results.json qf myapp collect flutter-results.json
The general shape is qf <project> collect [files...] [flags].
Useful flags: --environment,
--branch,
--commit, and
--dry-run to preview what would be uploaded. In CI,
upload even when the suite fails — a reporting step that only runs on green builds is useless on the day
you need it:
# .github/workflows/flutter.yml
- name: Run Flutter tests
run: flutter test --file-reporter json:flutter-results.json
- name: Upload results to Qualflare
if: always() # upload even when tests fail — that's the point
run: qf myapp collect flutter-results.json The same two steps work in GitLab CI, Bitbucket Pipelines and Jenkins. Authenticate the CLI once with your Qualflare access token, stored as a CI secret — see the CLI docs.
Adding labels, steps and screenshots with qualflare_flutter
The optional qualflare_flutter package is a dev dependency. Reading its data needs Qualflare CLI 0.3.0 or later:
flutter pub add --dev qualflare_flutter
From inside a test you can add labels, links (issue, tms or custom), tags, a priority, named steps that
nest, attachments and screenshots. The names match the qualflare.*
API of Qualflare’s JavaScript reporters:
import 'package:qualflare_flutter/qualflare_flutter.dart';
testWidgets('pays with a card', (tester) async {
qualflare.label('owner', 'mobile-team');
qualflare.link('https://tracker/QF-1', type: 'issue', name: 'QF-1');
qualflare.tags(['checkout', 'smoke']);
qualflare.priority('high');
await qualflare.step('fill in the card', () async {
// steps nest
});
await qualflare.screenshot(tester, 'checkout');
});
// testWidgets, plus a screenshot of the failing screen
qualflareTestWidgets('shows the receipt', (tester) async {
// ...
}); qualflare.screenshot(tester, 'name') works in widget
tests on the host and in integration_test runs on
devices. qualflareTestWidgets is a drop-in for
testWidgets that attaches a screenshot of the failing
screen. Two options cover the cases where the default capture falls short:
// Host widget tests draw text in Flutter's test font.
// Load real fonts once per test file (opt-in):
setUpAll(() => qualflare.loadFonts());
// On Android or iOS, let the device take the capture itself:
await qualflare.screenshot(tester, 'map', native: true); native: true, on Android and iOS, uses the device’s own
capture, which includes system UI and platform views.
qualflare.loadFonts() is opt-in, so host widget-test
screenshots show real text instead of Flutter’s test font only when you ask for it.
How it works. The package writes its data into the same
flutter test results file. Flutter has no reporter
plug-in API, and integration tests run on the device, so the results file is the one channel that works
for both. The two commands stay the same.
Limitations
- Attachments are capped. Each attachment or screenshot can be up to 5 MB, and a test’s attachments up to 20 MB in total. Keep non-image attachments small.
- No Flutter web. Tests run with
--platform chromeare not supported. - Maestro still has a place. For black-box flows that drive the built app from outside, Maestro is still useful, and its results land in the same project.
See a real Flutter project
The package’s own example suite runs on an Android emulator and an iOS simulator in CI and uploads to a public Qualflare project. The source is on GitHub.
What you get on top of Flutter’s own output
- History & trends. Every CI run becomes a launch, so pass rate and failures are visible across runs and branches rather than one console log at a time.
- Flaky tests flagged from retries. A test that failed and then passed arrives marked flaky with each attempt kept, instead of disappearing into a green build.
- AI failure clustering. When one change breaks many tests, Qualflare groups the failures by root cause, so you triage a few clusters instead of every stack trace.
- One place for the whole mobile stack. Flutter results sit next to Espresso, XCTest and Maestro results in the same project.
Get AI analysis on your Flutter runs
Start free — run flutter test, upload with qf collect, and get your first AI analysis in minutes.
Testing the native layers too? See our guides to Espresso reporting for Android, XCTest reporting for iOS, and Maestro reporting for cross-platform E2E flows, or the full list of supported frameworks. For the bigger picture, read fixing flaky mobile tests or the complete guide to mobile test management. Weighing tools? See how Qualflare compares to other test management platforms, or browse all framework reporting guides.
Frequently asked questions
Do I need Maestro to report Flutter tests?
No. Flutter’s own widget and integration tests upload directly: run flutter test --file-reporter json:flutter-results.json, then qf <project> collect flutter-results.json. Maestro is still an option for black-box UI flows that drive the built app from outside, and its results land in the same project.
Do I need a Flutter package to send results to Qualflare?
No. The Qualflare CLI (0.2.0 or later) reads the JSON that flutter test already writes: statuses, durations, the file and line of each test, print output, the real assertion message for a failed widget test, and per-attempt retry history. The optional qualflare_flutter package adds what only the test author knows — labels, links, tags, priority, named steps, attachments and screenshots. Reading its data needs qf 0.3.0 or later.
Why does a failed widget test show the real assertion message?
When an expect fails in a widget test, Flutter’s own output reports only “Test failed. See exception logs above.” The assertion message and stack trace are in the output, just not in the error. qf collect recovers them, so the test arrives as failed with the message and stack that explain it.
Does it work for integration_test on Android and iOS?
Yes. integration_test runs on an Android emulator or an iOS simulator report the same way as host tests. Flutter’s output never names the device, so pass --platform android or --platform ios to qf collect. With qualflare_flutter, screenshots work on devices too, and native: true lets Android or iOS take the capture itself, which includes system UI and platform views.
Is Flutter web supported?
No. Tests run with --platform chrome are not supported. Widget tests on the host and integration tests on Android and iOS are.
Does Qualflare run my Flutter tests?
No. Qualflare is a results-management and observability layer, not a device cloud. Your suite runs wherever it already runs — locally, in CI, on an emulator or a simulator — and Qualflare reads the results file it writes.
Setup reflects the Qualflare CLI (docs.qualflare.com) and the qualflare_flutter package as of October 2026, measured on real Flutter runs on the host, an Android emulator and an iOS simulator. Published 4 October 2026. Written by İbrahim Süren, founder of Qualflare.