@@ -663,14 +663,14 @@ class _SlidingTapGestureRecognizer extends VerticalDragGestureRecognizer {
663
663
// enters, leaves, or ends this `MetaData` widget, corresponding methods of this
664
664
// class will be called.
665
665
//
666
- // Multiple `_ActionSheetSlideTarget `s might be nested.
666
+ // Multiple `_SlideTarget `s might be nested.
667
667
// `_TargetSelectionGestureRecognizer` uses a simple algorithm that only
668
668
// compares if the inner-most slide target has changed (which suffices our use
669
669
// case). Semantically, this means that all outer targets will be treated as
670
670
// identical to the inner-most one, i.e. when the pointer enters or leaves a
671
671
// slide target, the corresponding method will be called on all targets that
672
672
// nest it.
673
- abstract class _ActionSheetSlideTarget {
673
+ abstract class _SlideTarget {
674
674
// A pointer has entered this region.
675
675
//
676
676
// This includes:
@@ -702,7 +702,7 @@ abstract class _ActionSheetSlideTarget {
702
702
}
703
703
704
704
// Recognizes sliding taps and thereupon interacts with
705
- // `_ActionSheetSlideTarget `s.
705
+ // `_SlideTarget `s.
706
706
class _TargetSelectionGestureRecognizer extends GestureRecognizer {
707
707
_TargetSelectionGestureRecognizer ({super .debugOwner, required this .hitTest})
708
708
: _slidingTap = _SlidingTapGestureRecognizer (debugOwner: debugOwner) {
@@ -715,7 +715,7 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
715
715
716
716
final _HitTester hitTest;
717
717
718
- final List <_ActionSheetSlideTarget > _currentTargets = < _ActionSheetSlideTarget > [];
718
+ final List <_SlideTarget > _currentTargets = < _SlideTarget > [];
719
719
final _SlidingTapGestureRecognizer _slidingTap;
720
720
721
721
@override
@@ -744,7 +744,7 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
744
744
super .dispose ();
745
745
}
746
746
747
- // Collect the `_ActionSheetSlideTarget `s that are currently hit by the
747
+ // Collect the `_SlideTarget `s that are currently hit by the
748
748
// pointer, check whether the current target have changed, and invoke their
749
749
// methods if necessary.
750
750
//
@@ -755,27 +755,27 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
755
755
756
756
// A slide target might nest other targets, therefore multiple targets might
757
757
// be found.
758
- final List <_ActionSheetSlideTarget > foundTargets = < _ActionSheetSlideTarget > [];
758
+ final List <_SlideTarget > foundTargets = < _SlideTarget > [];
759
759
for (final HitTestEntry entry in result.path) {
760
760
if (entry.target case final RenderMetaData target) {
761
- if (target.metaData is _ActionSheetSlideTarget ) {
762
- foundTargets.add (target.metaData as _ActionSheetSlideTarget );
761
+ if (target.metaData is _SlideTarget ) {
762
+ foundTargets.add (target.metaData as _SlideTarget );
763
763
}
764
764
}
765
765
}
766
766
767
767
// Compare whether the active target has changed by simply comparing the
768
768
// first (inner-most) avatar of the nest, ignoring the cases where
769
- // _currentTargets intersect with foundTargets (see _ActionSheetSlideTarget 's
769
+ // _currentTargets intersect with foundTargets (see _SlideTarget 's
770
770
// document for more explanation).
771
771
if (_currentTargets.firstOrNull != foundTargets.firstOrNull) {
772
- for (final _ActionSheetSlideTarget target in _currentTargets) {
772
+ for (final _SlideTarget target in _currentTargets) {
773
773
target.didLeave ();
774
774
}
775
775
_currentTargets
776
776
..clear ()
777
777
..addAll (foundTargets);
778
- for (final _ActionSheetSlideTarget target in _currentTargets) {
778
+ for (final _SlideTarget target in _currentTargets) {
779
779
target.didEnter (fromPointerDown: fromPointerDown);
780
780
}
781
781
}
@@ -791,14 +791,14 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
791
791
792
792
void _onEnd (Offset globalPosition) {
793
793
_updateDrag (globalPosition, fromPointerDown: false );
794
- for (final _ActionSheetSlideTarget target in _currentTargets) {
794
+ for (final _SlideTarget target in _currentTargets) {
795
795
target.didConfirm ();
796
796
}
797
797
_currentTargets.clear ();
798
798
}
799
799
800
800
void _onCancel () {
801
- for (final _ActionSheetSlideTarget target in _currentTargets) {
801
+ for (final _SlideTarget target in _currentTargets) {
802
802
target.didLeave ();
803
803
}
804
804
_currentTargets.clear ();
@@ -949,6 +949,9 @@ class CupertinoActionSheet extends StatefulWidget {
949
949
}
950
950
951
951
class _CupertinoActionSheetState extends State <CupertinoActionSheet > {
952
+ int ? _pressedIndex;
953
+ static const int _kCancelButtonIndex = - 1 ;
954
+
952
955
ScrollController ? _backupMessageScrollController;
953
956
954
957
ScrollController ? _backupActionScrollController;
@@ -1004,6 +1007,20 @@ class _CupertinoActionSheetState extends State<CupertinoActionSheet> {
1004
1007
);
1005
1008
}
1006
1009
1010
+ void _onPressedUpdate (int actionIndex, bool state) {
1011
+ if (! state) {
1012
+ if (_pressedIndex == actionIndex) {
1013
+ setState (() {
1014
+ _pressedIndex = null ;
1015
+ });
1016
+ }
1017
+ } else {
1018
+ setState (() {
1019
+ _pressedIndex = actionIndex;
1020
+ });
1021
+ }
1022
+ }
1023
+
1007
1024
Widget _buildCancelButton () {
1008
1025
assert (widget.cancelButton != null );
1009
1026
final double cancelPadding = (widget.actions != null || widget.message != null || widget.title != null )
@@ -1012,7 +1029,10 @@ class _CupertinoActionSheetState extends State<CupertinoActionSheet> {
1012
1029
padding: EdgeInsets .only (top: cancelPadding),
1013
1030
child: _ActionSheetButtonBackground (
1014
1031
isCancel: true ,
1015
- onPressStateChange: (_) {},
1032
+ pressed: _pressedIndex == _kCancelButtonIndex,
1033
+ onPressStateChange: (bool state) {
1034
+ _onPressedUpdate (_kCancelButtonIndex, state);
1035
+ },
1016
1036
child: widget.cancelButton! ,
1017
1037
),
1018
1038
);
@@ -1106,6 +1126,8 @@ class _CupertinoActionSheetState extends State<CupertinoActionSheet> {
1106
1126
child: BackdropFilter (
1107
1127
filter: ImageFilter .blur (sigmaX: _kBlurAmount, sigmaY: _kBlurAmount),
1108
1128
child: _ActionSheetMainSheet (
1129
+ pressedIndex: _pressedIndex,
1130
+ onPressedUpdate: _onPressedUpdate,
1109
1131
scrollController: _effectiveActionScrollController,
1110
1132
contentSection: _buildContent (context),
1111
1133
actions: widget.actions ?? List <Widget >.empty (),
@@ -1208,16 +1230,16 @@ class CupertinoActionSheetAction extends StatefulWidget {
1208
1230
}
1209
1231
1210
1232
class _CupertinoActionSheetActionState extends State <CupertinoActionSheetAction >
1211
- implements _ActionSheetSlideTarget {
1212
- // |_ActionSheetSlideTarget |
1233
+ implements _SlideTarget {
1234
+ // |_SlideTarget |
1213
1235
@override
1214
1236
void didEnter ({required bool fromPointerDown}) {}
1215
1237
1216
- // |_ActionSheetSlideTarget |
1238
+ // |_SlideTarget |
1217
1239
@override
1218
1240
void didLeave () {}
1219
1241
1220
- // |_ActionSheetSlideTarget |
1242
+ // |_SlideTarget |
1221
1243
@override
1222
1244
void didConfirm () {
1223
1245
widget.onPressed ();
@@ -1307,15 +1329,23 @@ class _CupertinoActionSheetActionState extends State<CupertinoActionSheetAction>
1307
1329
1308
1330
// Renders the background of a button (both the pressed background and the idle
1309
1331
// background) and reports its state to the parent with `onPressStateChange`.
1332
+ //
1333
+ // Although this class doesn't keep any states, it's still a stateful widget
1334
+ // because the state is used as a persistent object across rebuilds to provide
1335
+ // to [MetaData.data].
1310
1336
class _ActionSheetButtonBackground extends StatefulWidget {
1311
1337
const _ActionSheetButtonBackground ({
1312
1338
this .isCancel = false ,
1339
+ required this .pressed,
1313
1340
this .onPressStateChange,
1314
1341
required this .child,
1315
1342
});
1316
1343
1317
1344
final bool isCancel;
1318
1345
1346
+ /// Whether the user is holding on this button.
1347
+ final bool pressed;
1348
+
1319
1349
/// Called when the user taps down or lifts up on the button.
1320
1350
///
1321
1351
/// The boolean value is true if the user is tapping down on the button.
@@ -1330,9 +1360,7 @@ class _ActionSheetButtonBackground extends StatefulWidget {
1330
1360
_ActionSheetButtonBackgroundState createState () => _ActionSheetButtonBackgroundState ();
1331
1361
}
1332
1362
1333
- class _ActionSheetButtonBackgroundState extends State <_ActionSheetButtonBackground > implements _ActionSheetSlideTarget {
1334
- bool isBeingPressed = false ;
1335
-
1363
+ class _ActionSheetButtonBackgroundState extends State <_ActionSheetButtonBackground > implements _SlideTarget {
1336
1364
void _emitVibration (){
1337
1365
switch (defaultTargetPlatform) {
1338
1366
case TargetPlatform .iOS:
@@ -1346,27 +1374,24 @@ class _ActionSheetButtonBackgroundState extends State<_ActionSheetButtonBackgrou
1346
1374
}
1347
1375
}
1348
1376
1349
- // |_ActionSheetSlideTarget |
1377
+ // |_SlideTarget |
1350
1378
@override
1351
1379
void didEnter ({required bool fromPointerDown}) {
1352
- setState (() { isBeingPressed = true ; });
1353
1380
widget.onPressStateChange? .call (true );
1354
1381
if (! fromPointerDown) {
1355
1382
_emitVibration ();
1356
1383
}
1357
1384
}
1358
1385
1359
- // |_ActionSheetSlideTarget |
1386
+ // |_SlideTarget |
1360
1387
@override
1361
1388
void didLeave () {
1362
- setState (() { isBeingPressed = false ; });
1363
1389
widget.onPressStateChange? .call (false );
1364
1390
}
1365
1391
1366
- // |_ActionSheetSlideTarget |
1392
+ // |_SlideTarget |
1367
1393
@override
1368
1394
void didConfirm () {
1369
- setState (() { isBeingPressed = false ; });
1370
1395
widget.onPressStateChange? .call (false );
1371
1396
}
1372
1397
@@ -1375,11 +1400,11 @@ class _ActionSheetButtonBackgroundState extends State<_ActionSheetButtonBackgrou
1375
1400
late final Color backgroundColor;
1376
1401
BorderRadius ? borderRadius;
1377
1402
if (! widget.isCancel) {
1378
- backgroundColor = isBeingPressed
1403
+ backgroundColor = widget.pressed
1379
1404
? _kActionSheetPressedColor
1380
1405
: _kActionSheetBackgroundColor;
1381
1406
} else {
1382
- backgroundColor = isBeingPressed
1407
+ backgroundColor = widget.pressed
1383
1408
? _kActionSheetCancelPressedColor
1384
1409
: _kActionSheetCancelColor;
1385
1410
borderRadius = const BorderRadius .all (Radius .circular (_kCornerRadius));
@@ -1566,6 +1591,7 @@ class _ActionSheetActionSection extends StatelessWidget {
1566
1591
));
1567
1592
}
1568
1593
column.add (_ActionSheetButtonBackground (
1594
+ pressed: pressedIndex == actionIndex,
1569
1595
onPressStateChange: (bool state) {
1570
1596
onPressedUpdate (actionIndex, state);
1571
1597
},
@@ -1586,86 +1612,69 @@ class _ActionSheetActionSection extends StatelessWidget {
1586
1612
}
1587
1613
1588
1614
// The part of an action sheet without the cancel button.
1589
- class _ActionSheetMainSheet extends StatefulWidget {
1615
+ class _ActionSheetMainSheet extends StatelessWidget {
1590
1616
const _ActionSheetMainSheet ({
1617
+ required this .pressedIndex,
1618
+ required this .onPressedUpdate,
1591
1619
required this .scrollController,
1592
1620
required this .actions,
1593
1621
required this .contentSection,
1594
1622
required this .dividerColor,
1595
1623
});
1596
1624
1625
+ final int ? pressedIndex;
1626
+ final _PressedUpdateHandler onPressedUpdate;
1597
1627
final ScrollController ? scrollController;
1598
1628
final List <Widget > actions;
1599
1629
final Widget ? contentSection;
1600
1630
final Color dividerColor;
1601
1631
1602
- @override
1603
- _ActionSheetMainSheetState createState () => _ActionSheetMainSheetState ();
1604
- }
1605
-
1606
- class _ActionSheetMainSheetState extends State <_ActionSheetMainSheet > {
1607
- int ? _pressedIndex;
1608
-
1609
- bool get _hasContent => widget.contentSection != null ;
1610
- bool get _hasActions => widget.actions.isNotEmpty;
1611
-
1612
- void _onPressedUpdate (int actionIndex, bool state) {
1613
- if (! state) {
1614
- if (_pressedIndex == actionIndex) {
1615
- setState (() {
1616
- _pressedIndex = null ;
1617
- });
1618
- }
1619
- } else {
1620
- setState (() {
1621
- _pressedIndex = actionIndex;
1622
- });
1623
- }
1632
+ Widget _scrolledActionsSection (BuildContext context) {
1633
+ final Color backgroundColor = CupertinoDynamicColor .resolve (_kActionSheetBackgroundColor, context);
1634
+ return _OverscrollBackground (
1635
+ scrollController: scrollController,
1636
+ color: backgroundColor,
1637
+ child: _ActionSheetActionSection (
1638
+ actions: actions,
1639
+ scrollController: scrollController,
1640
+ dividerColor: dividerColor,
1641
+ backgroundColor: backgroundColor,
1642
+ pressedIndex: pressedIndex,
1643
+ onPressedUpdate: onPressedUpdate,
1644
+ ),
1645
+ );
1624
1646
}
1625
1647
1626
1648
Widget _dividerAndActionsSection (BuildContext context) {
1627
- if (! _hasActions) {
1628
- return _empty;
1629
- }
1630
1649
final Color backgroundColor = CupertinoDynamicColor .resolve (_kActionSheetBackgroundColor, context);
1631
1650
return Column (
1632
1651
mainAxisSize: MainAxisSize .min,
1633
1652
crossAxisAlignment: CrossAxisAlignment .stretch,
1634
1653
children: < Widget > [
1635
- if (_hasContent)
1636
- _Divider (
1637
- dividerColor: widget.dividerColor,
1638
- hiddenColor: backgroundColor,
1639
- hidden: false ,
1640
- ),
1654
+ _Divider (
1655
+ dividerColor: dividerColor,
1656
+ hiddenColor: backgroundColor,
1657
+ hidden: false ,
1658
+ ),
1641
1659
Flexible (
1642
- child: _OverscrollBackground (
1643
- scrollController: widget.scrollController,
1644
- color: backgroundColor,
1645
- child: _ActionSheetActionSection (
1646
- actions: widget.actions,
1647
- scrollController: widget.scrollController,
1648
- pressedIndex: _pressedIndex,
1649
- dividerColor: widget.dividerColor,
1650
- backgroundColor: backgroundColor,
1651
- onPressedUpdate: _onPressedUpdate,
1652
- ),
1653
- ),
1660
+ child: _scrolledActionsSection (context),
1654
1661
),
1655
1662
],
1656
1663
);
1657
1664
}
1658
1665
1659
1666
@override
1660
1667
Widget build (BuildContext context) {
1661
- return LayoutBuilder (
1662
- builder: (BuildContext context, BoxConstraints constraints) {
1663
- return _PriorityColumn (
1664
- top: widget.contentSection ?? _empty,
1665
- bottom: _dividerAndActionsSection (context),
1666
- bottomMinHeight: _kActionSheetActionsSectionMinHeight + _kDividerThickness,
1667
- );
1668
- },
1668
+ if (actions.isEmpty) {
1669
+ return contentSection ?? _empty;
1670
+ }
1671
+ if (contentSection == null ) {
1672
+ return _scrolledActionsSection (context);
1673
+ }
1674
+ return _PriorityColumn (
1675
+ top: contentSection! ,
1676
+ bottom: _dividerAndActionsSection (context),
1677
+ bottomMinHeight: _kActionSheetActionsSectionMinHeight + _kDividerThickness,
1669
1678
);
1670
1679
}
1671
1680
0 commit comments