Agent skill
flutter-accessibility
Flutter accessibility patterns for WCAG 2.2 Level AA compliance: Semantics widget, screen readers, focus management, dynamic font sizing, gesture alternatives, and touch targets (48x48dp minimum). Use when implementing a11y features, auditing accessibility, or ensuring WCAG 2.2 compliance (government deadline April 24, 2026).
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/flutter-accessibility
SKILL.md
Flutter Accessibility
⚠️ WCAG 2.2 Level AA Compliance (CRITICAL)
Government Compliance Deadline: April 24, 2026 for entities with 50k+ population
Flutter has first-class support for WCAG 2.2 accessibility standards. This guide covers all requirements for Level AA compliance.
WCAG 2.2 New Success Criteria (Added over 2.1)
WCAG 2.2 adds 9 new success criteria that Flutter apps must meet:
| Criterion | Level | Requirement | Flutter Implementation |
|---|---|---|---|
| 2.4.11 Focus Appearance | AA | Visible focus indicator (min 2px, contrast 3:1) | Use FocusNode with custom indicator |
| 2.4.12 Focus Not Obscured (Minimum) | AA | Focused element not fully hidden | Ensure dialogs/overlays don't cover focus |
| 2.4.13 Focus Not Obscured (Enhanced) | AAA | Focused element fully visible | Not required for AA |
| 2.5.7 Dragging Movements | AA | Alternative to drag gestures | Provide tap alternative for all drags |
| 2.5.8 Target Size (Minimum) | AA | 24x24px minimum (except inline text) | Flutter: 48x48dp recommended |
| 3.2.6 Consistent Help | A | Help in same location | Maintain consistent help button position |
| 3.3.7 Redundant Entry | A | Don't ask for same info twice | Auto-fill or remember previous inputs |
| 3.3.8 Accessible Authentication | AA | No cognitive function tests (CAPTCHA) | Use biometric, OTP, or link-based auth |
| 3.3.9 Accessible Authentication (Enhanced) | AAA | Alternative for object recognition | Not required for AA |
Key Requirements
| Requirement | WCAG 2.2 Standard | Flutter Target |
|---|---|---|
| Touch targets | 2.5.8 (AA): 24x24px min | 48x48dp minimum (exceeds standard) |
| Color contrast | 1.4.3 (AA): 4.5:1 text, 3:1 large | Use Material 3 ColorScheme |
| Focus order | 2.4.3 (A): Logical sequence | Use FocusTraversalGroup |
| Focus indicator | 2.4.11 (AA): 2px, 3:1 contrast | Custom FocusNode decoration |
| Screen reader | 4.1.2 (A): Name, role, value | Semantics widget |
| Gesture alternatives | 2.5.7 (AA): No drag-only | Provide tap alternatives |
| Font sizing | 1.4.4 (AA): Resize to 200% | Respect MediaQuery.textScaleFactor |
Semantics Widget
Semantics(
label: 'Event details',
hint: 'Double tap to view',
button: true,
child: CustomWidget(),
)
Images
// Informative
Image.asset('hero.png', semanticLabel: 'Dancer performing ballet')
// Decorative
Image.asset('pattern.png', excludeFromSemantics: true)
IconButtons (REQUIRED)
IconButton(
icon: const Icon(Icons.delete),
tooltip: 'Delete event', // REQUIRED
onPressed: _delete,
)
Touch Targets
// Minimum 48x48
SizedBox(
width: 48,
height: 48,
child: IconButton(icon: Icon(Icons.close), onPressed: _close),
)
// Or use padding
InkWell(
onTap: _tap,
child: Padding(
padding: const EdgeInsets.all(12), // Expand hit area
child: Icon(Icons.star, size: 24),
),
)
Color Contrast
// Check in theme
final primary = Theme.of(context).colorScheme.primary;
final onPrimary = Theme.of(context).colorScheme.onPrimary;
// onPrimary is designed to have 4.5:1 contrast with primary
MergeSemantics
// Combine into single announcement
MergeSemantics(
child: ListTile(
leading: const Icon(Icons.event),
title: const Text('Ballet Performance'),
subtitle: const Text('Tomorrow at 7pm'),
),
)
// Announces: "Ballet Performance, Tomorrow at 7pm"
ExcludeSemantics
ExcludeSemantics(
child: DecorativeWidget(), // Skip in screen reader
)
Live Regions
Semantics(
liveRegion: true,
child: Text(statusMessage), // Announced when changed
)
Testing
# iOS: Settings > Accessibility > VoiceOver
# Android: Settings > Accessibility > TalkBack
# In tests
testWidgets('has semantic label', (tester) async {
await tester.pumpWidget(MyWidget());
expect(find.bySemanticsLabel('Delete event'), findsOneWidget);
});
Focus Management (WCAG 2.4.11)
Requirement: Visible focus indicator with minimum 2px thickness and 3:1 contrast ratio.
class AccessibleButton extends StatefulWidget {
final VoidCallback onPressed;
final Widget child;
const AccessibleButton({required this.onPressed, required this.child});
@override
State<AccessibleButton> createState() => _AccessibleButtonState();
}
class _AccessibleButtonState extends State<AccessibleButton> {
final FocusNode _focusNode = FocusNode();
bool _isFocused = false;
@override
void initState() {
super.initState();
_focusNode.addListener(() {
setState(() {
_isFocused = _focusNode.hasFocus;
});
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Focus(
focusNode: _focusNode,
child: Container(
decoration: _isFocused
? BoxDecoration(
border: Border.all(
color: Theme.of(context).colorScheme.primary,
width: 3, // WCAG 2.2: Minimum 2px
),
borderRadius: BorderRadius.circular(8),
)
: null,
child: ElevatedButton(
onPressed: widget.onPressed,
focusNode: _focusNode,
child: widget.child,
),
),
);
}
}
Gesture Alternatives (WCAG 2.5.7)
Requirement: Provide tap alternative for all drag gestures.
// ❌ BAD: Drag-only interaction
Draggable(
data: item,
child: ItemWidget(item),
feedback: ItemWidget(item),
);
// ✅ GOOD: Drag with tap alternative
class AccessibleDraggable extends StatelessWidget {
final Item item;
final VoidCallback onTap; // Tap alternative
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap, // Tap to move/select
onLongPress: () {
// Show context menu with move options
showMenu(
context: context,
position: RelativeRect.fill,
items: [
PopupMenuItem(
child: Text('Move to top'),
onTap: () => moveItem(item, 0),
),
PopupMenuItem(
child: Text('Move to bottom'),
onTap: () => moveItem(item, -1),
),
],
);
},
child: Draggable(
data: item,
child: Semantics(
hint: 'Drag to reorder, or tap for options',
child: ItemWidget(item),
),
feedback: ItemWidget(item),
),
);
}
}
Dynamic Font Sizing (WCAG 1.4.4)
Requirement: Support text resizing up to 200% without loss of functionality.
// ✅ ALWAYS use MediaQuery.textScaleFactor
class AccessibleText extends StatelessWidget {
final String text;
@override
Widget build(BuildContext context) {
final textScaleFactor = MediaQuery.of(context).textScaleFactor;
return Text(
text,
style: TextStyle(
fontSize: 16, // Base size - will scale automatically
),
// Flutter respects textScaleFactor automatically
);
}
}
// ❌ BAD: Fixed size that ignores user preference
Text(
'Fixed text',
textScaleFactor: 1.0, // Don't do this!
);
// ✅ GOOD: Respect user's text size preference
Text(
'Scales with user preference',
// textScaleFactor uses MediaQuery by default
);
// Handle layout overflow with text scaling
LayoutBuilder(
builder: (context, constraints) {
final textScaleFactor = MediaQuery.of(context).textScaleFactor;
return SingleChildScrollView(
child: Text(
longText,
style: TextStyle(fontSize: 16), // Scales automatically
),
);
},
);
Screen Reader Testing
TalkBack (Android)
# Enable TalkBack
Settings > Accessibility > TalkBack > On
# Test navigation
# - Swipe right: Next element
# - Swipe left: Previous element
# - Double tap: Activate
# - Two-finger swipe: Scroll
Test Checklist:
- All interactive elements have labels
- Labels are descriptive ("Delete event" not "Delete")
- Form fields announce errors
- Focus order is logical
- Images have semantic labels or are excluded
VoiceOver (iOS)
# Enable VoiceOver
Settings > Accessibility > VoiceOver > On
# Test navigation
# - Swipe right: Next element
# - Swipe left: Previous element
# - Double tap: Activate
# - Three-finger swipe: Scroll
Flutter Semantics Debugger
// Enable semantics debugger in dev
void main() {
runApp(
MaterialApp(
showSemanticsDebugger: true, // Shows semantic tree overlay
home: MyApp(),
),
);
}
Checklist (WCAG 2.2 Level AA)
Essential (WCAG A)
- All images have semanticLabel or excluded (1.1.1)
- Form fields have labels (3.3.2)
- Focus order is logical (2.4.3)
- Consistent help location (3.2.6)
- No redundant data entry (3.3.7)
Important (WCAG AA)
- All IconButtons have tooltip (4.1.2)
- Touch targets >= 48x48dp (2.5.8 requires 24px min)
- Color contrast >= 4.5:1 text, 3:1 large (1.4.3)
- Focus indicator visible: 2px, 3:1 contrast (2.4.11)
- Gesture alternatives provided (2.5.7)
- Text resizes to 200% (1.4.4)
- No cognitive auth tests (3.3.8)
- Dynamic content in live regions (4.1.3)
Testing
- Tested with TalkBack (Android)
- Tested with VoiceOver (iOS)
- Tested with text scaling (200%)
- Tested keyboard navigation (web/desktop)
- Verified color contrast (use DevTools)
Changelog
v2.0.0 (2026-01-12)
- CRITICAL: Added WCAG 2.2 Level AA compliance section
- Government compliance deadline: April 24, 2026 (entities with 50k+ population)
- Documented all 9 new WCAG 2.2 success criteria with Flutter implementations:
- 2.4.11 Focus Appearance (AA) - Visible focus indicator (min 2px, contrast 3:1)
- 2.4.12 Focus Not Obscured (Minimum) (AA) - Focused element not fully hidden
- 2.5.7 Dragging Movements (AA) - Alternative to drag gestures
- 2.5.8 Target Size (Minimum) (AA) - 24x24px minimum (Flutter: 48x48dp recommended)
- 3.2.6 Consistent Help (A) - Help in same location
- 3.3.7 Redundant Entry (A) - Don't ask for same info twice
- 3.3.8 Accessible Authentication (AA) - No cognitive function tests
- Plus 2 AAA criteria (not required for AA compliance)
- Added requirements table comparing WCAG 2.2 standard vs Flutter targets
- Added Focus Management section with custom FocusNode implementation
- Added Gesture Alternatives pattern (WCAG 2.5.7) with tap alternatives for drag gestures
- Added Dynamic Font Sizing section (WCAG 1.4.4) - Support 200% text scaling
- Added Screen Reader Testing workflows:
- TalkBack (Android) testing guide
- VoiceOver (iOS) testing guide
- Flutter Semantics Debugger usage
- Added comprehensive WCAG 2.2 Level AA checklist (Essential + Important + Testing)
- Updated skill description with WCAG 2.2 compliance and deadline
- Impact: Legal compliance requirement for government entities, prevents accessibility lawsuits
v1.0.0 (Initial)
- Basic accessibility patterns (WCAG 2.1)
- Semantics widget usage
- Screen reader support
- Touch target recommendations
- Color contrast guidelines
Related Skills
- flutter-ui-components: Widget patterns
- flutter-theming: Color contrast, dynamic colors
- flutter-testing: Accessibility testing patterns
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?