Migrated Checkbox to Material 3 Colors (#110537)

This commit is contained in:
Qun Cheng 2022-08-31 13:38:32 -07:00 committed by GitHub
parent c213fd9b9c
commit a490a6a11d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 550 additions and 125 deletions

View file

@ -20,6 +20,7 @@ import 'dart:io';
import 'package:gen_defaults/app_bar_template.dart';
import 'package:gen_defaults/button_template.dart';
import 'package:gen_defaults/card_template.dart';
import 'package:gen_defaults/checkbox_template.dart';
import 'package:gen_defaults/chip_action_template.dart';
import 'package:gen_defaults/chip_filter_template.dart';
import 'package:gen_defaults/chip_input_template.dart';
@ -108,6 +109,7 @@ Future<void> main(List<String> args) async {
ButtonTemplate('md.comp.outlined-button', 'OutlinedButton', '$materialLib/outlined_button.dart', tokens).updateFile();
ButtonTemplate('md.comp.text-button', 'TextButton', '$materialLib/text_button.dart', tokens).updateFile();
CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
CheckboxTemplate('Checkbox', '$materialLib/checkbox.dart', tokens).updateFile();
ChipActionTemplate('ActionChip', '$materialLib/chip_action.dart', tokens).updateFile();
ChipFilterTemplate('FilterChip', '$materialLib/chip_filter.dart', tokens).updateFile();
ChipFilterTemplate('FilterChip', '$materialLib/chip_choice.dart', tokens).updateFile();

View file

@ -0,0 +1,119 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'template.dart';
class CheckboxTemplate extends TokenTemplate {
const CheckboxTemplate(super.blockName, super.fileName, super.tokens, {
super.colorSchemePrefix = '_colors.',
});
@override
String generate() => '''
class _${blockName}DefaultsM3 extends CheckboxThemeData {
_${blockName}DefaultsM3(BuildContext context)
: _theme = Theme.of(context),
_colors = Theme.of(context).colorScheme;
final ThemeData _theme;
final ColorScheme _colors;
@override
MaterialStateProperty<Color> get fillColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return ${componentColor('md.comp.checkbox.selected.disabled.container')};
}
return ${componentColor('md.comp.checkbox.unselected.disabled.outline')}.withOpacity(${opacity('md.comp.checkbox.unselected.disabled.container.opacity')});
}
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.checkbox.selected.pressed.container')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.checkbox.selected.hover.container')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.checkbox.selected.focus.container')};
}
return ${componentColor('md.comp.checkbox.selected.container')};
}
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.checkbox.unselected.pressed.outline')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.checkbox.unselected.hover.outline')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.checkbox.unselected.focus.outline')};
}
return ${componentColor('md.comp.checkbox.unselected.outline')};
});
}
@override
MaterialStateProperty<Color> get checkColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return ${componentColor('md.comp.checkbox.selected.disabled.icon')};
}
return Colors.transparent; // No icons available when the checkbox is unselected.
}
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.checkbox.selected.pressed.icon')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.checkbox.selected.hover.icon')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.checkbox.selected.focus.icon')};
}
return ${componentColor('md.comp.checkbox.selected.icon')};
}
return Colors.transparent; // No icons available when the checkbox is unselected.
});
}
@override
MaterialStateProperty<Color> get overlayColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.checkbox.selected.pressed.state-layer')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.checkbox.selected.hover.state-layer')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.checkbox.selected.focus.state-layer')};
}
return Colors.transparent;
}
if (states.contains(MaterialState.pressed)) {
return ${componentColor('md.comp.checkbox.unselected.pressed.state-layer')};
}
if (states.contains(MaterialState.hovered)) {
return ${componentColor('md.comp.checkbox.unselected.hover.state-layer')};
}
if (states.contains(MaterialState.focused)) {
return ${componentColor('md.comp.checkbox.unselected.focus.state-layer')};
}
return Colors.transparent;
});
}
@override
double get splashRadius => ${tokens['md.comp.checkbox.state-layer.size']} / 2;
@override
MaterialTapTargetSize get materialTapTargetSize => _theme.materialTapTargetSize;
@override
VisualDensity get visualDensity => _theme.visualDensity;
}
''';
}

View file

@ -5,6 +5,8 @@
import 'package:flutter/widgets.dart';
import 'checkbox_theme.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'debug.dart';
import 'material_state.dart';
@ -383,19 +385,6 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin, Togg
});
}
MaterialStateProperty<Color> get _defaultFillColor {
final ThemeData themeData = Theme.of(context);
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return themeData.disabledColor;
}
if (states.contains(MaterialState.selected)) {
return themeData.colorScheme.secondary;
}
return themeData.unselectedWidgetColor;
});
}
BorderSide? _resolveSide(BorderSide? side) {
if (side is MaterialStateBorderSide) {
return MaterialStateProperty.resolveAs<BorderSide?>(side, states);
@ -409,14 +398,16 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin, Togg
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
final ThemeData themeData = Theme.of(context);
final CheckboxThemeData checkboxTheme = CheckboxTheme.of(context);
final CheckboxThemeData defaults = Theme.of(context).useMaterial3
? _CheckboxDefaultsM3(context)
: _CheckboxDefaultsM2(context);
final MaterialTapTargetSize effectiveMaterialTapTargetSize = widget.materialTapTargetSize
?? checkboxTheme.materialTapTargetSize
?? themeData.materialTapTargetSize;
?? defaults.materialTapTargetSize!;
final VisualDensity effectiveVisualDensity = widget.visualDensity
?? checkboxTheme.visualDensity
?? themeData.visualDensity;
?? defaults.visualDensity!;
Size size;
switch (effectiveMaterialTapTargetSize) {
case MaterialTapTargetSize.padded:
@ -438,40 +429,48 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin, Togg
// so that they can be lerped between.
final Set<MaterialState> activeStates = states..add(MaterialState.selected);
final Set<MaterialState> inactiveStates = states..remove(MaterialState.selected);
final Color effectiveActiveColor = widget.fillColor?.resolve(activeStates)
final Color? activeColor = widget.fillColor?.resolve(activeStates)
?? _widgetFillColor.resolve(activeStates)
?? checkboxTheme.fillColor?.resolve(activeStates)
?? _defaultFillColor.resolve(activeStates);
final Color effectiveInactiveColor = widget.fillColor?.resolve(inactiveStates)
?? checkboxTheme.fillColor?.resolve(activeStates);
final Color effectiveActiveColor = activeColor
?? defaults.fillColor!.resolve(activeStates)!;
final Color? inactiveColor = widget.fillColor?.resolve(inactiveStates)
?? _widgetFillColor.resolve(inactiveStates)
?? checkboxTheme.fillColor?.resolve(inactiveStates)
?? _defaultFillColor.resolve(inactiveStates);
?? checkboxTheme.fillColor?.resolve(inactiveStates);
final Color effectiveInactiveColor = inactiveColor
?? defaults.fillColor!.resolve(inactiveStates)!;
final Set<MaterialState> focusedStates = states..add(MaterialState.focused);
final Color effectiveFocusOverlayColor = widget.overlayColor?.resolve(focusedStates)
?? widget.focusColor
?? checkboxTheme.overlayColor?.resolve(focusedStates)
?? themeData.focusColor;
?? defaults.overlayColor!.resolve(focusedStates)!;
final Set<MaterialState> hoveredStates = states..add(MaterialState.hovered);
final Color effectiveHoverOverlayColor = widget.overlayColor?.resolve(hoveredStates)
?? widget.hoverColor
?? checkboxTheme.overlayColor?.resolve(hoveredStates)
?? themeData.hoverColor;
?? widget.hoverColor
?? checkboxTheme.overlayColor?.resolve(hoveredStates)
?? defaults.overlayColor!.resolve(hoveredStates)!;
final Set<MaterialState> activePressedStates = activeStates..add(MaterialState.pressed);
final Color effectiveActivePressedOverlayColor = widget.overlayColor?.resolve(activePressedStates)
?? checkboxTheme.overlayColor?.resolve(activePressedStates)
?? effectiveActiveColor.withAlpha(kRadialReactionAlpha);
?? checkboxTheme.overlayColor?.resolve(activePressedStates)
?? activeColor?.withAlpha(kRadialReactionAlpha)
?? defaults.overlayColor!.resolve(activePressedStates)!;
final Set<MaterialState> inactivePressedStates = inactiveStates..add(MaterialState.pressed);
final Color effectiveInactivePressedOverlayColor = widget.overlayColor?.resolve(inactivePressedStates)
?? checkboxTheme.overlayColor?.resolve(inactivePressedStates)
?? effectiveActiveColor.withAlpha(kRadialReactionAlpha);
?? checkboxTheme.overlayColor?.resolve(inactivePressedStates)
?? inactiveColor?.withAlpha(kRadialReactionAlpha)
?? defaults.overlayColor!.resolve(inactivePressedStates)!;
final Color effectiveCheckColor = widget.checkColor
?? checkboxTheme.checkColor?.resolve(states)
?? const Color(0xFFFFFFFF);
?? defaults.checkColor!.resolve(states)!;
final double effectiveSplashRadius = widget.splashRadius
?? checkboxTheme.splashRadius
?? defaults.splashRadius!;
return Semantics(
checked: widget.value ?? false,
@ -489,7 +488,7 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin, Togg
..reactionColor = effectiveActivePressedOverlayColor
..hoverColor = effectiveHoverOverlayColor
..focusColor = effectiveFocusOverlayColor
..splashRadius = widget.splashRadius ?? checkboxTheme.splashRadius ?? kRadialReactionRadius
..splashRadius = effectiveSplashRadius
..downPosition = downPosition
..isFocused = states.contains(MaterialState.focused)
..isHovered = states.contains(MaterialState.hovered)
@ -683,3 +682,172 @@ class _CheckboxPainter extends ToggleablePainter {
}
}
}
// Hand coded defaults based on Material Design 2.
class _CheckboxDefaultsM2 extends CheckboxThemeData {
_CheckboxDefaultsM2(BuildContext context)
: _theme = Theme.of(context),
_colors = Theme.of(context).colorScheme;
final ThemeData _theme;
final ColorScheme _colors;
@override
MaterialStateProperty<Color> get fillColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _theme.disabledColor;
}
if (states.contains(MaterialState.selected)) {
return _colors.secondary;
}
return _theme.unselectedWidgetColor;
});
}
@override
MaterialStateProperty<Color> get checkColor {
return MaterialStateProperty.all<Color>(const Color(0xFFFFFFFF));
}
@override
MaterialStateProperty<Color?> get overlayColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return fillColor.resolve(states).withAlpha(kRadialReactionAlpha);
}
if (states.contains(MaterialState.hovered)) {
return _theme.hoverColor;
}
if (states.contains(MaterialState.focused)) {
return _theme.focusColor;
}
return Colors.transparent;
});
}
@override
double get splashRadius => kRadialReactionRadius;
@override
MaterialTapTargetSize get materialTapTargetSize => _theme.materialTapTargetSize;
@override
VisualDensity get visualDensity => _theme.visualDensity;
}
// BEGIN GENERATED TOKEN PROPERTIES - Checkbox
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_101
class _CheckboxDefaultsM3 extends CheckboxThemeData {
_CheckboxDefaultsM3(BuildContext context)
: _theme = Theme.of(context),
_colors = Theme.of(context).colorScheme;
final ThemeData _theme;
final ColorScheme _colors;
@override
MaterialStateProperty<Color> get fillColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return _colors.onSurface.withOpacity(0.38);
}
return _colors.onSurface.withOpacity(0.38);
}
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return _colors.primary;
}
if (states.contains(MaterialState.hovered)) {
return _colors.primary;
}
if (states.contains(MaterialState.focused)) {
return _colors.primary;
}
return _colors.primary;
}
if (states.contains(MaterialState.pressed)) {
return _colors.onSurface;
}
if (states.contains(MaterialState.hovered)) {
return _colors.onSurface;
}
if (states.contains(MaterialState.focused)) {
return _colors.onSurface;
}
return _colors.onSurface;
});
}
@override
MaterialStateProperty<Color> get checkColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return _colors.surface;
}
return Colors.transparent; // No icons available when the checkbox is unselected.
}
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return _colors.onPrimary;
}
if (states.contains(MaterialState.hovered)) {
return _colors.onPrimary;
}
if (states.contains(MaterialState.focused)) {
return _colors.onPrimary;
}
return _colors.onPrimary;
}
return Colors.transparent; // No icons available when the checkbox is unselected.
});
}
@override
MaterialStateProperty<Color> get overlayColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return _colors.onSurface.withOpacity(0.12);
}
if (states.contains(MaterialState.hovered)) {
return _colors.primary.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return _colors.primary.withOpacity(0.12);
}
return Colors.transparent;
}
if (states.contains(MaterialState.pressed)) {
return _colors.primary.withOpacity(0.12);
}
if (states.contains(MaterialState.hovered)) {
return _colors.onSurface.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return _colors.onSurface.withOpacity(0.12);
}
return Colors.transparent;
});
}
@override
double get splashRadius => 40.0 / 2;
@override
MaterialTapTargetSize get materialTapTargetSize => _theme.materialTapTargetSize;
@override
VisualDensity get visualDensity => _theme.visualDensity;
}
// END GENERATED TOKEN PROPERTIES - Checkbox

