Unverified Commit 0bb9409f authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Update `Switch` tests for M2/M3 (#129810)

Updated and reorganized unit tests for `Switch` to have M2 and M3 versions.

More info in https://github.com/flutter/flutter/issues/127064
parent 2fefe72c
...@@ -106,7 +106,7 @@ void main() { ...@@ -106,7 +106,7 @@ void main() {
expect(tester.getSize(find.byType(Switch)), material3 ? const Size(60.0, 40.0) : const Size(59.0, 40.0)); expect(tester.getSize(find.byType(Switch)), material3 ? const Size(60.0, 40.0) : const Size(59.0, 40.0));
}); });
testWidgets('Switch does not get distorted upon changing constraints with parent - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch does not get distorted upon changing constraints with parent', (WidgetTester tester) async {
const double maxWidth = 300; const double maxWidth = 300;
const double maxHeight = 100; const double maxHeight = 100;
...@@ -145,7 +145,7 @@ void main() { ...@@ -145,7 +145,7 @@ void main() {
)); ));
await expectLater( await expectLater(
find.byKey(boundaryKey), find.byKey(boundaryKey),
matchesGoldenFile('switch_test.big.on.png'), matchesGoldenFile('m2_switch_test.big.on.png'),
); );
await tester.pumpWidget(buildSwitch( await tester.pumpWidget(buildSwitch(
...@@ -154,7 +154,59 @@ void main() { ...@@ -154,7 +154,59 @@ void main() {
)); ));
await expectLater( await expectLater(
find.byKey(boundaryKey), find.byKey(boundaryKey),
matchesGoldenFile('switch_test.small.on.png'), matchesGoldenFile('m2_switch_test.small.on.png'),
);
});
testWidgets('Material3 - Switch does not get distorted upon changing constraints with parent', (WidgetTester tester) async {
const double maxWidth = 300;
const double maxHeight = 100;
const ValueKey<String> boundaryKey = ValueKey<String>('switch container');
Widget buildSwitch({required double width, required double height}) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Scaffold(
body: Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
width: maxWidth,
height: maxHeight,
child: RepaintBoundary(
key: boundaryKey,
child: SizedBox(
width: width,
height: height,
child: Switch(
dragStartBehavior: DragStartBehavior.down,
value: true,
onChanged: (_) {},
),
),
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch(
width: maxWidth,
height: maxHeight,
));
await expectLater(
find.byKey(boundaryKey),
matchesGoldenFile('m3_switch_test.big.on.png'),
);
await tester.pumpWidget(buildSwitch(
width: 20,
height: 10,
));
await expectLater(
find.byKey(boundaryKey),
matchesGoldenFile('m3_switch_test.small.on.png'),
); );
}); });
...@@ -348,7 +400,7 @@ void main() { ...@@ -348,7 +400,7 @@ void main() {
expect(value, isFalse); expect(value, isFalse);
}); });
testWidgets('Switch has default colors when enabled - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch has default colors when enabled', (WidgetTester tester) async {
bool value = false; bool value = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -407,7 +459,72 @@ void main() { ...@@ -407,7 +459,72 @@ void main() {
); );
}); });
testWidgets('Switch has default colors when disabled - M2', (WidgetTester tester) async { testWidgets('Material3 - Switch has default colors when enabled', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
final ColorScheme colors = theme.colorScheme;
bool value = false;
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Directionality(
textDirection: TextDirection.rtl,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: Switch(
dragStartBehavior: DragStartBehavior.down,
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
),
),
);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: colors.outline), // thumb color
reason: 'Inactive enabled switch should match these colors',
);
await tester.drag(find.byType(Switch), const Offset(-30.0, 0.0));
await tester.pump();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.onPrimary), // thumb color
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Material2 - Switch has default colors when disabled', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -471,10 +588,125 @@ void main() { ...@@ -471,10 +588,125 @@ void main() {
); );
}); });
testWidgets('Switch default overlayColor resolves hovered/focused state', (WidgetTester tester) async { testWidgets('Material3 - Inactive Switch has default colors when disabled', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
value: false,
onChanged: null,
),
),
),
),
));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)), // thumb color
reason: 'Inactive disabled switch should match these colors',
);
});
testWidgets('Material3 - Active Switch has default colors when disabled', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
value: true,
onChanged: null,
),
),
),
),
));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.surface), // thumb color
reason: 'Active disabled switch should match these colors',
);
});
testWidgets('Material2 - Switch default overlayColor resolves hovered/focused state', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
Finder findSwitch() {
return find.byWidgetPredicate((Widget widget) => widget is Switch);
}
MaterialInkController? getSwitchMaterial(WidgetTester tester) {
return Material.of(tester.element(findSwitch()));
}
await tester.pumpWidget(MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Scaffold(
body: Switch(
focusNode: focusNode,
value: true,
onChanged: (_) { },
),
),
));
// Focused.
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(getSwitchMaterial(tester),
paints
..circle(color: theme.focusColor)
);
// On both hovered and focused, the overlay color should show hovered overlay color.
final Offset center = tester.getCenter(find.byType(Switch));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getSwitchMaterial(tester),
paints..circle(color: theme.hoverColor)
);
});
testWidgets('Material3 - Switch default overlayColor resolves hovered/focused state', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final bool material3 = theme.useMaterial3;
Finder findSwitch() { Finder findSwitch() {
return find.byWidgetPredicate((Widget widget) => widget is Switch); return find.byWidgetPredicate((Widget widget) => widget is Switch);
...@@ -499,8 +731,7 @@ void main() { ...@@ -499,8 +731,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(getSwitchMaterial(tester), expect(getSwitchMaterial(tester),
paints paints..circle(color: theme.colorScheme.primary.withOpacity(0.12))
..circle(color: material3 ? theme.colorScheme.primary.withOpacity(0.12) : theme.focusColor)
); );
// On both hovered and focused, the overlay color should show hovered overlay color. // On both hovered and focused, the overlay color should show hovered overlay color.
...@@ -513,11 +744,11 @@ void main() { ...@@ -513,11 +744,11 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(getSwitchMaterial(tester), expect(getSwitchMaterial(tester),
paints..circle(color: material3 ? theme.colorScheme.primary.withOpacity(0.08) : theme.hoverColor) paints..circle(color: theme.colorScheme.primary.withOpacity(0.08))
); );
}); });
testWidgets('Switch can be set color - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch can be set color', (WidgetTester tester) async {
bool value = false; bool value = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -578,15 +809,16 @@ void main() { ...@@ -578,15 +809,16 @@ void main() {
); );
}); });
testWidgets('Drag ends after animation completes', (WidgetTester tester) async { testWidgets('Material3 - Switch can be set color', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/17773 final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
bool value = false; bool value = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: theme, theme: themeData,
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.rtl,
child: StatefulBuilder( child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) { builder: (BuildContext context, StateSetter setState) {
return Material( return Material(
...@@ -599,6 +831,10 @@ void main() { ...@@ -599,6 +831,10 @@ void main() {
value = newValue; value = newValue;
}); });
}, },
activeColor: Colors.red[500],
activeTrackColor: Colors.green[500],
inactiveThumbColor: Colors.yellow[500],
inactiveTrackColor: Colors.blue[500],
), ),
), ),
); );
...@@ -608,9 +844,69 @@ void main() { ...@@ -608,9 +844,69 @@ void main() {
), ),
); );
expect(value, isFalse); expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: Colors.blue[500],
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
)
..rrect(color: Colors.yellow[500]), // thumb color
);
await tester.drag(find.byType(Switch), const Offset(-30.0, 0.0));
await tester.pump();
final Rect switchRect = tester.getRect(find.byType(Switch)); expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: Colors.green[500],
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: Colors.red[500]), // thumb color
);
});
testWidgets('Drag ends after animation completes', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/17773
bool value = false;
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: Switch(
dragStartBehavior: DragStartBehavior.down,
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
),
),
);
expect(value, isFalse);
final Rect switchRect = tester.getRect(find.byType(Switch));
final TestGesture gesture = await tester.startGesture(switchRect.centerLeft); final TestGesture gesture = await tester.startGesture(switchRect.centerLeft);
await tester.pump(); await tester.pump();
await gesture.moveBy(Offset(switchRect.width, 0.0)); await gesture.moveBy(Offset(switchRect.width, 0.0));
...@@ -862,7 +1158,7 @@ void main() { ...@@ -862,7 +1158,7 @@ void main() {
} }
}); });
testWidgets('Switch is focusable and has correct focus color - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch is focusable and has correct focus color', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true; bool value = true;
...@@ -944,6 +1240,93 @@ void main() { ...@@ -944,6 +1240,93 @@ void main() {
); );
}); });
testWidgets('Material3 - Switch is focusable and has correct focus color', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
focusColor: Colors.orange[500],
autofocus: true,
focusNode: focusNode,
);
}),
),
),
);
}
await tester.pumpWidget(buildApp());
// active, enabled switch
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500]),
);
// Check the false value: inactive enabled switch
value = false;
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500])
);
// Check what happens when disabled: inactive disabled switch.
value = false;
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isFalse);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)),
);
});
testWidgets('Switch with splash radius set', (WidgetTester tester) async { testWidgets('Switch with splash radius set', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const double splashRadius = 30; const double splashRadius = 30;
...@@ -971,7 +1354,7 @@ void main() { ...@@ -971,7 +1354,7 @@ void main() {
); );
}); });
testWidgets('Switch can be hovered and has correct hover color - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch can be hovered and has correct hover color', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true; bool value = true;
Widget buildApp({bool enabled = true}) { Widget buildApp({bool enabled = true}) {
...@@ -1047,6 +1430,78 @@ void main() { ...@@ -1047,6 +1430,78 @@ void main() {
); );
}); });
testWidgets('Material3 - Switch can be hovered and has correct hover color', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
hoverColor: Colors.orange[500],
);
}),
),
),
);
}
// active enabled switch
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.onPrimary),
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500]),
);
// Check what happens for disabled active switch
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.surface.withOpacity(1.0)),
);
});
testWidgets('Switch can be toggled by keyboard shortcuts', (WidgetTester tester) async { testWidgets('Switch can be toggled by keyboard shortcuts', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true; bool value = true;
...@@ -1239,7 +1694,7 @@ void main() { ...@@ -1239,7 +1694,7 @@ void main() {
expect(updatedSwitchState.position.isDismissed, false); expect(updatedSwitchState.position.isDismissed, false);
}); });
testWidgets('Switch thumb color resolves in active/enabled states - M2', (WidgetTester tester) async { testWidgets('Material2 - Switch thumb color resolves in active/enabled states', (WidgetTester tester) async {
const Color activeEnabledThumbColor = Color(0xFF000001); const Color activeEnabledThumbColor = Color(0xFF000001);
const Color activeDisabledThumbColor = Color(0xFF000002); const Color activeDisabledThumbColor = Color(0xFF000002);
const Color inactiveEnabledThumbColor = Color(0xFF000003); const Color inactiveEnabledThumbColor = Color(0xFF000003);
...@@ -1347,51 +1802,160 @@ void main() { ...@@ -1347,51 +1802,160 @@ void main() {
); );
}); });
testWidgets('Switch thumb color resolves in hovered/focused states - M2', (WidgetTester tester) async { testWidgets('Material3 - Switch thumb color resolves in active/enabled states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); final ThemeData themeData = ThemeData(useMaterial3: true);
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; final ColorScheme colors = themeData.colorScheme;
const Color hoveredThumbColor = Color(0xFF000001); const Color activeEnabledThumbColor = Color(0xFF000001);
const Color focusedThumbColor = Color(0xFF000002); const Color activeDisabledThumbColor = Color(0xFF000002);
const Color inactiveEnabledThumbColor = Color(0xFF000003);
const Color inactiveDisabledThumbColor = Color(0xFF000004);
Color getThumbColor(Set<MaterialState> states) { Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) { if (states.contains(MaterialState.disabled)) {
return hoveredThumbColor; if (states.contains(MaterialState.selected)) {
return activeDisabledThumbColor;
} }
if (states.contains(MaterialState.focused)) { return inactiveDisabledThumbColor;
return focusedThumbColor;
} }
return Colors.transparent; if (states.contains(MaterialState.selected)) {
return activeEnabledThumbColor;
}
return inactiveEnabledThumbColor;
} }
final MaterialStateProperty<Color> thumbColor = final MaterialStateProperty<Color> thumbColor = MaterialStateColor.resolveWith(getThumbColor);
MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch() { Widget buildSwitch({required bool enabled, required bool active}) {
return MaterialApp( return Theme(
theme: ThemeData(useMaterial3: false), data: themeData,
home: Material( child: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center( child: Center(
child: Switch( child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
thumbColor: thumbColor, thumbColor: thumbColor,
onChanged: (_) { }, value: active,
onChanged: enabled ? (_) { } : null,
),
), ),
), ),
), ),
); );
} }
await tester.pumpWidget(buildSwitch()); await tester.pumpWidget(buildSwitch(enabled: false, active: false));
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect( expect(
Material.of(tester.element(find.byType(Switch))), Material.of(tester.element(find.byType(Switch))),
paints paints
..rrect( ..rrect(
color: const Color(0x802196f3), style: PaintingStyle.fill,
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)), color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: inactiveDisabledThumbColor),
reason: 'Inactive disabled switch should default track and custom thumb color',
);
await tester.pumpWidget(buildSwitch(enabled: false, active: true));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: activeDisabledThumbColor),
reason: 'Active disabled switch should match these colors',
);
await tester.pumpWidget(buildSwitch(enabled: true, active: false));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: inactiveEnabledThumbColor),
reason: 'Inactive enabled switch should match these colors',
);
await tester.pumpWidget(buildSwitch(enabled: true, active: true));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: activeEnabledThumbColor),
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Material2 - Switch thumb color resolves in hovered/focused states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredThumbColor = Color(0xFF000001);
const Color focusedThumbColor = Color(0xFF000002);
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredThumbColor;
}
if (states.contains(MaterialState.focused)) {
return focusedThumbColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> thumbColor =
MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch() {
return MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Material(
child: Center(
child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
thumbColor: thumbColor,
onChanged: (_) { },
),
),
),
);
}
await tester.pumpWidget(buildSwitch());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: const Color(0x802196f3),
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)),
) )
..circle() // Radial reaction ..circle() // Radial reaction
..rrect(color: const Color(0x33000000)) ..rrect(color: const Color(0x33000000))
...@@ -1423,7 +1987,83 @@ void main() { ...@@ -1423,7 +1987,83 @@ void main() {
); );
}); });
testWidgets('Track color resolves in active/enabled states - M2', (WidgetTester tester) async { testWidgets('Material3 - Switch thumb color resolves in hovered/focused states', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredThumbColor = Color(0xFF000001);
const Color focusedThumbColor = Color(0xFF000002);
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredThumbColor;
}
if (states.contains(MaterialState.focused)) {
return focusedThumbColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> thumbColor = MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch() {
return MaterialApp(
theme: themeData,
home: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
thumbColor: thumbColor,
onChanged: (_) { },
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: colors.primary.withOpacity(0.12))
..rrect(color: focusedThumbColor),
reason: 'active enabled switch should default track and custom thumb color',
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: colors.primary.withOpacity(0.08))
..rrect(color: hoveredThumbColor),
reason: 'active enabled switch should default track and custom thumb color',
);
});
testWidgets('Material2 - Track color resolves in active/enabled states', (WidgetTester tester) async {
const Color activeEnabledTrackColor = Color(0xFF000001); const Color activeEnabledTrackColor = Color(0xFF000001);
const Color activeDisabledTrackColor = Color(0xFF000002); const Color activeDisabledTrackColor = Color(0xFF000002);
const Color inactiveEnabledTrackColor = Color(0xFF000003); const Color inactiveEnabledTrackColor = Color(0xFF000003);
...@@ -1512,38 +2152,40 @@ void main() { ...@@ -1512,38 +2152,40 @@ void main() {
); );
}); });
testWidgets('Switch track color resolves in hovered/focused states - M2', (WidgetTester tester) async { testWidgets('Material3 - Track color resolves in active/enabled states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); final ThemeData themeData = ThemeData(useMaterial3: true);
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; const Color activeEnabledTrackColor = Color(0xFF000001);
const Color hoveredTrackColor = Color(0xFF000001); const Color activeDisabledTrackColor = Color(0xFF000002);
const Color focusedTrackColor = Color(0xFF000002); const Color inactiveEnabledTrackColor = Color(0xFF000003);
const Color inactiveDisabledTrackColor = Color(0xFF000004);
Color getTrackColor(Set<MaterialState> states) { Color getTrackColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) { if (states.contains(MaterialState.disabled)) {
return hoveredTrackColor; if (states.contains(MaterialState.selected)) {
return activeDisabledTrackColor;
} }
if (states.contains(MaterialState.focused)) { return inactiveDisabledTrackColor;
return focusedTrackColor;
} }
return Colors.transparent; if (states.contains(MaterialState.selected)) {
return activeEnabledTrackColor;
}
return inactiveEnabledTrackColor;
} }
final MaterialStateProperty<Color> trackColor = final MaterialStateProperty<Color> trackColor =
MaterialStateColor.resolveWith(getTrackColor); MaterialStateColor.resolveWith(getTrackColor);
Widget buildSwitch() { Widget buildSwitch({required bool enabled, required bool active}) {
return MaterialApp( return Theme(
theme: ThemeData(useMaterial3: false), data: themeData,
home: Directionality( child: Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: Material( child: Material(
child: Center( child: Center(
child: Switch( child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
trackColor: trackColor, trackColor: trackColor,
onChanged: (_) { }, value: active,
onChanged: enabled ? (_) { } : null,
), ),
), ),
), ),
...@@ -1551,46 +2193,207 @@ void main() { ...@@ -1551,46 +2193,207 @@ void main() {
); );
} }
await tester.pumpWidget(buildSwitch()); await tester.pumpWidget(buildSwitch(enabled: false, active: false));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: inactiveDisabledTrackColor,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
),
reason: 'Inactive disabled switch track should use this value',
);
await tester.pumpWidget(buildSwitch(enabled: false, active: true));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect( expect(
Material.of(tester.element(find.byType(Switch))), Material.of(tester.element(find.byType(Switch))),
paints paints
..rrect( ..rrect(
color: focusedTrackColor, color: activeDisabledTrackColor,
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)), rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
), ),
reason: 'Inactive enabled switch should match these colors', reason: 'Active disabled switch should match these colors',
); );
// Start hovering await tester.pumpWidget(buildSwitch(enabled: true, active: false));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect( expect(
Material.of(tester.element(find.byType(Switch))), Material.of(tester.element(find.byType(Switch))),
paints paints
..rrect( ..rrect(
color: hoveredTrackColor, color: inactiveEnabledTrackColor,
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)), rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
), ),
reason: 'Inactive enabled switch should match these colors', reason: 'Inactive enabled switch should match these colors',
); );
});
testWidgets('Switch thumb color is blended against surface color - M2', (WidgetTester tester) async { await tester.pumpWidget(buildSwitch(enabled: true, active: true));
final Color activeDisabledThumbColor = Colors.blue.withOpacity(.60); await tester.pumpAndSettle();
final ThemeData theme = ThemeData.light(useMaterial3: false);
Color getThumbColor(Set<MaterialState> states) { expect(
if (states.contains(MaterialState.disabled)) { Material.of(tester.element(find.byType(Switch))),
return activeDisabledThumbColor; paints
} ..rrect(
return Colors.black; color: activeEnabledTrackColor,
} rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
),
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Material2 - Switch track color resolves in hovered/focused states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredTrackColor = Color(0xFF000001);
const Color focusedTrackColor = Color(0xFF000002);
Color getTrackColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredTrackColor;
}
if (states.contains(MaterialState.focused)) {
return focusedTrackColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> trackColor =
MaterialStateColor.resolveWith(getTrackColor);
Widget buildSwitch() {
return MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
trackColor: trackColor,
onChanged: (_) { },
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: focusedTrackColor,
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)),
),
reason: 'Inactive enabled switch should match these colors',
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: hoveredTrackColor,
rrect: RRect.fromLTRBR(13.0, 17.0, 46.0, 31.0, const Radius.circular(7.0)),
),
reason: 'Inactive enabled switch should match these colors',
);
});
testWidgets('Material3 - Switch track color resolves in hovered/focused states', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredTrackColor = Color(0xFF000001);
const Color focusedTrackColor = Color(0xFF000002);
Color getTrackColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredTrackColor;
}
if (states.contains(MaterialState.focused)) {
return focusedTrackColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> trackColor =
MaterialStateColor.resolveWith(getTrackColor);
Widget buildSwitch() {
return Theme(
data: themeData,
child: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
trackColor: trackColor,
onChanged: (_) { },
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: focusedTrackColor,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
),
reason: 'Active enabled switch should match these colors',
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: hoveredTrackColor,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
),
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Material2 - Switch thumb color is blended against surface color', (WidgetTester tester) async {
final Color activeDisabledThumbColor = Colors.blue.withOpacity(.60);
final ThemeData theme = ThemeData.light(useMaterial3: false);
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return activeDisabledThumbColor;
}
return Colors.black;
}
final MaterialStateProperty<Color> thumbColor = final MaterialStateProperty<Color> thumbColor =
MaterialStateColor.resolveWith(getThumbColor); MaterialStateColor.resolveWith(getThumbColor);
...@@ -1632,6 +2435,56 @@ void main() { ...@@ -1632,6 +2435,56 @@ void main() {
); );
}); });
testWidgets('Material3 - Switch thumb color is blended against surface color', (WidgetTester tester) async {
final Color activeDisabledThumbColor = Colors.blue.withOpacity(.60);
final ThemeData theme = ThemeData(useMaterial3: true);
final ColorScheme colors = theme.colorScheme;
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return activeDisabledThumbColor;
}
return Colors.black;
}
final MaterialStateProperty<Color> thumbColor =
MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch({required bool enabled, required bool active}) {
return Directionality(
textDirection: TextDirection.rtl,
child: Theme(
data: theme,
child: Material(
child: Center(
child: Switch(
thumbColor: thumbColor,
value: active,
onChanged: enabled ? (_) { } : null,
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch(enabled: false, active: true));
final Color expectedThumbColor = Color.alphaBlend(activeDisabledThumbColor, theme.colorScheme.surface);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: expectedThumbColor),
reason: 'Active disabled thumb color should be blended on top of surface color',
);
});
testWidgets('Switch overlay color resolves in active/pressed/focused/hovered states', (WidgetTester tester) async { testWidgets('Switch overlay color resolves in active/pressed/focused/hovered states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
...@@ -1983,7 +2836,7 @@ void main() { ...@@ -1983,7 +2836,7 @@ void main() {
}); });
}); });
group('Switch M3 tests', () { group('Switch M3 only tests', () {
testWidgets('M3 Switch has a 300-millisecond animation in total', (WidgetTester tester) async { testWidgets('M3 Switch has a 300-millisecond animation in total', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true); final ThemeData theme = ThemeData(useMaterial3: true);
bool value = false; bool value = false;
...@@ -2067,849 +2920,139 @@ void main() { ...@@ -2067,849 +2920,139 @@ void main() {
find.byType(Switch), find.byType(Switch),
matchesGoldenFile('switch_test.m3.transition.png'), matchesGoldenFile('switch_test.m3.transition.png'),
); );
}); });
testWidgets('M3 Switch thumb bounces in the end of the animation', (WidgetTester tester) async { testWidgets('M3 Switch thumb bounces in the end of the animation', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true); final ThemeData theme = ThemeData(useMaterial3: true);
bool value = false; bool value = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: theme, theme: theme,
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: StatefulBuilder( child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) { builder: (BuildContext context, StateSetter setState) {
return Material( return Material(
child: Center(
child: Switch(
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
),
),
);
expect(value, isFalse);
final Rect switchRect = tester.getRect(find.byType(Switch));
final TestGesture gesture = await tester.startGesture(switchRect.centerLeft);
await tester.pump();
await gesture.up();
await tester.pump();
// The value on y axis is greater than 1 when t > 0.375
// 300 * 0.375 = 112.5
await tester.pump(const Duration(milliseconds: 113));
final ToggleableStateMixin state = tester.state<ToggleableStateMixin>(
find.descendant(
of: find.byType(Switch),
matching: find.byWidgetPredicate(
(Widget widget) => widget.runtimeType.toString() == '_MaterialSwitch',
),
),
);
expect(tester.hasRunningAnimations, true);
expect(state.position.value, greaterThan(1));
});
testWidgets('Switch has default colors when enabled - M3', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = theme.colorScheme;
bool value = false;
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Directionality(
textDirection: TextDirection.rtl,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: Switch(
dragStartBehavior: DragStartBehavior.down,
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
),
),
);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: colors.outline), // thumb color
reason: 'Inactive enabled switch should match these colors',
);
await tester.drag(find.byType(Switch), const Offset(-30.0, 0.0));
await tester.pump();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.onPrimary), // thumb color
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Inactive Switch has default colors when disabled - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
value: false,
onChanged: null,
),
),
),
),
));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)), // thumb color
reason: 'Inactive disabled switch should match these colors',
);
});
testWidgets('Active Switch has default colors when disabled - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true,
colorSchemeSeed: const Color(0xff6750a4),
brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
value: true,
onChanged: null,
),
),
),
),
));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..save()
..rrect(
style: PaintingStyle.fill,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.surface), // thumb color
reason: 'Active disabled switch should match these colors',
);
});
testWidgets('Switch can be set color - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
bool value = false;
await tester.pumpWidget(
MaterialApp(
theme: themeData,
home: Directionality(
textDirection: TextDirection.rtl,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: Switch(
dragStartBehavior: DragStartBehavior.down,
value: value,
onChanged: (bool newValue) {
setState(() {
value = newValue;
});
},
activeColor: Colors.red[500],
activeTrackColor: Colors.green[500],
inactiveThumbColor: Colors.yellow[500],
inactiveTrackColor: Colors.blue[500],
),
),
);
},
),
),
),
);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: Colors.blue[500],
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
)
..rrect(color: Colors.yellow[500]), // thumb color
);
await tester.drag(find.byType(Switch), const Offset(-30.0, 0.0));
await tester.pump();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: Colors.green[500],
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: Colors.red[500]), // thumb color
);
});
testWidgets('Switch is focusable and has correct focus color - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
focusColor: Colors.orange[500],
autofocus: true,
focusNode: focusNode,
);
}),
),
),
);
}
await tester.pumpWidget(buildApp());
// active, enabled switch
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500]),
);
// Check the false value: inactive enabled switch
value = false;
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.outline,
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500])
);
// Check what happens when disabled: inactive disabled switch.
value = false;
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isFalse);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)),
);
});
testWidgets('Switch can be hovered and has correct hover color - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
bool value = true;
Widget buildApp({bool enabled = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
hoverColor: Colors.orange[500],
);
}),
),
),
);
}
// active enabled switch
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.onPrimary),
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: Colors.orange[500]),
);
// Check what happens for disabled active switch
await tester.pumpWidget(buildApp(enabled: false));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: colors.surface.withOpacity(1.0)),
);
});
testWidgets('Switch thumb shows correct pressed color - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
Widget buildApp({bool enabled = true, bool value = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
);
}),
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.primary, // track color
style: PaintingStyle.fill,
)..rrect(
color: Colors.transparent, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.primaryContainer, rrect: RRect.fromLTRBR(26.0, 10.0, 54.0, 38.0, const Radius.circular(14.0))),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(value: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.surfaceVariant, // track color
style: PaintingStyle.fill
)..rrect(
color: colors.outline, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.onSurfaceVariant),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(enabled: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.onSurface.withOpacity(0.12), // track color
style: PaintingStyle.fill,
)..rrect(
color: Colors.transparent, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.surface.withOpacity(1.0)),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(enabled: false, value: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.surfaceVariant.withOpacity(0.12), // track color
style: PaintingStyle.fill,
)..rrect(
color: colors.onSurface.withOpacity(0.12), // track outline color
style: PaintingStyle.stroke,
)..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)),
);
}, variant: TargetPlatformVariant.mobile());
testWidgets('Switch thumb color resolves in active/enabled states - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
const Color activeEnabledThumbColor = Color(0xFF000001);
const Color activeDisabledThumbColor = Color(0xFF000002);
const Color inactiveEnabledThumbColor = Color(0xFF000003);
const Color inactiveDisabledThumbColor = Color(0xFF000004);
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return activeDisabledThumbColor;
}
return inactiveDisabledThumbColor;
}
if (states.contains(MaterialState.selected)) {
return activeEnabledThumbColor;
}
return inactiveEnabledThumbColor;
}
final MaterialStateProperty<Color> thumbColor = MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch({required bool enabled, required bool active}) {
return Theme(
data: themeData,
child: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
thumbColor: thumbColor,
value: active,
onChanged: enabled ? (_) { } : null,
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch(enabled: false, active: false));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect(
style: PaintingStyle.stroke,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(5.0, 9.0, 55.0, 39.0, const Radius.circular(16.0)),
)
..rrect(color: inactiveDisabledThumbColor),
reason: 'Inactive disabled switch should default track and custom thumb color',
);
await tester.pumpWidget(buildSwitch(enabled: false, active: true));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: activeDisabledThumbColor),
reason: 'Active disabled switch should match these colors',
);
await tester.pumpWidget(buildSwitch(enabled: true, active: false));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.surfaceVariant,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: inactiveEnabledThumbColor),
reason: 'Inactive enabled switch should match these colors',
);
await tester.pumpWidget(buildSwitch(enabled: true, active: true));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: activeEnabledThumbColor),
reason: 'Active enabled switch should match these colors',
);
});
testWidgets('Switch thumb color resolves in hovered/focused states - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredThumbColor = Color(0xFF000001);
const Color focusedThumbColor = Color(0xFF000002);
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredThumbColor;
}
if (states.contains(MaterialState.focused)) {
return focusedThumbColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> thumbColor = MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch() {
return MaterialApp(
theme: themeData,
home: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center(
child: Switch(
focusNode: focusNode,
autofocus: true,
value: true,
thumbColor: thumbColor,
onChanged: (_) { },
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch());
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: colors.primary.withOpacity(0.12))
..rrect(color: focusedThumbColor),
reason: 'active enabled switch should default track and custom thumb color',
);
// Start hovering
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
style: PaintingStyle.fill,
color: colors.primary,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..circle(color: colors.primary.withOpacity(0.08))
..rrect(color: hoveredThumbColor),
reason: 'active enabled switch should default track and custom thumb color',
);
});
testWidgets('Track color resolves in active/enabled states - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
const Color activeEnabledTrackColor = Color(0xFF000001);
const Color activeDisabledTrackColor = Color(0xFF000002);
const Color inactiveEnabledTrackColor = Color(0xFF000003);
const Color inactiveDisabledTrackColor = Color(0xFF000004);
Color getTrackColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return activeDisabledTrackColor;
}
return inactiveDisabledTrackColor;
}
if (states.contains(MaterialState.selected)) {
return activeEnabledTrackColor;
}
return inactiveEnabledTrackColor;
}
final MaterialStateProperty<Color> trackColor =
MaterialStateColor.resolveWith(getTrackColor);
Widget buildSwitch({required bool enabled, required bool active}) {
return Theme(
data: themeData,
child: Directionality(
textDirection: TextDirection.rtl,
child: Material(
child: Center( child: Center(
child: Switch( child: Switch(
trackColor: trackColor, value: value,
value: active, onChanged: (bool newValue) {
onChanged: enabled ? (_) { } : null, setState(() {
value = newValue;
});
},
), ),
), ),
);
},
), ),
), ),
);
}
await tester.pumpWidget(buildSwitch(enabled: false, active: false));
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: inactiveDisabledTrackColor,
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
), ),
reason: 'Inactive disabled switch track should use this value',
); );
expect(value, isFalse);
await tester.pumpWidget(buildSwitch(enabled: false, active: true)); final Rect switchRect = tester.getRect(find.byType(Switch));
await tester.pumpAndSettle(); final TestGesture gesture = await tester.startGesture(switchRect.centerLeft);
await tester.pump();
expect( await gesture.up();
Material.of(tester.element(find.byType(Switch))), await tester.pump();
paints // The value on y axis is greater than 1 when t > 0.375
..rrect( // 300 * 0.375 = 112.5
color: activeDisabledTrackColor, await tester.pump(const Duration(milliseconds: 113));
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)), final ToggleableStateMixin state = tester.state<ToggleableStateMixin>(
find.descendant(
of: find.byType(Switch),
matching: find.byWidgetPredicate(
(Widget widget) => widget.runtimeType.toString() == '_MaterialSwitch',
),
), ),
reason: 'Active disabled switch should match these colors',
); );
expect(tester.hasRunningAnimations, true);
expect(state.position.value, greaterThan(1));
});
await tester.pumpWidget(buildSwitch(enabled: true, active: false)); testWidgets('Switch thumb shows correct pressed color - M3', (WidgetTester tester) async {
await tester.pumpAndSettle(); final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
expect( Widget buildApp({bool enabled = true, bool value = true}) {
Material.of(tester.element(find.byType(Switch))), return MaterialApp(
paints theme: themeData,
..rrect( home: Material(
color: inactiveEnabledTrackColor, child: Center(
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)), child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
);
}),
),
), ),
reason: 'Inactive enabled switch should match these colors',
); );
}
await tester.pumpWidget(buildSwitch(enabled: true, active: true)); await tester.pumpWidget(buildApp());
await tester.press(find.byType(Switch));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect( expect(Material.of(tester.element(find.byType(Switch))),
Material.of(tester.element(find.byType(Switch))), paints..rrect(
paints color: colors.primary, // track color
..rrect( style: PaintingStyle.fill,
color: activeEnabledTrackColor, )..rrect(
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)), color: Colors.transparent, // track outline color
), style: PaintingStyle.stroke,
reason: 'Active enabled switch should match these colors', )..rrect(color: colors.primaryContainer, rrect: RRect.fromLTRBR(26.0, 10.0, 54.0, 38.0, const Radius.circular(14.0))),
); );
});
testWidgets('Switch track color resolves in hovered/focused states - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final FocusNode focusNode = FocusNode(debugLabel: 'Switch');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoveredTrackColor = Color(0xFF000001);
const Color focusedTrackColor = Color(0xFF000002);
Color getTrackColor(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveredTrackColor;
}
if (states.contains(MaterialState.focused)) {
return focusedTrackColor;
}
return Colors.transparent;
}
final MaterialStateProperty<Color> trackColor = await tester.pumpWidget(Container());
MaterialStateColor.resolveWith(getTrackColor); await tester.pumpWidget(buildApp(value: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
Widget buildSwitch() { expect(Material.of(tester.element(find.byType(Switch))),
return Theme( paints..rrect(
data: themeData, color: colors.surfaceVariant, // track color
child: Directionality( style: PaintingStyle.fill
textDirection: TextDirection.rtl, )..rrect(
child: Material( color: colors.outline, // track outline color
child: Center( style: PaintingStyle.stroke,
child: Switch( )..rrect(color: colors.onSurfaceVariant),
focusNode: focusNode,
autofocus: true,
value: true,
trackColor: trackColor,
onChanged: (_) { },
),
),
),
),
); );
}
await tester.pumpWidget(buildSwitch()); await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(enabled: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
expect( expect(Material.of(tester.element(find.byType(Switch))),
Material.of(tester.element(find.byType(Switch))), paints..rrect(
paints color: colors.onSurface.withOpacity(0.12), // track color
..rrect( style: PaintingStyle.fill,
color: focusedTrackColor, )..rrect(
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)), color: Colors.transparent, // track outline color
), style: PaintingStyle.stroke,
reason: 'Active enabled switch should match these colors', )..rrect(color: colors.surface.withOpacity(1.0)),
); );
// Start hovering await tester.pumpWidget(Container());
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); await tester.pumpWidget(buildApp(enabled: false, value: false));
await gesture.addPointer(); await tester.press(find.byType(Switch));
await gesture.moveTo(tester.getCenter(find.byType(Switch)));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect( expect(Material.of(tester.element(find.byType(Switch))),
Material.of(tester.element(find.byType(Switch))), paints..rrect(
paints color: colors.surfaceVariant.withOpacity(0.12), // track color
..rrect( style: PaintingStyle.fill,
color: hoveredTrackColor, )..rrect(
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)), color: colors.onSurface.withOpacity(0.12), // track outline color
), style: PaintingStyle.stroke,
reason: 'Active enabled switch should match these colors', )..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)),
); );
}); }, variant: TargetPlatformVariant.mobile());
testWidgets('Track outline color resolves in active/enabled states', (WidgetTester tester) async { testWidgets('Track outline color resolves in active/enabled states', (WidgetTester tester) async {
const Color activeEnabledTrackOutlineColor = Color(0xFF000001); const Color activeEnabledTrackOutlineColor = Color(0xFF000001);
...@@ -3181,56 +3324,6 @@ void main() { ...@@ -3181,56 +3324,6 @@ void main() {
); );
}); });
testWidgets('Switch thumb color is blended against surface color - M3', (WidgetTester tester) async {
final Color activeDisabledThumbColor = Colors.blue.withOpacity(.60);
final ThemeData theme = ThemeData.light(useMaterial3: true);
final ColorScheme colors = theme.colorScheme;
Color getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return activeDisabledThumbColor;
}
return Colors.black;
}
final MaterialStateProperty<Color> thumbColor =
MaterialStateColor.resolveWith(getThumbColor);
Widget buildSwitch({required bool enabled, required bool active}) {
return Directionality(
textDirection: TextDirection.rtl,
child: Theme(
data: theme,
child: Material(
child: Center(
child: Switch(
thumbColor: thumbColor,
value: active,
onChanged: enabled ? (_) { } : null,
),
),
),
),
);
}
await tester.pumpWidget(buildSwitch(enabled: false, active: true));
final Color expectedThumbColor = Color.alphaBlend(activeDisabledThumbColor, theme.colorScheme.surface);
expect(
Material.of(tester.element(find.byType(Switch))),
paints
..rrect(
color: colors.onSurface.withOpacity(0.12),
rrect: RRect.fromLTRBR(4.0, 8.0, 56.0, 40.0, const Radius.circular(16.0)),
)
..rrect()
..rrect(color: expectedThumbColor),
reason: 'Active disabled thumb color should be blended on top of surface color',
);
});
testWidgets('Switch can set icon - M3', (WidgetTester tester) async { testWidgets('Switch can set icon - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData( final ThemeData themeData = ThemeData(
useMaterial3: true, useMaterial3: true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment