Feat: Add fillColor property for cupertinoCheckbox (#151761)

Feat: Add `fillColor` property for `CupertinoCheckbox`

Required for #151252
This commit is contained in:
Kishan Rathore 2024-08-06 23:21:13 +05:30 committed by GitHub
parent 93b55edff1
commit a9b2d8d6d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 177 additions and 2 deletions

View File

@ -104,6 +104,7 @@ class CupertinoCheckbox extends StatefulWidget {
required this.onChanged,
this.activeColor,
this.inactiveColor,
this.fillColor,
this.checkColor,
this.focusColor,
this.focusNode,
@ -152,11 +153,51 @@ class CupertinoCheckbox extends StatefulWidget {
/// The color to use when this checkbox is checked.
///
/// If [fillColor] returns a non-null color in the [WidgetState.selected]
/// state, [fillColor] will be used instead of [activeColor].
///
/// Defaults to [CupertinoColors.activeBlue].
final Color? activeColor;
/// {@template flutter.cupertino.CupertinoCheckbox.fillColor}
/// The color used to fill this checkbox.
///
/// Resolves in the following states:
/// * [WidgetState.selected].
/// * [WidgetState.hovered].
/// * [WidgetState.focused].
/// * [WidgetState.disabled].
///
/// {@tool snippet}
/// This example resolves the [fillColor] based on the current [WidgetState]
/// of the [CupertinoCheckbox], providing a different [Color] when it is
/// [WidgetState.disabled].
///
/// ```dart
/// CupertinoCheckbox(
/// value: true,
/// onChanged: (_){},
/// fillColor: WidgetStateProperty.resolveWith<Color>((Set<WidgetState> states) {
/// if (states.contains(WidgetState.disabled)) {
/// return Colors.orange.withOpacity(.32);
/// }
/// return Colors.orange;
/// })
/// )
/// ```
/// {@end-tool}
/// {@endtemplate}
///
/// If [fillColor] resolves to null for the requested state, then the fill color
/// falls back to [activeColor] if the state includes [WidgetState.selected],
/// or [inactiveColor] otherwise.
final WidgetStateProperty<Color?>? fillColor;
/// The color used if the checkbox is inactive.
///
/// If [fillColor] returns a non-null color in the unselected
/// state, [fillColor] will be used instead of [inactiveColor].
///
/// By default, [CupertinoColors.inactiveGray] is used.
final Color? inactiveColor;
@ -318,6 +359,7 @@ class _CupertinoCheckboxState extends State<CupertinoCheckbox> with TickerProvid
@override
Widget build(BuildContext context) {
// Colors need to be resolved in selected and non selected states separately.
// The `states` getter constructs a new set every time, making it safe to edit in place.
final Set<WidgetState> activeStates = states..add(WidgetState.selected);
final Set<WidgetState> inactiveStates = states..remove(WidgetState.selected);
@ -325,7 +367,11 @@ class _CupertinoCheckboxState extends State<CupertinoCheckbox> with TickerProvid
// throughout the lifecycle of this build method.
final Set<WidgetState> currentStates = states;
final Color effectiveActiveColor = _defaultFillColor.resolve(activeStates);
final Color effectiveActiveColor = widget.fillColor?.resolve(activeStates)
?? _defaultFillColor.resolve(activeStates);
final Color effectiveInactiveColor = widget.fillColor?.resolve(inactiveStates)
?? _defaultFillColor.resolve(inactiveStates);
final BorderSide effectiveBorderSide = _resolveSide(widget.side, currentStates)
?? _defaultSide.resolve(currentStates);
@ -353,7 +399,7 @@ class _CupertinoCheckboxState extends State<CupertinoCheckbox> with TickerProvid
..isFocused = currentStates.contains(WidgetState.focused)
..isHovered = currentStates.contains(WidgetState.hovered)
..activeColor = effectiveActiveColor
..inactiveColor = _defaultFillColor.resolve(inactiveStates)
..inactiveColor = effectiveInactiveColor
..checkColor = _defaultCheckColor.resolve(currentStates)
..value = value
..previousValue = _previousValue

View File

@ -7,6 +7,7 @@
// machines.
@Tags(<String>['reduced-test-set'])
library;
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@ -575,6 +576,134 @@ void main() {
);
});
testWidgets('Checkbox fill color resolves in enabled/disabled states', (WidgetTester tester) async {
const Color activeEnabledFillColor = Color(0xFF000001);
const Color activeDisabledFillColor = Color(0xFF000002);
Color getFillColor(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return activeDisabledFillColor;
}
return activeEnabledFillColor;
}
final WidgetStateProperty<Color> fillColor = WidgetStateColor.resolveWith(getFillColor);
Widget buildApp({required bool enabled}) {
return CupertinoApp(
home: CupertinoCheckbox(
value: true,
fillColor: fillColor,
onChanged: enabled ? (bool? value) { } : null,
),
);
}
RenderBox getCheckboxRenderer() {
return tester.renderObject<RenderBox>(find.byType(CupertinoCheckbox));
}
await tester.pumpWidget(buildApp(enabled: true));
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: activeEnabledFillColor));
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: activeDisabledFillColor));
});
testWidgets('Checkbox fill color take precedence over active/inactive colors', (WidgetTester tester) async {
const Color activeEnabledFillColor = Color(0xFF000001);
const Color activeDisabledFillColor = Color(0xFF000002);
const Color activeColor = Color(0xFF000003);
const Color inactiveColor = Color(0xFF000004);
Color getFillColor(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return activeDisabledFillColor;
}
return activeEnabledFillColor;
}
final WidgetStateProperty<Color> fillColor = WidgetStateColor.resolveWith(getFillColor);
Widget buildApp({required bool enabled}) {
return CupertinoApp(
home: CupertinoCheckbox(
value: true,
fillColor: fillColor,
activeColor: activeColor,
inactiveColor: inactiveColor,
onChanged: enabled ? (bool? value) { } : null,
),
);
}
RenderBox getCheckboxRenderer() {
return tester.renderObject<RenderBox>(find.byType(CupertinoCheckbox));
}
await tester.pumpWidget(buildApp(enabled: true));
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: activeEnabledFillColor));
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: activeDisabledFillColor));
});
testWidgets('Checkbox fill color resolves in hovered/focused states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'checkbox');
addTearDown(focusNode.dispose);
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredFillColor = Color(0xFF000001);
const Color focusedFillColor = Color(0xFF000002);
const Color transparentColor = Color(0x00000000);
Color getFillColor(Set<WidgetState> states) {
if (states.contains(WidgetState.hovered)) {
return hoveredFillColor;
}
if (states.contains(WidgetState.focused)) {
return focusedFillColor;
}
return transparentColor;
}
final WidgetStateProperty<Color> fillColor = WidgetStateColor.resolveWith(getFillColor);
Widget buildApp({required bool enabled}) {
return CupertinoApp(
home: CupertinoCheckbox(
focusNode: focusNode,
value: enabled,
fillColor: fillColor,
onChanged: enabled ? (bool? value) { } : null,
),
);
}
RenderBox getCheckboxRenderer() {
return tester.renderObject<RenderBox>(find.byType(CupertinoCheckbox));
}
await tester.pumpWidget(buildApp(enabled: true));
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(getCheckboxRenderer(), paints..path(color: focusedFillColor));
// Start hovering.
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(tester.getCenter(find.byType(CupertinoCheckbox)));
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: hoveredFillColor));
});
testWidgets('Checkbox configures focus color', (WidgetTester tester) async {
const Color defaultCheckColor = Color(0xffffffff);
const Color defaultActiveFillColor = Color(0xff007aff);