diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index 6f33669e6f473..257944dc258b3 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -291,6 +291,7 @@ class SemanticsFlag { static const int _kIsMultilineIndex = 1 << 19; static const int _kIsReadOnlyIndex = 1 << 20; static const int _kIsFocusableIndex = 1 << 21; + static const int _kIsLinkIndex = 1 << 22; const SemanticsFlag._(this.index); @@ -333,7 +334,7 @@ class SemanticsFlag { /// Whether the semantic node represents a button. /// - /// Platforms has special handling for buttons, for example Android's TalkBack + /// Platforms have 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 SemanticsFlag isButton = SemanticsFlag._(_kIsButtonIndex); @@ -349,6 +350,13 @@ class SemanticsFlag { /// Only applicable when [isTextField] is true. static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex); + /// Whether the semantic node is an interactive link. + /// + /// Platforms have special handling for links, for example iOS's VoiceOver + /// provides an additional hint when the focused object is a link, as well as + /// the ability to parse the links through another navigation menu. + static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex); + /// Whether the semantic node is able to hold the user's focus. /// /// The focused element is usually the current receiver of keyboard inputs. @@ -528,6 +536,7 @@ class SemanticsFlag { _kHasImplicitScrollingIndex: hasImplicitScrolling, _kIsMultilineIndex: isMultiline, _kIsReadOnlyIndex: isReadOnly, + _kIsLinkIndex: isLink, }; @override @@ -575,6 +584,8 @@ class SemanticsFlag { return 'SemanticsFlag.isMultiline'; case _kIsReadOnlyIndex: return 'SemanticsFlag.isReadOnly'; + case _kIsLinkIndex: + return 'SemanticsFlag.isLink'; } return null; } diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h index c135b58b05501..e89ce87cc53b4 100644 --- a/lib/ui/semantics/semantics_node.h +++ b/lib/ui/semantics/semantics_node.h @@ -73,6 +73,7 @@ enum class SemanticsFlags : int32_t { // kIsMultiline = 1 << 19, kIsReadOnly = 1 << 20, kIsFocusable = 1 << 21, + kIsLink = 1 << 22, }; const int kScrollableSemanticsFlags = diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index c542da80f1f8a..1679d96869d95 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -591,7 +591,7 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { } } - if (semanticsNode.hasFlag(Flag.IS_BUTTON)) { + if (semanticsNode.hasFlag(Flag.IS_BUTTON) || semanticsNode.hasFlag(Flag.IS_LINK)) { result.setClassName("android.widget.Button"); } if (semanticsNode.hasFlag(Flag.IS_IMAGE)) { @@ -1648,7 +1648,8 @@ private enum Flag { // The Dart API defines the following flag but it isn't used in Android. // IS_MULTILINE(1 << 19); IS_READ_ONLY(1 << 20), - IS_FOCUSABLE(1 << 21); + IS_FOCUSABLE(1 << 21), + IS_LINK(1 << 22); final int value; diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index ddd5478f8c573..938cec276ac37 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -407,6 +407,9 @@ - (UIAccessibilityTraits)accessibilityTraits { if ([self node].HasFlag(flutter::SemanticsFlags::kIsLiveRegion)) { traits |= UIAccessibilityTraitUpdatesFrequently; } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsLink)) { + traits |= UIAccessibilityTraitLink; + } return traits; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 7490198b24aec..d116c0b0c550a 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -63,8 +63,7 @@ typedef enum { /// Must match the `SemanticsAction` enum in semantics.dart. typedef enum { /// The equivalent of a user briefly tapping the screen with the finger - /// without - /// moving it. + /// without moving it. kFlutterSemanticsActionTap = 1 << 0, /// The equivalent of a user pressing and holding the screen with the finger /// for a few seconds without moving it. @@ -172,6 +171,8 @@ typedef enum { kFlutterSemanticsFlagIsReadOnly = 1 << 20, /// Whether the semantic node can hold the user's focus. kFlutterSemanticsFlagIsFocusable = 1 << 21, + /// Whether the semantics node represents a link. + kFlutterSemanticsFlagIsLink = 1 << 22, } FlutterSemanticsFlag; typedef enum {