如何在 Flutter 中更改库比蒂诺开关非活动状态颜色



我的问题是,我如何在 Flutter 中更改 CupertinoSwitch 的非活动颜色,使用普通 Switch 这相当容易,因为我们有属性,但在 CupertinoSwitch (iOS( 上我们没有这些属性。

编辑:

这是现在的自定义类,以实现这一点。

import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
class MyCupertinoSwitch extends StatefulWidget {
/// Creates an iOS-style switch.
///
/// The [value] parameter must not be null.
/// The [dragStartBehavior] parameter defaults to [DragStartBehavior.start] and must not be null.
const MyCupertinoSwitch({
Key key,
@required this.value,
@required this.onChanged,
this.activeColor,
this.trackColor,
this.dragStartBehavior = DragStartBehavior.start,
})  : assert(value != null),
assert(dragStartBehavior != null),
super(key: key);
/// Whether this switch is on or off.
///
/// Must not be null.
final bool value;
/// Called when the user toggles with switch on or off.
///
/// The switch passes the new value to the callback but does not actually
/// change state until the parent widget rebuilds the switch with the new
/// value.
///
/// If null, the switch will be displayed as disabled, which has a reduced opacity.
///
/// The callback provided to onChanged should update the state of the parent
/// [StatefulWidget] using the [State.setState] method, so that the parent
/// gets rebuilt; for example:
///
/// ```dart
/// CupertinoSwitch(
///   value: _giveVerse,
///   onChanged: (bool newValue) {
///     setState(() {
///       _giveVerse = newValue;
///     });
///   },
/// )
/// ```
final ValueChanged<bool> onChanged;
/// The color to use when this switch is on.
///
/// Defaults to [CupertinoColors.activeGreen] when null and ignores the
/// [CupertinoTheme] in accordance to native iOS behavior.
final Color activeColor;
/// The color to use when this switch is off.
///
/// Defaults to [CupertinoColors.lightBackgroundGray] when null and ignores the
/// [CupertinoTheme] in accordance to native iOS behavior.
final Color trackColor;
/// {@template flutter.cupertino.switch.dragStartBehavior}
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], the drag behavior used to move the
/// switch from on to off will begin upon the detection of a drag gesture. If
/// set to [DragStartBehavior.down] it will begin when a down event is first
/// detected.
///
/// In general, setting this to [DragStartBehavior.start] will make drag
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
///  * [DragGestureRecognizer.dragStartBehavior], which gives an example for
///    the different behaviors.
///
/// {@endtemplate}
final DragStartBehavior dragStartBehavior;
@override
_MyCupertinoSwitchState createState() => _MyCupertinoSwitchState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('value',
value: value, ifTrue: 'on', ifFalse: 'off', showName: true));
properties.add(ObjectFlagProperty<ValueChanged<bool>>(
'onChanged', onChanged,
ifNull: 'disabled'));
}
}
class _MyCupertinoSwitchState extends State<MyCupertinoSwitch>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Opacity(
opacity:
widget.onChanged == null ? _kCupertinoSwitchDisabledOpacity : 1.0,
child: _CupertinoSwitchRenderObjectWidget(
value: widget.value,
activeColor: widget.activeColor ?? CupertinoColors.activeGreen,
trackColor: widget.trackColor ?? CupertinoColors.lightBackgroundGray,
onChanged: widget.onChanged,
vsync: this,
dragStartBehavior: widget.dragStartBehavior,
),
);
}
}
class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
const _CupertinoSwitchRenderObjectWidget({
Key key,
this.value,
this.activeColor,
this.trackColor,
this.onChanged,
this.vsync,
this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key);
final bool value;
final Color activeColor;
final Color trackColor;
final ValueChanged<bool> onChanged;
final TickerProvider vsync;
final DragStartBehavior dragStartBehavior;
@override
_RenderCupertinoSwitch createRenderObject(BuildContext context) {
return _RenderCupertinoSwitch(
value: value,
activeColor: activeColor,
trackColor: trackColor,
onChanged: onChanged,
textDirection: Directionality.of(context),
vsync: vsync,
dragStartBehavior: dragStartBehavior,
);
}
@override
void updateRenderObject(
BuildContext context, _RenderCupertinoSwitch renderObject) {
renderObject
..value = value
..activeColor = activeColor
..trackColor = trackColor
..onChanged = onChanged
..textDirection = Directionality.of(context)
..vsync = vsync
..dragStartBehavior = dragStartBehavior;
}
}
const double _kTrackWidth = 51.0;
const double _kTrackHeight = 31.0;
const double _kTrackRadius = _kTrackHeight / 2.0;
const double _kTrackInnerStart = _kTrackHeight / 2.0;
const double _kTrackInnerEnd = _kTrackWidth - _kTrackInnerStart;
const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart;
const double _kSwitchWidth = 59.0;
const double _kSwitchHeight = 39.0;
// Opacity of a disabled switch, as eye-balled from iOS Simulator on Mac.
const double _kCupertinoSwitchDisabledOpacity = 0.5;
const Duration _kReactionDuration = Duration(milliseconds: 300);
const Duration _kToggleDuration = Duration(milliseconds: 200);
class _RenderCupertinoSwitch extends RenderConstrainedBox {
_RenderCupertinoSwitch({
@required bool value,
@required Color activeColor,
@required Color trackColor,
ValueChanged<bool> onChanged,
@required TextDirection textDirection,
@required TickerProvider vsync,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
})  : assert(value != null),
assert(activeColor != null),
assert(vsync != null),
_value = value,
_activeColor = activeColor,
_trackColor = trackColor,
_onChanged = onChanged,
_textDirection = textDirection,
_vsync = vsync,
super(
additionalConstraints: const BoxConstraints.tightFor(
width: _kSwitchWidth, height: _kSwitchHeight)) {
_tap = TapGestureRecognizer()
..onTapDown = _handleTapDown
..onTap = _handleTap
..onTapUp = _handleTapUp
..onTapCancel = _handleTapCancel;
_drag = HorizontalDragGestureRecognizer()
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..dragStartBehavior = dragStartBehavior;
_positionController = AnimationController(
duration: _kToggleDuration,
value: value ? 1.0 : 0.0,
vsync: vsync,
);
_position = CurvedAnimation(
parent: _positionController,
curve: Curves.linear,
)
..addListener(markNeedsPaint)
..addStatusListener(_handlePositionStateChanged);
_reactionController = AnimationController(
duration: _kReactionDuration,
vsync: vsync,
);
_reaction = CurvedAnimation(
parent: _reactionController,
curve: Curves.ease,
)..addListener(markNeedsPaint);
}
AnimationController _positionController;
CurvedAnimation _position;
AnimationController _reactionController;
Animation<double> _reaction;
bool get value => _value;
bool _value;
set value(bool value) {
assert(value != null);
if (value == _value) return;
_value = value;
markNeedsSemanticsUpdate();
_position
..curve = Curves.ease
..reverseCurve = Curves.ease.flipped;
if (value)
_positionController.forward();
else
_positionController.reverse();
}
TickerProvider get vsync => _vsync;
TickerProvider _vsync;
set vsync(TickerProvider value) {
assert(value != null);
if (value == _vsync) return;
_vsync = value;
_positionController.resync(vsync);
_reactionController.resync(vsync);
}
Color get activeColor => _activeColor;
Color get trackColor => _trackColor;
Color _activeColor;
Color _trackColor;
set activeColor(Color value) {
assert(value != null);
if (value == _activeColor) return;
_activeColor = value;
markNeedsPaint();
}
set trackColor(Color value) {
assert(value != null);
if (value == _trackColor) return;
_trackColor = value;
markNeedsPaint();
}
ValueChanged<bool> get onChanged => _onChanged;
ValueChanged<bool> _onChanged;
set onChanged(ValueChanged<bool> value) {
if (value == _onChanged) return;
final bool wasInteractive = isInteractive;
_onChanged = value;
if (wasInteractive != isInteractive) {
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
assert(value != null);
if (_textDirection == value) return;
_textDirection = value;
markNeedsPaint();
}
DragStartBehavior get dragStartBehavior => _drag.dragStartBehavior;
set dragStartBehavior(DragStartBehavior value) {
assert(value != null);
if (_drag.dragStartBehavior == value) return;
_drag.dragStartBehavior = value;
}
bool get isInteractive => onChanged != null;
TapGestureRecognizer _tap;
HorizontalDragGestureRecognizer _drag;
@override
void attach(PipelineOwner owner) {
super.attach(owner);
if (value)
_positionController.forward();
else
_positionController.reverse();
if (isInteractive) {
switch (_reactionController.status) {
case AnimationStatus.forward:
_reactionController.forward();
break;
case AnimationStatus.reverse:
_reactionController.reverse();
break;
case AnimationStatus.dismissed:
case AnimationStatus.completed:
// nothing to do
break;
}
}
}
@override
void detach() {
_positionController.stop();
_reactionController.stop();
super.detach();
}
void _handlePositionStateChanged(AnimationStatus status) {
if (isInteractive) {
if (status == AnimationStatus.completed && !_value)
onChanged(true);
else if (status == AnimationStatus.dismissed && _value) onChanged(false);
}
}
void _handleTapDown(TapDownDetails details) {
if (isInteractive) _reactionController.forward();
}
void _handleTap() {
if (isInteractive) {
onChanged(!_value);
_emitVibration();
}
}
void _handleTapUp(TapUpDetails details) {
if (isInteractive) _reactionController.reverse();
}
void _handleTapCancel() {
if (isInteractive) _reactionController.reverse();
}
void _handleDragStart(DragStartDetails details) {
if (isInteractive) {
_reactionController.forward();
_emitVibration();
}
}
void _handleDragUpdate(DragUpdateDetails details) {
if (isInteractive) {
_position
..curve = null
..reverseCurve = null;
final double delta = details.primaryDelta / _kTrackInnerLength;
switch (textDirection) {
case TextDirection.rtl:
_positionController.value -= delta;
break;
case TextDirection.ltr:
_positionController.value += delta;
break;
}
}
}
void _handleDragEnd(DragEndDetails details) {
if (_position.value >= 0.5)
_positionController.forward();
else
_positionController.reverse();
_reactionController.reverse();
}
void _emitVibration() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
HapticFeedback.lightImpact();
break;
case TargetPlatform.fuchsia:
case TargetPlatform.android:
break;
}
}
@override
bool hitTestSelf(Offset position) => true;
@override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
assert(debugHandleEvent(event, entry));
if (event is PointerDownEvent && isInteractive) {
_drag.addPointer(event);
_tap.addPointer(event);
}
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
if (isInteractive) config.onTap = _handleTap;
config.isEnabled = isInteractive;
config.isToggled = _value;
}
final CupertinoThumbPainter _thumbPainter = CupertinoThumbPainter();
@override
void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas;
final double currentValue = _position.value;
final double currentReactionValue = _reaction.value;
double visualPosition;
switch (textDirection) {
case TextDirection.rtl:
visualPosition = 1.0 - currentValue;
break;
case TextDirection.ltr:
visualPosition = currentValue;
break;
}
final Paint paint = Paint()
..color = Color.lerp(trackColor, activeColor, currentValue);
final Rect trackRect = Rect.fromLTWH(
offset.dx + (size.width - _kTrackWidth) / 2.0,
offset.dy + (size.height - _kTrackHeight) / 2.0,
_kTrackWidth,
_kTrackHeight,
);
final RRect trackRRect = RRect.fromRectAndRadius(
trackRect, const Radius.circular(_kTrackRadius));
canvas.drawRRect(trackRRect, paint);
final double currentThumbExtension =
CupertinoThumbPainter.extension * currentReactionValue;
final double thumbLeft = lerpDouble(
trackRect.left + _kTrackInnerStart - CupertinoThumbPainter.radius,
trackRect.left +
_kTrackInnerEnd -
CupertinoThumbPainter.radius -
currentThumbExtension,
visualPosition,
);
final double thumbRight = lerpDouble(
trackRect.left +
_kTrackInnerStart +
CupertinoThumbPainter.radius +
currentThumbExtension,
trackRect.left + _kTrackInnerEnd + CupertinoThumbPainter.radius,
visualPosition,
);
final double thumbCenterY = offset.dy + size.height / 2.0;
final Rect thumbBounds = Rect.fromLTRB(
thumbLeft,
thumbCenterY - CupertinoThumbPainter.radius,
thumbRight,
thumbCenterY + CupertinoThumbPainter.radius,
);
context
.pushClipRRect(needsCompositing, Offset.zero, thumbBounds, trackRRect,
(PaintingContext innerContext, Offset offset) {
_thumbPainter.paint(innerContext.canvas, thumbBounds);
});
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(FlagProperty('value',
value: value, ifTrue: 'checked', ifFalse: 'unchecked', showName: true));
description.add(FlagProperty('isInteractive',
value: isInteractive,
ifTrue: 'enabled',
ifFalse: 'disabled',
showName: true,
defaultValue: true));
}
}

