mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +00:00
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)
This commit is contained in:
parent
cfabdca9ca
commit
ba719bc588
|
@ -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")},
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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> {
|
||||||
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> {
|
||||||
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 {
|
||||||
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 {
|
||||||
/// 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 {
|
||||||
///
|
///
|
||||||
/// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
dayForegroundColor,
|
dayForegroundColor,
|
||||||
dayBackgroundColor,
|
dayBackgroundColor,
|
||||||
dayOverlayColor,
|
dayOverlayColor,
|
||||||
|
dayShape,
|
||||||
todayForegroundColor,
|
todayForegroundColor,
|
||||||
todayBackgroundColor,
|
todayBackgroundColor,
|
||||||
todayBorder,
|
todayBorder,
|
||||||
|
@ -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 {
|
||||||
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 {
|
||||||
: 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 {
|
||||||
: 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 @@
|
||||||
// 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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
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() {
|
||||||
'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() {
|
||||||
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');
|
||||||
|
|
Loading…
Reference in a new issue