View file

@ -14,6 +14,7 @@ import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
void main() {
final ThemeData theme = ThemeData();
setUp(() {
debugResetSemanticsIdCounter();
});
@ -21,7 +22,7 @@ void main() {
testWidgets('Checkbox size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
await tester.pumpWidget(
Theme(
data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
data: theme.copyWith(materialTapTargetSize: MaterialTapTargetSize.padded),
child: Directionality(
textDirection: TextDirection.ltr,
child: Material(
@ -40,7 +41,7 @@ void main() {
await tester.pumpWidget(
Theme(
data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
data: theme.copyWith(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
child: Directionality(
textDirection: TextDirection.ltr,
child: Material(
@ -61,10 +62,13 @@ void main() {
testWidgets('CheckBox semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(Material(
child: Checkbox(
value: false,
onChanged: (bool? b) { },
await tester.pumpWidget(Theme(
data: theme,
child: Material(
child: Checkbox(
value: false,
onChanged: (bool? b) { },
),
),
));
@ -76,10 +80,13 @@ void main() {
isFocusable: true,
));
await tester.pumpWidget(Material(
child: Checkbox(
value: true,
onChanged: (bool? b) { },
await tester.pumpWidget(Theme(
data: theme,
child: Material(
child: Checkbox(
value: true,
onChanged: (bool? b) { },
),
),
));
@ -92,10 +99,13 @@ void main() {
isFocusable: true,
));
await tester.pumpWidget(const Material(
child: Checkbox(
value: false,
onChanged: null,
await tester.pumpWidget(Theme(
data: theme,
child: const Material(
child: Checkbox(
value: false,
onChanged: null,
),
),
));
@ -113,10 +123,13 @@ void main() {
hasEnabledState: true,
));
await tester.pumpWidget(const Material(
child: Checkbox(
value: true,
onChanged: null,
await tester.pumpWidget(Theme(
data: theme,
child: const Material(
child: Checkbox(
value: true,
onChanged: null,
),
),
));
@ -131,13 +144,16 @@ void main() {
testWidgets('Can wrap CheckBox with Semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(Material(
child: Semantics(
label: 'foo',
textDirection: TextDirection.ltr,
child: Checkbox(
value: false,
onChanged: (bool? b) { },
await tester.pumpWidget(Theme(
data: theme,
child: Material(
child: Semantics(
label: 'foo',
textDirection: TextDirection.ltr,
child: Checkbox(
value: false,
onChanged: (bool? b) { },
),
),
),
));
@ -158,19 +174,22 @@ void main() {
bool? checkBoxValue;
await tester.pumpWidget(
Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
tristate: true,
value: checkBoxValue,
onChanged: (bool? value) {
setState(() {
checkBoxValue = value;
});
},
);
},
Theme(
data: theme,
child: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
tristate: true,
value: checkBoxValue,
onChanged: (bool? value) {
setState(() {
checkBoxValue = value;
});
},
);
},
),
),
),
);
@ -201,11 +220,14 @@ void main() {
testWidgets('has semantics for tristate', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
Material(
child: Checkbox(
tristate: true,
value: null,
onChanged: (bool? newValue) { },
Theme(
data: theme,
child: Material(
child: Checkbox(
tristate: true,
value: null,
onChanged: (bool? newValue) { },
),
),
),
);
@ -221,11 +243,14 @@ void main() {
), hasLength(1));
await tester.pumpWidget(
Material(
child: Checkbox(
tristate: true,
value: true,
onChanged: (bool? newValue) { },
Theme(
data: theme,
child: Material(
child: Checkbox(
tristate: true,
value: true,
onChanged: (bool? newValue) { },
),
),
),
);
@ -242,11 +267,14 @@ void main() {
), hasLength(1));
await tester.pumpWidget(
Material(
child: Checkbox(
tristate: true,
value: false,
onChanged: (bool? newValue) { },
Theme(
data: theme,
child: Material(
child: Checkbox(
tristate: true,
value: false,
onChanged: (bool? newValue) { },
),
),
),
);
@ -273,18 +301,21 @@ void main() {
final SemanticsTester semanticsTester = SemanticsTester(tester);
await tester.pumpWidget(
Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
value: checkboxValue,
onChanged: (bool? value) {
setState(() {
checkboxValue = value;
});
},
);
},
Theme(
data: theme,
child: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
value: checkboxValue,
onChanged: (bool? value) {
setState(() {
checkboxValue = value;
});
},
);
},
),
),
),
);
@ -306,15 +337,18 @@ void main() {
testWidgets('CheckBox tristate rendering, programmatic transitions', (WidgetTester tester) async {
Widget buildFrame(bool? checkboxValue) {
return Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
tristate: true,
value: checkboxValue,
onChanged: (bool? value) { },
);
},
return Theme(
data: theme,
child: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
tristate: true,
value: checkboxValue,
onChanged: (bool? value) { },
);
},
),
),
);
}
@ -391,10 +425,14 @@ void main() {
activeColor = const Color(0xFF00FF00);
ThemeData themeData = ThemeData();
final bool material3 = themeData.useMaterial3;
final ColorScheme colorScheme = material3
? const ColorScheme.light().copyWith(primary: activeColor)
: const ColorScheme.light().copyWith(secondary: activeColor);
themeData = themeData.copyWith(colorScheme: colorScheme);
await tester.pumpWidget(buildFrame(
themeData: ThemeData(
colorScheme: const ColorScheme.light()
.copyWith(secondary: activeColor))),
themeData: themeData),
);
await tester.pumpAndSettle();
expect(getCheckboxRenderer(), paints..path(color: activeColor)); // paints's color is 0xFF00FF00 (theme)
@ -412,6 +450,7 @@ void main() {
bool? value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@ -434,13 +473,19 @@ void main() {
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
final bool material3 = theme.useMaterial3;
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Checkbox))),
paints
..circle(color: Colors.orange[500])
..path(color: const Color(0xff2196f3))
..path(color: Colors.white),
material3
? (paints
..circle(color: Colors.orange[500])
..path(color: const Color(0xff2196f3))
..path(color: theme.colorScheme.onPrimary))
: (paints
..circle(color: Colors.orange[500])
..path(color: const Color(0xff2196f3))
..path(color: Colors.white))
);
// Check the false value.
@ -453,7 +498,7 @@ void main() {
paints
..circle(color: Colors.orange[500])
..drrect(
color: const Color(0x8a000000),
color: material3 ? theme.colorScheme.onSurface : const Color(0x8a000000),
outer: RRect.fromLTRBR(15.0, 15.0, 33.0, 33.0, const Radius.circular(1.0)),
inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, const Radius.circular(-1.0)),
),
@ -468,7 +513,7 @@ void main() {
Material.of(tester.element(find.byType(Checkbox))),
paints
..drrect(
color: const Color(0x61000000),
color: material3 ? theme.colorScheme.onSurface.withOpacity(0.38) : const Color(0x61000000),
outer: RRect.fromLTRBR(15.0, 15.0, 33.0, 33.0, const Radius.circular(1.0)),
inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, const Radius.circular(-1.0)),
),
@ -480,6 +525,7 @@ void main() {
const double splashRadius = 30;
Widget buildApp() {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@ -506,8 +552,10 @@ void main() {
testWidgets('Checkbox can be hovered and has correct hover color', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool? value = true;
final bool material3 = theme.useMaterial3;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@ -531,7 +579,7 @@ void main() {
Material.of(tester.element(find.byType(Checkbox))),
paints
..path(color: const Color(0xff2196f3))
..path(color: const Color(0xffffffff),style: PaintingStyle.stroke, strokeWidth: 2.0),
..path(color: material3 ? theme.colorScheme.onPrimary : const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0),
);
// Start hovering
@ -544,7 +592,7 @@ void main() {
Material.of(tester.element(find.byType(Checkbox))),
paints
..path(color: const Color(0xff2196f3))
..path(color: const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0),
..path(color: material3 ? theme.colorScheme.onPrimary : const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0),
);
// Check what happens when disabled.
@ -553,8 +601,8 @@ void main() {
expect(
Material.of(tester.element(find.byType(Checkbox))),
paints
..path(color: const Color(0x61000000))
..path(color: const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0),
..path(color: material3 ? theme.colorScheme.onSurface.withOpacity(0.38) : const Color(0x61000000))
..path(color: material3 ? theme.colorScheme.surface : const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0),
);
});
@ -563,6 +611,7 @@ void main() {
bool? value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@ -603,6 +652,7 @@ void main() {
Future<void> buildTest(VisualDensity visualDensity) async {
return tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: Center(
child: Checkbox(
@ -693,6 +743,7 @@ void main() {
// Test Checkbox() constructor
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Scaffold(
body: Align(
alignment: Alignment.topLeft,
@ -721,6 +772,7 @@ void main() {
// Test default cursor
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Scaffold(
body: Align(
alignment: Alignment.topLeft,
@ -742,8 +794,9 @@ void main() {
// Test default cursor when disabled
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
MaterialApp(
theme: theme,
home: const Scaffold(
body: Align(
alignment: Alignment.topLeft,
child: Material(
@ -764,8 +817,9 @@ void main() {
// Test cursor when tristate
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
MaterialApp(
theme: theme,
home: const Scaffold(
body: Align(
alignment: Alignment.topLeft,
child: Material(
@ -807,7 +861,7 @@ void main() {
Widget buildFrame({required bool enabled}) {
return Material(
child: Theme(
data: ThemeData(),
data: theme,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
@ -856,7 +910,7 @@ void main() {
Widget buildFrame() {
return Material(
child: Theme(
data: ThemeData(),
data: theme,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Checkbox(
@ -901,6 +955,7 @@ void main() {
Widget buildApp() {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@ -932,6 +987,82 @@ void main() {
);
});
testWidgets('Checkbox default overlay color in active/pressed/focused/hovered states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Checkbox');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final ColorScheme colors = theme.colorScheme;
final bool material3 = theme.useMaterial3;
Widget buildCheckbox({bool active = false, bool focused = false}) {
return MaterialApp(
theme: theme,
home: Scaffold(
body: Checkbox(
focusNode: focusNode,
autofocus: focused,
value: active,
onChanged: (_) { },
),
),
);
}
await tester.pumpWidget(buildCheckbox());
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Checkbox))),
material3
? (paints..circle(color: colors.primary.withOpacity(0.12)))
: (paints
..circle(color: theme.unselectedWidgetColor.withAlpha(kRadialReactionAlpha),)
),
reason: 'Default inactive pressed Checkbox should have overlay color from default fillColor',
);
await tester.pumpWidget(buildCheckbox(active: true));
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Checkbox))),
material3
? (paints..circle(color: colors.onSurface.withOpacity(0.12)))
: (paints
..circle(color: colors.secondary.withAlpha(kRadialReactionAlpha),)
),
reason: 'Default active pressed Checkbox should have overlay color from default fillColor',
);
await tester.pumpWidget(buildCheckbox(focused: true));
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Checkbox))),
material3
? (paints..circle(color: colors.onSurface.withOpacity(0.12)))
: (paints..circle(color: theme.focusColor)),
reason: 'Focused Checkbox should use default focused overlay color',
);
await tester.pumpWidget(Container()); // reset test
await tester.pumpWidget(buildCheckbox());
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Checkbox))),
material3
? (paints..circle(color: colors.onSurface.withOpacity(0.08)))
: (paints..circle(color: theme.hoverColor)),
reason: 'Hovered Checkbox should use default hovered overlay color',
);
});
testWidgets('Checkbox overlay color resolves in active/pressed/focused/hovered states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Checkbox');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
@ -963,6 +1094,7 @@ void main() {
Widget buildCheckbox({bool active = false, bool focused = false, bool useOverlay = true}) {
return MaterialApp(
theme: theme,
home: Scaffold(
body: Checkbox(
focusNode: focusNode,
@ -1085,6 +1217,7 @@ void main() {
Widget buildTristateCheckbox() {
return MaterialApp(
theme: theme,
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
@ -1175,6 +1308,7 @@ void main() {
testWidgets('Do not crash when widget disappears while pointer is down', (WidgetTester tester) async {
Widget buildCheckbox(bool show) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: show ? Checkbox(value: true, onChanged: (_) { }) : Container(),
@ -1205,6 +1339,7 @@ void main() {
Widget buildApp({ bool? value, bool enabled = true }) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: Checkbox(
@ -1268,6 +1403,7 @@ void main() {
Widget buildApp({ bool? value, bool enabled = true }) {
return MaterialApp(
theme: theme,
home: Material(
child: Center(
child: Checkbox(