render method
Render a switch with the given request.
Implementation
@override
Widget render(RSwitchRenderRequest request) {
final tokens = request.resolvedTokens;
final state = request.state;
final spec = request.spec;
final slots = request.slots;
final policy = HeadlessThemeProvider.of(request.context)
?.capability<HeadlessRendererPolicy>();
assert(
policy?.requireResolvedTokens != true || tokens != null,
'CupertinoSwitchRenderer requires resolvedTokens when '
'HeadlessRendererPolicy.requireResolvedTokens is enabled.',
);
final motionTheme = HeadlessThemeProvider.of(request.context)
?.capability<HeadlessMotionTheme>() ??
HeadlessMotionTheme.cupertino;
final effectiveTokens = tokens ??
RSwitchResolvedTokens(
trackSize: const Size(51, 31),
trackBorderRadius: const BorderRadius.all(Radius.circular(16)),
trackOutlineColor: CupertinoColors.systemGrey4,
trackOutlineWidth: 0,
activeTrackColor: CupertinoColors.systemGreen,
inactiveTrackColor: CupertinoColors.systemGrey4,
thumbSizeUnselected: const Size(28, 28),
thumbSizeSelected: const Size(28, 28),
thumbSizePressed: const Size(28, 28),
thumbSizeTransition: const Size(28, 28),
activeThumbColor: CupertinoColors.white,
inactiveThumbColor: CupertinoColors.white,
thumbPadding: 2.0,
disabledOpacity: 0.5,
pressOverlayColor:
CupertinoColors.systemGreen.withValues(alpha: 0.12),
pressOpacity: 1.0,
minTapTargetSize: const Size(59, 39),
stateLayerRadius: 0.0,
stateLayerColor: WidgetStateProperty.all(const Color(0x00000000)),
motion: RSwitchMotionTokens(
stateChangeDuration: motionTheme.button.stateChangeDuration,
thumbSlideDuration: const Duration(milliseconds: 200),
),
);
final animationDuration = effectiveTokens.motion?.stateChangeDuration ??
motionTheme.button.stateChangeDuration;
final thumbSlideDuration =
effectiveTokens.motion?.thumbSlideDuration ?? animationDuration;
final reactionDuration = const Duration(milliseconds: 300);
final isDragging = state.isDragging;
final dragT = state.dragT;
final isOn = spec.value;
final isRtl = Directionality.of(request.context) == TextDirection.rtl;
final Color trackColor;
final Color thumbColor;
final double positionT;
if (isDragging && dragT != null) {
trackColor = Color.lerp(
effectiveTokens.inactiveTrackColor,
effectiveTokens.activeTrackColor,
dragT,
)!;
thumbColor = Color.lerp(
effectiveTokens.inactiveThumbColor,
effectiveTokens.activeThumbColor,
dragT,
)!;
positionT = dragT;
} else {
trackColor = isOn
? effectiveTokens.activeTrackColor
: effectiveTokens.inactiveTrackColor;
thumbColor = isOn
? effectiveTokens.activeThumbColor
: effectiveTokens.inactiveThumbColor;
positionT = isOn ? 1.0 : 0.0;
}
final visualValue = state.dragVisualValue ?? spec.value;
final statesForIcon = state.toWidgetStates();
if (visualValue != state.isSelected) {
if (visualValue) {
statesForIcon.add(WidgetState.selected);
} else {
statesForIcon.remove(WidgetState.selected);
}
}
final thumbIcon = effectiveTokens.thumbIcon?.resolve(statesForIcon);
final thumbIconColor = visualValue
? effectiveTokens.activeTrackColor
: CupertinoColors.systemGrey;
final track = CupertinoSwitchTrackAndThumb(
tokens: effectiveTokens,
trackColor: trackColor,
thumbColor: thumbColor,
thumbIcon: thumbIcon,
thumbIconColor: thumbIconColor,
hasIcon: thumbIcon != null,
isDragging: isDragging,
isPressed: state.isPressed,
isRtl: isRtl,
positionT: positionT,
reactionDuration: reactionDuration,
thumbSlideDuration: thumbSlideDuration,
slots: slots,
spec: spec,
state: state,
);
final focusRing = state.isFocused
? CupertinoSwitchFocusRing(
trackSize: effectiveTokens.trackSize,
focusColor: resolveCupertinoSwitchFocusColor(
context: request.context,
activeColor: effectiveTokens.activeTrackColor,
),
child: track,
)
: track;
final minWidth = request.constraints?.minWidth ?? 0;
final minHeight = request.constraints?.minHeight ?? 0;
final desiredWidth = math.max(
effectiveTokens.trackSize.width,
effectiveTokens.minTapTargetSize.width,
);
final desiredHeight = math.max(
effectiveTokens.trackSize.height,
effectiveTokens.minTapTargetSize.height,
);
Widget result = SizedBox(
width: math.max(desiredWidth, minWidth),
height: math.max(desiredHeight, minHeight),
child: Center(child: focusRing),
);
final defaultOverlay = CupertinoPressableOpacity(
duration: animationDuration,
pressedOpacity: effectiveTokens.pressOpacity,
isPressed: state.isPressed,
isEnabled: !state.isDisabled,
visualEffects: request.visualEffects,
child: result,
);
result = slots?.pressOverlay != null
? slots!.pressOverlay!.build(
RSwitchPressOverlayContext(
spec: spec,
state: state,
child: defaultOverlay,
),
(_) => defaultOverlay,
)
: defaultOverlay;
if (state.isDisabled && effectiveTokens.disabledOpacity < 1) {
result = Opacity(
opacity: effectiveTokens.disabledOpacity,
child: result,
);
}
return result;
}