diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index a93c46d16c1165613251d1d2e9db97ca316ef379..e63affede8ab55b81276f38c71fae84cec2a2cf9 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -873,10 +873,14 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
   /// focus, the control will then attach to the keyboard and request that the
   /// keyboard become visible.
   void requestKeyboard() {
-    if (_hasFocus)
+    if (_hasFocus) {
       _openInputConnection();
-    else
+    } else {
+      final List<FocusScopeNode> ancestorScopes = FocusScope.ancestorsOf(context);
+      for (int i = ancestorScopes.length - 1; i >= 1; i -= 1)
+        ancestorScopes[i].setFirstFocus(ancestorScopes[i - 1]);
       FocusScope.of(context).requestFocus(widget.focusNode);
+    }
   }
 
   void _hideSelectionOverlayIfNeeded() {
diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart
index 3453c2641c824f6072ca5f7dcbcf835e339032d7..ccca09ca15b302f09566c86b266484e37dddc785 100644
--- a/packages/flutter/lib/src/widgets/focus_manager.dart
+++ b/packages/flutter/lib/src/widgets/focus_manager.dart
@@ -140,10 +140,23 @@ class FocusScopeNode extends Object with DiagnosticableTreeMixin {
   FocusScopeNode _lastChild;
 
   FocusNode _focus;
+  List<FocusScopeNode> _focusPath;
 
   /// Whether this scope is currently active in its parent scope.
   bool get isFirstFocus => _parent == null || _parent._firstChild == this;
 
+  // Returns this FocusScopeNode's ancestors, starting with the node
+  // below the FocusManager's rootScope.
+  List<FocusScopeNode> _getFocusPath() {
+    final List<FocusScopeNode> nodes = <FocusScopeNode>[this];
+    FocusScopeNode node = _parent;
+    while(node != null && node != _manager?.rootScope) {
+      nodes.add(node);
+      node = node._parent;
+    }
+    return nodes;
+  }
+
   void _prepend(FocusScopeNode child) {
     assert(child != this);
     assert(child != _firstChild);
@@ -246,7 +259,7 @@ class FocusScopeNode extends Object with DiagnosticableTreeMixin {
   /// has received the overall focus in a microtask.
   void requestFocus(FocusNode node) {
     assert(node != null);
-    if (_focus == node)
+    if (_focus == node && listEquals<FocusScopeNode>(_focusPath, _manager?._getCurrentFocusPath()))
       return;
     _focus?.unfocus();
     node._hasKeyboardToken = true;
@@ -292,6 +305,7 @@ class FocusScopeNode extends Object with DiagnosticableTreeMixin {
     _focus._parent = this;
     _focus._manager = _manager;
     _focus._hasKeyboardToken = true;
+    _focusPath = _getFocusPath();
     _didChangeFocusChain();
   }
 
@@ -412,7 +426,7 @@ class FocusManager {
 
   /// The root [FocusScopeNode] in the focus tree.
   ///
-  /// This field is rarely used direction. Instead, to find the
+  /// This field is rarely used directly. Instead, to find the
   /// [FocusScopeNode] for a given [BuildContext], use [FocusScope.of].
   final FocusScopeNode rootScope = FocusScopeNode();
 
@@ -450,6 +464,8 @@ class FocusManager {
     _currentFocus?._notify();
   }
 
+  List<FocusScopeNode> _getCurrentFocusPath() => _currentFocus?._parent?._getFocusPath();
+
   @override
   String toString() {
     final String status = _haveScheduledUpdate ? ' UPDATE SCHEDULED' : '';
diff --git a/packages/flutter/lib/src/widgets/focus_scope.dart b/packages/flutter/lib/src/widgets/focus_scope.dart
index 6d0ff2df44b9c6e1ac650134c428dcf579836edf..51724b94f624005e91f4a26161ef3fb77d072369 100644
--- a/packages/flutter/lib/src/widgets/focus_scope.dart
+++ b/packages/flutter/lib/src/widgets/focus_scope.dart
@@ -72,11 +72,39 @@ class FocusScope extends StatefulWidget {
 
   /// Returns the [node] of the [FocusScope] that most tightly encloses the
   /// given [BuildContext].
+  ///
+  /// The [context] argument must not be null.
   static FocusScopeNode of(BuildContext context) {
+    assert(context != null);
     final _FocusScopeMarker scope = context.inheritFromWidgetOfExactType(_FocusScopeMarker);
     return scope?.node ?? context.owner.focusManager.rootScope;
   }
 
+  /// A list of the [FocusScopeNode]s for each [FocusScope] ancestor of
+  /// the given [BuildContext]. The first element of the list is the
+  /// nearest ancestor's [FocusScopeNode].
+  ///
+  /// The returned list does not include the [FocusManager]'s `rootScope`.
+  /// Only the [FocusScopeNode]s that belong to [FocusScope] widgets are
+  /// returned.
+  ///
+  /// The [context] argument must not be null.
+  static List<FocusScopeNode> ancestorsOf(BuildContext context) {
+    assert(context != null);
+    final List<FocusScopeNode> ancestors = <FocusScopeNode>[];
+    while (true) {
+      context = context.ancestorInheritedElementForWidgetOfExactType(_FocusScopeMarker);
+      if (context == null)
+        return ancestors;
+      final _FocusScopeMarker scope = context.widget;
+      ancestors.add(scope.node);
+      context.visitAncestorElements((Element parent) {
+        context = parent;
+        return false;
+      });
+    }
+  }
+
   @override
   _FocusScopeState createState() => _FocusScopeState();
 }
diff --git a/packages/flutter/test/material/text_field_focus_test.dart b/packages/flutter/test/material/text_field_focus_test.dart
index a21594723c40c658568584803c1eade6064d9030..67039eaf693497ac5a9867d68e9a75c43cdf4a10 100644
--- a/packages/flutter/test/material/text_field_focus_test.dart
+++ b/packages/flutter/test/material/text_field_focus_test.dart
@@ -227,4 +227,145 @@ void main() {
     await tester.idle();
     expect(tester.testTextInput.isVisible, isTrue);
   });
+
+  testWidgets('Sibling FocusScopes', (WidgetTester tester) async {
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    final FocusScopeNode focusScopeNode0 = FocusScopeNode();
+    final FocusScopeNode focusScopeNode1 = FocusScopeNode();
+    final Key textField0 = UniqueKey();
+    final Key textField1 = UniqueKey();
+
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: Center(
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: <Widget>[
+                FocusScope(
+                  node: focusScopeNode0,
+                  child: Builder(
+                    builder: (BuildContext context) => TextField(key: textField0)
+                  ),
+                ),
+                FocusScope(
+                  node: focusScopeNode1,
+                  child: Builder(
+                    builder: (BuildContext context) => TextField(key: textField1),
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    tester.testTextInput.hide();
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField1));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.tap(find.byKey(textField1));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    tester.testTextInput.hide();
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.pumpWidget(Container());
+    expect(tester.testTextInput.isVisible, isFalse);
+  });
+
+  testWidgets('Sibling Navigators', (WidgetTester tester) async {
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    final Key textField0 = UniqueKey();
+    final Key textField1 = UniqueKey();
+
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: Center(
+            child: Column(
+              children: <Widget>[
+                Expanded(
+                  child: Navigator(
+                    onGenerateRoute: (RouteSettings settings) {
+                      return MaterialPageRoute<void>(
+                        builder: (BuildContext context) {
+                          return TextField(key: textField0);
+                        },
+                        settings: settings,
+                      );
+                    },
+                  ),
+                ),
+                Expanded(
+                  child: Navigator(
+                    onGenerateRoute: (RouteSettings settings) {
+                      return MaterialPageRoute<void>(
+                        builder: (BuildContext context) {
+                          return TextField(key: textField1);
+                        },
+                        settings: settings,
+                      );
+                    },
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    tester.testTextInput.hide();
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField1));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.tap(find.byKey(textField1));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    tester.testTextInput.hide();
+    expect(tester.testTextInput.isVisible, isFalse);
+
+    await tester.tap(find.byKey(textField0));
+    await tester.idle();
+    expect(tester.testTextInput.isVisible, isTrue);
+
+    await tester.pumpWidget(Container());
+    expect(tester.testTextInput.isVisible, isFalse);
+  });
 }