
Reverts flutter/flutter#131998 Reverting for https://github.com/flutter/flutter/issues/132222
483 lines
20 KiB
Dart
483 lines
20 KiB
Dart
// 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 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../foundation/leak_tracking.dart';
|
|
|
|
void main() {
|
|
test('CheckboxThemeData copyWith, ==, hashCode basics', () {
|
|
expect(const CheckboxThemeData(), const CheckboxThemeData().copyWith());
|
|
expect(const CheckboxThemeData().hashCode, const CheckboxThemeData().copyWith().hashCode);
|
|
});
|
|
|
|
test('CheckboxThemeData lerp special cases', () {
|
|
expect(CheckboxThemeData.lerp(null, null, 0), const CheckboxThemeData());
|
|
const CheckboxThemeData data = CheckboxThemeData();
|
|
expect(identical(CheckboxThemeData.lerp(data, data, 0.5), data), true);
|
|
});
|
|
|
|
test('CheckboxThemeData defaults', () {
|
|
const CheckboxThemeData themeData = CheckboxThemeData();
|
|
expect(themeData.mouseCursor, null);
|
|
expect(themeData.fillColor, null);
|
|
expect(themeData.checkColor, null);
|
|
expect(themeData.overlayColor, null);
|
|
expect(themeData.splashRadius, null);
|
|
expect(themeData.materialTapTargetSize, null);
|
|
expect(themeData.visualDensity, null);
|
|
|
|
const CheckboxTheme theme = CheckboxTheme(data: CheckboxThemeData(), child: SizedBox());
|
|
expect(theme.data.mouseCursor, null);
|
|
expect(theme.data.fillColor, null);
|
|
expect(theme.data.checkColor, null);
|
|
expect(theme.data.overlayColor, null);
|
|
expect(theme.data.splashRadius, null);
|
|
expect(theme.data.materialTapTargetSize, null);
|
|
expect(theme.data.visualDensity, null);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Default CheckboxThemeData debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
const CheckboxThemeData().debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description, <String>[]);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('CheckboxThemeData implements debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
const CheckboxThemeData(
|
|
mouseCursor: MaterialStatePropertyAll<MouseCursor?>(SystemMouseCursors.click),
|
|
fillColor: MaterialStatePropertyAll<Color>(Color(0xfffffff0)),
|
|
checkColor: MaterialStatePropertyAll<Color>(Color(0xfffffff1)),
|
|
overlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff2)),
|
|
splashRadius: 1.0,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
visualDensity: VisualDensity.standard,
|
|
).debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(
|
|
description,
|
|
equalsIgnoringHashCodes(<String>[
|
|
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))',
|
|
'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))',
|
|
'checkColor: MaterialStatePropertyAll(Color(0xfffffff1))',
|
|
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff2))',
|
|
'splashRadius: 1.0',
|
|
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
|
|
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',
|
|
]),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Checkbox is themeable', (WidgetTester tester) async {
|
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
|
|
const MouseCursor mouseCursor = SystemMouseCursors.text;
|
|
const Color defaultFillColor = Color(0xfffffff0);
|
|
const Color selectedFillColor = Color(0xfffffff1);
|
|
const Color defaultCheckColor = Color(0xfffffff2);
|
|
const Color focusedCheckColor = Color(0xfffffff3);
|
|
const Color focusOverlayColor = Color(0xfffffff4);
|
|
const Color hoverOverlayColor = Color(0xfffffff5);
|
|
const double splashRadius = 1.0;
|
|
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
|
|
const VisualDensity visualDensity = VisualDensity(vertical: 1.0, horizontal: 1.0);
|
|
|
|
Widget buildCheckbox({bool selected = false, bool autofocus = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
checkboxTheme: CheckboxThemeData(
|
|
mouseCursor: const MaterialStatePropertyAll<MouseCursor?>(mouseCursor),
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}),
|
|
checkColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedCheckColor;
|
|
}
|
|
return defaultCheckColor;
|
|
}),
|
|
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusOverlayColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoverOverlayColor;
|
|
}
|
|
return null;
|
|
}),
|
|
splashRadius: splashRadius,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
visualDensity: visualDensity,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Checkbox(
|
|
onChanged: (bool? value) {},
|
|
value: selected,
|
|
autofocus: autofocus,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Checkbox.
|
|
await tester.pumpWidget(buildCheckbox());
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: defaultFillColor));
|
|
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
|
|
expect(tester.getSize(find.byType(Checkbox)), const Size(40.0, 40.0) + visualDensity.baseSizeAdjustment);
|
|
|
|
// Selected checkbox.
|
|
await tester.pumpWidget(buildCheckbox(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor));
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor)..path(color: defaultCheckColor));
|
|
|
|
// Checkbox with hover.
|
|
await tester.pumpWidget(buildCheckbox());
|
|
await _pointGestureToCheckbox(tester);
|
|
await tester.pumpAndSettle();
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
|
expect(_getCheckboxMaterial(tester), paints..circle(color: hoverOverlayColor));
|
|
|
|
// Checkbox with focus.
|
|
await tester.pumpWidget(buildCheckbox(autofocus: true, selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..circle(color: focusOverlayColor, radius: splashRadius));
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor)..path(color: focusedCheckColor));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Checkbox properties are taken over the theme values', (WidgetTester tester) async {
|
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
|
|
const MouseCursor themeMouseCursor = SystemMouseCursors.click;
|
|
const Color themeDefaultFillColor = Color(0xfffffff0);
|
|
const Color themeSelectedFillColor = Color(0xfffffff1);
|
|
const Color themeCheckColor = Color(0xfffffff2);
|
|
const Color themeFocusOverlayColor = Color(0xfffffff3);
|
|
const Color themeHoverOverlayColor = Color(0xfffffff4);
|
|
const double themeSplashRadius = 1.0;
|
|
const MaterialTapTargetSize themeMaterialTapTargetSize = MaterialTapTargetSize.padded;
|
|
const VisualDensity themeVisualDensity = VisualDensity.standard;
|
|
|
|
const MouseCursor mouseCursor = SystemMouseCursors.text;
|
|
const Color defaultFillColor = Color(0xfffffff5);
|
|
const Color selectedFillColor = Color(0xfffffff6);
|
|
const Color checkColor = Color(0xfffffff7);
|
|
const Color focusColor = Color(0xfffffff8);
|
|
const Color hoverColor = Color(0xfffffff9);
|
|
const double splashRadius = 2.0;
|
|
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
|
|
const VisualDensity visualDensity = VisualDensity.standard;
|
|
|
|
Widget buildCheckbox({bool selected = false, bool autofocus = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
checkboxTheme: CheckboxThemeData(
|
|
mouseCursor: const MaterialStatePropertyAll<MouseCursor?>(themeMouseCursor),
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return themeSelectedFillColor;
|
|
}
|
|
return themeDefaultFillColor;
|
|
}),
|
|
checkColor: const MaterialStatePropertyAll<Color?>(themeCheckColor),
|
|
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return themeFocusOverlayColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return themeHoverOverlayColor;
|
|
}
|
|
return null;
|
|
}),
|
|
splashRadius: themeSplashRadius,
|
|
materialTapTargetSize: themeMaterialTapTargetSize,
|
|
visualDensity: themeVisualDensity,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Checkbox(
|
|
onChanged: (bool? value) { },
|
|
value: selected,
|
|
autofocus: autofocus,
|
|
mouseCursor: mouseCursor,
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}),
|
|
checkColor: checkColor,
|
|
focusColor: focusColor,
|
|
hoverColor: hoverColor,
|
|
splashRadius: splashRadius,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
visualDensity: visualDensity,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Checkbox.
|
|
await tester.pumpWidget(buildCheckbox());
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: defaultFillColor));
|
|
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
|
|
expect(tester.getSize(find.byType(Checkbox)), const Size(40.0, 40.0) + visualDensity.baseSizeAdjustment);
|
|
|
|
// Selected checkbox.
|
|
await tester.pumpWidget(buildCheckbox(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor));
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor)..path(color: checkColor));
|
|
|
|
// Checkbox with hover.
|
|
await tester.pumpWidget(buildCheckbox());
|
|
await _pointGestureToCheckbox(tester);
|
|
await tester.pumpAndSettle();
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
|
expect(_getCheckboxMaterial(tester), paints..circle(color: hoverColor));
|
|
|
|
// Checkbox with focus.
|
|
await tester.pumpWidget(buildCheckbox(autofocus: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..circle(color: focusColor, radius: splashRadius));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Checkbox activeColor property is taken over the theme', (WidgetTester tester) async {
|
|
const Color themeSelectedFillColor = Color(0xfffffff1);
|
|
const Color themeDefaultFillColor = Color(0xfffffff0);
|
|
const Color selectedFillColor = Color(0xfffffff6);
|
|
|
|
Widget buildCheckbox({bool selected = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
checkboxTheme: CheckboxThemeData(
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return themeSelectedFillColor;
|
|
}
|
|
return themeDefaultFillColor;
|
|
}),
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Checkbox(
|
|
onChanged: (bool? value) { },
|
|
value: selected,
|
|
activeColor: selectedFillColor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Unselected checkbox.
|
|
await tester.pumpWidget(buildCheckbox());
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: themeDefaultFillColor));
|
|
|
|
// Selected checkbox.
|
|
await tester.pumpWidget(buildCheckbox(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: selectedFillColor));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Checkbox theme overlay color resolves in active/pressed states', (WidgetTester tester) async {
|
|
const Color activePressedOverlayColor = Color(0xFF000001);
|
|
const Color inactivePressedOverlayColor = Color(0xFF000002);
|
|
|
|
Color? getOverlayColor(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return activePressedOverlayColor;
|
|
}
|
|
return inactivePressedOverlayColor;
|
|
}
|
|
return null;
|
|
}
|
|
const double splashRadius = 24.0;
|
|
|
|
Widget buildCheckbox({required bool active}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
checkboxTheme: CheckboxThemeData(
|
|
overlayColor: MaterialStateProperty.resolveWith(getOverlayColor),
|
|
splashRadius: splashRadius,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Checkbox(
|
|
value: active,
|
|
onChanged: (_) { },
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildCheckbox(active: false));
|
|
await tester.startGesture(tester.getCenter(find.byType(Checkbox)));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
_getCheckboxMaterial(tester),
|
|
paints
|
|
..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(
|
|
_getCheckboxMaterial(tester),
|
|
paints
|
|
..circle(
|
|
color: activePressedOverlayColor,
|
|
radius: splashRadius,
|
|
),
|
|
reason: 'Active pressed Checkbox should have overlay color: $activePressedOverlayColor',
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Local CheckboxTheme can override global CheckboxTheme', (WidgetTester tester) async {
|
|
const Color globalThemeFillColor = Color(0xfffffff1);
|
|
const Color globalThemeCheckColor = Color(0xff000000);
|
|
const Color localThemeFillColor = Color(0xffff0000);
|
|
const Color localThemeCheckColor = Color(0xffffffff);
|
|
|
|
Widget buildCheckbox({required bool active}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
checkboxTheme: const CheckboxThemeData(
|
|
checkColor: MaterialStatePropertyAll<Color>(globalThemeCheckColor),
|
|
fillColor: MaterialStatePropertyAll<Color>(globalThemeFillColor),
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: CheckboxTheme(
|
|
data: const CheckboxThemeData(
|
|
fillColor: MaterialStatePropertyAll<Color>(localThemeFillColor),
|
|
checkColor: MaterialStatePropertyAll<Color>(localThemeCheckColor),
|
|
),
|
|
child: Checkbox(
|
|
value: active,
|
|
onChanged: (_) { },
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildCheckbox(active: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: localThemeFillColor));
|
|
expect(_getCheckboxMaterial(tester), paints..path(color: localThemeFillColor)..path(color: localThemeCheckColor));
|
|
});
|
|
|
|
test('CheckboxThemeData lerp with null parameters', () {
|
|
final CheckboxThemeData lerped = CheckboxThemeData.lerp(null, null, 0.25);
|
|
|
|
expect(lerped.mouseCursor, null);
|
|
expect(lerped.fillColor, null);
|
|
expect(lerped.checkColor, null);
|
|
expect(lerped.overlayColor, null);
|
|
expect(lerped.splashRadius, null);
|
|
expect(lerped.materialTapTargetSize, null);
|
|
expect(lerped.visualDensity, null);
|
|
expect(lerped.shape, null);
|
|
expect(lerped.side, null);
|
|
});
|
|
|
|
test('CheckboxThemeData lerp from populated to null parameters', () {
|
|
final CheckboxThemeData theme = CheckboxThemeData(
|
|
fillColor: MaterialStateProperty.all(const Color(0xfffffff0)),
|
|
checkColor: MaterialStateProperty.all(const Color(0xfffffff1)),
|
|
overlayColor: MaterialStateProperty.all(const Color(0xfffffff2)),
|
|
splashRadius: 3.0,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
visualDensity: const VisualDensity(vertical: 1.0, horizontal: 1.0),
|
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))),
|
|
side: const BorderSide(width: 4.0),
|
|
);
|
|
final CheckboxThemeData lerped = CheckboxThemeData.lerp(theme, null, 0.5);
|
|
|
|
expect(lerped.fillColor!.resolve(<MaterialState>{}), const Color(0x80fffff0));
|
|
expect(lerped.checkColor!.resolve(<MaterialState>{}), const Color(0x80fffff1));
|
|
expect(lerped.overlayColor!.resolve(<MaterialState>{}), const Color(0x80fffff2));
|
|
expect(lerped.splashRadius, 1.5);
|
|
expect(lerped.materialTapTargetSize, null);
|
|
expect(lerped.visualDensity, null);
|
|
expect(lerped.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))));
|
|
// Returns null if either lerp value is null.
|
|
expect(lerped.side, null);
|
|
});
|
|
|
|
test('CheckboxThemeData lerp from populated parameters', () {
|
|
final CheckboxThemeData themeA = CheckboxThemeData(
|
|
fillColor: MaterialStateProperty.all(const Color(0xfffffff0)),
|
|
checkColor: MaterialStateProperty.all(const Color(0xfffffff1)),
|
|
overlayColor: MaterialStateProperty.all(const Color(0xfffffff2)),
|
|
splashRadius: 3.0,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
visualDensity: const VisualDensity(vertical: 1.0, horizontal: 1.0),
|
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))),
|
|
side: const BorderSide(width: 4.0),
|
|
);
|
|
final CheckboxThemeData themeB = CheckboxThemeData(
|
|
fillColor: MaterialStateProperty.all(const Color(0xfffffff3)),
|
|
checkColor: MaterialStateProperty.all(const Color(0xfffffff4)),
|
|
overlayColor: MaterialStateProperty.all(const Color(0xfffffff5)),
|
|
splashRadius: 9.0,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
visualDensity: const VisualDensity(vertical: 2.0, horizontal: 2.0),
|
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(1.0))),
|
|
side: const BorderSide(width: 3.0),
|
|
);
|
|
final CheckboxThemeData lerped = CheckboxThemeData.lerp(themeA, themeB, 0.5);
|
|
|
|
expect(lerped.fillColor!.resolve(<MaterialState>{}), const Color(0xfffffff1));
|
|
expect(lerped.checkColor!.resolve(<MaterialState>{}), const Color(0xfffffff2));
|
|
expect(lerped.overlayColor!.resolve(<MaterialState>{}), const Color(0xfffffff3));
|
|
expect(lerped.splashRadius, 6);
|
|
expect(lerped.materialTapTargetSize, MaterialTapTargetSize.shrinkWrap);
|
|
expect(lerped.visualDensity, const VisualDensity(vertical: 2.0, horizontal: 2.0));
|
|
expect(lerped.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.5))));
|
|
expect(lerped.side, const BorderSide(width: 3.5));
|
|
});
|
|
}
|
|
|
|
Future<void> _pointGestureToCheckbox(WidgetTester tester) async {
|
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
await gesture.addPointer();
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.moveTo(tester.getCenter(find.byType(Checkbox)));
|
|
}
|
|
|
|
MaterialInkController? _getCheckboxMaterial(WidgetTester tester) {
|
|
return Material.of(tester.element(find.byType(Checkbox)));
|
|
}
|