thesimpleapp/flutter-app-skills

flutter-improving-accessibility

Make Flutter UIs work with TalkBack, VoiceOver, JAWS/NVDA, large text, high contrast, and reduced motion.

소스 보기

사용 방법

  1. 설치 명령을 복사하고 자신의 에이전트 환경에 설치하기 전에 소스 패키지를 확인하세요.
  2. 필요한 도구, 인증 정보, 지원 환경은 원본 문서에서 확인하세요.
  3. 입력과 원하는 산출물을 설명하고 출처와 결과를 대조하세요.
npx skills add https://github.com/thesimpleapp/flutter-app-skills --skill flutter-improving-accessibility

시작 전 확인

  • 설치에는 모델 구독, API 한도, 파일 접근 권한이 포함되지 않습니다.
  • 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

Flutter accessibility

Prefer built-in Material and Cupertino widgets — they already emit a TalkBack / VoiceOver / JAWS / NVDA tree. Add Semantics (or Semantics.fromProperties) only on custom painted or gestured widgets.

If this repo already has a Semantics helper, a11y test harness, labeled-control convention, or a pressable with semanticLabel, extend that. Do not wrap it in a second Semantics(label:) that repeats visible text.

Official: Accessibility, assistive technologies, UI design and styling, accessibility testing.

References

Read references/recipes.md when implementing a custom control, merge/exclude/block, MediaQuery wiring, or the guideline test.

Detect first

Search the repo for Semantics(, MergeSemantics, ExcludeSemantics, BlockSemantics, semanticLabel, excludeFromSemantics, tooltip:, meetsGuideline(, ensureSemantics(, highContrastTheme, accessibleNavigationOf, textScalerOf. Open a nearby production screen and copy that pattern.

Hard rules

Semantics

  • Custom paint / GestureDetector / InkWell: set label, a role flag (button, slider, textField), tooltip / hint, and onTap so the node is announced as a control.
  • Role is a flag: Semantics(button: true, label: 'Submit'). Never put "button" in the label. label / hint / tooltip require an ambient Directionality or an explicit textDirection. Transient status: liveRegion: true.
  • container: true introduces a SemanticsNode — use it for a distinct TalkBack/VoiceOver focus stop.

Merge, exclude, block

  • One control + its visible Text: wrap both in MergeSemantics. Never merge two checkboxes, two tappable children, or conflicting selected / checked values.
  • ExcludeSemantics only for decorative or redundant descendants. Semantics(excludeSemantics: true) replaces all child semantics with this node. Hide widgets painted behind a modal or Drawer with BlockSemantics on the overlay, not ExcludeSemantics on the overlay child.

Hit targets and names

  • Every tappable target ≥ 48×48 logical pixels (kMinInteractiveDimension). Cupertino floor is kMinInteractiveDimensionCupertino (44.0). Prefer IconButton so the hit region stays 48 regardless of iconSize. Do not pass IconButton.constraints or compact VisualDensity that shrinks below 48.
  • Icon-only IconButton / FAB: tooltip is a short action name. Custom icon taps: Semantics(label: ..., button: true). Decorative Icon: semanticLabel. Meaningful Image: semanticLabel. Decorative Image: excludeFromSemantics: true.

Text scale

  • Read with MediaQuery.textScalerOf(context) and compute sizes with textScaler.scale(fontSize). Never textScaleFactor — deprecated and cannot represent Android 14 nonlinear scaling.
  • Text already honors the OS font-size setting. Test every screen on a small device at the largest OS text size. MediaQuery.withNoTextScaling only to stop icon fonts from scaling. Never disable scaling at the MaterialApp root.

MediaQuery flags

  • Call MediaQuery.accessibleNavigationOf, highContrastOf, disableAnimationsOf, and textScalerOf — not MediaQuery.of.
  • When accessibleNavigationOf is true, disable timeouts or raise their minimum duration.
  • When highContrastOf is true, set MaterialApp.highContrastTheme and highContrastDarkTheme. Wrapping a subtree in MediaQuery(data: copyWith(highContrast: true)) does not switch those themes.
  • disableAnimationsOf is Android Remove animations. AnimationController already honors SemanticsBinding.disableAnimations. Custom explicit animations must still read disableAnimationsOf. iOS Reduce Motion is AccessibilityFeatures.reduceMotion and does not set disableAnimations.

Traversal

  • TalkBack/VoiceOver sibling order: Semantics(sortKey: OrdinalSortKey(n)). Keyboard/Tab order is a different tree: FocusTraversalGroup / FocusTraversalOrder. Do not use sortKey to fix Tab order.

Contrast

  • At least 4.5:1 between text/controls and background (except disabled). 3.0:1 for large text (18pt+ regular or 14pt+ bold). Do not convey state by color alone.

Tests and devices

  • tester.ensureSemantics(), then expectLater(tester, meetsGuideline(...)), then handle.dispose(). Guidelines: androidTapTargetGuideline, iOSTapTargetGuideline, labeledTapTargetGuideline, textContrastGuideline.
  • Walk every changed screen with TalkBack and VoiceOver. Flutter web: SemanticsBinding.instance.ensureSemantics() in main(). Debug with showSemanticsDebugger or flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true.

Samples

dart
Semantics(
  button: true,
  label: 'Submit',
  hint: 'Sends the form',
  onTap: onSubmit,
  child: CustomPaint(painter: SubmitPainter()),
)

IconButton(
  tooltip: 'Search',
  icon: const Icon(Icons.search),
  onPressed: onSearch,
)

final TextScaler scaler = MediaQuery.textScalerOf(context);
final bool screenReader = MediaQuery.accessibleNavigationOf(context);

Do not

  • Wrap an already-semantic Material/Cupertino widget in Semantics(label:) that repeats visible text.
  • Put ExcludeSemantics around interactive content, or use it instead of BlockSemantics to hide widgets behind a dialog.
  • MergeSemantics over two checkboxes or two tappable children.
  • Icon-only control with no tooltip, no Semantics.label, and no Icon.semanticLabel.
  • MediaQuery.of(context).textScaleFactor or Text(textScaleFactor:).
  • Disable text scaling at the app root, or shrink IconButton below 48.
  • Assume MediaQuery(data: copyWith(disableAnimations: true)) stops AnimationController.
  • Write the role into the label (label: 'Submit button').
  • Use Semantics.sortKey to fix keyboard Tab order.

Fix a screen

  1. Detect the existing a11y convention and reuse it.
  2. Leave stock Material/Cupertino widgets unlabeled unless they are custom-painted or icon-only.
  3. Give every icon-only control a tooltip or Semantics(label:, button: true). Pad the hit target to 48, not the glyph.
  4. Read MediaQuery.*Of. Honor largest text, high-contrast themes, screen-reader timeouts, and reduced motion.
  5. Add the guideline test. Walk TalkBack and VoiceOver.

Verify

  1. tester.ensureSemantics() + the four meetsGuideline checks + handle.dispose() on the screen you touched.
  2. Layout still fits at the OS largest text size on a small device.
  3. Contrast ≥ 4.5:1 (3:1 large text); state is not color-only.
  4. TalkBack and VoiceOver: every control has an intelligible name and the right role.
  5. Web: semantics enabled from main().

관련 작업

같은 저장소의 Skills

더 많은 Skills

모든 Skills