[State Restoration] Material DateRangePicker, adds some general state restoration tests (#78506)
This commit is contained in:
parent
2977a346fe
commit
e58ee0fbc7
@ -901,14 +901,14 @@ class _DatePickerHeader extends StatelessWidget {
|
||||
/// returned.
|
||||
///
|
||||
/// If [initialDateRange] is non-null, then it will be used as the initially
|
||||
/// selected date range. If it is provided, [initialDateRange.start] must be
|
||||
/// before or on [initialDateRange.end].
|
||||
/// selected date range. If it is provided, `initialDateRange.start` must be
|
||||
/// before or on `initialDateRange.end`.
|
||||
///
|
||||
/// The [firstDate] is the earliest allowable date. The [lastDate] is the latest
|
||||
/// allowable date. Both must be non-null.
|
||||
///
|
||||
/// If an initial date range is provided, [initialDateRange.start]
|
||||
/// and [initialDateRange.end] must both fall between or on [firstDate] and
|
||||
/// If an initial date range is provided, `initialDateRange.start`
|
||||
/// and `initialDateRange.end` must both fall between or on [firstDate] and
|
||||
/// [lastDate]. For all of these [DateTime] values, only their dates are
|
||||
/// considered. Their time fields are ignored.
|
||||
///
|
||||
@ -958,6 +958,133 @@ class _DatePickerHeader extends StatelessWidget {
|
||||
/// The [builder] parameter can be used to wrap the dialog widget
|
||||
/// to add inherited widgets like [Theme].
|
||||
///
|
||||
/// ### State Restoration
|
||||
///
|
||||
/// Using this method will not enable state restoration for the date range picker.
|
||||
/// In order to enable state restoration for a date range picker, use
|
||||
/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with
|
||||
/// [DateRangePickerDialog].
|
||||
///
|
||||
/// For more information about state restoration, see [RestorationManager].
|
||||
///
|
||||
/// {@macro flutter.widgets.RestorationManager}
|
||||
///
|
||||
/// {@tool sample --template=freeform}
|
||||
///
|
||||
/// This sample demonstrates how to create a restorable Material date range picker.
|
||||
/// This is accomplished by enabling state restoration by specifying
|
||||
/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to
|
||||
/// push [DateRangePickerDialog] when the button is tapped.
|
||||
///
|
||||
/// ```dart imports
|
||||
/// import 'package:flutter/material.dart';
|
||||
/// ```
|
||||
///
|
||||
/// ```dart
|
||||
/// void main() {
|
||||
/// runApp(const MyApp());
|
||||
/// }
|
||||
///
|
||||
/// class MyApp extends StatelessWidget {
|
||||
/// const MyApp({Key? key}) : super(key: key);
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return const MaterialApp(
|
||||
/// restorationScopeId: 'app',
|
||||
/// title: 'Restorable Date Range Picker Demo',
|
||||
/// home: MyHomePage(),
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// class MyHomePage extends StatefulWidget {
|
||||
/// const MyHomePage({Key? key}) : super(key: key);
|
||||
///
|
||||
/// @override
|
||||
/// _MyHomePageState createState() => _MyHomePageState();
|
||||
/// }
|
||||
///
|
||||
/// class _MyHomePageState extends State<MyHomePage> with RestorationMixin {
|
||||
/// final RestorableDateTimeN _startDate = RestorableDateTimeN(DateTime(2021, 1, 1));
|
||||
/// final RestorableDateTimeN _endDate = RestorableDateTimeN(DateTime(2021, 1, 5));
|
||||
/// late final RestorableRouteFuture<DateTimeRange?> _restorableDateRangePickerRouteFuture = RestorableRouteFuture<DateTimeRange?>(
|
||||
/// onComplete: _selectDateRange,
|
||||
/// onPresent: (NavigatorState navigator, Object? arguments) {
|
||||
/// return navigator.restorablePush(
|
||||
/// _dateRangePickerRoute,
|
||||
/// arguments: <String, dynamic>{
|
||||
/// 'initialStartDate': _startDate.value?.millisecondsSinceEpoch,
|
||||
/// 'initialEndDate': _endDate.value?.millisecondsSinceEpoch,
|
||||
/// }
|
||||
/// );
|
||||
/// },
|
||||
/// );
|
||||
///
|
||||
/// void _selectDateRange(DateTimeRange? newSelectedDate) {
|
||||
/// if (newSelectedDate != null) {
|
||||
/// setState(() {
|
||||
/// _startDate.value = newSelectedDate.start;
|
||||
/// _endDate.value = newSelectedDate.end;
|
||||
/// });
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// String get restorationId => 'scaffold_state';
|
||||
///
|
||||
/// @override
|
||||
/// void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
|
||||
/// registerForRestoration(_startDate, 'start_date');
|
||||
/// registerForRestoration(_endDate, 'end_date');
|
||||
/// registerForRestoration(_restorableDateRangePickerRouteFuture, 'date_picker_route_future');
|
||||
/// }
|
||||
///
|
||||
/// static Route<DateTimeRange?> _dateRangePickerRoute(
|
||||
/// BuildContext context,
|
||||
/// Object? arguments,
|
||||
/// ) {
|
||||
/// return DialogRoute<DateTimeRange?>(
|
||||
/// context: context,
|
||||
/// builder: (BuildContext context) {
|
||||
/// return DateRangePickerDialog(
|
||||
/// restorationId: 'date_picker_dialog',
|
||||
/// initialDateRange: _initialDateTimeRange(arguments! as Map<dynamic, dynamic>),
|
||||
/// firstDate: DateTime(2021, 1, 1),
|
||||
/// currentDate: DateTime(2021, 1, 25),
|
||||
/// lastDate: DateTime(2022, 1, 1),
|
||||
/// );
|
||||
/// },
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// static DateTimeRange? _initialDateTimeRange(Map<dynamic, dynamic> arguments) {
|
||||
/// if (arguments['initialStartDate'] != null && arguments['initialEndDate'] != null) {
|
||||
/// return DateTimeRange(
|
||||
/// start: DateTime.fromMillisecondsSinceEpoch(arguments['initialStartDate'] as int),
|
||||
/// end: DateTime.fromMillisecondsSinceEpoch(arguments['initialEndDate'] as int),
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// return null;
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Scaffold(
|
||||
/// body: Center(
|
||||
/// child: OutlinedButton(
|
||||
/// onPressed: () { _restorableDateRangePickerRouteFuture.present(); },
|
||||
/// child: const Text('Open Date Range Picker'),
|
||||
/// ),
|
||||
/// ),
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [showDatePicker], which shows a material design date picker used to
|
||||
@ -1027,7 +1154,7 @@ Future<DateTimeRange?> showDateRangePicker({
|
||||
assert(useRootNavigator != null);
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
Widget dialog = _DateRangePickerDialog(
|
||||
Widget dialog = DateRangePickerDialog(
|
||||
initialDateRange: initialDateRange,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
@ -1100,8 +1227,18 @@ String _formatRangeEndDate(MaterialLocalizations localizations, DateTime? startD
|
||||
: localizations.formatShortDate(endDate);
|
||||
}
|
||||
|
||||
class _DateRangePickerDialog extends StatefulWidget {
|
||||
const _DateRangePickerDialog({
|
||||
/// A Material-style date range picker dialog.
|
||||
///
|
||||
/// It is used internally by [showDateRangePicker] or can be directly pushed
|
||||
/// onto the [Navigator] stack to enable state restoration. See
|
||||
/// [showDateRangePicker] for a state restoration app example.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [showDateRangePicker], which is a way to display the date picker.
|
||||
class DateRangePickerDialog extends StatefulWidget {
|
||||
/// A Material-style date range picker dialog.
|
||||
const DateRangePickerDialog({
|
||||
Key? key,
|
||||
this.initialDateRange,
|
||||
required this.firstDate,
|
||||
@ -1119,58 +1256,161 @@ class _DateRangePickerDialog extends StatefulWidget {
|
||||
this.fieldEndHintText,
|
||||
this.fieldStartLabelText,
|
||||
this.fieldEndLabelText,
|
||||
this.restorationId,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The date range that the date range picker starts with when it opens.
|
||||
///
|
||||
/// If an initial date range is provided, `initialDateRange.start`
|
||||
/// and `initialDateRange.end` must both fall between or on [firstDate] and
|
||||
/// [lastDate]. For all of these [DateTime] values, only their dates are
|
||||
/// considered. Their time fields are ignored.
|
||||
///
|
||||
/// If [initialDateRange] is non-null, then it will be used as the initially
|
||||
/// selected date range. If it is provided, `initialDateRange.start` must be
|
||||
/// before or on `initialDateRange.end`.
|
||||
final DateTimeRange? initialDateRange;
|
||||
|
||||
/// The earliest allowable date on the date range.
|
||||
final DateTime firstDate;
|
||||
|
||||
/// The latest allowable date on the date range.
|
||||
final DateTime lastDate;
|
||||
|
||||
/// The [currentDate] represents the current day (i.e. today).
|
||||
///
|
||||
/// This date will be highlighted in the day grid.
|
||||
///
|
||||
/// If `null`, the date of `DateTime.now()` will be used.
|
||||
final DateTime? currentDate;
|
||||
|
||||
/// The initial date range picker entry mode.
|
||||
///
|
||||
/// The date range has two main modes: [DatePickerEntryMode.calendar] (a
|
||||
/// scrollable calendar month grid) or [DatePickerEntryMode.input] (two text
|
||||
/// input fields) mode.
|
||||
///
|
||||
/// It defaults to [DatePickerEntryMode.calendar] and must be non-null.
|
||||
final DatePickerEntryMode initialEntryMode;
|
||||
|
||||
/// The label on the cancel button for the text input mode.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.cancelButtonLabel] is used.
|
||||
final String? cancelText;
|
||||
|
||||
/// The label on the "OK" button for the text input mode.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.okButtonLabel] is used.
|
||||
final String? confirmText;
|
||||
|
||||
/// The label on the save button for the fullscreen calendar mode.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.saveButtonLabel] is used.
|
||||
final String? saveText;
|
||||
|
||||
/// The label displayed at the top of the dialog.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.dateRangePickerHelpText] is used.
|
||||
final String? helpText;
|
||||
|
||||
/// The message used when the date range is invalid (e.g. start date is after
|
||||
/// end date).
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.invalidDateRangeLabel] is used.
|
||||
final String? errorInvalidRangeText;
|
||||
|
||||
/// The message used when an input text isn't in a proper date format.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.invalidDateFormatLabel] is used.
|
||||
final String? errorFormatText;
|
||||
|
||||
/// The message used when an input text isn't a selectable date.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.dateOutOfRangeLabel] is used.
|
||||
final String? errorInvalidText;
|
||||
|
||||
/// The text used to prompt the user when no text has been entered in the
|
||||
/// start field.
|
||||
///
|
||||
/// If null, the localized value of
|
||||
/// [MaterialLocalizations.dateHelpText] is used.
|
||||
final String? fieldStartHintText;
|
||||
|
||||
/// The text used to prompt the user when no text has been entered in the
|
||||
/// end field.
|
||||
///
|
||||
/// If null, the localized value of [MaterialLocalizations.dateHelpText] is
|
||||
/// used.
|
||||
final String? fieldEndHintText;
|
||||
|
||||
/// The label for the start date text input field.
|
||||
///
|
||||
/// If null, the localized value of [MaterialLocalizations.dateRangeStartLabel]
|
||||
/// is used.
|
||||
final String? fieldStartLabelText;
|
||||
|
||||
/// The label for the end date text input field.
|
||||
///
|
||||
/// If null, the localized value of [MaterialLocalizations.dateRangeEndLabel]
|
||||
/// is used.
|
||||
final String? fieldEndLabelText;
|
||||
|
||||
/// Restoration ID to save and restore the state of the [DateRangePickerDialog].
|
||||
///
|
||||
/// If it is non-null, the date range picker will persist and restore the
|
||||
/// date range selected on the dialog.
|
||||
///
|
||||
/// The state of this widget is persisted in a [RestorationBucket] claimed
|
||||
/// from the surrounding [RestorationScope] using the provided restoration ID.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [RestorationManager], which explains how state restoration works in
|
||||
/// Flutter.
|
||||
final String? restorationId;
|
||||
|
||||
@override
|
||||
_DateRangePickerDialogState createState() => _DateRangePickerDialogState();
|
||||
}
|
||||
|
||||
class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
late DatePickerEntryMode _entryMode;
|
||||
DateTime? _selectedStart;
|
||||
DateTime? _selectedEnd;
|
||||
late bool _autoValidate;
|
||||
class _DateRangePickerDialogState extends State<DateRangePickerDialog> with RestorationMixin {
|
||||
late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode);
|
||||
late final RestorableDateTimeN _selectedStart = RestorableDateTimeN(widget.initialDateRange?.start);
|
||||
late final RestorableDateTimeN _selectedEnd = RestorableDateTimeN(widget.initialDateRange?.end);
|
||||
final RestorableBool _autoValidate = RestorableBool(false);
|
||||
final GlobalKey _calendarPickerKey = GlobalKey();
|
||||
final GlobalKey<_InputDateRangePickerState> _inputPickerKey = GlobalKey<_InputDateRangePickerState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedStart = widget.initialDateRange?.start;
|
||||
_selectedEnd = widget.initialDateRange?.end;
|
||||
_entryMode = widget.initialEntryMode;
|
||||
_autoValidate = false;
|
||||
String? get restorationId => widget.restorationId;
|
||||
|
||||
@override
|
||||
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
|
||||
registerForRestoration(_entryMode, 'entry_mode');
|
||||
registerForRestoration(_selectedStart, 'selected_start');
|
||||
registerForRestoration(_selectedEnd, 'selected_end');
|
||||
registerForRestoration(_autoValidate, 'autovalidate');
|
||||
}
|
||||
|
||||
void _handleOk() {
|
||||
if (_entryMode == DatePickerEntryMode.input || _entryMode == DatePickerEntryMode.inputOnly) {
|
||||
if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) {
|
||||
final _InputDateRangePickerState picker = _inputPickerKey.currentState!;
|
||||
if (!picker.validate()) {
|
||||
setState(() {
|
||||
_autoValidate = true;
|
||||
_autoValidate.value = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
final DateTimeRange? selectedRange = _hasSelectedDateRange
|
||||
? DateTimeRange(start: _selectedStart!, end: _selectedEnd!)
|
||||
? DateTimeRange(start: _selectedStart.value!, end: _selectedEnd.value!)
|
||||
: null;
|
||||
|
||||
Navigator.pop(context, selectedRange);
|
||||
@ -1182,29 +1422,29 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
|
||||
void _handleEntryModeToggle() {
|
||||
setState(() {
|
||||
switch (_entryMode) {
|
||||
switch (_entryMode.value) {
|
||||
case DatePickerEntryMode.calendar:
|
||||
_autoValidate = false;
|
||||
_entryMode = DatePickerEntryMode.input;
|
||||
_autoValidate.value = false;
|
||||
_entryMode.value = DatePickerEntryMode.input;
|
||||
break;
|
||||
|
||||
case DatePickerEntryMode.input:
|
||||
// Validate the range dates
|
||||
if (_selectedStart != null &&
|
||||
(_selectedStart!.isBefore(widget.firstDate) || _selectedStart!.isAfter(widget.lastDate))) {
|
||||
_selectedStart = null;
|
||||
if (_selectedStart.value != null &&
|
||||
(_selectedStart.value!.isBefore(widget.firstDate) || _selectedStart.value!.isAfter(widget.lastDate))) {
|
||||
_selectedStart.value = null;
|
||||
// With no valid start date, having an end date makes no sense for the UI.
|
||||
_selectedEnd = null;
|
||||
_selectedEnd.value = null;
|
||||
}
|
||||
if (_selectedEnd != null &&
|
||||
(_selectedEnd!.isBefore(widget.firstDate) || _selectedEnd!.isAfter(widget.lastDate))) {
|
||||
_selectedEnd = null;
|
||||
if (_selectedEnd.value != null &&
|
||||
(_selectedEnd.value!.isBefore(widget.firstDate) || _selectedEnd.value!.isAfter(widget.lastDate))) {
|
||||
_selectedEnd.value = null;
|
||||
}
|
||||
// If invalid range (start after end), then just use the start date
|
||||
if (_selectedStart != null && _selectedEnd != null && _selectedStart!.isAfter(_selectedEnd!)) {
|
||||
_selectedEnd = null;
|
||||
if (_selectedStart.value != null && _selectedEnd.value != null && _selectedStart.value!.isAfter(_selectedEnd.value!)) {
|
||||
_selectedEnd.value = null;
|
||||
}
|
||||
_entryMode = DatePickerEntryMode.calendar;
|
||||
_entryMode.value = DatePickerEntryMode.calendar;
|
||||
break;
|
||||
|
||||
case DatePickerEntryMode.calendarOnly:
|
||||
@ -1216,14 +1456,14 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
}
|
||||
|
||||
void _handleStartDateChanged(DateTime? date) {
|
||||
setState(() => _selectedStart = date);
|
||||
setState(() => _selectedStart.value = date);
|
||||
}
|
||||
|
||||
void _handleEndDateChanged(DateTime? date) {
|
||||
setState(() => _selectedEnd = date);
|
||||
setState(() => _selectedEnd.value = date);
|
||||
}
|
||||
|
||||
bool get _hasSelectedDateRange => _selectedStart != null && _selectedEnd != null;
|
||||
bool get _hasSelectedDateRange => _selectedStart.value != null && _selectedEnd.value != null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -1242,15 +1482,15 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
final double elevation;
|
||||
final EdgeInsets insetPadding;
|
||||
final bool showEntryModeButton =
|
||||
_entryMode == DatePickerEntryMode.calendar ||
|
||||
_entryMode == DatePickerEntryMode.input;
|
||||
switch (_entryMode) {
|
||||
_entryMode.value == DatePickerEntryMode.calendar ||
|
||||
_entryMode.value == DatePickerEntryMode.input;
|
||||
switch (_entryMode.value) {
|
||||
case DatePickerEntryMode.calendar:
|
||||
case DatePickerEntryMode.calendarOnly:
|
||||
contents = _CalendarRangePickerDialog(
|
||||
key: _calendarPickerKey,
|
||||
selectedStartDate: _selectedStart,
|
||||
selectedEndDate: _selectedEnd,
|
||||
selectedStartDate: _selectedStart.value,
|
||||
selectedEndDate: _selectedEnd.value,
|
||||
firstDate: widget.firstDate,
|
||||
lastDate: widget.lastDate,
|
||||
currentDate: widget.currentDate,
|
||||
@ -1279,8 +1519,8 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
case DatePickerEntryMode.input:
|
||||
case DatePickerEntryMode.inputOnly:
|
||||
contents = _InputDateRangePickerDialog(
|
||||
selectedStartDate: _selectedStart,
|
||||
selectedEndDate: _selectedEnd,
|
||||
selectedStartDate: _selectedStart.value,
|
||||
selectedEndDate: _selectedEnd.value,
|
||||
currentDate: widget.currentDate,
|
||||
picker: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
@ -1292,14 +1532,14 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> {
|
||||
const Spacer(),
|
||||
_InputDateRangePicker(
|
||||
key: _inputPickerKey,
|
||||
initialStartDate: _selectedStart,
|
||||
initialEndDate: _selectedEnd,
|
||||
initialStartDate: _selectedStart.value,
|
||||
initialEndDate: _selectedEnd.value,
|
||||
firstDate: widget.firstDate,
|
||||
lastDate: widget.lastDate,
|
||||
onStartDateChanged: _handleStartDateChanged,
|
||||
onEndDateChanged: _handleEndDateChanged,
|
||||
autofocus: true,
|
||||
autovalidate: _autoValidate,
|
||||
autovalidate: _autoValidate.value,
|
||||
helpText: widget.helpText,
|
||||
errorInvalidRangeText: widget.errorInvalidRangeText,
|
||||
errorFormatText: widget.errorFormatText,
|
||||
|
@ -385,6 +385,34 @@ class RestorableDateTime extends RestorableValue<DateTime> {
|
||||
Object? toPrimitives() => value.millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
/// A [RestorableValue] that knows how to save and restore [DateTime] that is
|
||||
/// nullable.
|
||||
///
|
||||
/// {@macro flutter.widgets.RestorableNum}.
|
||||
class RestorableDateTimeN extends RestorableValue<DateTime?> {
|
||||
/// Creates a [RestorableDateTime].
|
||||
///
|
||||
/// {@macro flutter.widgets.RestorableNum.constructor}
|
||||
RestorableDateTimeN(DateTime? defaultValue) : _defaultValue = defaultValue;
|
||||
|
||||
final DateTime? _defaultValue;
|
||||
|
||||
@override
|
||||
DateTime? createDefaultValue() => _defaultValue;
|
||||
|
||||
@override
|
||||
void didUpdateValue(DateTime? oldValue) {
|
||||
assert(debugIsSerializableForRestoration(value?.millisecondsSinceEpoch));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
DateTime? fromPrimitives(Object? data) => data != null ? DateTime.fromMillisecondsSinceEpoch(data as int) : null;
|
||||
|
||||
@override
|
||||
Object? toPrimitives() => value?.millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
/// A base class for creating a [RestorableProperty] that stores and restores a
|
||||
/// [Listenable].
|
||||
///
|
||||
|
@ -1162,9 +1162,14 @@ void main() {
|
||||
await tester.restoreFrom(restorationData);
|
||||
expect(find.byType(DatePickerDialog), findsOneWidget);
|
||||
|
||||
// Select a different date and close the date picker.
|
||||
// Select a different date.
|
||||
await tester.tap(find.text('30'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Restart after the new selection. It should remain selected.
|
||||
await tester.restartAndRestore();
|
||||
|
||||
// Close the date picker.
|
||||
await tester.tap(find.text('OK'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1210,8 +1215,8 @@ void main() {
|
||||
await tester.tapAt(const Offset(10.0, 10.0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The date picker should be closed, the text value updated to the
|
||||
// newly selected date.
|
||||
// The date picker should be closed, the text value should be the same
|
||||
// as before.
|
||||
expect(find.byType(DatePickerDialog), findsNothing);
|
||||
expect(find.text('25/7/2021'), findsOneWidget);
|
||||
|
||||
|
@ -853,4 +853,198 @@ void main() {
|
||||
_testInputDecorator(tester.widget(borderContainers.last), border, Colors.transparent);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('DatePickerDialog is state restorable', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
restorationScopeId: 'app',
|
||||
home: _RestorableDateRangePickerDialogTestWidget(),
|
||||
),
|
||||
);
|
||||
|
||||
// The date range picker should be closed.
|
||||
expect(find.byType(DateRangePickerDialog), findsNothing);
|
||||
expect(find.text('1/1/2021 to 5/1/2021'), findsOneWidget);
|
||||
|
||||
// Open the date range picker.
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
|
||||
final TestRestorationData restorationData = await tester.getRestorationData();
|
||||
await tester.restartAndRestore();
|
||||
|
||||
// The date range picker should be open after restoring.
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
|
||||
// Close the date range picker.
|
||||
await tester.tap(find.byIcon(Icons.close));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The date range picker should be closed, the text value updated to the
|
||||
// newly selected date.
|
||||
expect(find.byType(DateRangePickerDialog), findsNothing);
|
||||
expect(find.text('1/1/2021 to 5/1/2021'), findsOneWidget);
|
||||
|
||||
// The date range picker should be open after restoring.
|
||||
await tester.restoreFrom(restorationData);
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
|
||||
// // Select a different date and close the date range picker.
|
||||
await tester.tap(find.text('12').first);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('14').first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Restart after the new selection. It should remain selected.
|
||||
await tester.restartAndRestore();
|
||||
|
||||
// Close the date range picker.
|
||||
await tester.tap(find.text('SAVE'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The date range picker should be closed, the text value updated to the
|
||||
// newly selected date.
|
||||
expect(find.byType(DateRangePickerDialog), findsNothing);
|
||||
expect(find.text('12/1/2021 to 14/1/2021'), findsOneWidget);
|
||||
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615
|
||||
|
||||
testWidgets('DateRangePickerDialog state restoration - DatePickerEntryMode', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
restorationScopeId: 'app',
|
||||
home: _RestorableDateRangePickerDialogTestWidget(
|
||||
datePickerEntryMode: DatePickerEntryMode.calendarOnly,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// The date range picker should be closed.
|
||||
expect(find.byType(DateRangePickerDialog), findsNothing);
|
||||
expect(find.text('1/1/2021 to 5/1/2021'), findsOneWidget);
|
||||
|
||||
// Open the date range picker.
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
|
||||
// Only in calendar mode and cannot switch out.
|
||||
expect(find.byType(TextField), findsNothing);
|
||||
expect(find.byIcon(Icons.edit), findsNothing);
|
||||
|
||||
final TestRestorationData restorationData = await tester.getRestorationData();
|
||||
await tester.restartAndRestore();
|
||||
|
||||
// The date range picker should be open after restoring.
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
// Only in calendar mode and cannot switch out.
|
||||
expect(find.byType(TextField), findsNothing);
|
||||
expect(find.byIcon(Icons.edit), findsNothing);
|
||||
|
||||
// Tap on the barrier.
|
||||
await tester.tap(find.byIcon(Icons.close));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The date range picker should be closed, the text value should be the same
|
||||
// as before.
|
||||
expect(find.byType(DateRangePickerDialog), findsNothing);
|
||||
expect(find.text('1/1/2021 to 5/1/2021'), findsOneWidget);
|
||||
|
||||
// The date range picker should be open after restoring.
|
||||
await tester.restoreFrom(restorationData);
|
||||
expect(find.byType(DateRangePickerDialog), findsOneWidget);
|
||||
// Only in calendar mode and cannot switch out.
|
||||
expect(find.byType(TextField), findsNothing);
|
||||
expect(find.byIcon(Icons.edit), findsNothing);
|
||||
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615
|
||||
}
|
||||
|
||||
class _RestorableDateRangePickerDialogTestWidget extends StatefulWidget {
|
||||
const _RestorableDateRangePickerDialogTestWidget({
|
||||
Key? key,
|
||||
this.datePickerEntryMode = DatePickerEntryMode.calendar,
|
||||
}) : super(key: key);
|
||||
|
||||
final DatePickerEntryMode datePickerEntryMode;
|
||||
|
||||
@override
|
||||
_RestorableDateRangePickerDialogTestWidgetState createState() => _RestorableDateRangePickerDialogTestWidgetState();
|
||||
}
|
||||
|
||||
class _RestorableDateRangePickerDialogTestWidgetState extends State<_RestorableDateRangePickerDialogTestWidget> with RestorationMixin {
|
||||
@override
|
||||
String? get restorationId => 'scaffold_state';
|
||||
|
||||
final RestorableDateTimeN _startDate = RestorableDateTimeN(DateTime(2021, 1, 1));
|
||||
final RestorableDateTimeN _endDate = RestorableDateTimeN(DateTime(2021, 1, 5));
|
||||
late final RestorableRouteFuture<DateTimeRange?> _restorableDateRangePickerRouteFuture = RestorableRouteFuture<DateTimeRange?>(
|
||||
onComplete: _selectDateRange,
|
||||
onPresent: (NavigatorState navigator, Object? arguments) {
|
||||
return navigator.restorablePush(
|
||||
_dateRangePickerRoute,
|
||||
arguments: <String, dynamic>{
|
||||
'datePickerEntryMode': widget.datePickerEntryMode.index,
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
|
||||
registerForRestoration(_startDate, 'start_date');
|
||||
registerForRestoration(_endDate, 'end_date');
|
||||
registerForRestoration(_restorableDateRangePickerRouteFuture, 'date_picker_route_future');
|
||||
}
|
||||
|
||||
void _selectDateRange(DateTimeRange? newSelectedDate) {
|
||||
if (newSelectedDate != null) {
|
||||
setState(() {
|
||||
_startDate.value = newSelectedDate.start;
|
||||
_endDate.value = newSelectedDate.end;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static Route<DateTimeRange?> _dateRangePickerRoute(
|
||||
BuildContext context,
|
||||
Object? arguments,
|
||||
) {
|
||||
return DialogRoute<DateTimeRange?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final Map<dynamic, dynamic> args = arguments! as Map<dynamic, dynamic>;
|
||||
return DateRangePickerDialog(
|
||||
restorationId: 'date_picker_dialog',
|
||||
initialEntryMode: DatePickerEntryMode.values[args['datePickerEntryMode'] as int],
|
||||
firstDate: DateTime(2021, 1, 1),
|
||||
currentDate: DateTime(2021, 1, 25),
|
||||
lastDate: DateTime(2022, 1, 1),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final DateTime? startDateTime = _startDate.value;
|
||||
final DateTime? endDateTime = _endDate.value;
|
||||
// Example: "25/7/1994"
|
||||
final String startDateTimeString = '${startDateTime?.day}/${startDateTime?.month}/${startDateTime?.year}';
|
||||
final String endDateTimeString = '${endDateTime?.day}/${endDateTime?.month}/${endDateTime?.year}';
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
_restorableDateRangePickerRouteFuture.present();
|
||||
},
|
||||
child: const Text('X'),
|
||||
),
|
||||
Text('$startDateTimeString to $endDateTimeString'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ void main() {
|
||||
expect(() => RestorableBoolN(true).value, throwsAssertionError);
|
||||
expect(() => RestorableTextEditingController().value, throwsAssertionError);
|
||||
expect(() => RestorableDateTime(DateTime(2020, 4, 3)).value, throwsAssertionError);
|
||||
expect(() => RestorableDateTimeN(DateTime(2020, 4, 3)).value, throwsAssertionError);
|
||||
expect(() => _TestRestorableValue().value, throwsAssertionError);
|
||||
});
|
||||
|
||||
@ -34,8 +35,14 @@ void main() {
|
||||
expect(state.intValue.value, 42);
|
||||
expect(state.stringValue.value, 'hello world');
|
||||
expect(state.boolValue.value, false);
|
||||
expect(state.controllerValue.value.text, 'FooBar');
|
||||
expect(state.dateTimeValue.value, DateTime(2021, 3, 16));
|
||||
expect(state.nullableNumValue.value, null);
|
||||
expect(state.nullableDoubleValue.value, null);
|
||||
expect(state.nullableIntValue.value, null);
|
||||
expect(state.nullableStringValue.value, null);
|
||||
expect(state.nullableBoolValue.value, null);
|
||||
expect(state.nullableDateTimeValue.value, null);
|
||||
expect(state.controllerValue.value.text, 'FooBar');
|
||||
expect(state.objectValue.value, 55);
|
||||
|
||||
// Modify values.
|
||||
@ -45,8 +52,14 @@ void main() {
|
||||
state.intValue.value = 10;
|
||||
state.stringValue.value = 'guten tag';
|
||||
state.boolValue.value = true;
|
||||
state.controllerValue.value.text = 'blabla';
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
state.nullableNumValue.value = 5.0;
|
||||
state.nullableDoubleValue.value = 2.0;
|
||||
state.nullableIntValue.value = 1;
|
||||
state.nullableStringValue.value = 'hullo';
|
||||
state.nullableBoolValue.value = false;
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 4, 4);
|
||||
state.controllerValue.value.text = 'blabla';
|
||||
state.objectValue.value = 53;
|
||||
});
|
||||
await tester.pump();
|
||||
@ -56,8 +69,14 @@ void main() {
|
||||
expect(state.intValue.value, 10);
|
||||
expect(state.stringValue.value, 'guten tag');
|
||||
expect(state.boolValue.value, true);
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.dateTimeValue.value, DateTime(2020, 7, 4));
|
||||
expect(state.nullableNumValue.value, 5.0);
|
||||
expect(state.nullableDoubleValue.value, 2.0);
|
||||
expect(state.nullableIntValue.value, 1);
|
||||
expect(state.nullableStringValue.value, 'hullo');
|
||||
expect(state.nullableBoolValue.value, false);
|
||||
expect(state.nullableDateTimeValue.value, DateTime(2020, 4, 4));
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.objectValue.value, 53);
|
||||
expect(find.text('guten tag'), findsOneWidget);
|
||||
});
|
||||
@ -77,8 +96,14 @@ void main() {
|
||||
expect(state.intValue.value, 42);
|
||||
expect(state.stringValue.value, 'hello world');
|
||||
expect(state.boolValue.value, false);
|
||||
expect(state.controllerValue.value.text, 'FooBar');
|
||||
expect(state.dateTimeValue.value, DateTime(2021, 3, 16));
|
||||
expect(state.nullableNumValue.value, null);
|
||||
expect(state.nullableDoubleValue.value, null);
|
||||
expect(state.nullableIntValue.value, null);
|
||||
expect(state.nullableStringValue.value, null);
|
||||
expect(state.nullableBoolValue.value, null);
|
||||
expect(state.nullableDateTimeValue.value, null);
|
||||
expect(state.controllerValue.value.text, 'FooBar');
|
||||
expect(state.objectValue.value, 55);
|
||||
|
||||
// Modify values.
|
||||
@ -88,8 +113,14 @@ void main() {
|
||||
state.intValue.value = 10;
|
||||
state.stringValue.value = 'guten tag';
|
||||
state.boolValue.value = true;
|
||||
state.controllerValue.value.text = 'blabla';
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
state.nullableNumValue.value = 5.0;
|
||||
state.nullableDoubleValue.value = 2.0;
|
||||
state.nullableIntValue.value = 1;
|
||||
state.nullableStringValue.value = 'hullo';
|
||||
state.nullableBoolValue.value = false;
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 4, 4);
|
||||
state.controllerValue.value.text = 'blabla';
|
||||
state.objectValue.value = 53;
|
||||
});
|
||||
await tester.pump();
|
||||
@ -99,8 +130,14 @@ void main() {
|
||||
expect(state.intValue.value, 10);
|
||||
expect(state.stringValue.value, 'guten tag');
|
||||
expect(state.boolValue.value, true);
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.dateTimeValue.value, DateTime(2020, 7, 4));
|
||||
expect(state.nullableNumValue.value, 5.0);
|
||||
expect(state.nullableDoubleValue.value, 2.0);
|
||||
expect(state.nullableIntValue.value, 1);
|
||||
expect(state.nullableStringValue.value, 'hullo');
|
||||
expect(state.nullableBoolValue.value, false);
|
||||
expect(state.nullableDateTimeValue.value, DateTime(2020, 4, 4));
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.objectValue.value, 53);
|
||||
expect(find.text('guten tag'), findsOneWidget);
|
||||
|
||||
@ -115,8 +152,14 @@ void main() {
|
||||
expect(state.intValue.value, 10);
|
||||
expect(state.stringValue.value, 'guten tag');
|
||||
expect(state.boolValue.value, true);
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.dateTimeValue.value, DateTime(2020, 7, 4));
|
||||
expect(state.nullableNumValue.value, 5.0);
|
||||
expect(state.nullableDoubleValue.value, 2.0);
|
||||
expect(state.nullableIntValue.value, 1);
|
||||
expect(state.nullableStringValue.value, 'hullo');
|
||||
expect(state.nullableBoolValue.value, false);
|
||||
expect(state.nullableDateTimeValue.value, DateTime(2020, 4, 4));
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.objectValue.value, 53);
|
||||
expect(find.text('guten tag'), findsOneWidget);
|
||||
});
|
||||
@ -137,13 +180,14 @@ void main() {
|
||||
state.intValue.value = 10;
|
||||
state.stringValue.value = 'guten tag';
|
||||
state.boolValue.value = true;
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
state.nullableNumValue.value = 5.0;
|
||||
state.nullableDoubleValue.value = 2.0;
|
||||
state.nullableIntValue.value = 1;
|
||||
state.nullableStringValue.value = 'hullo';
|
||||
state.nullableBoolValue.value = false;
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 4, 4);
|
||||
state.controllerValue.value.text = 'blabla';
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
state.objectValue.value = 53;
|
||||
});
|
||||
await tester.pump();
|
||||
@ -158,13 +202,14 @@ void main() {
|
||||
state.intValue.value = 20;
|
||||
state.stringValue.value = 'ciao';
|
||||
state.boolValue.value = false;
|
||||
state.dateTimeValue.value = DateTime(2020, 3, 2);
|
||||
state.nullableNumValue.value = 20.0;
|
||||
state.nullableDoubleValue.value = 20.0;
|
||||
state.nullableIntValue.value = 20;
|
||||
state.nullableStringValue.value = 'ni hao';
|
||||
state.nullableBoolValue.value = null;
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 5, 5);
|
||||
state.controllerValue.value.text = 'blub';
|
||||
state.dateTimeValue.value = DateTime(2020, 3, 2);
|
||||
state.objectValue.value = 20;
|
||||
});
|
||||
await tester.pump();
|
||||
@ -178,13 +223,14 @@ void main() {
|
||||
expect(state.intValue.value, 10);
|
||||
expect(state.stringValue.value, 'guten tag');
|
||||
expect(state.boolValue.value, true);
|
||||
expect(state.dateTimeValue.value, DateTime(2020, 7, 4));
|
||||
expect(state.nullableNumValue.value, 5.0);
|
||||
expect(state.nullableDoubleValue.value, 2.0);
|
||||
expect(state.nullableIntValue.value, 1);
|
||||
expect(state.nullableStringValue.value, 'hullo');
|
||||
expect(state.nullableBoolValue.value, false);
|
||||
expect(state.nullableDateTimeValue.value, DateTime(2020, 4, 4));
|
||||
expect(state.controllerValue.value.text, 'blabla');
|
||||
expect(state.dateTimeValue.value, DateTime(2020, 7, 4));
|
||||
expect(state.objectValue.value, 53);
|
||||
expect(find.text('guten tag'), findsOneWidget);
|
||||
expect(state.controllerValue.value, isNot(same(controller)));
|
||||
@ -196,13 +242,14 @@ void main() {
|
||||
expect(state.intValue.value, 42);
|
||||
expect(state.stringValue.value, 'hello world');
|
||||
expect(state.boolValue.value, false);
|
||||
expect(state.dateTimeValue.value, DateTime(2021, 3, 16));
|
||||
expect(state.nullableNumValue.value, null);
|
||||
expect(state.nullableDoubleValue.value, null);
|
||||
expect(state.nullableIntValue.value, null);
|
||||
expect(state.nullableStringValue.value, null);
|
||||
expect(state.nullableBoolValue.value, null);
|
||||
expect(state.nullableDateTimeValue.value, null);
|
||||
expect(state.controllerValue.value.text, 'FooBar');
|
||||
expect(state.dateTimeValue.value, DateTime(2021, 3, 16));
|
||||
expect(state.objectValue.value, 55);
|
||||
expect(find.text('hello world'), findsOneWidget);
|
||||
});
|
||||
@ -217,6 +264,7 @@ void main() {
|
||||
final _RestorableWidgetState state = tester.state(find.byType(_RestorableWidget));
|
||||
|
||||
final List<String> notifyLog = <String>[];
|
||||
|
||||
state.numValue.addListener(() {
|
||||
notifyLog.add('num');
|
||||
});
|
||||
@ -232,6 +280,27 @@ void main() {
|
||||
state.boolValue.addListener(() {
|
||||
notifyLog.add('bool');
|
||||
});
|
||||
state.dateTimeValue.addListener(() {
|
||||
notifyLog.add('date-time');
|
||||
});
|
||||
state.nullableNumValue.addListener(() {
|
||||
notifyLog.add('nullable-num');
|
||||
});
|
||||
state.nullableDoubleValue.addListener(() {
|
||||
notifyLog.add('nullable-double');
|
||||
});
|
||||
state.nullableIntValue.addListener(() {
|
||||
notifyLog.add('nullable-int');
|
||||
});
|
||||
state.nullableStringValue.addListener(() {
|
||||
notifyLog.add('nullable-string');
|
||||
});
|
||||
state.nullableBoolValue.addListener(() {
|
||||
notifyLog.add('nullable-bool');
|
||||
});
|
||||
state.nullableDateTimeValue.addListener(() {
|
||||
notifyLog.add('nullable-date-time');
|
||||
});
|
||||
state.controllerValue.addListener(() {
|
||||
notifyLog.add('controller');
|
||||
});
|
||||
@ -269,6 +338,48 @@ void main() {
|
||||
expect(notifyLog.single, 'bool');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
});
|
||||
expect(notifyLog.single, 'date-time');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableNumValue.value = 42.2;
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-num');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableDoubleValue.value = 42.2;
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-double');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableIntValue.value = 45;
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-int');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableStringValue.value = 'bar';
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-string');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableBoolValue.value = true;
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-bool');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 4, 4);
|
||||
});
|
||||
expect(notifyLog.single, 'nullable-date-time');
|
||||
notifyLog.clear();
|
||||
|
||||
state.setProperties(() {
|
||||
state.controllerValue.value.text = 'foo';
|
||||
});
|
||||
@ -291,8 +402,17 @@ void main() {
|
||||
state.intValue.value = 45;
|
||||
state.stringValue.value = 'bar';
|
||||
state.boolValue.value = true;
|
||||
state.dateTimeValue.value = DateTime(2020, 7, 4);
|
||||
state.nullableNumValue.value = 42.2;
|
||||
state.nullableDoubleValue.value = 42.2;
|
||||
state.nullableIntValue.value = 45;
|
||||
state.nullableStringValue.value = 'bar';
|
||||
state.nullableBoolValue.value = true;
|
||||
state.nullableDateTimeValue.value = DateTime(2020, 4, 4);
|
||||
state.controllerValue.value.text = 'foo';
|
||||
state.objectValue.value = 42;
|
||||
});
|
||||
|
||||
expect(notifyLog, isEmpty);
|
||||
});
|
||||
|
||||
@ -396,13 +516,14 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
|
||||
final RestorableInt intValue = RestorableInt(42);
|
||||
final RestorableString stringValue = RestorableString('hello world');
|
||||
final RestorableBool boolValue = RestorableBool(false);
|
||||
final RestorableDateTime dateTimeValue = RestorableDateTime(DateTime(2021, 3, 16));
|
||||
final RestorableNumN<num?> nullableNumValue = RestorableNumN<num?>(null);
|
||||
final RestorableDoubleN nullableDoubleValue = RestorableDoubleN(null);
|
||||
final RestorableIntN nullableIntValue = RestorableIntN(null);
|
||||
final RestorableStringN nullableStringValue = RestorableStringN(null);
|
||||
final RestorableBoolN nullableBoolValue = RestorableBoolN(null);
|
||||
final RestorableDateTimeN nullableDateTimeValue = RestorableDateTimeN(null);
|
||||
final RestorableTextEditingController controllerValue = RestorableTextEditingController(text: 'FooBar');
|
||||
final RestorableDateTime dateTimeValue = RestorableDateTime(DateTime(2021, 3, 16));
|
||||
final _TestRestorableValue objectValue = _TestRestorableValue();
|
||||
|
||||
@override
|
||||
@ -412,13 +533,14 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
|
||||
registerForRestoration(intValue, 'int');
|
||||
registerForRestoration(stringValue, 'string');
|
||||
registerForRestoration(boolValue, 'bool');
|
||||
registerForRestoration(dateTimeValue, 'dateTime');
|
||||
registerForRestoration(nullableNumValue, 'nullableNum');
|
||||
registerForRestoration(nullableDoubleValue, 'nullableDouble');
|
||||
registerForRestoration(nullableIntValue, 'nullableInt');
|
||||
registerForRestoration(nullableStringValue, 'nullableString');
|
||||
registerForRestoration(nullableBoolValue, 'nullableBool');
|
||||
registerForRestoration(nullableDateTimeValue, 'nullableDateTime');
|
||||
registerForRestoration(controllerValue, 'controller');
|
||||
registerForRestoration(dateTimeValue, 'dateTime');
|
||||
registerForRestoration(objectValue, 'object');
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user