@@ -75,6 +75,8 @@ class CupertinoSwitch extends StatefulWidget {
75
75
this .thumbColor,
76
76
this .applyTheme,
77
77
this .focusColor,
78
+ this .onLabelColor,
79
+ this .offLabelColor,
78
80
this .focusNode,
79
81
this .onFocusChange,
80
82
this .autofocus = false ,
@@ -133,6 +135,17 @@ class CupertinoSwitch extends StatefulWidget {
133
135
/// Defaults to a slightly transparent [activeColor] .
134
136
final Color ? focusColor;
135
137
138
+ /// The color to use for the accessibility label when the switch is on.
139
+ ///
140
+ /// Defaults to [CupertinoColors.white] when null.
141
+ final Color ? onLabelColor;
142
+
143
+ /// The color to use for the accessibility label when the switch is off.
144
+ ///
145
+ /// Defaults to [Color.fromARGB(255, 179, 179, 179)]
146
+ /// (or [Color.fromARGB(255, 255, 255, 255)] in high contrast) when null.
147
+ final Color ? offLabelColor;
148
+
136
149
/// {@macro flutter.widgets.Focus.focusNode}
137
150
final FocusNode ? focusNode;
138
151
@@ -357,6 +370,19 @@ class _CupertinoSwitchState extends State<CupertinoSwitch> with TickerProviderSt
357
370
?? CupertinoColors .systemGreen,
358
371
context,
359
372
);
373
+ final (Color onLabelColor, Color offLabelColor)? onOffLabelColors =
374
+ MediaQuery .onOffSwitchLabelsOf (context)
375
+ ? (
376
+ CupertinoDynamicColor .resolve (
377
+ widget.onLabelColor ?? CupertinoColors .white,
378
+ context,
379
+ ),
380
+ CupertinoDynamicColor .resolve (
381
+ widget.offLabelColor ?? _kOffLabelColor,
382
+ context,
383
+ ),
384
+ )
385
+ : null ;
360
386
if (needsPositionAnimation) {
361
387
_resumePositionAnimation ();
362
388
}
@@ -389,6 +415,7 @@ class _CupertinoSwitchState extends State<CupertinoSwitch> with TickerProviderSt
389
415
textDirection: Directionality .of (context),
390
416
isFocused: isFocused,
391
417
state: this ,
418
+ onOffLabelColors: onOffLabelColors,
392
419
),
393
420
),
394
421
),
@@ -417,6 +444,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
417
444
required this .textDirection,
418
445
required this .isFocused,
419
446
required this .state,
447
+ required this .onOffLabelColors,
420
448
});
421
449
422
450
final bool value;
@@ -428,6 +456,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
428
456
final _CupertinoSwitchState state;
429
457
final TextDirection textDirection;
430
458
final bool isFocused;
459
+ final (Color onLabelColor, Color offLabelColor)? onOffLabelColors;
431
460
432
461
@override
433
462
_RenderCupertinoSwitch createRenderObject (BuildContext context) {
@@ -441,6 +470,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
441
470
textDirection: textDirection,
442
471
isFocused: isFocused,
443
472
state: state,
473
+ onOffLabelColors: onOffLabelColors,
444
474
);
445
475
}
446
476
@@ -467,6 +497,24 @@ const double _kTrackInnerEnd = _kTrackWidth - _kTrackInnerStart;
467
497
const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart;
468
498
const double _kSwitchWidth = 59.0 ;
469
499
const double _kSwitchHeight = 39.0 ;
500
+ // Label sizes and padding taken from xcode inspector.
501
+ // See https://github.com/flutter/flutter/issues/4830#issuecomment-528495360
502
+ const double _kOnLabelWidth = 1.0 ;
503
+ const double _kOnLabelHeight = 10.0 ;
504
+ const double _kOnLabelPaddingHorizontal = 11.0 ;
505
+ const double _kOffLabelWidth = 1.0 ;
506
+ const double _kOffLabelPaddingHorizontal = 12.0 ;
507
+ const double _kOffLabelRadius = 5.0 ;
508
+ const CupertinoDynamicColor _kOffLabelColor = CupertinoDynamicColor .withBrightnessAndContrast (
509
+ debugLabel: 'offSwitchLabel' ,
510
+ // Source: https://github.com/flutter/flutter/pull/39993#discussion_r321946033
511
+ color: Color .fromARGB (255 , 179 , 179 , 179 ),
512
+ // Source: https://github.com/flutter/flutter/pull/39993#issuecomment-535196665
513
+ darkColor: Color .fromARGB (255 , 179 , 179 , 179 ),
514
+ // Source: https://github.com/flutter/flutter/pull/127776#discussion_r1244208264
515
+ highContrastColor: Color .fromARGB (255 , 255 , 255 , 255 ),
516
+ darkHighContrastColor: Color .fromARGB (255 , 255 , 255 , 255 ),
517
+ );
470
518
// Opacity of a disabled switch, as eye-balled from iOS Simulator on Mac.
471
519
const double _kCupertinoSwitchDisabledOpacity = 0.5 ;
472
520
@@ -484,6 +532,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
484
532
required TextDirection textDirection,
485
533
required bool isFocused,
486
534
required _CupertinoSwitchState state,
535
+ required (Color onLabelColor, Color offLabelColor)? onOffLabelColors,
487
536
}) : _value = value,
488
537
_activeColor = activeColor,
489
538
_trackColor = trackColor,
@@ -493,6 +542,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
493
542
_textDirection = textDirection,
494
543
_isFocused = isFocused,
495
544
_state = state,
545
+ _onOffLabelColors = onOffLabelColors,
496
546
super (additionalConstraints: const BoxConstraints .tightFor (width: _kSwitchWidth, height: _kSwitchHeight)) {
497
547
state.position.addListener (markNeedsPaint);
498
548
state._reaction.addListener (markNeedsPaint);
@@ -584,6 +634,16 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
584
634
markNeedsPaint ();
585
635
}
586
636
637
+ (Color onLabelColor, Color offLabelColor)? get onOffLabelColors => _onOffLabelColors;
638
+ (Color onLabelColor, Color offLabelColor)? _onOffLabelColors;
639
+ set onOffLabelColors ((Color onLabelColor, Color offLabelColor)? value) {
640
+ if (value == _onOffLabelColors) {
641
+ return ;
642
+ }
643
+ _onOffLabelColors = value;
644
+ markNeedsPaint ();
645
+ }
646
+
587
647
bool get isInteractive => onChanged != null ;
588
648
589
649
@override
@@ -649,6 +709,52 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
649
709
canvas.drawRRect (borderTrackRRect, borderPaint);
650
710
}
651
711
712
+ if (_onOffLabelColors != null ) {
713
+ final (Color onLabelColor, Color offLabelColor) = onOffLabelColors! ;
714
+
715
+ final double leftLabelOpacity = visualPosition * (1.0 - currentReactionValue);
716
+ final double rightLabelOpacity = (1.0 - visualPosition) * (1.0 - currentReactionValue);
717
+ final (double onLabelOpacity, double offLabelOpacity) =
718
+ switch (textDirection) {
719
+ TextDirection .ltr => (leftLabelOpacity, rightLabelOpacity),
720
+ TextDirection .rtl => (rightLabelOpacity, leftLabelOpacity),
721
+ };
722
+
723
+ final (Offset onLabelOffset, Offset offLabelOffset) =
724
+ switch (textDirection) {
725
+ TextDirection .ltr => (
726
+ trackRect.centerLeft.translate (_kOnLabelPaddingHorizontal, 0 ),
727
+ trackRect.centerRight.translate (- _kOffLabelPaddingHorizontal, 0 ),
728
+ ),
729
+ TextDirection .rtl => (
730
+ trackRect.centerRight.translate (- _kOnLabelPaddingHorizontal, 0 ),
731
+ trackRect.centerLeft.translate (_kOffLabelPaddingHorizontal, 0 ),
732
+ ),
733
+ };
734
+
735
+ // Draws '|' label
736
+ final Rect onLabelRect = Rect .fromCenter (
737
+ center: onLabelOffset,
738
+ width: _kOnLabelWidth,
739
+ height: _kOnLabelHeight,
740
+ );
741
+ final Paint onLabelPaint = Paint ()
742
+ ..color = onLabelColor.withOpacity (onLabelOpacity)
743
+ ..style = PaintingStyle .fill;
744
+ canvas.drawRect (onLabelRect, onLabelPaint);
745
+
746
+ // Draws 'O' label
747
+ final Paint offLabelPaint = Paint ()
748
+ ..color = offLabelColor.withOpacity (offLabelOpacity)
749
+ ..style = PaintingStyle .stroke
750
+ ..strokeWidth = _kOffLabelWidth;
751
+ canvas.drawCircle (
752
+ offLabelOffset,
753
+ _kOffLabelRadius,
754
+ offLabelPaint,
755
+ );
756
+ }
757
+
652
758
final double currentThumbExtension = CupertinoThumbPainter .extension * currentReactionValue;
653
759
final double thumbLeft = lerpDouble (
654
760
trackRect.left + _kTrackInnerStart - CupertinoThumbPainter .radius,
0 commit comments