Feat: Add fillColor property for cupertinoCheckbox (#151761)
Feat: Add `fillColor` property for `CupertinoCheckbox` Required for #151252
This commit is contained in:
parent
93b55edff1
commit
a9b2d8d6d4
@ -104,6 +104,7 @@ class CupertinoCheckbox extends StatefulWidget {
|
|||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
this.activeColor,
|
this.activeColor,
|
||||||
this.inactiveColor,
|
this.inactiveColor,
|
||||||
|
this.fillColor,
|
||||||
this.checkColor,
|
this.checkColor,
|
||||||
this.focusColor,
|
this.focusColor,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
@ -152,11 +153,51 @@ class CupertinoCheckbox extends StatefulWidget {
|
|||||||
|
|
||||||
/// The color to use when this checkbox is checked.
|
/// 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].
|
/// Defaults to [CupertinoColors.activeBlue].
|
||||||
final Color? activeColor;
|
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.
|
/// 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.
|
/// By default, [CupertinoColors.inactiveGray] is used.
|
||||||
final Color? inactiveColor;
|
final Color? inactiveColor;
|
||||||
|
|
||||||
@ -318,6 +359,7 @@ class _CupertinoCheckboxState extends State<CupertinoCheckbox> with TickerProvid
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Colors need to be resolved in selected and non selected states separately.
|
// 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> activeStates = states..add(WidgetState.selected);
|
||||||
final Set<WidgetState> inactiveStates = states..remove(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.
|
// throughout the lifecycle of this build method.
|
||||||
final Set<WidgetState> currentStates = states;
|
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)
|
final BorderSide effectiveBorderSide = _resolveSide(widget.side, currentStates)
|
||||||
?? _defaultSide.resolve(currentStates);
|
?? _defaultSide.resolve(currentStates);
|
||||||
@ -353,7 +399,7 @@ class _CupertinoCheckboxState extends State<CupertinoCheckbox> with TickerProvid
|
|||||||
..isFocused = currentStates.contains(WidgetState.focused)
|
..isFocused = currentStates.contains(WidgetState.focused)
|
||||||
..isHovered = currentStates.contains(WidgetState.hovered)
|
..isHovered = currentStates.contains(WidgetState.hovered)
|
||||||
..activeColor = effectiveActiveColor
|
..activeColor = effectiveActiveColor
|
||||||
..inactiveColor = _defaultFillColor.resolve(inactiveStates)
|
..inactiveColor = effectiveInactiveColor
|
||||||
..checkColor = _defaultCheckColor.resolve(currentStates)
|
..checkColor = _defaultCheckColor.resolve(currentStates)
|
||||||
..value = value
|
..value = value
|
||||||
..previousValue = _previousValue
|
..previousValue = _previousValue
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
// machines.
|
// machines.
|
||||||
@Tags(<String>['reduced-test-set'])
|
@Tags(<String>['reduced-test-set'])
|
||||||
library;
|
library;
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.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 {
|
testWidgets('Checkbox configures focus color', (WidgetTester tester) async {
|
||||||
const Color defaultCheckColor = Color(0xffffffff);
|
const Color defaultCheckColor = Color(0xffffffff);
|
||||||
const Color defaultActiveFillColor = Color(0xff007aff);
|
const Color defaultActiveFillColor = Color(0xff007aff);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user