diff --git a/packages/flutter/lib/src/material/input.dart b/packages/flutter/lib/src/material/input.dart
index d9e7200f4a2..b55f74646c1 100644
--- a/packages/flutter/lib/src/material/input.dart
+++ b/packages/flutter/lib/src/material/input.dart
@@ -90,6 +90,7 @@ class InputState extends ScrollableState {
}
void _handleTextSubmitted() {
+ Focus.clear(context);
if (config.onSubmitted != null)
config.onSubmitted(_value);
}
diff --git a/packages/flutter/lib/src/widgets/editable.dart b/packages/flutter/lib/src/widgets/editable.dart
index 043aea224f5..40362b5c889 100644
--- a/packages/flutter/lib/src/widgets/editable.dart
+++ b/packages/flutter/lib/src/widgets/editable.dart
@@ -136,6 +136,7 @@ class EditableString implements KeyboardClient {
}
void submit(SubmitAction action) {
+ composing = const TextRange.empty();
onSubmitted();
}
}
diff --git a/packages/flutter/lib/src/widgets/focus.dart b/packages/flutter/lib/src/widgets/focus.dart
index 2a1e05cb41a..760df06b881 100644
--- a/packages/flutter/lib/src/widgets/focus.dart
+++ b/packages/flutter/lib/src/widgets/focus.dart
@@ -25,7 +25,7 @@ class _FocusScope extends InheritedWidget {
Widget child
}) : super(key: key, child: child);
- final FocusState focusState;
+ final _FocusState focusState;
final bool scopeFocused;
// These are mutable because we implicitly change them when they're null in
@@ -144,6 +144,12 @@ class Focus extends StatefulComponent {
}
}
+ static void clear(BuildContext context) {
+ _FocusScope focusScope = context.ancestorWidgetOfExactType(_FocusScope);
+ if (focusScope != null)
+ focusScope.focusState._clearFocusedWidget();
+ }
+
/// Focuses a particular focus scope, identified by its GlobalKey. The widget
/// must be in the widget tree.
///
@@ -157,10 +163,10 @@ class Focus extends StatefulComponent {
focusScope.focusState._setFocusedScope(key);
}
- FocusState createState() => new FocusState();
+ _FocusState createState() => new _FocusState();
}
-class FocusState extends State {
+class _FocusState extends State {
GlobalKey _focusedWidget; // when null, the first component to ask if it's focused will get the focus
GlobalKey _currentlyRegisteredWidgetRemovalListenerKey;
@@ -181,12 +187,19 @@ class FocusState extends State {
}
}
+ void _clearFocusedWidget() {
+ if (_focusedWidget != null) {
+ _updateWidgetRemovalListener(null);
+ setState(() {
+ _focusedWidget = null;
+ });
+ }
+ }
+
void _handleWidgetRemoved(GlobalKey key) {
+ assert(key != null);
assert(_focusedWidget == key);
- _updateWidgetRemovalListener(null);
- setState(() {
- _focusedWidget = null;
- });
+ _clearFocusedWidget();
}
void _updateWidgetRemovalListener(GlobalKey key) {
diff --git a/packages/flutter/test/widget/focus_test.dart b/packages/flutter/test/widget/focus_test.dart
index 8dbe0155be4..d0b31111ed0 100644
--- a/packages/flutter/test/widget/focus_test.dart
+++ b/packages/flutter/test/widget/focus_test.dart
@@ -7,11 +7,19 @@ import 'package:flutter/widgets.dart';
import 'package:test/test.dart';
class TestFocusable extends StatelessComponent {
- TestFocusable(this.no, this.yes, GlobalKey key) : super(key: key);
+ TestFocusable({
+ GlobalKey key,
+ this.no,
+ this.yes,
+ this.autofocus: true
+ }) : super(key: key);
+
final String no;
final String yes;
+ final bool autofocus;
+
Widget build(BuildContext context) {
- bool focused = Focus.at(context, autofocus: true);
+ bool focused = Focus.at(context, autofocus: autofocus);
return new GestureDetector(
onTap: () { Focus.moveTo(key); },
child: new Text(focused ? yes : no)
@@ -29,8 +37,16 @@ void main() {
child: new Column(
children: [
// reverse these when you fix https://github.com/flutter/engine/issues/1495
- new TestFocusable('b', 'B FOCUSED', keyB),
- new TestFocusable('a', 'A FOCUSED', keyA),
+ new TestFocusable(
+ key: keyB,
+ no: 'b',
+ yes: 'B FOCUSED'
+ ),
+ new TestFocusable(
+ key: keyA,
+ no: 'a',
+ yes: 'A FOCUSED'
+ ),
]
)
)
@@ -65,4 +81,35 @@ void main() {
expect(tester.findText('B FOCUSED'), isNull);
});
});
+
+ test('Can blur', () {
+ testWidgets((WidgetTester tester) {
+ GlobalKey keyA = new GlobalKey();
+ tester.pumpWidget(
+ new Focus(
+ child: new TestFocusable(
+ key: keyA,
+ no: 'a',
+ yes: 'A FOCUSED',
+ autofocus: false
+ )
+ )
+ );
+
+ expect(tester.findText('a'), isNotNull);
+ expect(tester.findText('A FOCUSED'), isNull);
+
+ Focus.moveTo(keyA);
+ tester.pump();
+
+ expect(tester.findText('a'), isNull);
+ expect(tester.findText('A FOCUSED'), isNotNull);
+
+ Focus.clear(keyA.currentContext);
+ tester.pump();
+
+ expect(tester.findText('a'), isNotNull);
+ expect(tester.findText('A FOCUSED'), isNull);
+ });
+ });
}