Only show the keyboard when the user explicitly focuses an Input. (#6713)
Fixes https://github.com/flutter/flutter/issues/6603
This commit is contained in:
parent
fb03b3137e
commit
b9bff6a912
@ -273,8 +273,12 @@ class RawInputState extends ScrollableState<RawInput> implements TextInputClient
|
|||||||
return newScrollOffset;
|
return newScrollOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// True if the focus was explicitly requested last frame. This ensures we
|
||||||
|
// don't show the keyboard when focus defaults back to the RawInput.
|
||||||
|
bool _requestingFocus = false;
|
||||||
|
|
||||||
void _attachOrDetachKeyboard(bool focused) {
|
void _attachOrDetachKeyboard(bool focused) {
|
||||||
if (focused && !_isAttachedToKeyboard) {
|
if (focused && !_isAttachedToKeyboard && _requestingFocus) {
|
||||||
_textInputConnection = TextInput.attach(
|
_textInputConnection = TextInput.attach(
|
||||||
this, new TextInputConfiguration(inputType: config.keyboardType))
|
this, new TextInputConfiguration(inputType: config.keyboardType))
|
||||||
..setEditingState(_getTextEditingStateFromInputValue(_currentValue))
|
..setEditingState(_getTextEditingStateFromInputValue(_currentValue))
|
||||||
@ -286,6 +290,7 @@ class RawInputState extends ScrollableState<RawInput> implements TextInputClient
|
|||||||
}
|
}
|
||||||
_clearComposing();
|
_clearComposing();
|
||||||
}
|
}
|
||||||
|
_requestingFocus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearComposing() {
|
void _clearComposing() {
|
||||||
@ -306,6 +311,9 @@ class RawInputState extends ScrollableState<RawInput> implements TextInputClient
|
|||||||
_textInputConnection.show();
|
_textInputConnection.show();
|
||||||
} else {
|
} else {
|
||||||
Focus.moveTo(config.focusKey);
|
Focus.moveTo(config.focusKey);
|
||||||
|
setState(() {
|
||||||
|
_requestingFocus = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,12 @@ void main() {
|
|||||||
mockTextInput.enterText(text);
|
mockTextInput.enterText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Null> showKeyboard(WidgetTester tester) async {
|
||||||
|
RawInputState editable = tester.state(find.byType(RawInput).first);
|
||||||
|
editable.requestKeyboard();
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
|
||||||
testWidgets('onSaved callback is called', (WidgetTester tester) async {
|
testWidgets('onSaved callback is called', (WidgetTester tester) async {
|
||||||
GlobalKey<FormState> formKey = new GlobalKey<FormState>();
|
GlobalKey<FormState> formKey = new GlobalKey<FormState>();
|
||||||
String fieldValue;
|
String fieldValue;
|
||||||
@ -32,6 +38,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
expect(fieldValue, isNull);
|
expect(fieldValue, isNull);
|
||||||
|
|
||||||
@ -65,6 +72,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
Future<Null> checkErrorText(String testValue) async {
|
Future<Null> checkErrorText(String testValue) async {
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -81,7 +89,6 @@ void main() {
|
|||||||
testWidgets('Multiple Inputs communicate', (WidgetTester tester) async {
|
testWidgets('Multiple Inputs communicate', (WidgetTester tester) async {
|
||||||
GlobalKey<FormState> formKey = new GlobalKey<FormState>();
|
GlobalKey<FormState> formKey = new GlobalKey<FormState>();
|
||||||
GlobalKey<FormFieldState<InputValue>> fieldKey = new GlobalKey<FormFieldState<InputValue>>();
|
GlobalKey<FormFieldState<InputValue>> fieldKey = new GlobalKey<FormFieldState<InputValue>>();
|
||||||
GlobalKey inputFocusKey = new GlobalKey();
|
|
||||||
GlobalKey focusKey = new GlobalKey();
|
GlobalKey focusKey = new GlobalKey();
|
||||||
// Input 2's validator depends on a input 1's value.
|
// Input 2's validator depends on a input 1's value.
|
||||||
String errorText(InputValue input) => fieldKey.currentState.value?.text.toString() + '/error';
|
String errorText(InputValue input) => fieldKey.currentState.value?.text.toString() + '/error';
|
||||||
@ -96,9 +103,7 @@ void main() {
|
|||||||
child: new Block(
|
child: new Block(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new InputFormField(
|
new InputFormField(
|
||||||
autofocus: true,
|
key: fieldKey
|
||||||
key: fieldKey,
|
|
||||||
focusKey: inputFocusKey,
|
|
||||||
),
|
),
|
||||||
new InputFormField(
|
new InputFormField(
|
||||||
validator: errorText,
|
validator: errorText,
|
||||||
@ -112,8 +117,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
await tester.pump();
|
await showKeyboard(tester);
|
||||||
Focus.moveTo(inputFocusKey);
|
|
||||||
|
|
||||||
Future<Null> checkErrorText(String testValue) async {
|
Future<Null> checkErrorText(String testValue) async {
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -147,6 +151,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
// initial value should be loaded into keyboard editing state
|
// initial value should be loaded into keyboard editing state
|
||||||
expect(mockTextInput.editingState, isNotNull);
|
expect(mockTextInput.editingState, isNotNull);
|
||||||
@ -187,7 +192,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder(false));
|
await tester.pumpWidget(builder(false));
|
||||||
await tester.pump();
|
await showKeyboard(tester);
|
||||||
|
|
||||||
expect(fieldValue, isNull);
|
expect(fieldValue, isNull);
|
||||||
expect(formKey.currentState.hasErrors, isFalse);
|
expect(formKey.currentState.hasErrors, isFalse);
|
||||||
|
@ -50,6 +50,12 @@ void main() {
|
|||||||
mockTextInput.enterText(text);
|
mockTextInput.enterText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Null> showKeyboard(WidgetTester tester) async {
|
||||||
|
RawInputState editable = tester.state(find.byType(RawInput).first);
|
||||||
|
editable.requestKeyboard();
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the first RenderEditable.
|
// Returns the first RenderEditable.
|
||||||
RenderEditable findRenderEditable(WidgetTester tester) {
|
RenderEditable findRenderEditable(WidgetTester tester) {
|
||||||
RenderObject root = tester.renderObject(find.byType(RawInput));
|
RenderObject root = tester.renderObject(find.byType(RawInput));
|
||||||
@ -94,6 +100,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
||||||
|
|
||||||
@ -134,6 +141,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
RawInputState editableText = tester.state(find.byType(RawInput));
|
RawInputState editableText = tester.state(find.byType(RawInput));
|
||||||
|
|
||||||
@ -179,6 +187,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
const String testValue = 'ABC';
|
const String testValue = 'ABC';
|
||||||
updateEditingState(new TextEditingState(
|
updateEditingState(new TextEditingState(
|
||||||
@ -215,6 +224,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
String testValue = 'abc def ghi';
|
String testValue = 'abc def ghi';
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -262,6 +272,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
String testValue = 'abc def ghi';
|
String testValue = 'abc def ghi';
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -337,6 +348,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
String testValue = 'abc def ghi';
|
String testValue = 'abc def ghi';
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -402,6 +414,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
String testValue = 'abc def ghi';
|
String testValue = 'abc def ghi';
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -452,6 +465,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder(3));
|
await tester.pumpWidget(builder(3));
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
||||||
|
|
||||||
@ -514,6 +528,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
String testValue = kThreeLines;
|
String testValue = kThreeLines;
|
||||||
String cutValue = 'First line of stuff keeps going until abcdef ghijk. ';
|
String cutValue = 'First line of stuff keeps going until abcdef ghijk. ';
|
||||||
@ -605,6 +620,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
enterText(kFourLines);
|
enterText(kFourLines);
|
||||||
await tester.idle();
|
await tester.idle();
|
||||||
@ -690,6 +706,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
Future<Null> checkText(String testValue) async {
|
Future<Null> checkText(String testValue) async {
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
@ -722,6 +739,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
await showKeyboard(tester);
|
||||||
|
|
||||||
Future<Null> checkText(String testValue) async {
|
Future<Null> checkText(String testValue) async {
|
||||||
enterText(testValue);
|
enterText(testValue);
|
||||||
|
@ -163,6 +163,10 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
|||||||
/// microtasks, by calling [pump] with the same `duration` (if any). The
|
/// microtasks, by calling [pump] with the same `duration` (if any). The
|
||||||
/// supplied [EnginePhase] is the final phase reached during the pump pass; if
|
/// supplied [EnginePhase] is the final phase reached during the pump pass; if
|
||||||
/// not supplied, the whole pass is executed.
|
/// not supplied, the whole pass is executed.
|
||||||
|
///
|
||||||
|
/// Subsequent calls to this is different from [pump] in that it forces a full
|
||||||
|
/// rebuild of the tree, even if [widget] is the same as the previous call.
|
||||||
|
/// [pump] will only rebuild the widgets that have changed.
|
||||||
Future<Null> pumpWidget(Widget widget, [
|
Future<Null> pumpWidget(Widget widget, [
|
||||||
Duration duration,
|
Duration duration,
|
||||||
EnginePhase phase = EnginePhase.sendSemanticsTree
|
EnginePhase phase = EnginePhase.sendSemanticsTree
|
||||||
|
Loading…
x
Reference in New Issue
Block a user