Unverified Commit ba719bc5 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `CalendarDatePicker` day selection shape and overlay (#144317)

fixes [`DatePickerDialog` date entry hover background and ink splash have different radius](https://github.com/flutter/flutter/issues/141350)
fixes [Ability to customize DatePicker day selection background and overlay shape](https://github.com/flutter/flutter/issues/144220)

### Code sample

<details>
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Builder(builder: (context) {
            return FilledButton(
              onPressed: () {
                showDatePicker(
                  context: context,
                  initialDate: DateTime.now(),
                  firstDate: DateTime.utc(2010),
                  lastDate: DateTime.utc(2030),
                );
              },
              child: const Text('Show Date picker'),
            );
          }),
        ),
      ),
    );
  }
}
```

</details>

### Material DatePicker states specs

![overlay_specs](https://github.com/flutter/flutter/assets/48603081/45ce16cf-7ee9-41e1-a4fa-327de07b78d1)

### Day selection overlay

| Before | After |
| --------------- | --------------- |
| <img src="https://github.com/flutter/flutter/assets/48603081/b529d38d-0232-494b-8bf2-55d28420a245" /> | <img src="https://github.com/flutter/flutter/assets/48603081/c4799559-a7ef-45fd-aed9-aeb386370580"  /> |

### Hover, pressed, highlight preview

| Before | After |
| --------------- | --------------- |
| <video src="https://github.com/flutter/flutter/assets/48603081/8edde82a-7f39-4482-afab-183e1bce5991" /> | <video src="https://github.com/flutter/flutter/assets/48603081/04e1502e-67a4-4b33-973d-463067d70151" /> |

### Using `DatePickerThemeData.dayShape` to customize day selection background and overlay shape

| Before | After |
| --------------- | --------------- |
| <img src="https://github.com/flutter/flutter/assets/48603081/a0c85f58-a69b-4e14-a45d-41e580ceedce"  />  | <img src="https://github.com/flutter/flutter/assets/48603081/db67cee1-d28d-4168-98b8-fd7a9cb70cda" /> | 

### Example preview

![Screenshot 2024-02-29 at 15 07 50](https://github.com/flutter/flutter/assets/48603081/3770ed5c-28bf-4d0a-9514-87e1cd2ce515)
parent cfabdca9
...@@ -44,6 +44,9 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData { ...@@ -44,6 +44,9 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
: super( : super(
elevation: ${elevation("md.comp.date-picker.modal.container")}, elevation: ${elevation("md.comp.date-picker.modal.container")},
shape: ${shape("md.comp.date-picker.modal.container")}, shape: ${shape("md.comp.date-picker.modal.container")},
// TODO(tahatesser): Update this to use token when gen_defaults
// supports `CircleBorder` for fully rounded corners.
dayShape: const MaterialStatePropertyAll<OutlinedBorder>(CircleBorder()),
rangePickerElevation: ${elevation("md.comp.date-picker.modal.range-selection.container")}, rangePickerElevation: ${elevation("md.comp.date-picker.modal.range-selection.container")},
rangePickerShape: ${shape("md.comp.date-picker.modal.range-selection.container")}, rangePickerShape: ${shape("md.comp.date-picker.modal.range-selection.container")},
); );
......
// 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/material.dart';
/// Flutter code sample for [DatePickerThemeData].
void main() => runApp(const DatePickerApp());
class DatePickerApp extends StatelessWidget {
const DatePickerApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
datePickerTheme: DatePickerThemeData(
todayBackgroundColor: const MaterialStatePropertyAll<Color>(Colors.amber),
todayForegroundColor: const MaterialStatePropertyAll<Color>(Colors.black),
todayBorder: const BorderSide(width: 2),
dayShape: MaterialStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
),
),
home: const DatePickerExample(),
);
}
}
class DatePickerExample extends StatefulWidget {
const DatePickerExample({super.key});
@override
State<DatePickerExample> createState() => _DatePickerExampleState();
}
class _DatePickerExampleState extends State<DatePickerExample> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlinedButton(
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime(2021, 1, 20),
currentDate: DateTime(2021, 1, 15),
firstDate: DateTime(2021),
lastDate: DateTime(2022),
);
},
child: const Text('Open Date Picker'),
),
),
);
}
}
// 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/material.dart';
import 'package:flutter_api_samples/material/date_picker/date_picker_theme_day_shape.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('DatePickerThemeData.dayShape updates day selection shape decoration', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
final OutlinedBorder dayShape = RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0));
const Color todayBackgroundColor = Colors.amber;
const Color todayForegroundColor = Colors.black;
const BorderSide todayBorder = BorderSide(width: 2);
await tester.pumpWidget(
const example.DatePickerApp(),
);
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.
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.
expect(dayShapeDecoration.color, theme.colorScheme.primary);
expect(dayShapeDecoration.shape, dayShape);
// Tap to select current day as the selected day.
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.
expect(dayShapeDecoration.color, todayBackgroundColor);
expect(dayShapeDecoration.shape, dayShape.copyWith(side: todayBorder.copyWith(color: todayForegroundColor)));
});
}
...@@ -1057,21 +1057,21 @@ class _DayState extends State<_Day> { ...@@ -1057,21 +1057,21 @@ class _DayState extends State<_Day> {
final MaterialStateProperty<Color?> dayOverlayColor = MaterialStateProperty.resolveWith<Color?>( final MaterialStateProperty<Color?> dayOverlayColor = MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) => effectiveValue((DatePickerThemeData? theme) => theme?.dayOverlayColor?.resolve(states)), (Set<MaterialState> states) => effectiveValue((DatePickerThemeData? theme) => theme?.dayOverlayColor?.resolve(states)),
); );
final BoxDecoration decoration = widget.isToday final OutlinedBorder dayShape = resolve<OutlinedBorder?>((DatePickerThemeData? theme) => theme?.dayShape, states)!;
? BoxDecoration( final ShapeDecoration decoration = widget.isToday
? ShapeDecoration(
color: dayBackgroundColor, color: dayBackgroundColor,
border: Border.fromBorderSide( shape: dayShape.copyWith(
(datePickerTheme.todayBorder ?? defaults.todayBorder!) side: (datePickerTheme.todayBorder ?? defaults.todayBorder!)
.copyWith(color: dayForegroundColor) .copyWith(color: dayForegroundColor),
), ),
shape: BoxShape.circle,
) )
: BoxDecoration( : ShapeDecoration(
color: dayBackgroundColor, color: dayBackgroundColor,
shape: BoxShape.circle, shape: dayShape,
); );
Widget dayWidget = Container( Widget dayWidget = DecoratedBox(
decoration: decoration, decoration: decoration,
child: Center( child: Center(
child: Text(localizations.formatDecimal(widget.day.day), style: dayStyle?.apply(color: dayForegroundColor)), child: Text(localizations.formatDecimal(widget.day.day), style: dayStyle?.apply(color: dayForegroundColor)),
...@@ -1086,9 +1086,10 @@ class _DayState extends State<_Day> { ...@@ -1086,9 +1086,10 @@ class _DayState extends State<_Day> {
dayWidget = InkResponse( dayWidget = InkResponse(
focusNode: widget.focusNode, focusNode: widget.focusNode,
onTap: () => widget.onChanged(widget.day), onTap: () => widget.onChanged(widget.day),
radius: _dayPickerRowHeight / 2 + 4,
statesController: _statesController, statesController: _statesController,
overlayColor: dayOverlayColor, overlayColor: dayOverlayColor,
customBorder: dayShape,
containedInkWell: true,
child: Semantics( child: Semantics(
// We want the day of month to be spoken first irrespective of the // We want the day of month to be spoken first irrespective of the
// locale-specific preferences or TextDirection. This is because // locale-specific preferences or TextDirection. This is because
......
...@@ -52,6 +52,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -52,6 +52,7 @@ class DatePickerThemeData with Diagnosticable {
this.dayForegroundColor, this.dayForegroundColor,
this.dayBackgroundColor, this.dayBackgroundColor,
this.dayOverlayColor, this.dayOverlayColor,
this.dayShape,
this.todayForegroundColor, this.todayForegroundColor,
this.todayBackgroundColor, this.todayBackgroundColor,
this.todayBorder, this.todayBorder,
...@@ -163,12 +164,41 @@ class DatePickerThemeData with Diagnosticable { ...@@ -163,12 +164,41 @@ class DatePickerThemeData with Diagnosticable {
/// indicate that a day in the grid is focused, hovered, or pressed. /// indicate that a day in the grid is focused, hovered, or pressed.
final MaterialStateProperty<Color?>? dayOverlayColor; final MaterialStateProperty<Color?>? dayOverlayColor;
/// Overrides the default shape used to paint the shape decoration of the
/// day labels in the grid of the date picker.
///
/// If the selected day is the current day, the provided shape with the
/// value of [todayBackgroundColor] is used to paint the shape decoration of
/// the day label and the value of [todayBorder] and [todayForegroundColor] is
/// used to paint the border.
///
/// If the selected day is not the current day, the provided shape with the
/// value of [dayBackgroundColor] is used to paint the shape decoration of
/// the day label.
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the day selector shape decoration
/// using the [dayShape], [todayForegroundColor], [todayBackgroundColor], and
/// [todayBorder] properties.
///
/// ** See code in examples/api/lib/material/date_picker/date_picker_theme_day_shape.0.dart **
/// {@end-tool}
final MaterialStateProperty<OutlinedBorder?>? dayShape;
/// Overrides the default color used to paint the /// Overrides the default color used to paint the
/// [DatePickerDialog.currentDate] label in the grid of the dialog's /// [DatePickerDialog.currentDate] label in the grid of the dialog's
/// [CalendarDatePicker] and the corresponding year in the dialog's /// [CalendarDatePicker] and the corresponding year in the dialog's
/// [YearPicker]. /// [YearPicker].
/// ///
/// This will be used instead of the [TextStyle.color] provided in [dayStyle]. /// This will be used instead of the [TextStyle.color] provided in [dayStyle].
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the day selector shape decoration
/// using the [dayShape], [todayForegroundColor], [todayBackgroundColor], and
/// [todayBorder] properties.
///
/// ** See code in examples/api/lib/material/date_picker/date_picker_theme_day_shape.0.dart **
/// {@end-tool}
final MaterialStateProperty<Color?>? todayForegroundColor; final MaterialStateProperty<Color?>? todayForegroundColor;
/// Overrides the default color used to paint the background of the /// Overrides the default color used to paint the background of the
...@@ -181,6 +211,14 @@ class DatePickerThemeData with Diagnosticable { ...@@ -181,6 +211,14 @@ class DatePickerThemeData with Diagnosticable {
/// ///
/// The border side's [BorderSide.color] is not used, /// The border side's [BorderSide.color] is not used,
/// [todayForegroundColor] is used instead. /// [todayForegroundColor] is used instead.
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the day selector shape decoration
/// using the [dayShape], [todayForegroundColor], [todayBackgroundColor], and
/// [todayBorder] properties.
///
/// ** See code in examples/api/lib/material/date_picker/date_picker_theme_day_shape.0.dart **
/// {@end-tool}
final BorderSide? todayBorder; final BorderSide? todayBorder;
/// Overrides the default text style used to paint each of the year /// Overrides the default text style used to paint each of the year
...@@ -326,6 +364,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -326,6 +364,7 @@ class DatePickerThemeData with Diagnosticable {
MaterialStateProperty<Color?>? dayForegroundColor, MaterialStateProperty<Color?>? dayForegroundColor,
MaterialStateProperty<Color?>? dayBackgroundColor, MaterialStateProperty<Color?>? dayBackgroundColor,
MaterialStateProperty<Color?>? dayOverlayColor, MaterialStateProperty<Color?>? dayOverlayColor,
MaterialStateProperty<OutlinedBorder?>? dayShape,
MaterialStateProperty<Color?>? todayForegroundColor, MaterialStateProperty<Color?>? todayForegroundColor,
MaterialStateProperty<Color?>? todayBackgroundColor, MaterialStateProperty<Color?>? todayBackgroundColor,
BorderSide? todayBorder, BorderSide? todayBorder,
...@@ -364,6 +403,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -364,6 +403,7 @@ class DatePickerThemeData with Diagnosticable {
dayForegroundColor: dayForegroundColor ?? this.dayForegroundColor, dayForegroundColor: dayForegroundColor ?? this.dayForegroundColor,
dayBackgroundColor: dayBackgroundColor ?? this.dayBackgroundColor, dayBackgroundColor: dayBackgroundColor ?? this.dayBackgroundColor,
dayOverlayColor: dayOverlayColor ?? this.dayOverlayColor, dayOverlayColor: dayOverlayColor ?? this.dayOverlayColor,
dayShape: dayShape ?? this.dayShape,
todayForegroundColor: todayForegroundColor ?? this.todayForegroundColor, todayForegroundColor: todayForegroundColor ?? this.todayForegroundColor,
todayBackgroundColor: todayBackgroundColor ?? this.todayBackgroundColor, todayBackgroundColor: todayBackgroundColor ?? this.todayBackgroundColor,
todayBorder: todayBorder ?? this.todayBorder, todayBorder: todayBorder ?? this.todayBorder,
...@@ -409,6 +449,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -409,6 +449,7 @@ class DatePickerThemeData with Diagnosticable {
dayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.dayForegroundColor, b?.dayForegroundColor, t, Color.lerp), dayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.dayForegroundColor, b?.dayForegroundColor, t, Color.lerp),
dayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.dayBackgroundColor, b?.dayBackgroundColor, t, Color.lerp), dayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.dayBackgroundColor, b?.dayBackgroundColor, t, Color.lerp),
dayOverlayColor: MaterialStateProperty.lerp<Color?>(a?.dayOverlayColor, b?.dayOverlayColor, t, Color.lerp), dayOverlayColor: MaterialStateProperty.lerp<Color?>(a?.dayOverlayColor, b?.dayOverlayColor, t, Color.lerp),
dayShape: MaterialStateProperty.lerp<OutlinedBorder?>(a?.dayShape, b?.dayShape, t, OutlinedBorder.lerp),
todayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.todayForegroundColor, b?.todayForegroundColor, t, Color.lerp), todayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.todayForegroundColor, b?.todayForegroundColor, t, Color.lerp),
todayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.todayBackgroundColor, b?.todayBackgroundColor, t, Color.lerp), todayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.todayBackgroundColor, b?.todayBackgroundColor, t, Color.lerp),
todayBorder: _lerpBorderSide(a?.todayBorder, b?.todayBorder, t), todayBorder: _lerpBorderSide(a?.todayBorder, b?.todayBorder, t),
...@@ -460,6 +501,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -460,6 +501,7 @@ class DatePickerThemeData with Diagnosticable {
dayForegroundColor, dayForegroundColor,
dayBackgroundColor, dayBackgroundColor,
dayOverlayColor, dayOverlayColor,
dayShape,
todayForegroundColor, todayForegroundColor,
todayBackgroundColor, todayBackgroundColor,
todayBorder, todayBorder,
...@@ -504,6 +546,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -504,6 +546,7 @@ class DatePickerThemeData with Diagnosticable {
&& other.dayForegroundColor == dayForegroundColor && other.dayForegroundColor == dayForegroundColor
&& other.dayBackgroundColor == dayBackgroundColor && other.dayBackgroundColor == dayBackgroundColor
&& other.dayOverlayColor == dayOverlayColor && other.dayOverlayColor == dayOverlayColor
&& other.dayShape == dayShape
&& other.todayForegroundColor == todayForegroundColor && other.todayForegroundColor == todayForegroundColor
&& other.todayBackgroundColor == todayBackgroundColor && other.todayBackgroundColor == todayBackgroundColor
&& other.todayBorder == todayBorder && other.todayBorder == todayBorder
...@@ -545,6 +588,7 @@ class DatePickerThemeData with Diagnosticable { ...@@ -545,6 +588,7 @@ class DatePickerThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayForegroundColor', dayForegroundColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayForegroundColor', dayForegroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayBackgroundColor', dayBackgroundColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayBackgroundColor', dayBackgroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayOverlayColor', dayOverlayColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayOverlayColor', dayOverlayColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('dayShape', dayShape, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayForegroundColor', todayForegroundColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayForegroundColor', todayForegroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayBackgroundColor', todayBackgroundColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayBackgroundColor', todayBackgroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<BorderSide?>('todayBorder', todayBorder, defaultValue: null)); properties.add(DiagnosticsProperty<BorderSide?>('todayBorder', todayBorder, defaultValue: null));
...@@ -672,6 +716,7 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData { ...@@ -672,6 +716,7 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData {
: super( : super(
elevation: 24.0, elevation: 24.0,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))), shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))),
dayShape: const MaterialStatePropertyAll<OutlinedBorder>(CircleBorder()),
rangePickerElevation: 0.0, rangePickerElevation: 0.0,
rangePickerShape: const RoundedRectangleBorder(), rangePickerShape: const RoundedRectangleBorder(),
); );
...@@ -843,6 +888,9 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData { ...@@ -843,6 +888,9 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
: super( : super(
elevation: 6.0, elevation: 6.0,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0))), shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0))),
// TODO(tahatesser): Update this to use token when gen_defaults
// supports `CircleBorder` for fully rounded corners.
dayShape: const MaterialStatePropertyAll<OutlinedBorder>(CircleBorder()),
rangePickerElevation: 0.0, rangePickerElevation: 0.0,
rangePickerShape: const RoundedRectangleBorder(), rangePickerShape: const RoundedRectangleBorder(),
); );
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -25,10 +26,11 @@ void main() { ...@@ -25,10 +26,11 @@ void main() {
DatePickerMode initialCalendarMode = DatePickerMode.day, DatePickerMode initialCalendarMode = DatePickerMode.day,
SelectableDayPredicate? selectableDayPredicate, SelectableDayPredicate? selectableDayPredicate,
TextDirection textDirection = TextDirection.ltr, TextDirection textDirection = TextDirection.ltr,
ThemeData? theme,
bool? useMaterial3, bool? useMaterial3,
}) { }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(useMaterial3: useMaterial3), theme: theme ?? ThemeData(useMaterial3: useMaterial3),
home: Material( home: Material(
child: Directionality( child: Directionality(
textDirection: textDirection, textDirection: textDirection,
...@@ -1132,6 +1134,43 @@ void main() { ...@@ -1132,6 +1134,43 @@ void main() {
semantics.dispose(); semantics.dispose();
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
}); });
// This is a regression test for https://github.com/flutter/flutter/issues/141350.
testWidgets('Default day selection overlay', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await tester.pumpWidget(calendarDatePicker(
firstDate: DateTime(2016, DateTime.december, 15),
initialDate: DateTime(2017, DateTime.january, 15),
lastDate: DateTime(2017, DateTime.february, 15),
onDisplayedMonthChanged: (DateTime date) {},
theme: theme,
));
RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
expect(inkFeatures, isNot(paints..circle(radius: 35.0, color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08))));
expect(inkFeatures, paintsExactlyCountTimes(#clipPath, 0));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
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, paintsExactlyCountTimes(#clipPath, 1));
final Rect expectedClipRect = Rect.fromCircle(center: const Offset(400.0, 241.0), radius: 35.0);
final Path expectedClipPath = Path()..addRect(expectedClipRect);
expect(
inkFeatures,
paints..clipPath(pathMatcher: coversSameAreaAs(
expectedClipPath,
areaToCompare: expectedClipRect,
sampleSize: 100,
)),
);
});
}); });
group('YearPicker', () { group('YearPicker', () {
......
...@@ -24,6 +24,7 @@ void main() { ...@@ -24,6 +24,7 @@ void main() {
dayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff5)), dayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff5)),
dayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)), dayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)),
dayOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff7)), dayOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff7)),
dayShape: MaterialStatePropertyAll<OutlinedBorder>(RoundedRectangleBorder()),
todayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff8)), todayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff8)),
todayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff9)), todayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff9)),
todayBorder: BorderSide(width: 3), todayBorder: BorderSide(width: 3),
...@@ -79,6 +80,15 @@ void main() { ...@@ -79,6 +80,15 @@ void main() {
return container.decoration as BoxDecoration?; return container.decoration as BoxDecoration?;
} }
ShapeDecoration? findDayDecoration(WidgetTester tester, String day) {
return tester.widget<DecoratedBox>(
find.ancestor(
of: find.text(day),
matching: find.byType(DecoratedBox)
),
).decoration as ShapeDecoration?;
}
ButtonStyle actionButtonStyle(WidgetTester tester, String text) { ButtonStyle actionButtonStyle(WidgetTester tester, String text) {
return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!; return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!;
} }
...@@ -112,6 +122,7 @@ void main() { ...@@ -112,6 +122,7 @@ void main() {
expect(theme.dayForegroundColor, null); expect(theme.dayForegroundColor, null);
expect(theme.dayBackgroundColor, null); expect(theme.dayBackgroundColor, null);
expect(theme.dayOverlayColor, null); expect(theme.dayOverlayColor, null);
expect(theme.dayShape, null);
expect(theme.todayForegroundColor, null); expect(theme.todayForegroundColor, null);
expect(theme.todayBackgroundColor, null); expect(theme.todayBackgroundColor, null);
expect(theme.todayBorder, null); expect(theme.todayBorder, null);
...@@ -183,6 +194,7 @@ void main() { ...@@ -183,6 +194,7 @@ void main() {
expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.1)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.1));
expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.08));
expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.1)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.1));
expect(m3.dayShape?.resolve(<MaterialState>{}), const CircleBorder());
expect(m3.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary); expect(m3.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary);
expect(m3.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.primary.withOpacity(0.38)); expect(m3.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.primary.withOpacity(0.38));
expect(m3.todayBorder, BorderSide(color: colorScheme.primary)); expect(m3.todayBorder, BorderSide(color: colorScheme.primary));
...@@ -256,6 +268,7 @@ void main() { ...@@ -256,6 +268,7 @@ void main() {
expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08));
expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12));
expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12));
expect(m2.dayShape?.resolve(<MaterialState>{}), const CircleBorder());
expect(m2.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary); expect(m2.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary);
expect(m2.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurface.withOpacity(0.38)); expect(m2.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurface.withOpacity(0.38));
expect(m2.todayBorder, BorderSide(color: colorScheme.primary)); expect(m2.todayBorder, BorderSide(color: colorScheme.primary));
...@@ -319,6 +332,7 @@ void main() { ...@@ -319,6 +332,7 @@ void main() {
'dayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff5))', 'dayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff5))',
'dayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff6))', 'dayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff6))',
'dayOverlayColor: MaterialStatePropertyAll(Color(0xfffffff7))', 'dayOverlayColor: MaterialStatePropertyAll(Color(0xfffffff7))',
'dayShape: MaterialStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
'todayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff8))', 'todayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff8))',
'todayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff9))', 'todayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff9))',
'todayBorder: BorderSide(width: 3.0)', 'todayBorder: BorderSide(width: 3.0)',
...@@ -389,18 +403,24 @@ void main() { ...@@ -389,18 +403,24 @@ void main() {
expect(selectedDate.style?.fontSize, datePickerTheme.headerHeadlineStyle?.fontSize); expect(selectedDate.style?.fontSize, datePickerTheme.headerHeadlineStyle?.fontSize);
final Text day31 = tester.widget<Text>(find.text('31')); final Text day31 = tester.widget<Text>(find.text('31'));
final BoxDecoration day31Decoration = findTextDecoration(tester, '31')!; final ShapeDecoration day31Decoration = findDayDecoration(tester, '31')!;
expect(day31.style?.color, datePickerTheme.dayForegroundColor?.resolve(<MaterialState>{})); expect(day31.style?.color, datePickerTheme.dayForegroundColor?.resolve(<MaterialState>{}));
expect(day31.style?.fontSize, datePickerTheme.dayStyle?.fontSize); expect(day31.style?.fontSize, datePickerTheme.dayStyle?.fontSize);
expect(day31Decoration.color, datePickerTheme.dayBackgroundColor?.resolve(<MaterialState>{})); expect(day31Decoration.color, datePickerTheme.dayBackgroundColor?.resolve(<MaterialState>{}));
expect(day31Decoration.shape, datePickerTheme.dayShape?.resolve(<MaterialState>{}));
final Text day24 = tester.widget<Text>(find.text('24')); // DatePickerDialog.currentDate final Text day24 = tester.widget<Text>(find.text('24')); // DatePickerDialog.currentDate
final BoxDecoration day24Decoration = findTextDecoration(tester, '24')!; final ShapeDecoration day24Decoration = findDayDecoration(tester, '24')!;
final OutlinedBorder day24Shape = day24Decoration.shape as OutlinedBorder;
expect(day24.style?.fontSize, datePickerTheme.dayStyle?.fontSize); expect(day24.style?.fontSize, datePickerTheme.dayStyle?.fontSize);
expect(day24.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{})); expect(day24.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}));
expect(day24Decoration.color, datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{})); expect(day24Decoration.color, datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{}));
expect(day24Decoration.border?.top.width, datePickerTheme.todayBorder?.width); expect(
expect(day24Decoration.border?.bottom.width, datePickerTheme.todayBorder?.width); day24Decoration.shape,
datePickerTheme.dayShape?.resolve(<MaterialState>{})!
.copyWith(side: datePickerTheme.todayBorder?.copyWith(color: datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}))),
);
expect(day24Shape.side.width, datePickerTheme.todayBorder?.width);
// Test the day overlay color. // Test the day overlay color.
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
......
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