7
7
library ;
8
8
9
9
import 'package:flutter/foundation.dart' ;
10
+ import 'package:flutter/gestures.dart' ;
10
11
import 'package:flutter/rendering.dart' ;
11
12
import 'package:flutter/services.dart' ;
12
13
import 'package:flutter/widgets.dart' ;
@@ -24,6 +25,13 @@ const double _kSqueeze = 1.45;
24
25
// lens.
25
26
const double _kOverAndUnderCenterOpacity = 0.447 ;
26
27
28
+ // The duration and curve of the tap-to-scroll gesture's animation when a picker
29
+ // item is tapped.
30
+ //
31
+ // Eyeballed from an iPhone 15 Pro simulator running iOS 17.5.
32
+ const Duration _kCupertinoPickerTapToScrollDuration = Duration (milliseconds: 300 );
33
+ const Curve _kCupertinoPickerTapToScrollCurve = Curves .easeInOut;
34
+
27
35
/// An iOS-styled picker.
28
36
///
29
37
/// Displays its children widgets on a wheel for selection and
@@ -258,6 +266,14 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
258
266
widget.onSelectedItemChanged? .call (index);
259
267
}
260
268
269
+ void _handleChildTap (int index, FixedExtentScrollController controller) {
270
+ controller.animateToItem (
271
+ index,
272
+ duration: _kCupertinoPickerTapToScrollDuration,
273
+ curve: _kCupertinoPickerTapToScrollCurve,
274
+ );
275
+ }
276
+
261
277
/// Draws the selectionOverlay.
262
278
Widget _buildSelectionOverlay (Widget selectionOverlay) {
263
279
final double height = widget.itemExtent * widget.magnification;
@@ -280,15 +296,16 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
280
296
final Color ? resolvedBackgroundColor = CupertinoDynamicColor .maybeResolve (widget.backgroundColor, context);
281
297
282
298
assert (RenderListWheelViewport .defaultPerspective == _kDefaultPerspective);
299
+ final FixedExtentScrollController controller = widget.scrollController ?? _controller! ;
283
300
final Widget result = DefaultTextStyle (
284
301
style: textStyle.copyWith (color: CupertinoDynamicColor .maybeResolve (textStyle.color, context)),
285
302
child: Stack (
286
303
children: < Widget > [
287
304
Positioned .fill (
288
305
child: _CupertinoPickerSemantics (
289
- scrollController: widget.scrollController ?? _controller ! ,
306
+ scrollController: controller ,
290
307
child: ListWheelScrollView .useDelegate (
291
- controller: widget.scrollController ?? _controller ,
308
+ controller: controller ,
292
309
physics: const FixedExtentScrollPhysics (),
293
310
diameterRatio: widget.diameterRatio,
294
311
offAxisFraction: widget.offAxisFraction,
@@ -298,7 +315,11 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
298
315
itemExtent: widget.itemExtent,
299
316
squeeze: widget.squeeze,
300
317
onSelectedItemChanged: _handleSelectedItemChanged,
301
- childDelegate: widget.childDelegate,
318
+ dragStartBehavior: DragStartBehavior .down,
319
+ childDelegate: _CupertinoPickerListWheelChildDelegateWrapper (
320
+ widget.childDelegate,
321
+ onTappedChild: (int index) => _handleChildTap (index, controller),
322
+ ),
302
323
),
303
324
),
304
325
),
@@ -512,3 +533,35 @@ class _RenderCupertinoPickerSemantics extends RenderProxyBox {
512
533
controller.removeListener (_handleScrollUpdate);
513
534
}
514
535
}
536
+
537
+ class _CupertinoPickerListWheelChildDelegateWrapper implements ListWheelChildDelegate {
538
+ _CupertinoPickerListWheelChildDelegateWrapper (
539
+ this ._wrapped, {
540
+ required this .onTappedChild,
541
+ });
542
+ final ListWheelChildDelegate _wrapped;
543
+ final void Function (int index) onTappedChild;
544
+
545
+ @override
546
+ Widget ? build (BuildContext context, int index) {
547
+ final Widget ? child = _wrapped.build (context, index);
548
+ if (child == null ) {
549
+ return child;
550
+ }
551
+ return GestureDetector (
552
+ behavior: HitTestBehavior .translucent,
553
+ excludeFromSemantics: true ,
554
+ onTap: () => onTappedChild (index),
555
+ child: child,
556
+ );
557
+ }
558
+
559
+ @override
560
+ int ? get estimatedChildCount => _wrapped.estimatedChildCount;
561
+
562
+ @override
563
+ bool shouldRebuild (covariant _CupertinoPickerListWheelChildDelegateWrapper oldDelegate) => _wrapped.shouldRebuild (oldDelegate._wrapped);
564
+
565
+ @override
566
+ int trueIndexOf (int index) => _wrapped.trueIndexOf (index);
567
+ }
0 commit comments