diff --git a/engine/src/flutter/lib/ui/semantics.dart b/engine/src/flutter/lib/ui/semantics.dart index a7095ebc0d1..81d8962ed2c 100644 --- a/engine/src/flutter/lib/ui/semantics.dart +++ b/engine/src/flutter/lib/ui/semantics.dart @@ -123,6 +123,7 @@ class SemanticsFlags { static const int _kHasCheckedStateIndex = 1 << 0; static const int _kIsCheckedIndex = 1 << 1; static const int _kIsSelectedIndex = 1 << 2; + static const int _kIsButton = 1 << 3; const SemanticsFlags._(this.index); @@ -153,6 +154,13 @@ class SemanticsFlags { /// For example, the active tab in a tab bar has [isSelected] set to true. static const SemanticsFlags isSelected = const SemanticsFlags._(_kIsSelectedIndex); + /// Whether the semantic node represents a button. + /// + /// Platforms has special handling for buttons, for example Android's TalkBack + /// and iOS's VoiceOver provides an additional hint when the focused object is + /// a button. + static const SemanticsFlags isButton = const SemanticsFlags._(_kIsButton); + /// The possible semantics flags. /// /// The map's key is the [index] of the flag and the value is the flag itself. diff --git a/engine/src/flutter/lib/ui/semantics/semantics_node.h b/engine/src/flutter/lib/ui/semantics/semantics_node.h index 1b2c44a72af..347152dc8bc 100644 --- a/engine/src/flutter/lib/ui/semantics/semantics_node.h +++ b/engine/src/flutter/lib/ui/semantics/semantics_node.h @@ -38,6 +38,7 @@ enum class SemanticsFlags : int32_t { kHasCheckedState = 1 << 0, kIsChecked = 1 << 1, kIsSelected = 1 << 2, + kIsButton = 1 << 3, }; struct SemanticsNode { diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 6f5432e1de5..afeb0c57ef1 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -56,6 +56,7 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess private static final int SEMANTICS_FLAG_HAS_CHECKED_STATE = 1 << 0; private static final int SEMANTICS_FLAG_IS_CHECKED = 1 << 1; private static final int SEMANTICS_FLAG_IS_SELECTED = 1 << 2; + private static final int SEMANTICS_FLAG_IS_BUTTON = 1 << 3; AccessibilityBridge(FlutterView owner) { assert owner != null; @@ -159,6 +160,10 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess result.setSelected((object.flags & SEMANTICS_FLAG_IS_SELECTED) != 0); result.setText(object.label); + if ((object.flags & SEMANTICS_FLAG_IS_BUTTON) != 0) { + result.setClassName("android.widget.Button"); + } + // Accessibility Focus if (mFocusedObject != null && mFocusedObject.id == virtualViewId) { result.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index dcdd4ee18ae..7167ce049b5 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -164,9 +164,6 @@ bool GeometryComparator(SemanticsObject* a, SemanticsObject* b) { - (UIAccessibilityTraits)accessibilityTraits { UIAccessibilityTraits traits = UIAccessibilityTraitNone; - if (_node.HasAction(blink::SemanticsAction::kTap)) { - traits |= UIAccessibilityTraitButton; - } if (_node.HasAction(blink::SemanticsAction::kIncrease) || _node.HasAction(blink::SemanticsAction::kDecrease)) { traits |= UIAccessibilityTraitAdjustable; @@ -175,6 +172,9 @@ bool GeometryComparator(SemanticsObject* a, SemanticsObject* b) { _node.HasFlag(blink::SemanticsFlags::kIsChecked)) { traits |= UIAccessibilityTraitSelected; } + if (_node.HasFlag(blink::SemanticsFlags::kIsButton)) { + traits |= UIAccessibilityTraitButton; + } return traits; }