Use scrollPhysics.allowImplicitScrolling to configure scrollable semantics (#20210)

diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart
index 4920f23..c23e973 100644
--- a/packages/flutter/lib/src/semantics/semantics.dart
+++ b/packages/flutter/lib/src/semantics/semantics.dart
@@ -3136,6 +3136,18 @@
     _setFlag(SemanticsFlag.isObscured, value);
   }
 
+  /// Whether the platform can scroll the semantics node when the user attempts
+  /// to move focus to an offscreen child.
+  ///
+  /// For example, a [ListView] widget has implicit scrolling so that users can
+  /// easily move to the next visible set of children. A [TabBar] widget does
+  /// not have implicit scrolling, so that users can navigate into the tab
+  /// body when reaching the end of the tab bar.
+  bool get hasImplicitScrolling => _hasFlag(SemanticsFlag.hasImplicitScrolling);
+  set hasImplicitScrolling(bool value) {
+    _setFlag(SemanticsFlag.hasImplicitScrolling, value);
+  }
+
   /// The currently selected text (or the position of the cursor) within [value]
   /// if this node represents a text field.
   TextSelection get textSelection => _textSelection;
diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart
index 563ccc0..7193941 100644
--- a/packages/flutter/lib/src/widgets/scrollable.dart
+++ b/packages/flutter/lib/src/widgets/scrollable.dart
@@ -508,6 +508,7 @@
         key: _excludableScrollSemanticsKey,
         child: result,
         position: position,
+        allowImplicitScrolling: widget?.physics?.allowImplicitScrolling ?? false,
       );
     }
 
@@ -539,25 +540,39 @@
   const _ExcludableScrollSemantics({
     Key key,
     @required this.position,
+    @required this.allowImplicitScrolling,
     Widget child
   }) : assert(position != null), super(key: key, child: child);
 
   final ScrollPosition position;
+  final bool allowImplicitScrolling;
 
   @override
-  _RenderExcludableScrollSemantics createRenderObject(BuildContext context) => new _RenderExcludableScrollSemantics(position: position);
+  _RenderExcludableScrollSemantics createRenderObject(BuildContext context) {
+    return new _RenderExcludableScrollSemantics(
+      position: position,
+      allowImplicitScrolling: allowImplicitScrolling,
+    );
+  }
+
+
 
   @override
   void updateRenderObject(BuildContext context, _RenderExcludableScrollSemantics renderObject) {
-    renderObject.position = position;
+    renderObject
+      ..allowImplicitScrolling = allowImplicitScrolling
+      ..position = position;
   }
 }
 
 class _RenderExcludableScrollSemantics extends RenderProxyBox {
   _RenderExcludableScrollSemantics({
     @required ScrollPosition position,
+    @required bool allowImplicitScrolling,
     RenderBox child,
-  }) : _position = position, assert(position != null), super(child) {
+  }) : _position = position,
+       _allowImplicitScrolling = allowImplicitScrolling,
+       assert(position != null), super(child) {
     position.addListener(markNeedsSemanticsUpdate);
   }
 
@@ -574,12 +589,23 @@
     markNeedsSemanticsUpdate();
   }
 
+  /// Whether this node can be scrolled implicitly.
+  bool get allowImplicitScrolling => _allowImplicitScrolling;
+  bool _allowImplicitScrolling;
+  set allowImplicitScrolling(bool value) {
+    if (value == _allowImplicitScrolling)
+      return;
+    _allowImplicitScrolling = value;
+    markNeedsSemanticsUpdate();
+  }
+
   @override
   void describeSemanticsConfiguration(SemanticsConfiguration config) {
     super.describeSemanticsConfiguration(config);
     config.isSemanticBoundary = true;
     if (position.haveDimensions) {
       config
+          ..hasImplicitScrolling = allowImplicitScrolling
           ..scrollPosition = _position.pixels
           ..scrollExtentMax = _position.maxScrollExtent
           ..scrollExtentMin = _position.minScrollExtent;
diff --git a/packages/flutter/test/widgets/custom_painter_test.dart b/packages/flutter/test/widgets/custom_painter_test.dart
index 4f50cff..444fce5 100644
--- a/packages/flutter/test/widgets/custom_painter_test.dart
+++ b/packages/flutter/test/widgets/custom_painter_test.dart
@@ -428,6 +428,7 @@
     flags
       ..remove(SemanticsFlag.hasImplicitScrolling)
       ..remove(SemanticsFlag.hasToggledState)
+      ..remove(SemanticsFlag.hasImplicitScrolling)
       ..remove(SemanticsFlag.isToggled);
     TestSemantics expectedSemantics = new TestSemantics.root(
       children: <TestSemantics>[
@@ -473,6 +474,7 @@
     flags
       ..remove(SemanticsFlag.hasImplicitScrolling)
       ..remove(SemanticsFlag.hasCheckedState)
+      ..remove(SemanticsFlag.hasImplicitScrolling)
       ..remove(SemanticsFlag.isChecked);
 
     expectedSemantics = new TestSemantics.root(
diff --git a/packages/flutter/test/widgets/scrollable_semantics_test.dart b/packages/flutter/test/widgets/scrollable_semantics_test.dart
index bfb3fc3..42840aa 100644
--- a/packages/flutter/test/widgets/scrollable_semantics_test.dart
+++ b/packages/flutter/test/widgets/scrollable_semantics_test.dart
@@ -342,6 +342,9 @@
         new TestSemantics.rootChild(
           children: <TestSemantics>[
             new TestSemantics(
+              flags: <SemanticsFlag>[
+                SemanticsFlag.hasImplicitScrolling,
+              ],
               actions: <SemanticsAction>[SemanticsAction.scrollUp],
               children: <TestSemantics>[
                 new TestSemantics(
diff --git a/packages/flutter/test/widgets/semantics_test.dart b/packages/flutter/test/widgets/semantics_test.dart
index 5b1c511..0064558 100644
--- a/packages/flutter/test/widgets/semantics_test.dart
+++ b/packages/flutter/test/widgets/semantics_test.dart
@@ -486,9 +486,9 @@
     );
     final List<SemanticsFlag> flags = SemanticsFlag.values.values.toList();
     flags
-      ..remove(SemanticsFlag.hasImplicitScrolling)
       ..remove(SemanticsFlag.hasToggledState)
-      ..remove(SemanticsFlag.isToggled);
+      ..remove(SemanticsFlag.isToggled)
+      ..remove(SemanticsFlag.hasImplicitScrolling);
 
     TestSemantics expectedSemantics = new TestSemantics.root(
       children: <TestSemantics>[
diff --git a/packages/flutter/test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart b/packages/flutter/test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart
index 0cffd37..db07298 100644
--- a/packages/flutter/test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart
+++ b/packages/flutter/test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart
@@ -116,6 +116,7 @@
                       children: <TestSemantics>[
                         new TestSemantics(
                           id: 6,
+                          flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
                           children: <TestSemantics>[
                             new TestSemantics(
                               id: 4,
diff --git a/packages/flutter/test/widgets/sliver_semantics_test.dart b/packages/flutter/test/widgets/sliver_semantics_test.dart
index 3671aac..b3f89c5 100644
--- a/packages/flutter/test/widgets/sliver_semantics_test.dart
+++ b/packages/flutter/test/widgets/sliver_semantics_test.dart
@@ -384,6 +384,9 @@
               new TestSemantics(
                 children: <TestSemantics>[
                   new TestSemantics(
+                    flags: <SemanticsFlag>[
+                      SemanticsFlag.hasImplicitScrolling,
+                    ],
                     children: <TestSemantics>[
                       new TestSemantics(
                         label: 'Item 4',