a11yship 0.1.0
a11yship: ^0.1.0 copied to clipboard
Accessibility checks for Flutter apps (WCAG 2.2, EN 301 549, European Accessibility Act). Finds unlabeled buttons, low contrast, small touch targets and 200% text-size overflows in your tests and poin [...]
a11yship #
Accessibility checks for Flutter apps that point to the line of code to fix.
a11yship runs inside your widget or integration tests, reads the same semantics
tree TalkBack and VoiceOver use, and checks each captured screen against WCAG 2.2:
| Rule | WCAG | Severity |
|---|---|---|
| Interactive element has no accessible name | 4.1.2, 1.1.1 | critical |
| Image has no text alternative | 1.1.1 | serious |
| Text field has no label | 1.3.1, 3.3.2, 4.1.2 | serious |
| Text contrast is too low (measured on rendered pixels) | 1.4.3 | serious |
| Touch target smaller than 24 x 24 (with spacing exception) | 2.5.8 | serious |
| Layout breaks at 200% text size | 1.4.4 | serious |
Accessible name is not descriptive (ic_back.png, "button") |
2.4.6 | moderate |
| Screen has no heading | 1.3.1, 2.4.6 | moderate |
| Below 48dp / 44pt, duplicate names, placeholder-only labels | best practice | minor |
Every finding includes where the widget was created in your code:
CRITICAL Interactive element has no accessible name (1) WCAG 4.1.2, 1.1.1
• [Product list] This button has no accessible name. lib/main.dart:40
SERIOUS Text contrast is too low (1) WCAG 1.4.3
• [Product list] Contrast 2.21:1 for "Free returns within 30 days" (needs 4.5:1). lib/main.dart:48
Install #
flutter pub add dev:a11yship
Use #
Create test/a11y/app_a11y_test.dart:
import 'package:a11yship/a11yship.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
final scan = A11yShip(app: 'My App', guide: TargetGuide.android);
void main() {
setUpAll(A11yShip.loadFonts); // real fonts in screenshots and measurements
tearDownAll(scan.writeReport);
testWidgets('home', (tester) async {
tester.view.physicalSize = const Size(1170, 2532); // a phone
tester.view.devicePixelRatio = 3;
addTearDown(tester.view.reset);
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
await scan.capture(tester, 'Home');
});
}
Run it:
dart run a11yship scan # runs test/a11y, prints a summary
dart run a11yship scan --fail-on serious # fail CI on serious or critical issues
Gate a single test instead: scan.expectNoIssues(severity: Severity.serious);
The report is written to build/a11yship/report.json with a PNG per screen.
Dashboard (optional) #
Upload results to A11yShip to track every release, see issues on screenshots, run the manual WCAG checklist and export a PDF audit report and a European Accessibility Act statement:
A11YSHIP_TOKEN=... A11YSHIP_ENDPOINT=... dart run a11yship scan --upload
Limits #
Automated checks find many common barriers but not all of them. Focus order, gestures, timing and screen reader announcements still need manual testing. A good score is not a certification of WCAG or EAA compliance.