attest_flutter 1.0.0 copy "attest_flutter: ^1.0.0" to clipboard
attest_flutter: ^1.0.0 copied to clipboard

Flutter widget-test integration for attest: a WidgetTester accessibility audit, raster and text-scale collectors, and gate matchers for CI.

attest_flutter #

Accessibility testing for Flutter — in your widget tests, gated in CI.

pub package pub points likes license: BSD-3-Clause

Web accessibility tools like axe-core, Lighthouse and WAVE can't test a Flutter app — Flutter paints to a canvas with no DOM, so to those tools your whole app is one anonymous <canvas>. attest_flutter audits accessibility from inside an ordinary WidgetTester: it walks the live semantics tree, measures real colour contrast from rendered pixels, catches layout overflow at 200% text size, and maps every finding to a WCAG success criterion and EN 301 549 clause, pointed at the file:line that produced it. It gates CI on new issues and can print the transcript a screen reader (TalkBack / VoiceOver) would announce.

Honest framing. Automated checks catch roughly a third of accessibility issues — the machine-checkable ones. This does not make your app WCAG- or EAA-compliant, and it complements real screen-reader testing rather than replacing it. It gives you automated coverage of the checkable part plus a structured checklist for the rest.

Correctness is measured, not claimed: every rule is scored against a validation corpus of 109 hand-labelled cases on each CI run and currently holds precision 1.0 and recall 1.0 with zero false positives on clean fixtures (details in the attest core README).

Install #

flutter pub add dev:attest_flutter

Quick start #

Audit any pumped screen from a widget test:

import 'package:flutter_test/flutter_test.dart';
import 'package:attest_flutter/attest_flutter.dart';

void main() {
  testWidgets('CheckoutScreen is accessible', (tester) async {
    await tester.pumpWidget(const MyApp(home: CheckoutScreen()));

    final report = await tester.auditAccessibility();

    expect(report, passesAccessibilityGate());
  });
}

When the gate fails, the message is grouped and source-located — one entry per finding, each citing its WCAG criterion and EN 301 549 clause with a concrete fix and the file:line to change.

What it checks #

Twelve rules across three detection methods that source-level linters can't do:

  • Names & roles — unlabeled buttons, images with no alt text, form fields with no label, generic "Button" labels, duplicate/ambiguous names.
  • Rendered output — real text contrast measured from the rasterized screen, and layout overflow / reflow when the system font is scaled to 200%.
  • Reachability & structure — touch target size, interactive elements hidden from assistive technology, illogical focus order, missing heading semantics.

Every finding cites WCAG 2.1 / 2.2 and the matching EN 301 549 clause, so the output maps to the standard an auditor will actually ask about.

Fail CI on new issues #

Write each report to JSON, then let the attest_cli ci command diff a baseline by fingerprint and fail only on new findings — like a coverage gate. It emits SARIF for GitHub's Problems panel and HTML for humans. See the CLI README for a ready-made GitHub Actions workflow.

Screen-reader transcript #

auditAccessibility() also returns the sequence a screen reader would announce, in traversal order — the fastest way to hear what your screen sounds like without a device:

final report = await tester.auditAccessibility();
print(report.transcript); // [Your orders, heading, Pay, button, Email, edit box, …]

FAQ #

How do I test accessibility in a Flutter app? #

Pump the screen in a widget test and call tester.auditAccessibility(), then assert passesAccessibilityGate(). It runs on the flutter_tester — no device, no real screen reader needed for the automated pass.

Why can't I use axe-core, Lighthouse or Accessibility Scanner for this? #

Those read the DOM (web) or the Android view hierarchy. Flutter renders its own widgets to a canvas, so they see one opaque surface. attest reads Flutter's semantics tree directly — the same data TalkBack and VoiceOver consume.

Does this make my app WCAG or EAA compliant? #

No. No automated tool can. It covers the machine-checkable criteria and hands you a checklist for the rest (meaningful alt text, media captions, real assistive-technology testing).

How do I silence a false positive? #

Mute any rule in one line via RuleConfig(disabledRules: {'attest/heading-structure'}). Heuristic rules ship as warnings for exactly this reason.

  • attest — the pure-Dart rule engine and data model (no Flutter dependency).
  • attest_cli — the CI baseline gate and SARIF / HTML / JSON reporter.

Supported versions #

Requires Flutter ≥ 3.32 (Dart ≥ 3.8): the snapshot builder maps the tri-state SemanticsData.flagsCollection API introduced there. The support policy: the current and the previous three stable Flutter releases, with each new stable Flutter tracked within one release cycle — semantics-API drift is a standing maintenance commitment, not an afterthought.

API stability #

The developer-facing surface — tester.auditAccessibility(), the gate matchers and everything re-exported from the core — is frozen for 1.0: no breaking change within a major. The plumbing classes (SemanticsSnapshotBuilder, RasterCollector, TextScaleCollector) are annotated @experimental: ordinary tests never need them directly, and their signatures may still evolve (notably for integration_test support).

Feedback — especially false positives from real apps — is very welcome on the issue tracker.

License #

BSD-3-Clause.

1
likes
160
points
--
downloads

Documentation

API reference

Publisher

verified publishersahland.tech

Weekly Downloads

Flutter widget-test integration for attest: a WidgetTester accessibility audit, raster and text-scale collectors, and gate matchers for CI.

Repository (GitHub)
View/report issues
Contributing

Topics

#accessibility #a11y #wcag #testing

License

BSD-3-Clause (license)

Dependencies

attest, flutter, flutter_test, meta

More

Packages that depend on attest_flutter