From 55558367127f32efc9d87b5917013e0fbd4a7ec0 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Sat, 9 Sep 2023 08:45:27 -0700 Subject: [PATCH] Day picker should dispose created MaterialStatesController's. (#133884) Fixes https://github.com/flutter/flutter/issues/133862 --- .../src/material/calendar_date_picker.dart | 12 +++++++++- .../material/calendar_date_picker_test.dart | 24 +++++++++++++++++++ .../test/material/date_picker_test.dart | 10 ++------ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/packages/flutter/lib/src/material/calendar_date_picker.dart b/packages/flutter/lib/src/material/calendar_date_picker.dart index 2a3e842dddb..4b1f892dd88 100644 --- a/packages/flutter/lib/src/material/calendar_date_picker.dart +++ b/packages/flutter/lib/src/material/calendar_date_picker.dart @@ -868,6 +868,10 @@ class _DayPickerState extends State<_DayPicker> { /// List of [FocusNode]s, one for each day of the month. late List _dayFocusNodes; + // TODO(polina-c): a cleaner solution is to create separate statefull widget for a day. + // https://github.com/flutter/flutter/issues/134323 + final Map _statesControllers = {}; + @override void initState() { super.initState(); @@ -893,6 +897,9 @@ class _DayPickerState extends State<_DayPicker> { for (final FocusNode node in _dayFocusNodes) { node.dispose(); } + for (final MaterialStatesController controller in _statesControllers.values) { + controller.dispose(); + } super.dispose(); } @@ -973,6 +980,9 @@ class _DayPickerState extends State<_DayPicker> { if (isSelectedDay) MaterialState.selected, }; + final MaterialStatesController statesController = _statesControllers.putIfAbsent(day, () => MaterialStatesController()); + statesController.value = states; + final Color? dayForegroundColor = resolve((DatePickerThemeData? theme) => isToday ? theme?.todayForegroundColor : theme?.dayForegroundColor, states); final Color? dayBackgroundColor = resolve((DatePickerThemeData? theme) => isToday ? theme?.todayBackgroundColor : theme?.dayBackgroundColor, states); final MaterialStateProperty dayOverlayColor = MaterialStateProperty.resolveWith( @@ -1008,7 +1018,7 @@ class _DayPickerState extends State<_DayPicker> { focusNode: _dayFocusNodes[day - 1], onTap: () => widget.onChanged(dayToBuild), radius: _dayPickerRowHeight / 2 + 4, - statesController: MaterialStatesController(states), + statesController: statesController, overlayColor: dayOverlayColor, child: Semantics( // We want the day of month to be spoken first irrespective of the diff --git a/packages/flutter/test/material/calendar_date_picker_test.dart b/packages/flutter/test/material/calendar_date_picker_test.dart index c3b3c80845c..98b95e8c427 100644 --- a/packages/flutter/test/material/calendar_date_picker_test.dart +++ b/packages/flutter/test/material/calendar_date_picker_test.dart @@ -516,6 +516,30 @@ void main() { expect(find.text('2017'), findsNothing); }); + testWidgets('Selecting disabled date does not change current selection', (WidgetTester tester) async { + DateTime day(int day) => DateTime(2020, DateTime.may, day); + + DateTime selection = day(2); + await tester.pumpWidget(calendarDatePicker( + initialDate: selection, + firstDate: day(2), + lastDate: day(3), + onDateChanged: (DateTime date) { + selection = date; + }, + )); + + await tester.tap(find.text('3')); + await tester.pumpAndSettle(); + expect(selection, day(3)); + await tester.tap(find.text('4')); + await tester.pumpAndSettle(); + expect(selection, day(3)); + await tester.tap(find.text('5')); + await tester.pumpAndSettle(); + expect(selection, day(3)); + }); + for (final bool useMaterial3 in [false, true]) { testWidgets('Updates to initialDate parameter are not reflected in the state (useMaterial3=$useMaterial3)', (WidgetTester tester) async { final Key pickerKey = UniqueKey(); diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart index d58fa42ced2..66cddc20c91 100644 --- a/packages/flutter/test/material/date_picker_test.dart +++ b/packages/flutter/test/material/date_picker_test.dart @@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -173,16 +172,11 @@ void main() { }, useMaterial3: theme.useMaterial3); }); - testWidgetsWithLeakTracking('Material3 uses sentence case labels', (WidgetTester tester) async { + testWidgets('Material3 uses sentence case labels', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.text('Select date'), findsOneWidget); }, useMaterial3: true); - }, - leakTrackingTestConfig: const LeakTrackingTestConfig( - // TODO(polina-c): remove after fixing - // https://github.com/flutter/flutter/issues/133862 - allowAllNotDisposed: true, - )); + }); testWidgets('Cancel, confirm, and help text is used', (WidgetTester tester) async { cancelText = 'nope';