Skip to content

Commit

Permalink
Day picker should dispose created MaterialStatesController's. (#133884)
Browse files Browse the repository at this point in the history
  • Loading branch information
polina-c authored Sep 9, 2023
1 parent 3f4ee3f commit 5555836
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
12 changes: 11 additions & 1 deletion packages/flutter/lib/src/material/calendar_date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,10 @@ class _DayPickerState extends State<_DayPicker> {
/// List of [FocusNode]s, one for each day of the month.
late List<FocusNode> _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<int, MaterialStatesController> _statesControllers = <int, MaterialStatesController>{};

@override
void initState() {
super.initState();
Expand All @@ -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();
}

Expand Down Expand Up @@ -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<Color?>((DatePickerThemeData? theme) => isToday ? theme?.todayForegroundColor : theme?.dayForegroundColor, states);
final Color? dayBackgroundColor = resolve<Color?>((DatePickerThemeData? theme) => isToday ? theme?.todayBackgroundColor : theme?.dayBackgroundColor, states);
final MaterialStateProperty<Color?> dayOverlayColor = MaterialStateProperty.resolveWith<Color?>(
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions packages/flutter/test/material/calendar_date_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 <bool>[false, true]) {
testWidgets('Updates to initialDate parameter are not reflected in the state (useMaterial3=$useMaterial3)', (WidgetTester tester) async {
final Key pickerKey = UniqueKey();
Expand Down
10 changes: 2 additions & 8 deletions packages/flutter/test/material/date_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<DateTime?> 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';
Expand Down

0 comments on commit 5555836

Please sign in to comment.