Add missing parameters to CheckboxListTile
(#120118)
* Add missing parameters to CheckboxListTile * Update test message and api doc * Reorder parameters --------- Co-authored-by: Qun Cheng <quncheng@google.com>
This commit is contained in:
parent
c8c8621416
commit
91dc513a39
@ -254,10 +254,12 @@ class Checkbox extends StatefulWidget {
|
|||||||
/// [ThemeData.focusColor] is used.
|
/// [ThemeData.focusColor] is used.
|
||||||
final Color? focusColor;
|
final Color? focusColor;
|
||||||
|
|
||||||
|
/// {@template flutter.material.checkbox.hoverColor}
|
||||||
/// The color for the checkbox's [Material] when a pointer is hovering over it.
|
/// The color for the checkbox's [Material] when a pointer is hovering over it.
|
||||||
///
|
///
|
||||||
/// If [overlayColor] returns a non-null color in the [MaterialState.hovered]
|
/// If [overlayColor] returns a non-null color in the [MaterialState.hovered]
|
||||||
/// state, it will be used instead.
|
/// state, it will be used instead.
|
||||||
|
/// {@endtemplate}
|
||||||
///
|
///
|
||||||
/// If null, then the value of [CheckboxThemeData.overlayColor] is used in the
|
/// If null, then the value of [CheckboxThemeData.overlayColor] is used in the
|
||||||
/// hovered state. If that is also null, then the value of
|
/// hovered state. If that is also null, then the value of
|
||||||
@ -332,10 +334,12 @@ class Checkbox extends StatefulWidget {
|
|||||||
/// will be width 2.
|
/// will be width 2.
|
||||||
final BorderSide? side;
|
final BorderSide? side;
|
||||||
|
|
||||||
|
/// {@template flutter.material.checkbox.isError}
|
||||||
/// True if this checkbox wants to show an error state.
|
/// True if this checkbox wants to show an error state.
|
||||||
///
|
///
|
||||||
/// The checkbox will have different default container color and check color when
|
/// The checkbox will have different default container color and check color when
|
||||||
/// this is true. This is only used when [ThemeData.useMaterial3] is set to true.
|
/// this is true. This is only used when [ThemeData.useMaterial3] is set to true.
|
||||||
|
/// {@endtemplate}
|
||||||
///
|
///
|
||||||
/// Must not be null. Defaults to false.
|
/// Must not be null. Defaults to false.
|
||||||
final bool isError;
|
final bool isError;
|
||||||
|
@ -163,8 +163,20 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
|
this.mouseCursor,
|
||||||
this.activeColor,
|
this.activeColor,
|
||||||
|
this.fillColor,
|
||||||
this.checkColor,
|
this.checkColor,
|
||||||
|
this.hoverColor,
|
||||||
|
this.overlayColor,
|
||||||
|
this.splashRadius,
|
||||||
|
this.materialTapTargetSize,
|
||||||
|
this.visualDensity,
|
||||||
|
this.focusNode,
|
||||||
|
this.autofocus = false,
|
||||||
|
this.shape,
|
||||||
|
this.side,
|
||||||
|
this.isError = false,
|
||||||
this.enabled,
|
this.enabled,
|
||||||
this.tileColor,
|
this.tileColor,
|
||||||
this.title,
|
this.title,
|
||||||
@ -174,15 +186,10 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
this.secondary,
|
this.secondary,
|
||||||
this.selected = false,
|
this.selected = false,
|
||||||
this.controlAffinity = ListTileControlAffinity.platform,
|
this.controlAffinity = ListTileControlAffinity.platform,
|
||||||
this.autofocus = false,
|
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.tristate = false,
|
this.tristate = false,
|
||||||
this.shape,
|
|
||||||
this.checkboxShape,
|
this.checkboxShape,
|
||||||
this.selectedTileColor,
|
this.selectedTileColor,
|
||||||
this.side,
|
|
||||||
this.visualDensity,
|
|
||||||
this.focusNode,
|
|
||||||
this.onFocusChange,
|
this.onFocusChange,
|
||||||
this.enableFeedback,
|
this.enableFeedback,
|
||||||
}) : assert(tristate || value != null),
|
}) : assert(tristate || value != null),
|
||||||
@ -219,16 +226,98 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
/// {@end-tool}
|
/// {@end-tool}
|
||||||
final ValueChanged<bool?>? onChanged;
|
final ValueChanged<bool?>? onChanged;
|
||||||
|
|
||||||
|
/// The cursor for a mouse pointer when it enters or is hovering over the
|
||||||
|
/// widget.
|
||||||
|
///
|
||||||
|
/// If [mouseCursor] is a [MaterialStateProperty<MouseCursor>],
|
||||||
|
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
|
||||||
|
///
|
||||||
|
/// * [MaterialState.selected].
|
||||||
|
/// * [MaterialState.hovered].
|
||||||
|
/// * [MaterialState.disabled].
|
||||||
|
///
|
||||||
|
/// If null, then the value of [CheckboxThemeData.mouseCursor] is used. If
|
||||||
|
/// that is also null, then [MaterialStateMouseCursor.clickable] is used.
|
||||||
|
final MouseCursor? mouseCursor;
|
||||||
|
|
||||||
/// The color to use when this checkbox is checked.
|
/// The color to use when this checkbox is checked.
|
||||||
///
|
///
|
||||||
/// Defaults to [ColorScheme.secondary] of the current [Theme].
|
/// Defaults to [ColorScheme.secondary] of the current [Theme].
|
||||||
final Color? activeColor;
|
final Color? activeColor;
|
||||||
|
|
||||||
|
/// The color that fills the checkbox.
|
||||||
|
///
|
||||||
|
/// Resolves in the following states:
|
||||||
|
/// * [MaterialState.selected].
|
||||||
|
/// * [MaterialState.hovered].
|
||||||
|
/// * [MaterialState.disabled].
|
||||||
|
///
|
||||||
|
/// If null, then the value of [activeColor] is used in the selected
|
||||||
|
/// state. If that is also null, the value of [CheckboxThemeData.fillColor]
|
||||||
|
/// is used. If that is also null, then the default value is used.
|
||||||
|
final MaterialStateProperty<Color?>? fillColor;
|
||||||
|
|
||||||
/// The color to use for the check icon when this checkbox is checked.
|
/// The color to use for the check icon when this checkbox is checked.
|
||||||
///
|
///
|
||||||
/// Defaults to Color(0xFFFFFFFF).
|
/// Defaults to Color(0xFFFFFFFF).
|
||||||
final Color? checkColor;
|
final Color? checkColor;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.checkbox.hoverColor}
|
||||||
|
final Color? hoverColor;
|
||||||
|
|
||||||
|
/// The color for the checkbox's [Material].
|
||||||
|
///
|
||||||
|
/// Resolves in the following states:
|
||||||
|
/// * [MaterialState.pressed].
|
||||||
|
/// * [MaterialState.selected].
|
||||||
|
/// * [MaterialState.hovered].
|
||||||
|
///
|
||||||
|
/// If null, then the value of [activeColor] with alpha [kRadialReactionAlpha]
|
||||||
|
/// and [hoverColor] is used in the pressed and hovered state. If that is also null,
|
||||||
|
/// the value of [CheckboxThemeData.overlayColor] is used. If that is also null,
|
||||||
|
/// then the the default value is used in the pressed and hovered state.
|
||||||
|
final MaterialStateProperty<Color?>? overlayColor;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.checkbox.splashRadius}
|
||||||
|
///
|
||||||
|
/// If null, then the value of [CheckboxThemeData.splashRadius] is used. If
|
||||||
|
/// that is also null, then [kRadialReactionRadius] is used.
|
||||||
|
final double? splashRadius;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.checkbox.materialTapTargetSize}
|
||||||
|
///
|
||||||
|
/// Defaults to [MaterialTapTargetSize.shrinkWrap].
|
||||||
|
final MaterialTapTargetSize? materialTapTargetSize;
|
||||||
|
|
||||||
|
/// Defines how compact the list tile's layout will be.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.material.themedata.visualDensity}
|
||||||
|
final VisualDensity? visualDensity;
|
||||||
|
|
||||||
|
|
||||||
|
/// {@macro flutter.widgets.Focus.focusNode}
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
|
||||||
|
/// {@macro flutter.widgets.Focus.autofocus}
|
||||||
|
final bool autofocus;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.ListTile.shape}
|
||||||
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.checkbox.side}
|
||||||
|
///
|
||||||
|
/// The given value is passed directly to [Checkbox.side].
|
||||||
|
///
|
||||||
|
/// If this property is null, then [CheckboxThemeData.side] of
|
||||||
|
/// [ThemeData.checkboxTheme] is used. If that is also null, then the side
|
||||||
|
/// will be width 2.
|
||||||
|
final BorderSide? side;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.checkbox.isError}
|
||||||
|
///
|
||||||
|
/// Defaults to false.
|
||||||
|
final bool isError;
|
||||||
|
|
||||||
/// {@macro flutter.material.ListTile.tileColor}
|
/// {@macro flutter.material.ListTile.tileColor}
|
||||||
final Color? tileColor;
|
final Color? tileColor;
|
||||||
|
|
||||||
@ -270,9 +359,6 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
/// Where to place the control relative to the text.
|
/// Where to place the control relative to the text.
|
||||||
final ListTileControlAffinity controlAffinity;
|
final ListTileControlAffinity controlAffinity;
|
||||||
|
|
||||||
/// {@macro flutter.widgets.Focus.autofocus}
|
|
||||||
final bool autofocus;
|
|
||||||
|
|
||||||
/// Defines insets surrounding the tile's contents.
|
/// Defines insets surrounding the tile's contents.
|
||||||
///
|
///
|
||||||
/// This value will surround the [Checkbox], [title], [subtitle], and [secondary]
|
/// This value will surround the [Checkbox], [title], [subtitle], and [secondary]
|
||||||
@ -293,9 +379,6 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
/// If tristate is false (the default), [value] must not be null.
|
/// If tristate is false (the default), [value] must not be null.
|
||||||
final bool tristate;
|
final bool tristate;
|
||||||
|
|
||||||
/// {@macro flutter.material.ListTile.shape}
|
|
||||||
final ShapeBorder? shape;
|
|
||||||
|
|
||||||
/// {@macro flutter.material.checkbox.shape}
|
/// {@macro flutter.material.checkbox.shape}
|
||||||
///
|
///
|
||||||
/// If this property is null then [CheckboxThemeData.shape] of [ThemeData.checkboxTheme]
|
/// If this property is null then [CheckboxThemeData.shape] of [ThemeData.checkboxTheme]
|
||||||
@ -306,23 +389,6 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
/// If non-null, defines the background color when [CheckboxListTile.selected] is true.
|
/// If non-null, defines the background color when [CheckboxListTile.selected] is true.
|
||||||
final Color? selectedTileColor;
|
final Color? selectedTileColor;
|
||||||
|
|
||||||
/// {@macro flutter.material.checkbox.side}
|
|
||||||
///
|
|
||||||
/// The given value is passed directly to [Checkbox.side].
|
|
||||||
///
|
|
||||||
/// If this property is null, then [CheckboxThemeData.side] of
|
|
||||||
/// [ThemeData.checkboxTheme] is used. If that is also null, then the side
|
|
||||||
/// will be width 2.
|
|
||||||
final BorderSide? side;
|
|
||||||
|
|
||||||
/// Defines how compact the list tile's layout will be.
|
|
||||||
///
|
|
||||||
/// {@macro flutter.material.themedata.visualDensity}
|
|
||||||
final VisualDensity? visualDensity;
|
|
||||||
|
|
||||||
/// {@macro flutter.widgets.Focus.focusNode}
|
|
||||||
final FocusNode? focusNode;
|
|
||||||
|
|
||||||
/// {@macro flutter.material.inkwell.onFocusChange}
|
/// {@macro flutter.material.inkwell.onFocusChange}
|
||||||
final ValueChanged<bool>? onFocusChange;
|
final ValueChanged<bool>? onFocusChange;
|
||||||
|
|
||||||
@ -360,13 +426,19 @@ class CheckboxListTile extends StatelessWidget {
|
|||||||
final Widget control = Checkbox(
|
final Widget control = Checkbox(
|
||||||
value: value,
|
value: value,
|
||||||
onChanged: enabled ?? true ? onChanged : null,
|
onChanged: enabled ?? true ? onChanged : null,
|
||||||
|
mouseCursor: mouseCursor,
|
||||||
activeColor: activeColor,
|
activeColor: activeColor,
|
||||||
|
fillColor: fillColor,
|
||||||
checkColor: checkColor,
|
checkColor: checkColor,
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
hoverColor: hoverColor,
|
||||||
|
overlayColor: overlayColor,
|
||||||
|
splashRadius: splashRadius,
|
||||||
|
materialTapTargetSize: materialTapTargetSize ?? MaterialTapTargetSize.shrinkWrap,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
tristate: tristate,
|
tristate: tristate,
|
||||||
shape: checkboxShape,
|
shape: checkboxShape,
|
||||||
side: side,
|
side: side,
|
||||||
|
isError: isError,
|
||||||
);
|
);
|
||||||
Widget? leading, trailing;
|
Widget? leading, trailing;
|
||||||
switch (controlAffinity) {
|
switch (controlAffinity) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -501,6 +502,425 @@ void main() {
|
|||||||
expect(tester.widget<Checkbox>(checkbox).value, true);
|
expect(tester.widget<Checkbox>(checkbox).value, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects mouseCursor when hovered', (WidgetTester tester) async {
|
||||||
|
// Test Checkbox() constructor
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.forbidden,
|
||||||
|
child: CheckboxListTile(
|
||||||
|
mouseCursor: SystemMouseCursors.text,
|
||||||
|
value: true,
|
||||||
|
onChanged: (_) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
|
||||||
|
await gesture.addPointer(location: tester.getCenter(find.byType(Checkbox)));
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
||||||
|
|
||||||
|
// Test default cursor
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
onChanged: (_) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
|
||||||
|
|
||||||
|
// Test default cursor when disabled
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: const MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.forbidden,
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
onChanged: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
|
||||||
|
|
||||||
|
// Test cursor when tristate
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: const MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.forbidden,
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: null,
|
||||||
|
tristate: true,
|
||||||
|
onChanged: null,
|
||||||
|
mouseCursor: _SelectedGrabMouseCursor(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.grab);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects fillColor in enabled/disabled states', (WidgetTester tester) async {
|
||||||
|
const Color activeEnabledFillColor = Color(0xFF000001);
|
||||||
|
const Color activeDisabledFillColor = Color(0xFF000002);
|
||||||
|
|
||||||
|
Color getFillColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return activeDisabledFillColor;
|
||||||
|
}
|
||||||
|
return activeEnabledFillColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
final MaterialStateProperty<Color> fillColor = MaterialStateColor.resolveWith(getFillColor);
|
||||||
|
|
||||||
|
Widget buildFrame({required bool enabled}) {
|
||||||
|
return wrap(
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
fillColor: fillColor,
|
||||||
|
onChanged: enabled ? (bool? value) { } : null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderBox getCheckboxRenderer() {
|
||||||
|
return tester.renderObject<RenderBox>(find.byType(Checkbox));
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(enabled: true));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getCheckboxRenderer(), paints..path(color: activeEnabledFillColor));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(enabled: false));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getCheckboxRenderer(), paints..path(color: activeDisabledFillColor));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects fillColor in hovered state', (WidgetTester tester) async {
|
||||||
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
|
const Color hoveredFillColor = Color(0xFF000001);
|
||||||
|
|
||||||
|
Color getFillColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.hovered)) {
|
||||||
|
return hoveredFillColor;
|
||||||
|
}
|
||||||
|
return Colors.transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
final MaterialStateProperty<Color> fillColor =
|
||||||
|
MaterialStateColor.resolveWith(getFillColor);
|
||||||
|
|
||||||
|
Widget buildFrame() {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
fillColor: fillColor,
|
||||||
|
onChanged: (bool? value) { },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderBox getCheckboxRenderer() {
|
||||||
|
return tester.renderObject<RenderBox>(find.byType(Checkbox));
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Start hovering
|
||||||
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||||
|
await gesture.addPointer();
|
||||||
|
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(getCheckboxRenderer(), paints..path(color: hoveredFillColor));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects hoverColor', (WidgetTester tester) async {
|
||||||
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
|
bool? value = true;
|
||||||
|
Widget buildApp({bool enabled = true}) {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return CheckboxListTile(
|
||||||
|
value: value,
|
||||||
|
onChanged: enabled ? (bool? newValue) {
|
||||||
|
setState(() {
|
||||||
|
value = newValue;
|
||||||
|
});
|
||||||
|
} : null,
|
||||||
|
hoverColor: Colors.orange[500],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await tester.pumpWidget(buildApp());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints
|
||||||
|
..path(style: PaintingStyle.fill)
|
||||||
|
..path(style: PaintingStyle.stroke, strokeWidth: 2.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start hovering
|
||||||
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||||
|
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildApp());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints
|
||||||
|
..circle(color: Colors.orange[500])
|
||||||
|
..path(style: PaintingStyle.fill)
|
||||||
|
..path(style: PaintingStyle.stroke, strokeWidth: 2.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check what happens when disabled.
|
||||||
|
await tester.pumpWidget(buildApp(enabled: false));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints
|
||||||
|
..path(style: PaintingStyle.fill)
|
||||||
|
..path(style: PaintingStyle.stroke, strokeWidth: 2.0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects overlayColor in active/pressed/hovered states', (WidgetTester tester) async {
|
||||||
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
|
|
||||||
|
const Color fillColor = Color(0xFF000000);
|
||||||
|
const Color activePressedOverlayColor = Color(0xFF000001);
|
||||||
|
const Color inactivePressedOverlayColor = Color(0xFF000002);
|
||||||
|
const Color hoverOverlayColor = Color(0xFF000003);
|
||||||
|
const Color hoverColor = Color(0xFF000005);
|
||||||
|
|
||||||
|
Color? getOverlayColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.pressed)) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return activePressedOverlayColor;
|
||||||
|
}
|
||||||
|
return inactivePressedOverlayColor;
|
||||||
|
}
|
||||||
|
if (states.contains(MaterialState.hovered)) {
|
||||||
|
return hoverOverlayColor;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const double splashRadius = 24.0;
|
||||||
|
|
||||||
|
Widget buildCheckbox({bool active = false, bool useOverlay = true}) {
|
||||||
|
return wrap(
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: active,
|
||||||
|
onChanged: (_) { },
|
||||||
|
fillColor: const MaterialStatePropertyAll<Color>(fillColor),
|
||||||
|
overlayColor: useOverlay ? MaterialStateProperty.resolveWith(getOverlayColor) : null,
|
||||||
|
hoverColor: hoverColor,
|
||||||
|
splashRadius: splashRadius,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildCheckbox(useOverlay: false));
|
||||||
|
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints..circle()
|
||||||
|
..circle(
|
||||||
|
color: fillColor.withAlpha(kRadialReactionAlpha),
|
||||||
|
radius: splashRadius,
|
||||||
|
),
|
||||||
|
reason: 'Default inactive pressed Checkbox should have overlay color from fillColor',
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildCheckbox(active: true, useOverlay: false));
|
||||||
|
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints..circle()
|
||||||
|
..circle(
|
||||||
|
color: fillColor.withAlpha(kRadialReactionAlpha),
|
||||||
|
radius: splashRadius,
|
||||||
|
),
|
||||||
|
reason: 'Default active pressed Checkbox should have overlay color from fillColor',
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildCheckbox());
|
||||||
|
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints..circle()
|
||||||
|
..circle(
|
||||||
|
color: inactivePressedOverlayColor,
|
||||||
|
radius: splashRadius,
|
||||||
|
),
|
||||||
|
reason: 'Inactive pressed Checkbox should have overlay color: $inactivePressedOverlayColor',
|
||||||
|
);
|
||||||
|
|
||||||
|
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))),
|
||||||
|
paints..circle()
|
||||||
|
..circle(
|
||||||
|
color: activePressedOverlayColor,
|
||||||
|
radius: splashRadius,
|
||||||
|
),
|
||||||
|
reason: 'Active pressed Checkbox should have overlay color: $activePressedOverlayColor',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start hovering
|
||||||
|
await tester.pumpWidget(Container());
|
||||||
|
await tester.pumpWidget(buildCheckbox());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
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))),
|
||||||
|
paints
|
||||||
|
..circle(
|
||||||
|
color: hoverOverlayColor,
|
||||||
|
radius: splashRadius,
|
||||||
|
),
|
||||||
|
reason: 'Hovered Checkbox should use overlay color $hoverOverlayColor over $hoverColor',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects splashRadius', (WidgetTester tester) async {
|
||||||
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
|
const double splashRadius = 30;
|
||||||
|
Widget buildApp() {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return CheckboxListTile(
|
||||||
|
value: false,
|
||||||
|
onChanged: (bool? newValue) {},
|
||||||
|
hoverColor: Colors.orange[500],
|
||||||
|
splashRadius: splashRadius,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await tester.pumpWidget(buildApp());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
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))),
|
||||||
|
paints..circle(color: Colors.orange[500], radius: splashRadius),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects materialTapTargetSize', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
onChanged: (bool? newValue) { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// default test
|
||||||
|
expect(tester.getSize(find.byType(Checkbox)), const Size(40.0, 40.0));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
child: CheckboxListTile(
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||||
|
value: true,
|
||||||
|
onChanged: (bool? newValue) { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Checkbox)), const Size(48.0, 48.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CheckboxListTile respects isError - M3', (WidgetTester tester) async {
|
||||||
|
final ThemeData themeData = ThemeData(useMaterial3: true);
|
||||||
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
|
bool? value = true;
|
||||||
|
Widget buildApp() {
|
||||||
|
return MaterialApp(
|
||||||
|
theme: themeData,
|
||||||
|
home: Material(
|
||||||
|
child: Center(
|
||||||
|
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return CheckboxListTile(
|
||||||
|
isError: true,
|
||||||
|
value: value,
|
||||||
|
onChanged: (bool? newValue) {
|
||||||
|
setState(() {
|
||||||
|
value = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default color
|
||||||
|
await tester.pumpWidget(Container());
|
||||||
|
await tester.pumpWidget(buildApp());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(
|
||||||
|
Material.of(tester.element(find.byType(Checkbox))),
|
||||||
|
paints..path(color: themeData.colorScheme.error)..path(color: themeData.colorScheme.onError)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start hovering
|
||||||
|
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))),
|
||||||
|
paints
|
||||||
|
..circle(color: themeData.colorScheme.error.withOpacity(0.08))
|
||||||
|
..path(color: themeData.colorScheme.error)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
group('feedback', () {
|
group('feedback', () {
|
||||||
late FeedbackTester feedback;
|
late FeedbackTester feedback;
|
||||||
|
|
||||||
@ -541,3 +961,18 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _SelectedGrabMouseCursor extends MaterialStateMouseCursor {
|
||||||
|
const _SelectedGrabMouseCursor();
|
||||||
|
|
||||||
|
@override
|
||||||
|
MouseCursor resolve(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return SystemMouseCursors.grab;
|
||||||
|
}
|
||||||
|
return SystemMouseCursors.basic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get debugDescription => '_SelectedGrabMouseCursor()';
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user