让我们看看 Flutter 开发团队对下面提出的问题的回应。

编辑:支持已添加到 Flutter 的代码库中:

见 https://github.com/flutter/flutter/pull/45074/files


你不能,不幸的是。 它在switch.dart中是硬编码的。

你可以用ShaderMask做一些黑客的事情:


bool value = false;
@override
Widget build(BuildContext context) {
final Color activeColor = Color.fromARGB(255, 52, 199, 89);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Theme(
data: ThemeData(),
child: ShaderMask(
child: CupertinoSwitch(
activeColor: activeColor,
value: value,
onChanged: (v) => setState(() => value = v),
),
shaderCallback: (r) {
return LinearGradient(
colors: value
? [activeColor, activeColor]
: [Colors.red, Colors.red],
).createShader(r);
},
),
)),
);
}

或者使用此自定义类(我添加了一个非活动颜色trackColor(:


import 'dart:ui' show lerpDouble;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
// Examples can assume:
// bool _lights;
// void setState(VoidCallback fn) { }
/// An iOS-style switch.
///
/// Used to toggle the on/off state of a single setting.
///
/// The switch itself does not maintain any state. Instead, when the state of
/// the switch changes, the widget calls the [onChanged] callback. Most widgets
/// that use a switch will listen for the [onChanged] callback and rebuild the
/// switch with a new [value] to update the visual appearance of the switch.
///
/// {@tool sample}
///
/// This sample shows how to use a [CupertinoSwitch] in a [ListTile]. The
/// [MergeSemantics] is used to turn the entire [ListTile] into a single item
/// for accessibility tools.
///
/// ```dart
/// MergeSemantics(
///   child: ListTile(
///     title: Text('Lights'),
///     trailing: CupertinoSwitch(
///       value: _lights,
///       onChanged: (bool value) { setState(() { _lights = value; }); },
///     ),
///     onTap: () { setState(() { _lights = !_lights; }); },
///   ),
/// )
/// ```
/// {@end-tool}
///
/// See also:
///
///  * [Switch], the material design equivalent.
///  * <https://developer.apple.com/ios/human-interface-guidelines/controls/switches/>
class CupertinoSwitch extends StatefulWidget {
/// Creates an iOS-style switch.
///
/// The [value] parameter must not be null.
/// The [dragStartBehavior] parameter defaults to [DragStartBehavior.start] and must not be null.
const CupertinoSwitch({
Key key,
@required this.value,
@required this.onChanged,
this.activeColor,
this.trackColor,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null),
assert(dragStartBehavior != null),
super(key: key);
/// Whether this switch is on or off.
///
/// Must not be null.
final bool value;
/// Called when the user toggles with switch on or off.
///
/// The switch passes the new value to the callback but does not actually
/// change state until the parent widget rebuilds the switch with the new
/// value.
///
/// If null, the switch will be displayed as disabled, which has a reduced opacity.
///
/// The callback provided to onChanged should update the state of the parent
/// [StatefulWidget] using the [State.setState] method, so that the parent
/// gets rebuilt; for example:
///
/// ```dart
/// CupertinoSwitch(
///   value: _giveVerse,
///   onChanged: (bool newValue) {
///     setState(() {
///       _giveVerse = newValue;
///     });
///   },
/// )
/// ```
final ValueChanged<bool> onChanged;
/// The color to use when this switch is on.
///
/// Defaults to [CupertinoColors.systemGreen] when null and ignores
/// the [CupertinoTheme] in accordance to native iOS behavior.
final Color activeColor;
final Color trackColor;
/// {@template flutter.cupertino.switch.dragStartBehavior}
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], the drag behavior used to move the
/// switch from on to off will begin upon the detection of a drag gesture. If
/// set to [DragStartBehavior.down] it will begin when a down event is first
/// detected.
///
/// In general, setting this to [DragStartBehavior.start] will make drag
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
///  * [DragGestureRecognizer.dragStartBehavior], which gives an example for
///    the different behaviors.
///
/// {@endtemplate}
final DragStartBehavior dragStartBehavior;
@override
_CupertinoSwitchState createState() => _CupertinoSwitchState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('value', value: value, ifTrue: 'on', ifFalse: 'off', showName: true));
properties.add(ObjectFlagProperty<ValueChanged<bool>>('onChanged', onChanged, ifNull: 'disabled'));
}
}
class _CupertinoSwitchState extends State<CupertinoSwitch> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Opacity(
opacity: widget.onChanged == null ? _kCupertinoSwitchDisabledOpacity : 1.0,
child: _CupertinoSwitchRenderObjectWidget(
value: widget.value,
activeColor: CupertinoDynamicColor.resolve(
widget.activeColor ?? CupertinoColors.systemGreen,
context,
),
trackColor: widget.trackColor,
onChanged: widget.onChanged,
vsync: this,
dragStartBehavior: widget.dragStartBehavior,
),
);
}
}
class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
const _CupertinoSwitchRenderObjectWidget({
Key key,
this.value,
this.activeColor,
this.onChanged,
this.vsync,
this.trackColor,
this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key);
final bool value;
final Color activeColor;
final Color trackColor;
final ValueChanged<bool> onChanged;
final TickerProvider vsync;
final DragStartBehavior dragStartBehavior;
@override
_RenderCupertinoSwitch createRenderObject(BuildContext context) {
return _RenderCupertinoSwitch(
value: value,
activeColor: activeColor,
trackColor: trackColor ?? CupertinoDynamicColor.resolve(CupertinoColors.secondarySystemFill, context),
onChanged: onChanged,
textDirection: Directionality.of(context),
vsync: vsync,
dragStartBehavior: dragStartBehavior,
);
}
@override
void updateRenderObject(BuildContext context, _RenderCupertinoSwitch renderObject) {
renderObject
..value = value
..activeColor = activeColor
..trackColor = trackColor ?? CupertinoDynamicColor.resolve(CupertinoColors.secondarySystemFill, context)
..onChanged = onChanged
..textDirection = Directionality.of(context)
..vsync = vsync
..dragStartBehavior = dragStartBehavior;
}
}
const double _kTrackWidth = 51.0;
const double _kTrackHeight = 31.0;
const double _kTrackRadius = _kTrackHeight / 2.0;
const double _kTrackInnerStart = _kTrackHeight / 2.0;
const double _kTrackInnerEnd = _kTrackWidth - _kTrackInnerStart;
const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart;
const double _kSwitchWidth = 59.0;
const double _kSwitchHeight = 39.0;
// Opacity of a disabled switch, as eye-balled from iOS Simulator on Mac.
const double _kCupertinoSwitchDisabledOpacity = 0.5;
const Duration _kReactionDuration = Duration(milliseconds: 300);
const Duration _kToggleDuration = Duration(milliseconds: 200);
class _RenderCupertinoSwitch extends RenderConstrainedBox {
_RenderCupertinoSwitch({
@required bool value,
@required Color activeColor,
@required Color trackColor,
ValueChanged<bool> onChanged,
@required TextDirection textDirection,
@required TickerProvider vsync,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null),
assert(activeColor != null),
assert(vsync != null),
_value = value,
_activeColor = activeColor,
_trackColor = trackColor,
_onChanged = onChanged,
_textDirection = textDirection,
_vsync = vsync,
super(additionalConstraints: const BoxConstraints.tightFor(width: _kSwitchWidth, height: _kSwitchHeight)) {
_tap = TapGestureRecognizer()
..onTapDown = _handleTapDown
..onTap = _handleTap
..onTapUp = _handleTapUp
..onTapCancel = _handleTapCancel;
_drag = HorizontalDragGestureRecognizer()
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..dragStartBehavior = dragStartBehavior;
_positionController = AnimationController(
duration: _kToggleDuration,
value: value ? 1.0 : 0.0,
vsync: vsync,
);
_position = CurvedAnimation(
parent: _positionController,
curve: Curves.linear,
)..addListener(markNeedsPaint)
..addStatusListener(_handlePositionStateChanged);
_reactionController = AnimationController(
duration: _kReactionDuration,
vsync: vsync,
);
_reaction = CurvedAnimation(
parent: _reactionController,
curve: Curves.ease,
)..addListener(markNeedsPaint);
}
AnimationController _positionController;
CurvedAnimation _position;
AnimationController _reactionController;
Animation<double> _reaction;
bool get value => _value;
bool _value;
set value(bool value) {
assert(value != null);
if (value == _value)
return;
_value = value;
markNeedsSemanticsUpdate();
_position
..curve = Curves.ease
..reverseCurve = Curves.ease.flipped;
if (value)
_positionController.forward();
else
_positionController.reverse();
}
TickerProvider get vsync => _vsync;
TickerProvider _vsync;
set vsync(TickerProvider value) {
assert(value != null);
if (value == _vsync)
return;
_vsync = value;
_positionController.resync(vsync);
_reactionController.resync(vsync);
}
Color get activeColor => _activeColor;
Color _activeColor;
set activeColor(Color value) {
assert(value != null);
if (value == _activeColor)
return;
_activeColor = value;
markNeedsPaint();
}
Color get trackColor => _trackColor;
Color _trackColor;
set trackColor(Color value) {
assert(value != null);
if (value == _trackColor)
return;
_trackColor = value;
markNeedsPaint();
}
ValueChanged<bool> get onChanged => _onChanged;
ValueChanged<bool> _onChanged;
set onChanged(ValueChanged<bool> value) {
if (value == _onChanged)
return;
final bool wasInteractive = isInteractive;
_onChanged = value;
if (wasInteractive != isInteractive) {
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
assert(value != null);
if (_textDirection == value)
return;
_textDirection = value;
markNeedsPaint();
}
DragStartBehavior get dragStartBehavior => _drag.dragStartBehavior;
set dragStartBehavior(DragStartBehavior value) {
assert(value != null);
if (_drag.dragStartBehavior == value)
return;
_drag.dragStartBehavior = value;
}
bool get isInteractive => onChanged != null;
TapGestureRecognizer _tap;
HorizontalDragGestureRecognizer _drag;
@override
void attach(PipelineOwner owner) {
super.attach(owner);
if (value)
_positionController.forward();
else
_positionController.reverse();
if (isInteractive) {
switch (_reactionController.status) {
case AnimationStatus.forward:
_reactionController.forward();
break;
case AnimationStatus.reverse:
_reactionController.reverse();
break;
case AnimationStatus.dismissed:
case AnimationStatus.completed:
// nothing to do
break;
}
}
}
@override
void detach() {
_positionController.stop();
_reactionController.stop();
super.detach();
}
void _handlePositionStateChanged(AnimationStatus status) {
if (isInteractive) {
if (status == AnimationStatus.completed && !_value)
onChanged(true);
else if (status == AnimationStatus.dismissed && _value)
onChanged(false);
}
}
void _handleTapDown(TapDownDetails details) {
if (isInteractive)
_reactionController.forward();
}
void _handleTap() {
if (isInteractive) {
onChanged(!_value);
_emitVibration();
}
}
void _handleTapUp(TapUpDetails details) {
if (isInteractive)
_reactionController.reverse();
}
void _handleTapCancel() {
if (isInteractive)
_reactionController.reverse();
}
void _handleDragStart(DragStartDetails details) {
if (isInteractive) {
_reactionController.forward();
_emitVibration();
}
}
void _handleDragUpdate(DragUpdateDetails details) {
if (isInteractive) {
_position
..curve = null
..reverseCurve = null;
final double delta = details.primaryDelta / _kTrackInnerLength;
switch (textDirection) {
case TextDirection.rtl:
_positionController.value -= delta;
break;
case TextDirection.ltr:
_positionController.value += delta;
break;
}
}
}
void _handleDragEnd(DragEndDetails details) {
if (_position.value >= 0.5)
_positionController.forward();
else
_positionController.reverse();
_reactionController.reverse();
}
void _emitVibration() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
HapticFeedback.lightImpact();
break;
case TargetPlatform.fuchsia:
case TargetPlatform.android:
break;
}
}
@override
bool hitTestSelf(Offset position) => true;
@override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
assert(debugHandleEvent(event, entry));
if (event is PointerDownEvent && isInteractive) {
_drag.addPointer(event);
_tap.addPointer(event);
}
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
if (isInteractive)
config.onTap = _handleTap;
config.isEnabled = isInteractive;
config.isToggled = _value;
}
@override
void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas;
final double currentValue = _position.value;
final double currentReactionValue = _reaction.value;
double visualPosition;
switch (textDirection) {
case TextDirection.rtl:
visualPosition = 1.0 - currentValue;
break;
case TextDirection.ltr:
visualPosition = currentValue;
break;
}
final Paint paint = Paint()
..color = Color.lerp(trackColor, activeColor, currentValue);
final Rect trackRect = Rect.fromLTWH(
offset.dx + (size.width - _kTrackWidth) / 2.0,
offset.dy + (size.height - _kTrackHeight) / 2.0,
_kTrackWidth,
_kTrackHeight,
);
final RRect trackRRect = RRect.fromRectAndRadius(trackRect, const Radius.circular(_kTrackRadius));
canvas.drawRRect(trackRRect, paint);
final double currentThumbExtension = CupertinoThumbPainter.extension * currentReactionValue;
final double thumbLeft = lerpDouble(
trackRect.left + _kTrackInnerStart - CupertinoThumbPainter.radius,
trackRect.left + _kTrackInnerEnd - CupertinoThumbPainter.radius - currentThumbExtension,
visualPosition,
);
final double thumbRight = lerpDouble(
trackRect.left + _kTrackInnerStart + CupertinoThumbPainter.radius + currentThumbExtension,
trackRect.left + _kTrackInnerEnd + CupertinoThumbPainter.radius,
visualPosition,
);
final double thumbCenterY = offset.dy + size.height / 2.0;
final Rect thumbBounds = Rect.fromLTRB(
thumbLeft,
thumbCenterY - CupertinoThumbPainter.radius,
thumbRight,
thumbCenterY + CupertinoThumbPainter.radius,
);
context.pushClipRRect(needsCompositing, Offset.zero, thumbBounds, trackRRect, (PaintingContext innerContext, Offset offset) {
const CupertinoThumbPainter.switchThumb().paint(innerContext.canvas, thumbBounds);
});
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(FlagProperty('value', value: value, ifTrue: 'checked', ifFalse: 'unchecked', showName: true));
description.add(FlagProperty('isInteractive', value: isInteractive, ifTrue: 'enabled', ifFalse: 'disabled', showName: true, defaultValue: true));
}
}

但我不推荐他们中的任何一个。 我建议您在颤振存储库 https://github.com/flutter/flutter/issues 中打开一个问题并在那里解释您的问题。

我希望这对某人有所帮助。

Transform.scale(
scale: .7,
child: InkWell(
CupertinoSwitch(
trackColor: Colors.red, // **INACTIVE STATE COLOR**
activeColor: Colors.green, // **ACTIVE STATE COLOR**
value: _switchValue,
onChanged: (bool value) {
setState(() { _switchValue = value; }
},
)
onTap: () { setState(() { _switchValue = !_switchValue; }); },
),
),

相关内容

  • 没有找到相关文章

最新更新