@@ -16,6 +16,7 @@ import 'package:flutter/widgets.dart';
16
16
17
17
import 'colors.dart' ;
18
18
import 'localizations.dart' ;
19
+ import 'scrollbar.dart' ;
19
20
20
21
// The scale of the child at the time that the CupertinoContextMenu opens.
21
22
// This value was eyeballed from a physical device running iOS 13.1.2.
@@ -58,6 +59,11 @@ const Color _borderColor = CupertinoDynamicColor.withBrightness(
58
59
darkColor: Color (0xFF57585A ),
59
60
);
60
61
62
+ const Color _kBackgroundColor = CupertinoDynamicColor .withBrightness (
63
+ color: Color (0xFFF1F1F1 ),
64
+ darkColor: Color (0xFF212122 ),
65
+ );
66
+
61
67
typedef _DismissCallback = void Function (
62
68
BuildContext context,
63
69
double scale,
@@ -233,6 +239,10 @@ class CupertinoContextMenu extends StatefulWidget {
233
239
static final double animationOpensAt =
234
240
_previewLongPressTimeout.inMilliseconds / _animationDuration;
235
241
242
+ /// The background color of a [CupertinoContextMenuAction] and a
243
+ /// [CupertinoContextMenu] sheet.
244
+ static const Color kBackgroundColor = _kBackgroundColor;
245
+
236
246
/// A function that returns a widget to be used alternatively from [child] .
237
247
///
238
248
/// The widget returned by the function will be shown at all times: when the
@@ -1369,21 +1379,36 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
1369
1379
1370
1380
// The menu that displays when CupertinoContextMenu is open. It consists of a
1371
1381
// list of actions that are typically CupertinoContextMenuActions.
1372
- class _ContextMenuSheet extends StatelessWidget {
1382
+ class _ContextMenuSheet extends StatefulWidget {
1373
1383
_ContextMenuSheet ({
1374
1384
super .key,
1375
1385
required this .actions,
1376
- required _ContextMenuLocation contextMenuLocation,
1377
- required Orientation orientation,
1378
- }) : assert (actions.isNotEmpty),
1379
- _contextMenuLocation = contextMenuLocation,
1380
- _orientation = orientation;
1386
+ required this .contextMenuLocation,
1387
+ required this .orientation,
1388
+ }) : assert (actions.isNotEmpty);
1381
1389
1382
1390
final List <Widget > actions;
1383
- final _ContextMenuLocation _contextMenuLocation ;
1384
- final Orientation _orientation ;
1391
+ final _ContextMenuLocation contextMenuLocation ;
1392
+ final Orientation orientation ;
1385
1393
1394
+ @override
1395
+ State <_ContextMenuSheet > createState () => _ContextMenuSheetState ();
1396
+ }
1397
+
1398
+ class _ContextMenuSheetState extends State <_ContextMenuSheet > {
1399
+ late final ScrollController _controller;
1386
1400
static const double _kMenuWidth = 250.0 ;
1401
+ // Eyeballed on a context menu on an iOS 15 simulator running iOS 17.5.
1402
+ static const double _kScrollbarMainAxisMargin = 13.0 ;
1403
+
1404
+ @override
1405
+ void initState () {
1406
+ super .initState ();
1407
+ // Link the scrollbar to the scroll view by providing both the same scroll
1408
+ // controller. Using SingleChildScrollview.primary might conflict with users
1409
+ // already using the PrimaryScrollController.
1410
+ _controller = ScrollController ();
1411
+ }
1387
1412
1388
1413
// Get the children, whose order depends on orientation and
1389
1414
// contextMenuLocation.
@@ -1393,40 +1418,59 @@ class _ContextMenuSheet extends StatelessWidget {
1393
1418
child: IntrinsicHeight (
1394
1419
child: ClipRRect (
1395
1420
borderRadius: const BorderRadius .all (Radius .circular (13.0 )),
1396
- child: Column (
1397
- crossAxisAlignment: CrossAxisAlignment .stretch,
1398
- children: < Widget > [
1399
- actions.first,
1400
- for (final Widget action in actions.skip (1 ))
1401
- DecoratedBox (
1402
- decoration: BoxDecoration (
1403
- border: Border (
1404
- top: BorderSide (
1405
- color: CupertinoDynamicColor .resolve (
1406
- _borderColor,
1407
- context,
1421
+ child: ColoredBox (
1422
+ color: CupertinoDynamicColor .resolve (CupertinoContextMenu .kBackgroundColor, context),
1423
+ child: ScrollConfiguration (
1424
+ behavior: ScrollConfiguration .of (context).copyWith (scrollbars: false ),
1425
+ child: CupertinoScrollbar (
1426
+ mainAxisMargin: _kScrollbarMainAxisMargin,
1427
+ controller: _controller,
1428
+ child: SingleChildScrollView (
1429
+ controller: _controller,
1430
+ child: Column (
1431
+ crossAxisAlignment: CrossAxisAlignment .stretch,
1432
+ children: < Widget > [
1433
+ widget.actions.first,
1434
+ for (final Widget action in widget.actions.skip (1 ))
1435
+ DecoratedBox (
1436
+ decoration: BoxDecoration (
1437
+ border: Border (
1438
+ top: BorderSide (
1439
+ color: CupertinoDynamicColor .resolve (
1440
+ _borderColor,
1441
+ context,
1442
+ ),
1443
+ width: 0.4 ,
1444
+ ),
1445
+ ),
1446
+ ),
1447
+ position: DecorationPosition .foreground,
1448
+ child: action,
1408
1449
),
1409
- width: 0.4 ,
1410
- ),
1411
- ),
1450
+ ],
1412
1451
),
1413
- position: DecorationPosition .foreground,
1414
- child: action,
1415
1452
),
1416
- ],
1453
+ ),
1454
+ ),
1417
1455
),
1418
1456
),
1419
1457
),
1420
1458
);
1421
1459
1422
- return switch (_contextMenuLocation ) {
1423
- _ContextMenuLocation .center when _orientation == Orientation .portrait => < Widget > [const Spacer (), menu, const Spacer ()],
1460
+ return switch (widget.contextMenuLocation ) {
1461
+ _ContextMenuLocation .center when widget.orientation == Orientation .portrait => < Widget > [const Spacer (), menu, const Spacer ()],
1424
1462
_ContextMenuLocation .center => < Widget > [menu, const Spacer ()],
1425
1463
_ContextMenuLocation .right => < Widget > [const Spacer (), menu],
1426
1464
_ContextMenuLocation .left => < Widget > [menu, const Spacer ()],
1427
1465
};
1428
1466
}
1429
1467
1468
+ @override
1469
+ void dispose () {
1470
+ _controller.dispose ();
1471
+ super .dispose ();
1472
+ }
1473
+
1430
1474
@override
1431
1475
Widget build (BuildContext context) {
1432
1476
return Row (
0 commit comments