Skip to content

Commit 8268427

Browse files
huycozydkwingsmt
andauthored
Fix todayBorder todayBorder color is incorrectly overridden by todayForegroundColor in CalendarDatePicker (flutter#178792)
- Fix flutter#177911 - In this PR: - Avoid auto-overriding border side color with `dayForegroundColor` when users specify an explicit border color - Improve `todayBorder` documentation to clarify cases can happen. - Add an opacity check: this is because if we don't respect/specify an explicit border color, we still need to keep enabled/disabled states (state-aware) that `dayForegroundColor` always does: https://github.com/flutter/flutter/blob/de3f804590cea1e68065b93f4a0b55a7a451f233/packages/flutter/lib/src/material/date_picker_theme.dart#L1174 So we need a fallback compatibility here: ```dart final bool hasCustomBorderColor = datePickerTheme.todayBorder != null && datePickerTheme.todayBorder?.color.opacity != 0.0; final BorderSide todayBorderSide = hasCustomBorderColor ? datePickerTheme.todayBorder! : (datePickerTheme.todayBorder ?? defaults.todayBorder!).copyWith( color: dayForegroundColor, ); ``` - Added 3 tests in `calendar_date_picker_test.dart‎`. - Update test data in `date_picker_theme_test.dart`: Updated todayBorder from `BorderSide(width: 3)` to `BorderSide(width: 3, color: Color(0x00000000))` to maintain backward compatible behavior in tests: after changing color to transparent, it should fall back to `todayForegroundColor`. This is because the test's real purpose was to verify theme properties work, and it was expecting foreground color (which is the bug reported). <details open> <summary>Demo</summary> | Before | After | | --------------- | --------------- | <img width="400" src="https://github.com/user-attachments/assets/59c70071-d278-46f8-a566-40589cf01f9a"> | <img width="400" src="https://github.com/user-attachments/assets/a4309fb0-7360-4c11-a9c1-dd1f1ebf9190"> </details> ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Signed-off-by: huycozy <huy@nevercode.io> Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
1 parent d7afdcb commit 8268427

4 files changed

Lines changed: 87 additions & 9 deletions

File tree

packages/flutter/lib/src/material/calendar_date_picker.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,14 +1213,17 @@ class _DayState extends State<_Day> {
12131213
(DatePickerThemeData? theme) => theme?.dayShape,
12141214
states,
12151215
)!;
1216+
final bool hasCustomBorderColor =
1217+
datePickerTheme.todayBorder != null && datePickerTheme.todayBorder!.color.opacity != 0.0;
1218+
final BorderSide todayBorderSide = hasCustomBorderColor
1219+
? datePickerTheme.todayBorder!
1220+
: (datePickerTheme.todayBorder ?? defaults.todayBorder!).copyWith(
1221+
color: dayForegroundColor,
1222+
);
12161223
final decoration = widget.isToday
12171224
? ShapeDecoration(
12181225
color: dayBackgroundColor,
1219-
shape: dayShape.copyWith(
1220-
side: (datePickerTheme.todayBorder ?? defaults.todayBorder!).copyWith(
1221-
color: dayForegroundColor,
1222-
),
1223-
),
1226+
shape: dayShape.copyWith(side: todayBorderSide),
12241227
)
12251228
: ShapeDecoration(color: dayBackgroundColor, shape: dayShape);
12261229

packages/flutter/lib/src/material/date_picker_theme.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,10 @@ class DatePickerThemeData with Diagnosticable {
276276
/// [DatePickerDialog.currentDate] label in the grid of the date
277277
/// picker.
278278
///
279-
/// The border side's [BorderSide.color] is not used,
280-
/// [todayForegroundColor] is used instead.
279+
/// If the border side's [BorderSide.color] is transparent (has 0 opacity),
280+
/// [todayForegroundColor] is used instead. Otherwise, the border's color
281+
/// is used as specified. To omit the border entirely,
282+
/// set [todayBorder] to [BorderSide.none].
281283
///
282284
/// {@tool dartpad}
283285
/// This sample demonstrates how to customize the day selector shape decoration

packages/flutter/test/material/calendar_date_picker_test.dart

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,79 @@ void main() {
540540
);
541541
});
542542

