Unverified Commit 94dab8fe authored by Hans Muller's avatar Hans Muller Committed by GitHub

Added TextField.onTap() (#24536)

parent 9e0ce09f
......@@ -127,6 +127,7 @@ class TextField extends StatefulWidget {
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.enableInteractiveSelection = true,
this.onTap,
}) : assert(textAlign != null),
assert(autofocus != null),
assert(obscureText != null),
......@@ -144,9 +145,44 @@ class TextField extends StatefulWidget {
/// If null, this widget will create its own [TextEditingController].
final TextEditingController controller;
/// Controls whether this widget has keyboard focus.
/// Defines the keyboard focus for this widget.
///
/// The [focusNode] is a long-lived object that's typically managed by a
/// [StatefulWidget] parent. See [FocusNode] for more information.
///
/// To give the keyboard focus to this widget, provide a [focusNode] and then
/// use the current [FocusScope] to request the focus:
///
/// ```dart
/// FocusScope.of(context).requestFocus(myFocusNode);
/// ```
///
/// This happens automatically when the widget is tapped.
///
/// To be notified when the widget gains or loses the focus, add a listener
/// to the [focusNode]:
///
/// ```dart
/// focusNode.addListener(() { print(myFocusNode.hasFocus); });
/// ```
///
/// If null, this widget will create its own [FocusNode].
///
/// ## Keyboard
///
/// Requesting the focus will typically cause the the keyboard to be shown
/// if it's not showing already.
///
/// On Android, the user can hide the keyboard - withouth changing the focus -
/// with the system back button. They can restore the keyboard's visibility
/// by tapping on a text field. The user might hide the keyboard and
/// switch to a physical keyboard, or they might just need to get it
/// out of the way for a moment, to expose something it's
/// obscuring. In this case requesting the focus again will not
/// cause the focus to change, and will not make the keyboard visible.
///
/// This widget builds an [EditableText] and will ensure that the keyboard is
/// showing when it is tapped by calling [EditableTextState.requestKeyboard()].
final FocusNode focusNode;
/// The decoration to show around the text field.
......@@ -298,6 +334,26 @@ class TextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.enableInteractiveSelection}
final bool enableInteractiveSelection;
/// Called when the user taps on this textfield.
///
/// The textfield builds a [GestureDetector] to handle input events like tap,
/// to trigger focus requests, to move the caret, adjust the selection, etc.
/// Handling some of those events by wrapping the textfield with a competing
/// GestureDetector is problematic.
///
/// To unconditionally handle taps, without interfering with the textfield's
/// internal gesture detector, provide this callback.
///
/// If the textfield is created with [enabled] false, taps will not be
/// recognized.
///
/// To be notified when the textfield gains or loses the focus, provide a
/// [focusNode] and add a listener to that.
///
/// To listen to arbitrary pointer events without competing with the
/// textfield's internal gesture detector, use a [Listener].
final GestureTapCallback onTap;
@override
_TextFieldState createState() => _TextFieldState();
......@@ -306,6 +362,7 @@ class TextField extends StatefulWidget {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<TextEditingController>('controller', controller, defaultValue: null));
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('enabled', enabled, defaultValue: null));
properties.add(DiagnosticsProperty<InputDecoration>('decoration', decoration));
properties.add(DiagnosticsProperty<TextInputType>('keyboardType', keyboardType, defaultValue: TextInputType.text));
properties.add(DiagnosticsProperty<TextStyle>('style', style, defaultValue: null));
......@@ -315,6 +372,7 @@ class TextField extends StatefulWidget {
properties.add(IntProperty('maxLines', maxLines, defaultValue: 1));
properties.add(IntProperty('maxLength', maxLength, defaultValue: null));
properties.add(FlagProperty('maxLengthEnforced', value: maxLengthEnforced, ifTrue: 'max length enforced'));
properties.add(DiagnosticsProperty<GestureTapCallback>('onTap', onTap, defaultValue: false));
}
}
......@@ -453,6 +511,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
_renderEditable.handleTap();
_requestKeyboard();
_confirmCurrentSplash();
if (widget.onTap != null)
widget.onTap();
}
void _handleTapCancel() {
......
......@@ -67,11 +67,13 @@ Widget overlay({ Widget child }) {
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
builder: (BuildContext context) => Center(
builder: (BuildContext context) {
return Center(
child: Material(
child: child,
),
),
);
},
),
],
),
......@@ -3341,4 +3343,43 @@ void main() {
expect(minOffset, 0.0);
expect(maxOffset, 50.0);
});
testWidgets('onTap is called upon tap', (WidgetTester tester) async {
int tapCount = 0;
await tester.pumpWidget(
overlay(
child: TextField(
onTap: () {
tapCount += 1;
},
),
),
);
expect(tapCount, 0);
await tester.tap(find.byType(TextField));
await tester.tap(find.byType(TextField));
await tester.tap(find.byType(TextField));
expect(tapCount, 3);
});
testWidgets('onTap is not called, field is disabled', (WidgetTester tester) async {
int tapCount = 0;
await tester.pumpWidget(
overlay(
child: TextField(
enabled: false,
onTap: () {
tapCount += 1;
},
),
),
);
expect(tapCount, 0);
await tester.tap(find.byType(TextField));
await tester.tap(find.byType(TextField));
await tester.tap(find.byType(TextField));
expect(tapCount, 0);
});
}
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