Add CupertinoTextField.onTap (#34964)

diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart
index 09a0e0c..5cc8b26 100644
--- a/packages/flutter/lib/src/cupertino/text_field.dart
+++ b/packages/flutter/lib/src/cupertino/text_field.dart
@@ -196,6 +196,7 @@
     this.scrollPadding = const EdgeInsets.all(20.0),
     this.dragStartBehavior = DragStartBehavior.start,
     this.enableInteractiveSelection,
+    this.onTap,
     this.scrollController,
     this.scrollPhysics,
   }) : assert(textAlign != null),
@@ -458,6 +459,9 @@
     return enableInteractiveSelection ?? !obscureText;
   }
 
+  /// {@macro flutter.material.textfield.onTap}
+  final GestureTapCallback onTap;
+
   @override
   _CupertinoTextFieldState createState() => _CupertinoTextFieldState();
 
@@ -584,6 +588,9 @@
       _renderEditable.selectWordEdge(cause: SelectionChangedCause.tap);
     }
     _requestKeyboard();
+    if (widget.onTap != null) {
+      widget.onTap();
+    }
   }
 
   void _handleSingleLongTapStart(LongPressStartDetails details) {
diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart
index e8061e2..cce50a1 100644
--- a/packages/flutter/lib/src/material/text_field.dart
+++ b/packages/flutter/lib/src/material/text_field.dart
@@ -432,7 +432,8 @@
     return enableInteractiveSelection ?? !obscureText;
   }
 
-  /// Called when the user taps on this text field.
+  /// {@template flutter.material.textfield.onTap}
+  /// Called for each distinct tap except for every second tap of a double tap.
   ///
   /// The text field builds a [GestureDetector] to handle input events like tap,
   /// to trigger focus requests, to move the caret, adjust the selection, etc.
@@ -450,6 +451,7 @@
   ///
   /// To listen to arbitrary pointer events without competing with the
   /// text field's internal gesture detector, use a [Listener].
+  /// {@endtemplate}
   final GestureTapCallback onTap;
 
   /// Callback that generates a custom [InputDecorator.counter] widget.
diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart
index 3ac4098..7183fdb 100644
--- a/packages/flutter/test/cupertino/text_field_test.dart
+++ b/packages/flutter/test/cupertino/text_field_test.dart
@@ -2512,6 +2512,90 @@
     },
   );
 
+  testWidgets('onTap is called upon tap', (WidgetTester tester) async {
+    int tapCount = 0;
+    await tester.pumpWidget(
+      CupertinoApp(
+        home: Center(
+          child: CupertinoTextField(
+            onTap: () => tapCount++,
+          ),
+        ),
+      ),
+    );
+
+    expect(tapCount, 0);
+    await tester.tap(find.byType(CupertinoTextField));
+    await tester.pump();
+    expect(tapCount, 1);
+
+    // Wait out the double tap interval so the next tap doesn't end up being
+    // recognized as a double tap.
+    await tester.pump(const Duration(seconds: 1));
+
+    // Double tap count as one single tap.
+    await tester.tap(find.byType(CupertinoTextField));
+    await tester.pump(const Duration(milliseconds: 100));
+    await tester.tap(find.byType(CupertinoTextField));
+    await tester.pump();
+    expect(tapCount, 2);
+  });
+
+  testWidgets('onTap does not work when the text field is disabled',
+    (WidgetTester tester) async {
+      int tapCount = 0;
+      await tester.pumpWidget(
+        CupertinoApp(
+          home: Center(
+            child: CupertinoTextField(
+              enabled: false,
+              onTap: () => tapCount++,
+            ),
+          ),
+        ),
+      );
+
+      expect(tapCount, 0);
+      await tester.tap(find.byType(CupertinoTextField));
+      await tester.pump();
+      expect(tapCount, 0);
+
+      // Wait out the double tap interval so the next tap doesn't end up being
+      // recognized as a double tap.
+      await tester.pump(const Duration(seconds: 1));
+
+      // Enabling the text field, now it should accept taps.
+      await tester.pumpWidget(
+        CupertinoApp(
+          home: Center(
+            child: CupertinoTextField(
+              onTap: () => tapCount++,
+            ),
+          ),
+        ),
+      );
+
+      await tester.tap(find.byType(CupertinoTextField));
+      expect(tapCount, 1);
+
+      await tester.pump(const Duration(seconds: 1));
+
+      // Disable it again.
+      await tester.pumpWidget(
+        CupertinoApp(
+          home: Center(
+            child: CupertinoTextField(
+              enabled: false,
+              onTap: () => tapCount++,
+            ),
+          ),
+        ),
+      );
+      await tester.tap(find.byType(CupertinoTextField));
+      await tester.pump();
+      expect(tapCount, 1);
+  });
+
   testWidgets(
     'text field respects theme',
     (WidgetTester tester) async {