Unverified Commit 9525934e authored by Tirth's avatar Tirth Committed by GitHub

[date_picker] [date_range_picker] add properties to change switch-to icons (#124881)

parent 482d1aaf
...@@ -71,6 +71,22 @@ const double _inputFormLandscapeHeight = 108.0; ...@@ -71,6 +71,22 @@ const double _inputFormLandscapeHeight = 108.0;
/// or [DatePickerEntryMode.input] (a text input field) mode. /// or [DatePickerEntryMode.input] (a text input field) mode.
/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. /// It defaults to [DatePickerEntryMode.calendar] and must be non-null.
/// ///
/// {@template flutter.material.date_picker.switchToInputEntryModeIcon}
/// An optional [switchToInputEntryModeIcon] argument can be used to
/// display a custom Icon in the corner of the dialog
/// when [DatePickerEntryMode] is [DatePickerEntryMode.calendar]. Clicking on
/// icon changes the [DatePickerEntryMode] to [DatePickerEntryMode.input].
/// If null, `Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit)` is used.
/// {@endtemplate}
///
/// {@template flutter.material.date_picker.switchToCalendarEntryModeIcon}
/// An optional [switchToCalendarEntryModeIcon] argument can be used to
/// display a custom Icon in the corner of the dialog
/// when [DatePickerEntryMode] is [DatePickerEntryMode.input]. Clicking on
/// icon changes the [DatePickerEntryMode] to [DatePickerEntryMode.calendar].
/// If null, `Icon(Icons.calendar_today)` is used.
/// {@endtemplate}
///
/// An optional [selectableDayPredicate] function can be passed in to only allow /// An optional [selectableDayPredicate] function can be passed in to only allow
/// certain days for selection. If provided, only the days that /// certain days for selection. If provided, only the days that
/// [selectableDayPredicate] returns true for will be selectable. For example, /// [selectableDayPredicate] returns true for will be selectable. For example,
...@@ -164,7 +180,9 @@ Future<DateTime?> showDatePicker({ ...@@ -164,7 +180,9 @@ Future<DateTime?> showDatePicker({
String? fieldLabelText, String? fieldLabelText,
TextInputType? keyboardType, TextInputType? keyboardType,
Offset? anchorPoint, Offset? anchorPoint,
final ValueChanged<DatePickerEntryMode>? onDatePickerModeChange final ValueChanged<DatePickerEntryMode>? onDatePickerModeChange,
final Icon? switchToInputEntryModeIcon,
final Icon? switchToCalendarEntryModeIcon,
}) async { }) async {
initialDate = DateUtils.dateOnly(initialDate); initialDate = DateUtils.dateOnly(initialDate);
firstDate = DateUtils.dateOnly(firstDate); firstDate = DateUtils.dateOnly(firstDate);
...@@ -204,6 +222,8 @@ Future<DateTime?> showDatePicker({ ...@@ -204,6 +222,8 @@ Future<DateTime?> showDatePicker({
fieldLabelText: fieldLabelText, fieldLabelText: fieldLabelText,
keyboardType: keyboardType, keyboardType: keyboardType,
onDatePickerModeChange: onDatePickerModeChange, onDatePickerModeChange: onDatePickerModeChange,
switchToInputEntryModeIcon: switchToInputEntryModeIcon,
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon,
); );
if (textDirection != null) { if (textDirection != null) {
...@@ -261,7 +281,9 @@ class DatePickerDialog extends StatefulWidget { ...@@ -261,7 +281,9 @@ class DatePickerDialog extends StatefulWidget {
this.fieldLabelText, this.fieldLabelText,
this.keyboardType, this.keyboardType,
this.restorationId, this.restorationId,
this.onDatePickerModeChange this.onDatePickerModeChange,
this.switchToInputEntryModeIcon,
this.switchToCalendarEntryModeIcon,
}) : initialDate = DateUtils.dateOnly(initialDate), }) : initialDate = DateUtils.dateOnly(initialDate),
firstDate = DateUtils.dateOnly(firstDate), firstDate = DateUtils.dateOnly(firstDate),
lastDate = DateUtils.dateOnly(lastDate), lastDate = DateUtils.dateOnly(lastDate),
...@@ -370,6 +392,12 @@ class DatePickerDialog extends StatefulWidget { ...@@ -370,6 +392,12 @@ class DatePickerDialog extends StatefulWidget {
/// `initialEntryMode` parameter the next time the date picker is shown. /// `initialEntryMode` parameter the next time the date picker is shown.
final ValueChanged<DatePickerEntryMode>? onDatePickerModeChange; final ValueChanged<DatePickerEntryMode>? onDatePickerModeChange;
/// {@macro flutter.material.date_picker.switchToInputEntryModeIcon}
final Icon? switchToInputEntryModeIcon;
/// {@macro flutter.material.date_picker.switchToCalendarEntryModeIcon}
final Icon? switchToCalendarEntryModeIcon;
@override @override
State<DatePickerDialog> createState() => _DatePickerDialogState(); State<DatePickerDialog> createState() => _DatePickerDialogState();
} }
...@@ -576,7 +604,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix ...@@ -576,7 +604,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
case DatePickerEntryMode.calendar: case DatePickerEntryMode.calendar:
picker = calendarDatePicker(); picker = calendarDatePicker();
entryModeButton = IconButton( entryModeButton = IconButton(
icon: Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit), icon: widget.switchToInputEntryModeIcon ?? Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit),
color: headerForegroundColor, color: headerForegroundColor,
tooltip: localizations.inputDateModeButtonLabel, tooltip: localizations.inputDateModeButtonLabel,
onPressed: _handleEntryModeToggle, onPressed: _handleEntryModeToggle,
...@@ -589,7 +617,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix ...@@ -589,7 +617,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
case DatePickerEntryMode.input: case DatePickerEntryMode.input:
picker = inputDatePicker(); picker = inputDatePicker();
entryModeButton = IconButton( entryModeButton = IconButton(
icon: const Icon(Icons.calendar_today), icon: widget.switchToCalendarEntryModeIcon ?? const Icon(Icons.calendar_today),
color: headerForegroundColor, color: headerForegroundColor,
tooltip: localizations.calendarModeButtonLabel, tooltip: localizations.calendarModeButtonLabel,
onPressed: _handleEntryModeToggle, onPressed: _handleEntryModeToggle,
...@@ -906,6 +934,10 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -906,6 +934,10 @@ class _DatePickerHeader extends StatelessWidget {
/// grid) or [DatePickerEntryMode.input] (two text input fields) mode. /// grid) or [DatePickerEntryMode.input] (two text input fields) mode.
/// It defaults to [DatePickerEntryMode.calendar] and must be non-null. /// It defaults to [DatePickerEntryMode.calendar] and must be non-null.
/// ///
/// {@macro flutter.material.date_picker.switchToInputEntryModeIcon}
///
/// {@macro flutter.material.date_picker.switchToCalendarEntryModeIcon}
///
/// The following optional string parameters allow you to override the default /// The following optional string parameters allow you to override the default
/// text used for various parts of the dialog: /// text used for various parts of the dialog:
/// ///
...@@ -997,6 +1029,8 @@ Future<DateTimeRange?> showDateRangePicker({ ...@@ -997,6 +1029,8 @@ Future<DateTimeRange?> showDateRangePicker({
TransitionBuilder? builder, TransitionBuilder? builder,
Offset? anchorPoint, Offset? anchorPoint,
TextInputType keyboardType = TextInputType.datetime, TextInputType keyboardType = TextInputType.datetime,
final Icon? switchToInputEntryModeIcon,
final Icon? switchToCalendarEntryModeIcon,
}) async { }) async {
assert( assert(
initialDateRange == null || !initialDateRange.start.isAfter(initialDateRange.end), initialDateRange == null || !initialDateRange.start.isAfter(initialDateRange.end),
...@@ -1046,6 +1080,8 @@ Future<DateTimeRange?> showDateRangePicker({ ...@@ -1046,6 +1080,8 @@ Future<DateTimeRange?> showDateRangePicker({
fieldStartLabelText: fieldStartLabelText, fieldStartLabelText: fieldStartLabelText,
fieldEndLabelText: fieldEndLabelText, fieldEndLabelText: fieldEndLabelText,
keyboardType: keyboardType, keyboardType: keyboardType,
switchToInputEntryModeIcon: switchToInputEntryModeIcon,
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon,
); );
if (textDirection != null) { if (textDirection != null) {
...@@ -1134,6 +1170,8 @@ class DateRangePickerDialog extends StatefulWidget { ...@@ -1134,6 +1170,8 @@ class DateRangePickerDialog extends StatefulWidget {
this.fieldEndLabelText, this.fieldEndLabelText,
this.keyboardType = TextInputType.datetime, this.keyboardType = TextInputType.datetime,
this.restorationId, this.restorationId,
this.switchToInputEntryModeIcon,
this.switchToCalendarEntryModeIcon,
}); });
/// The date range that the date range picker starts with when it opens. /// The date range that the date range picker starts with when it opens.
...@@ -1256,6 +1294,12 @@ class DateRangePickerDialog extends StatefulWidget { ...@@ -1256,6 +1294,12 @@ class DateRangePickerDialog extends StatefulWidget {
/// Flutter. /// Flutter.
final String? restorationId; final String? restorationId;
/// {@macro flutter.material.date_picker.switchToInputEntryModeIcon}
final Icon? switchToInputEntryModeIcon;
/// {@macro flutter.material.date_picker.switchToCalendarEntryModeIcon}
final Icon? switchToCalendarEntryModeIcon;
@override @override
State<DateRangePickerDialog> createState() => _DateRangePickerDialogState(); State<DateRangePickerDialog> createState() => _DateRangePickerDialogState();
} }
...@@ -1378,7 +1422,7 @@ class _DateRangePickerDialogState extends State<DateRangePickerDialog> with Rest ...@@ -1378,7 +1422,7 @@ class _DateRangePickerDialogState extends State<DateRangePickerDialog> with Rest
onCancel: _handleCancel, onCancel: _handleCancel,
entryModeButton: showEntryModeButton entryModeButton: showEntryModeButton
? IconButton( ? IconButton(
icon: Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit), icon: widget.switchToInputEntryModeIcon ?? Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
tooltip: localizations.inputDateModeButtonLabel, tooltip: localizations.inputDateModeButtonLabel,
onPressed: _handleEntryModeToggle, onPressed: _handleEntryModeToggle,
...@@ -1444,7 +1488,7 @@ class _DateRangePickerDialogState extends State<DateRangePickerDialog> with Rest ...@@ -1444,7 +1488,7 @@ class _DateRangePickerDialogState extends State<DateRangePickerDialog> with Rest
onCancel: _handleCancel, onCancel: _handleCancel,
entryModeButton: showEntryModeButton entryModeButton: showEntryModeButton
? IconButton( ? IconButton(
icon: const Icon(Icons.calendar_today), icon: widget.switchToCalendarEntryModeIcon ?? const Icon(Icons.calendar_today),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
tooltip: localizations.calendarModeButtonLabel, tooltip: localizations.calendarModeButtonLabel,
onPressed: _handleEntryModeToggle, onPressed: _handleEntryModeToggle,
......
...@@ -461,6 +461,119 @@ void main() { ...@@ -461,6 +461,119 @@ void main() {
await tester.tap(find.text('OK')); await tester.tap(find.text('OK'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('honors switchToInputEntryModeIcon', (WidgetTester tester) async {
Widget buildApp({bool? useMaterial3, Icon? switchToInputEntryModeIcon}) {
return MaterialApp(
theme: ThemeData(
useMaterial3: useMaterial3 ?? false,
),
home: Material(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('Click X'),
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2018),
lastDate: DateTime(2030),
switchToInputEntryModeIcon: switchToInputEntryModeIcon,
);
},
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.edit), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.pumpWidget(buildApp(useMaterial3: true));
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.edit_outlined), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.pumpWidget(
buildApp(
switchToInputEntryModeIcon: const Icon(Icons.keyboard),
),
);
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.keyboard), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
});
testWidgets('honors switchToCalendarEntryModeIcon', (WidgetTester tester) async {
Widget buildApp({bool? useMaterial3, Icon? switchToCalendarEntryModeIcon}) {
return MaterialApp(
theme: ThemeData(
useMaterial3: useMaterial3 ?? false,
),
home: Material(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('Click X'),
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2018),
lastDate: DateTime(2030),
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon,
initialEntryMode: DatePickerEntryMode.input,
);
},
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.calendar_today), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.pumpWidget(buildApp(useMaterial3: true));
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.calendar_today), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.pumpWidget(
buildApp(
switchToCalendarEntryModeIcon: const Icon(Icons.favorite),
),
);
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.favorite), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
});
}); });
group('Calendar mode', () { group('Calendar mode', () {
......
...@@ -1187,6 +1187,118 @@ void main() { ...@@ -1187,6 +1187,118 @@ void main() {
expect(picker.keyboardType, keyboardType ?? TextInputType.datetime); expect(picker.keyboardType, keyboardType ?? TextInputType.datetime);
}); });
} }
testWidgets('honors switchToInputEntryModeIcon', (WidgetTester tester) async {
Widget buildApp({bool? useMaterial3, Icon? switchToInputEntryModeIcon}) {
return MaterialApp(
theme: ThemeData(
useMaterial3: useMaterial3 ?? false,
),
home: Material(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('Click X'),
onPressed: () {
showDateRangePicker(
context: context,
firstDate: DateTime(2020),
lastDate: DateTime(2030),
switchToInputEntryModeIcon: switchToInputEntryModeIcon,
);
},
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.edit), findsOneWidget);
await tester.tap(find.byIcon(Icons.close));
await tester.pumpAndSettle();
await tester.pumpWidget(buildApp(useMaterial3: true));
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.edit_outlined), findsOneWidget);
await tester.tap(find.byIcon(Icons.close));
await tester.pumpAndSettle();
await tester.pumpWidget(
buildApp(
switchToInputEntryModeIcon: const Icon(Icons.keyboard),
),
);
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.keyboard), findsOneWidget);
await tester.tap(find.byIcon(Icons.close));
await tester.pumpAndSettle();
});
testWidgets('honors switchToCalendarEntryModeIcon', (WidgetTester tester) async {
Widget buildApp({bool? useMaterial3, Icon? switchToCalendarEntryModeIcon}) {
return MaterialApp(
theme: ThemeData(
useMaterial3: useMaterial3 ?? false,
),
home: Material(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('Click X'),
onPressed: () {
showDateRangePicker(
context: context,
firstDate: DateTime(2020),
lastDate: DateTime(2030),
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon,
initialEntryMode: DatePickerEntryMode.input,
cancelText: 'CANCEL',
);
},
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.calendar_today), findsOneWidget);
await tester.tap(find.text('CANCEL'));
await tester.pumpAndSettle();
await tester.pumpWidget(buildApp(useMaterial3: true));
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.calendar_today), findsOneWidget);
await tester.tap(find.text('CANCEL'));
await tester.pumpAndSettle();
await tester.pumpWidget(
buildApp(
switchToCalendarEntryModeIcon: const Icon(Icons.favorite),
),
);
await tester.pumpAndSettle();
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.favorite), findsOneWidget);
await tester.tap(find.text('CANCEL'));
await tester.pumpAndSettle();
});
} }
class _RestorableDateRangePickerDialogTestWidget extends StatefulWidget { class _RestorableDateRangePickerDialogTestWidget extends StatefulWidget {
......
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