Commit ed346222 authored by Adam Barth's avatar Adam Barth

Merge pull request #1348 from abarth/focus_blur

Add the ability to lose focus
parents d081dc67 4e83a5cc
......@@ -90,6 +90,7 @@ class InputState extends ScrollableState<Input> {
}
void _handleTextSubmitted() {
Focus.clear(context);
if (config.onSubmitted != null)
config.onSubmitted(_value);
}
......
......@@ -136,6 +136,7 @@ class EditableString implements KeyboardClient {
}
void submit(SubmitAction action) {
composing = const TextRange.empty();
onSubmitted();
}
}
......
......@@ -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<Focus> {
class _FocusState extends State<Focus> {
GlobalKey _focusedWidget; // when null, the first component to ask if it's focused will get the focus
GlobalKey _currentlyRegisteredWidgetRemovalListenerKey;
......@@ -181,13 +187,20 @@ class FocusState extends State<Focus> {
}
}
void _handleWidgetRemoved(GlobalKey key) {
assert(_focusedWidget == key);
void _clearFocusedWidget() {
if (_focusedWidget != null) {
_updateWidgetRemovalListener(null);
setState(() {
_focusedWidget = null;
});
}
}
void _handleWidgetRemoved(GlobalKey key) {
assert(key != null);
assert(_focusedWidget == key);
_clearFocusedWidget();
}
void _updateWidgetRemovalListener(GlobalKey key) {
if (_currentlyRegisteredWidgetRemovalListenerKey != key) {
......
......@@ -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: <Widget>[
// 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);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment