Reland Fix Date picker overlay colors aren't applied on selected state (#159839)

Reland https://github.com/flutter/flutter/pull/159203 without change.
The initial PR was reverted in
https://github.com/flutter/flutter/pull/159583.

Fixes [Date picker overlay colors aren't applied on
MaterialState.selected
state](https://github.com/flutter/flutter/issues/130586).
This commit is contained in:
Bruno Leroux 2024-12-05 17:44:06 +01:00 committed by GitHub
parent 44cc2c4303
commit c5132b52c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 365 additions and 54 deletions

View File

@ -14,6 +14,15 @@ void main() {
const Color todayForegroundColor = Colors.black;
const BorderSide todayBorder = BorderSide(width: 2);
ShapeDecoration? findDayDecoration(WidgetTester tester, String day) {
return tester.widget<Ink>(
find.ancestor(
of: find.text(day),
matching: find.byType(Ink)
),
).decoration as ShapeDecoration?;
}
await tester.pumpWidget(
const example.DatePickerApp(),
);
@ -21,21 +30,13 @@ void main() {
await tester.tap(find.text('Open Date Picker'));
await tester.pumpAndSettle();
ShapeDecoration dayShapeDecoration = tester.widget<DecoratedBox>(find.ancestor(
of: find.text('15'),
matching: find.byType(DecoratedBox),
)).decoration as ShapeDecoration;
// Test the current day shape decoration.
ShapeDecoration dayShapeDecoration = findDayDecoration(tester, '15')!;
expect(dayShapeDecoration.color, todayBackgroundColor);
expect(dayShapeDecoration.shape, dayShape.copyWith(side: todayBorder.copyWith(color: todayForegroundColor)));
dayShapeDecoration = tester.widget<DecoratedBox>(find.ancestor(
of: find.text('20'),
matching: find.byType(DecoratedBox),
)).decoration as ShapeDecoration;
// Test the selected day shape decoration.
dayShapeDecoration = findDayDecoration(tester, '20')!;
expect(dayShapeDecoration.color, theme.colorScheme.primary);
expect(dayShapeDecoration.shape, dayShape);
@ -43,12 +44,8 @@ void main() {
await tester.tap(find.text('15'));
await tester.pumpAndSettle();
dayShapeDecoration = tester.widget<DecoratedBox>(find.ancestor(
of: find.text('15'),
matching: find.byType(DecoratedBox),
)).decoration as ShapeDecoration;
// Test the selected day shape decoration.
dayShapeDecoration = findDayDecoration(tester, '15')!;
expect(dayShapeDecoration.color, todayBackgroundColor);
expect(dayShapeDecoration.shape, dayShape.copyWith(side: todayBorder.copyWith(color: todayForegroundColor)));
});

View File

@ -20,6 +20,7 @@ import 'debug.dart';
import 'divider.dart';
import 'icon_button.dart';
import 'icons.dart';
import 'ink_decoration.dart';
import 'ink_well.dart';
import 'material_localizations.dart';
import 'material_state.dart';
@ -1056,7 +1057,7 @@ class _Day extends StatefulWidget {
final bool isSelectedDay;
final bool isToday;
final ValueChanged<DateTime> onChanged;
final FocusNode? focusNode;
final FocusNode focusNode;
@override
State<_Day> createState() => _DayState();
@ -1111,7 +1112,7 @@ class _DayState extends State<_Day> {
shape: dayShape,
);
Widget dayWidget = DecoratedBox(
Widget dayWidget = Ink(
decoration: decoration,
child: Center(
child: Text(localizations.formatDecimal(widget.day.day), style: dayStyle?.apply(color: dayForegroundColor)),

View File

@ -1196,7 +1196,7 @@ void main() {
await gesture.moveTo(tester.getCenter(find.text('25')));
await tester.pumpAndSettle();
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
expect(inkFeatures, paints..circle(radius: 35.0, color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
expect(inkFeatures, paints..circle()..circle(radius: 35.0, color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
expect(inkFeatures, paintsExactlyCountTimes(#clipPath, 1));
final Rect expectedClipRect = Rect.fromCircle(center: const Offset(400.0, 241.0), radius: 35.0);

View File

@ -132,6 +132,21 @@ void main() {
await callback(date);
}
ShapeDecoration? findDayDecoration(WidgetTester tester, String day) {
return tester.widget<Ink>(
find.ancestor(
of: find.text(day),
matching: find.byType(Ink)
),
).decoration as ShapeDecoration?;
}
MaterialInkController findDayGridMaterial(WidgetTester tester) {
// All days are painted on the same Material widget.
// Use an arbitrary day to find this Material.
return Material.of(tester.element(find.text('17')));
}
group('showDatePicker Dialog', () {
testWidgets('Default dialog size', (WidgetTester tester) async {
Future<void> showPicker(WidgetTester tester, Size size) async {
@ -1264,43 +1279,170 @@ void main() {
await tester.pump();
const Color todayColor = Color(0xff2196f3); // default primary color
expect(
Material.of(tester.element(find.text('2'))),
findDayGridMaterial(tester),
// The current day should be painted with a circle outline
paints..circle(color: todayColor, style: PaintingStyle.stroke, strokeWidth: 1.0),
);
});
});
testWidgets('Date picker dayOverlayColor resolves pressed state', (WidgetTester tester) async {
today = DateTime(2023, 5, 4);
testWidgets('Date picker dayOverlayColor resolves hovered state', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
final bool material3 = theme.useMaterial3;
await prepareDatePicker(tester, (Future<DateTime?> date) async {
await tester.pump();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Hovered.
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.text('30'))),
paints..circle(color: material3 ? theme.colorScheme.onSurfaceVariant.withOpacity(0.08) : theme.colorScheme.onSurfaceVariant.withOpacity(0.08)),
);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(center);
await tester.pumpAndSettle();
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
Material.of(tester.element(find.text('30'))),
paints..circle()..circle(color: material3 ? theme.colorScheme.onSurfaceVariant.withOpacity(0.1) : theme.colorScheme.onSurfaceVariant.withOpacity(0.12))
);
await gesture.up();
await tester.pumpAndSettle();
}, theme: theme);
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)),
);
});
testWidgets('Date picker dayOverlayColor resolves focused state', (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final ThemeData theme = ThemeData();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Navigate to the grid.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
// Navigate to day 30.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.10)),
);
});
testWidgets('Date picker dayOverlayColor resolves pressed state', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.down(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle() // Hovered decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.10)),
);
await gesture.up();
});
// Regression test for https://github.com/flutter/flutter/issues/130586.
testWidgets('Date picker dayOverlayColor resolves selected and hovered state', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.08)),
);
});
testWidgets('Date picker dayOverlayColor resolves selected and focused state', (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final ThemeData theme = ThemeData();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
// Navigate to the grid.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
// Day 30 is selected and focused.
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.10)),
);
});
testWidgets('Date picker dayOverlayColor resolves selected and pressed state', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.down(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle() // Hovered decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.10)),
);
await gesture.up();
});
testWidgets('Selecting date does not switch picker to year selection', (WidgetTester tester) async {
@ -2306,6 +2448,164 @@ void main() {
});
});
});
testWidgets('Date picker dayOverlayColor resolves hovered state', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)),
);
});
testWidgets('Date picker dayOverlayColor resolves focused state', (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Navigate to the grid.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
// Navigate to day 30.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.12)),
);
});
testWidgets('Date picker dayOverlayColor resolves pressed state', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.down(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle() // Hovered decoration.
..circle(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.12)),
);
await gesture.up();
});
// Regression test for https://github.com/flutter/flutter/issues/130586.
testWidgets('Date picker dayOverlayColor resolves selected and hovered state', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.08)),
);
});
testWidgets('Date picker dayOverlayColor resolves selected and focused state', (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
// Navigate to the grid.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
// Day 30 is selected and focused.
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.12)),
);
});
testWidgets('Date picker dayOverlayColor resolves selected and pressed state', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await prepareDatePicker(tester, (Future<DateTime?> date) async {}, theme: theme);
// Select day 30.
await tester.tap(find.text('30'));
await tester.pumpAndSettle();
final ShapeDecoration day30Decoration = findDayDecoration(tester, '30')!;
expect(day30Decoration.color, theme.colorScheme.primary);
final Offset center = tester.getCenter(find.text('30'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.down(center);
await tester.pumpAndSettle();
expect(
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle() // Hovered decoration.
..circle(color: theme.colorScheme.onPrimary.withOpacity(0.38)),
);
await gesture.up();
});
});
}