543+
testWidgets('Non-null todayBorder color should be respected over foreground color', (
544+
WidgetTester tester,
545+
) async {
546+
const Color customBorderColor = Colors.red;
547+
await tester.pumpWidget(
548+
calendarDatePicker(
549+
initialDate: DateTime(2016, 1, 15),
550+
currentDate: DateTime(2016, 1, 2),
551+
theme: ThemeData(
552+
datePickerTheme: DatePickerThemeData(
553+
todayBorder: const BorderSide(color: customBorderColor),
554+
todayForegroundColor: WidgetStateProperty.all(Colors.blue),
555+
),
556+
),
557+
),
558+
);
559+
expect(
560+
Material.of(tester.element(find.text('2'))),
561+
// The current day should be painted with the custom border color.
562+
paints..circle(color: customBorderColor, style: PaintingStyle.stroke, strokeWidth: 1.0),
563+
);
564+
});
565+
566+
testWidgets('Non-null todayBorder color is used even when disabled', (
567+
WidgetTester tester,
568+
) async {
569+
const Color customBorderColor = Colors.red;
570+
await tester.pumpWidget(
571+
calendarDatePicker(
572+
firstDate: DateTime(2016, 1, 3),
573+
lastDate: DateTime(2016, 1, 31),
574+
currentDate: DateTime(2016, 1, 2), // not between first and last
575+
initialDate: DateTime(2016, 1, 5),
576+
theme: ThemeData(
577+
datePickerTheme: DatePickerThemeData(
578+
todayBorder: const BorderSide(color: customBorderColor),
579+
todayForegroundColor: WidgetStateProperty.all(Colors.blue),
580+
),
581+
),
582+
),
583+
);
584+
expect(
585+
Material.of(tester.element(find.text('2'))),
586+
// The current day should be painted with the custom border color,
587+
// not with foreground color opacity applied, even it's disabled day.
588+
paints..circle(color: customBorderColor, style: PaintingStyle.stroke, strokeWidth: 1.0),
589+
);
590+
});
591+
592+
testWidgets('Transparent todayBorder should fall back to foreground color', (
593+
WidgetTester tester,
594+
) async {
595+
const Color customForegroundColor = Colors.green;
596+
await tester.pumpWidget(
597+
calendarDatePicker(
598+
initialDate: DateTime(2016, 1, 15),
599+
currentDate: DateTime(2016, 1, 2),
600+
theme: ThemeData(
601+
datePickerTheme: DatePickerThemeData(
602+
todayBorder: const BorderSide(color: Color(0x00000000)),
603+
todayForegroundColor: WidgetStateProperty.all(customForegroundColor),
604+
),
605+
),
606+
),
607+
);
608+
expect(
609+
Material.of(tester.element(find.text('2'))),
610+
// The current day should use the foreground color since
611+
// todayBorder color is transparent.
612+
paints..circle(color: customForegroundColor, style: PaintingStyle.stroke, strokeWidth: 1.0),
613+
);
614+
});
615+
543616
testWidgets('Selecting date does not switch picker to year selection', (
544617
WidgetTester tester,
545618
) async {

packages/flutter/test/material/date_picker_theme_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void main() {
2727
dayShape: MaterialStatePropertyAll<OutlinedBorder>(RoundedRectangleBorder()),
2828
todayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff8)),
2929
todayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff9)),
30-
todayBorder: BorderSide(width: 3),
30+
todayBorder: BorderSide(width: 3, color: Color(0x00000000)),
3131
yearStyle: TextStyle(fontSize: 13),
3232
yearForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffa)),
3333
yearBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffb)),
@@ -509,7 +509,7 @@ void main() {
509509
'dayShape: WidgetStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
510510
'todayForegroundColor: WidgetStatePropertyAll(${const Color(0xfffffff8)})',
511511
'todayBackgroundColor: WidgetStatePropertyAll(${const Color(0xfffffff9)})',
512-
'todayBorder: BorderSide(width: 3.0)',
512+
'todayBorder: BorderSide(color: Color(alpha: 0.0000, red: 0.0000, green: 0.0000, blue: 0.0000, colorSpace: ColorSpace.sRGB), width: 3.0)',
513513
'yearStyle: TextStyle(inherit: true, size: 13.0)',
514514
'yearForegroundColor: WidgetStatePropertyAll(${const Color(0xfffffffa)})',
515515
'yearBackgroundColor: WidgetStatePropertyAll(${const Color(0xfffffffb)})',

0 commit comments

Comments
 (0)