Add SemanticsFlag.isDisabled (#4503)

diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart
index 398dff2..fcddf84 100644
--- a/lib/ui/semantics.dart
+++ b/lib/ui/semantics.dart
@@ -145,6 +145,7 @@
   static const int _kIsButtonIndex = 1 << 3;
   static const int _kIsTextFieldIndex = 1 << 4;
   static const int _kIsFocusedIndex = 1 << 5;
+  static const int _kIsDisabledIndex = 1 << 6;
 
   const SemanticsFlags._(this.index);
 
@@ -193,6 +194,16 @@
   /// The focused element is usually the current receiver of keyboard inputs.
   static const SemanticsFlags isFocused = const SemanticsFlags._(_kIsFocusedIndex);
 
+  /// Whether the semantic node is currently disabled.
+  ///
+  /// A disabled element does not respond to user interaction. For example, a
+  /// button that currently does not respond to user interaction should be
+  /// marked as disabled.
+  ///
+  /// Elements, that never respond to user interactions (e.g. static text)
+  /// should not be marked as disabled.
+  static const SemanticsFlags isDisabled = const SemanticsFlags._(_kIsDisabledIndex);
+
   /// The possible semantics flags.
   ///
   /// The map's key is the [index] of the flag and the value is the flag itself.
@@ -203,6 +214,7 @@
     _kIsButtonIndex: isButton,
     _kIsTextFieldIndex: isTextField,
     _kIsFocusedIndex: isFocused,
+    _kIsDisabledIndex: isDisabled,
   };
 
   @override
@@ -220,6 +232,8 @@
         return 'SemanticsFlags.isTextField';
       case _kIsFocusedIndex:
         return 'SemanticsFlags.isFocused';
+      case _kIsDisabledIndex:
+        return 'SemanticsFlags.isDisabled';
     }
     return null;
   }
diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h
index 4335bab..5e09248 100644
--- a/lib/ui/semantics/semantics_node.h
+++ b/lib/ui/semantics/semantics_node.h
@@ -41,6 +41,7 @@
   kIsButton = 1 << 3,
   kIsTextField = 1 << 4,
   kIsFocused = 1 << 5,
+  kIsDisabled = 1 << 6,
 };
 
 struct SemanticsNode {
diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java
index 958aa6f..c85f45d 100644
--- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java
+++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java
@@ -65,7 +65,8 @@
         IS_SELECTED(1 << 2),
         IS_BUTTON(1 << 3),
         IS_TEXT_FIELD(1 << 4),
-        IS_FOCUSED(1 << 5);
+        IS_FOCUSED(1 << 5),
+        IS_DISABLED(1 << 6);
 
         Flag(int value) {
             this.value = value;
@@ -157,7 +158,7 @@
         }
         result.setBoundsInScreen(bounds);
         result.setVisibleToUser(true);
-        result.setEnabled(true); // TODO(ianh): Expose disabled subtrees
+        result.setEnabled(!object.hasFlag(Flag.IS_DISABLED));
 
         if (object.hasAction(Action.TAP)) {
             result.addAction(AccessibilityNodeInfo.ACTION_CLICK);
diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
index b72d57b..1c531aa 100644
--- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
+++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
@@ -194,6 +194,9 @@
   if (_node.HasFlag(blink::SemanticsFlags::kIsButton)) {
     traits |= UIAccessibilityTraitButton;
   }
+  if (_node.HasFlag(blink::SemanticsFlags::kIsDisabled)) {
+    traits |= UIAccessibilityTraitNotEnabled;
+  }
   return traits;
 }