Provides Flutter accessibility mastery covering Semantics API, screen readers (VoiceOver/TalkBack), WCAG 2.1 AA/AAA compliance, inclusive design, and automated a11y testing with code examples.
How this skill is triggered — by the user, by Claude, or both
Slash command
/custom-plugin-flutter:accessibilityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```dart
class AccessibleProductCard extends StatelessWidget {
final Product product;
final VoidCallback onTap;
final VoidCallback onAddToCart;
const AccessibleProductCard({
required this.product,
required this.onTap,
required this.onAddToCart,
});
@override
Widget build(BuildContext context) {
return Semantics(
label: '${product.name}, ${product.formattedPrice}',
hint: 'Double tap to view details',
button: true,
enabled: true,
child: Card(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image with semantic description
Semantics(
image: true,
label: 'Product image: ${product.imageDescription}',
excludeSemantics: true,
child: Image.network(product.imageUrl, height: 120),
),
const SizedBox(height: 12),
Text(product.name),
Text(product.formattedPrice),
const SizedBox(height: 12),
// 48dp minimum touch target
ElevatedButton.icon(
onPressed: onAddToCart,
icon: const Icon(Icons.add_shopping_cart),
label: const Text('Add to Cart'),
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 48),
),
),
],
),
),
),
),
);
}
}
Semantics(
// Identity
label: 'Submit button', // What this element IS
value: '5 items', // Current value
hint: 'Double tap to submit', // How to interact
// Traits
button: true,
link: false,
header: false,
image: false,
textField: false,
slider: false,
// State
enabled: true,
checked: true, // Checkbox state
selected: false,
toggled: false,
focused: false,
hidden: false,
obscured: false, // Password field
// Actions
onTap: () {},
onLongPress: () {},
onIncrease: () {},
onDecrease: () {},
onDismiss: () {},
// Grouping
container: false,
explicitChildNodes: false,
excludeSemantics: false,
child: MyWidget(),
)
// MergeSemantics - Combine into single node
MergeSemantics(
child: Row(
children: [
Icon(Icons.star, semanticLabel: null),
Text('4.5 stars'),
],
),
)
// Screen reader: "4.5 stars"
// ExcludeSemantics - Remove decorative elements
ExcludeSemantics(
child: DecorativeBackground(),
)
// BlockSemantics - Block underlying content
BlockSemantics(
child: ModalOverlay(),
)
// Custom sort order
Semantics(
sortKey: OrdinalSortKey(1.0),
child: FirstItem(),
)
// Announce changes dynamically
void announceChange(String message) {
SemanticsService.announce(message, TextDirection.ltr);
}
// Live region for dynamic content
Semantics(
liveRegion: true,
child: CountdownTimer(),
)
// Custom actions
Semantics(
customSemanticsActions: {
CustomSemanticsAction(label: 'Mark as favorite'): markFavorite,
CustomSemanticsAction(label: 'Share'): share,
},
child: ItemCard(),
)
class ContrastChecker {
// WCAG AA: 4.5:1 normal, 3:1 large text
// WCAG AAA: 7:1 normal, 4.5:1 large text
static double calculateContrast(Color fg, Color bg) {
final fgL = fg.computeLuminance();
final bgL = bg.computeLuminance();
return (max(fgL, bgL) + 0.05) / (min(fgL, bgL) + 0.05);
}
static bool meetsAA(Color fg, Color bg, {bool largeText = false}) {
return calculateContrast(fg, bg) >= (largeText ? 3.0 : 4.5);
}
}
// Respect system text scale
final textScaler = MediaQuery.textScalerOf(context);
// Check reduced motion preference
final reduceMotion = MediaQuery.disableAnimationsOf(context);
if (reduceMotion) {
return StaticWidget();
}
return AnimatedWidget(duration: Duration(milliseconds: 300));
// Minimum 48x48 dp touch targets
ElevatedButton(
onPressed: () {},
child: Text('Tap'),
style: ElevatedButton.styleFrom(
minimumSize: Size(48, 48),
),
)
// Focus management
Focus(
focusNode: _focusNode,
onKeyEvent: (node, event) {
if (event.logicalKey == LogicalKeyboardKey.enter) {
handleAction();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: MyWidget(),
)
// Custom focus order
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: [
FocusTraversalOrder(order: NumericFocusOrder(1), child: Field1()),
FocusTraversalOrder(order: NumericFocusOrder(2), child: Field2()),
],
),
)
testWidgets('meets accessibility guidelines', (tester) async {
final semanticsHandle = tester.ensureSemantics();
addTearDown(semanticsHandle.dispose);
await tester.pumpWidget(MyApp());
// Check semantic labels
expect(find.bySemanticsLabel('Submit button'), findsOneWidget);
// Check touch target size
final button = tester.getSize(find.byType(ElevatedButton));
expect(button.width, greaterThanOrEqualTo(48));
expect(button.height, greaterThanOrEqualTo(48));
// Verify semantics
expect(tester.getSemantics(find.byType(MyButton)), matchesSemantics(
label: 'Submit',
isButton: true,
isEnabled: true,
hasTapAction: true,
));
});
Issue: Screen reader skips element
1. Check ExcludeSemantics wrapping
2. Verify semantic label exists
3. Check MergeSemantics parent
4. Run: flutter run --show-semantics-debug
Issue: Wrong reading order
1. Add OrdinalSortKey for custom order
2. Use FocusTraversalGroup
3. Check visual layout matches logical order
Issue: Contrast too low
1. Use ContrastChecker utility
2. Test light and dark themes
3. Check disabled state colors
[ ] All interactive elements have labels
[ ] Reading order is logical
[ ] Color contrast >= 4.5:1
[ ] Text scales to 200%
[ ] Touch targets >= 48dp
[ ] Keyboard navigation works
[ ] Focus indicators visible
Build inclusive Flutter apps that everyone can use.
npx claudepluginhub pluginagentmarketplace/custom-plugin-flutter --plugin custom-plugin-flutter2plugins reuse this skill
First indexed Jul 3, 2026
Audit or remediate Flutter widgets against WCAG 2.2 accessibility conformance levels A, AA, or AAA across iOS, Android, Web, macOS, Windows, and Linux.
Provides mobile accessibility patterns for Android Jetpack Compose and iOS: content descriptions, semantics, touch targets, screen reader support, WCAG compliance, dynamic type, color contrast.
Designs, implements, and audits WCAG 2.2 AA accessible UIs for Web (ARIA/HTML5), iOS (SwiftUI traits), and Android (Compose semantics). Audits code for compliance gaps.