View File

@ -82,10 +82,10 @@ void main() {
}
ShapeDecoration? findDayDecoration(WidgetTester tester, String day) {
return tester.widget<DecoratedBox>(
return tester.widget<Ink>(
find.ancestor(
of: find.text(day),
matching: find.byType(DecoratedBox)
matching: find.byType(Ink)
),
).decoration as ShapeDecoration?;
}
@ -756,7 +756,6 @@ void main() {
datePickerTheme: DatePickerThemeData(
dayOverlayColor: dayOverlayColor,
),
useMaterial3: true,
),
home: Directionality(
textDirection: TextDirection.ltr,
@ -776,17 +775,25 @@ void main() {
),
);
MaterialInkController findDayGridMaterial(WidgetTester tester) {
// All days are painted on the same Material widget.
// Use an arbitrary day to find this Material.
return Material.of(tester.element(find.text('17')));
}
// Test the hover overlay color.
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.text('20')));
await tester.pumpAndSettle();
expect(
inkFeatures,
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})),
);
@ -796,16 +803,20 @@ void main() {
if (kIsWeb) {
// An extra circle is painted on the web for the hovered state.
expect(
inkFeatures,
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})),
);
} else {
expect(
inkFeatures,
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})),
);
@ -822,8 +833,10 @@ void main() {
// Test the focused overlay color.
expect(
inkFeatures,
findDayGridMaterial(tester),
paints
..circle() // Today decoration.
..circle() // Selected day decoration.
..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.focused})),
);
});