Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
594ce21d
Unverified
Commit
594ce21d
authored
Mar 17, 2021
by
Shi-Hao Hong
Committed by
GitHub
Mar 17, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[State Restoration] Material Date Picker (#77879)
parent
640ae7a3
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
439 additions
and
29 deletions
+439
-29
date_picker.dart
packages/flutter/lib/src/material/date_picker.dart
+216
-28
restoration_properties.dart
packages/flutter/lib/src/widgets/restoration_properties.dart
+27
-0
date_picker_test.dart
packages/flutter/test/material/date_picker_test.dart
+182
-1
restorable_property_test.dart
packages/flutter/test/widgets/restorable_property_test.dart
+14
-0
No files found.
packages/flutter/lib/src/material/date_picker.dart
View file @
594ce21d
...
@@ -102,6 +102,122 @@ const double _inputFormLandscapeHeight = 108.0;
...
@@ -102,6 +102,122 @@ const double _inputFormLandscapeHeight = 108.0;
/// [DatePickerMode.day] mode. It defaults to [DatePickerMode.day], and
/// [DatePickerMode.day] mode. It defaults to [DatePickerMode.day], and
/// must be non-null.
/// must be non-null.
///
///
/// ### State Restoration
///
/// Using this method will not enable state restoration for the date picker.
/// In order to enable state restoration for a date picker, use
/// [Navigator.restorablePush] or [Navigator.restorablePushNamed] with
/// [DatePickerDialog].
///
/// For more information about state restoration, see [RestorationManager].
///
/// {@tool sample --template=freeform}
///
/// This sample demonstrates how to create a restorable Material date picker.
/// This is accomplished by enabling state restoration by specifying
/// [MaterialApp.restorationScopeId] and using [Navigator.restorablePush] to
/// push [DatePickerDialog] when the button is tapped.
///
/// {@macro flutter.widgets.RestorationManager}
///
/// ```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 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 {
/// @override
/// String get restorationId => 'scaffold_state';
///
/// final RestorableDateTime _selectedDate = RestorableDateTime(DateTime(2021, 7, 25));
/// late final RestorableRouteFuture<DateTime?> _restorableDatePickerRouteFuture = RestorableRouteFuture<DateTime?>(
/// onComplete: _selectDate,
/// onPresent: (NavigatorState navigator, Object? arguments) {
/// return navigator.restorablePush(
/// _datePickerRoute,
/// arguments: _selectedDate.value.millisecondsSinceEpoch,
/// );
/// },
/// );
///
/// static Route<DateTime> _datePickerRoute(
/// BuildContext context,
/// Object? arguments,
/// ) {
/// return DialogRoute<DateTime>(
/// context: context,
/// builder: (BuildContext context) {
/// return DatePickerDialog(
/// restorationId: 'date_picker_dialog',
/// initialEntryMode: DatePickerEntryMode.calendarOnly,
/// initialDate: DateTime.fromMillisecondsSinceEpoch(arguments! as int),
/// firstDate: DateTime(2021, 1, 1),
/// lastDate: DateTime(2022, 1, 1),
/// );
/// },
/// );
/// }
///
/// @override
/// void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
/// registerForRestoration(_selectedDate, 'selected_date');
/// registerForRestoration(_restorableDatePickerRouteFuture, 'date_picker_route_future');
/// }
///
/// void _selectDate(DateTime? newSelectedDate) {
/// if (newSelectedDate != null) {
/// setState(() {
/// _selectedDate.value = newSelectedDate;
/// ScaffoldMessenger.of(context).showSnackBar(SnackBar(
/// content: Text(
/// 'Selected: ${_selectedDate.value.day}/${_selectedDate.value.month}/${_selectedDate.value.year}'),
/// ));
/// });
/// }
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// return Scaffold(
/// body: Center(
/// child: OutlinedButton(
/// onPressed: () {
/// _restorableDatePickerRouteFuture.present();
/// },
/// child: const Text('Open Date Picker'),
/// ),
/// ),
/// );
/// }
/// }
/// ```
///
/// {@end-tool}
///
/// See also:
/// See also:
///
///
/// * [showDateRangePicker], which shows a material design date range picker
/// * [showDateRangePicker], which shows a material design date range picker
...
@@ -159,7 +275,7 @@ Future<DateTime?> showDatePicker({
...
@@ -159,7 +275,7 @@ Future<DateTime?> showDatePicker({
assert
(
initialDatePickerMode
!=
null
);
assert
(
initialDatePickerMode
!=
null
);
assert
(
debugCheckHasMaterialLocalizations
(
context
));
assert
(
debugCheckHasMaterialLocalizations
(
context
));
Widget
dialog
=
_
DatePickerDialog
(
Widget
dialog
=
DatePickerDialog
(
initialDate:
initialDate
,
initialDate:
initialDate
,
firstDate:
firstDate
,
firstDate:
firstDate
,
lastDate:
lastDate
,
lastDate:
lastDate
,
...
@@ -201,8 +317,18 @@ Future<DateTime?> showDatePicker({
...
@@ -201,8 +317,18 @@ Future<DateTime?> showDatePicker({
);
);
}
}
class
_DatePickerDialog
extends
StatefulWidget
{
/// A Material-style date picker dialog.
_DatePickerDialog
({
///
/// It is used internally by [showDatePicker] or can be directly pushed
/// onto the [Navigator] stack to enable state restoration. See
/// [showDatePicker] for a state restoration app example.
///
/// See also:
///
/// * [showDatePicker], which is a way to display the date picker.
class
DatePickerDialog
extends
StatefulWidget
{
/// A Material-style date picker dialog.
DatePickerDialog
({
Key
?
key
,
Key
?
key
,
required
DateTime
initialDate
,
required
DateTime
initialDate
,
required
DateTime
firstDate
,
required
DateTime
firstDate
,
...
@@ -218,6 +344,7 @@ class _DatePickerDialog extends StatefulWidget {
...
@@ -218,6 +344,7 @@ class _DatePickerDialog extends StatefulWidget {
this
.
errorInvalidText
,
this
.
errorInvalidText
,
this
.
fieldHintText
,
this
.
fieldHintText
,
this
.
fieldLabelText
,
this
.
fieldLabelText
,
this
.
restorationId
,
})
:
assert
(
initialDate
!=
null
),
})
:
assert
(
initialDate
!=
null
),
assert
(
firstDate
!=
null
),
assert
(
firstDate
!=
null
),
assert
(
lastDate
!=
null
),
assert
(
lastDate
!=
null
),
...
@@ -258,6 +385,10 @@ class _DatePickerDialog extends StatefulWidget {
...
@@ -258,6 +385,10 @@ class _DatePickerDialog extends StatefulWidget {
/// The [DateTime] representing today. It will be highlighted in the day grid.
/// The [DateTime] representing today. It will be highlighted in the day grid.
final
DateTime
currentDate
;
final
DateTime
currentDate
;
/// The initial mode of date entry method for the date picker dialog.
///
/// See [DatePickerEntryMode] for more details on the different data entry
/// modes available.
final
DatePickerEntryMode
initialEntryMode
;
final
DatePickerEntryMode
initialEntryMode
;
/// Function to provide full control over which [DateTime] can be selected.
/// Function to provide full control over which [DateTime] can be selected.
...
@@ -277,44 +408,73 @@ class _DatePickerDialog extends StatefulWidget {
...
@@ -277,44 +408,73 @@ class _DatePickerDialog extends StatefulWidget {
/// The initial display of the calendar picker.
/// The initial display of the calendar picker.
final
DatePickerMode
initialCalendarMode
;
final
DatePickerMode
initialCalendarMode
;
/// The error text displayed if the entered date is not in the correct format.
final
String
?
errorFormatText
;
final
String
?
errorFormatText
;
/// The error text displayed if the date is not valid.
///
/// A date is not valid if it is earlier than [firstDate], later than
/// [lastDate], or doesn't pass the [selectableDayPredicate].
final
String
?
errorInvalidText
;
final
String
?
errorInvalidText
;
/// The hint text displayed in the [TextField].
///
/// If this is null, it will default to the date format string. For example,
/// 'mm/dd/yyyy' for en_US.
final
String
?
fieldHintText
;
final
String
?
fieldHintText
;
/// The label text displayed in the [TextField].
///
/// If this is null, it will default to the words representing the date format
/// string. For example, 'Month, Day, Year' for en_US.
final
String
?
fieldLabelText
;
final
String
?
fieldLabelText
;
/// Restoration ID to save and restore the state of the [DatePickerDialog].
///
/// If it is non-null, the date picker will persist and restore the
/// date 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
@override
_DatePickerDialogState
createState
()
=>
_DatePickerDialogState
();
_DatePickerDialogState
createState
()
=>
_DatePickerDialogState
();
}
}
class
_DatePickerDialogState
extends
State
<
_DatePickerDialog
>
{
class
_DatePickerDialogState
extends
State
<
DatePickerDialog
>
with
RestorationMixin
{
late
final
RestorableDateTime
_selectedDate
=
RestorableDateTime
(
widget
.
initialDate
);
late
final
_RestorableDatePickerEntryMode
_entryMode
=
_RestorableDatePickerEntryMode
(
widget
.
initialEntryMode
);
final
RestorableBool
_autoValidate
=
RestorableBool
(
false
);
late
DatePickerEntryMode
_entryMode
;
@override
late
DateTime
_selectedDate
;
String
?
get
restorationId
=>
widget
.
restorationId
;
late
bool
_autoValidate
;
final
GlobalKey
_calendarPickerKey
=
GlobalKey
();
final
GlobalKey
<
FormState
>
_formKey
=
GlobalKey
<
FormState
>();
@override
@override
void
initState
()
{
void
restoreState
(
RestorationBucket
?
oldBucket
,
bool
initialRestore
)
{
super
.
initState
();
registerForRestoration
(
_selectedDate
,
'selected_date'
);
_entryMode
=
widget
.
initialEntryMode
;
registerForRestoration
(
_autoValidate
,
'autovalidate'
);
_selectedDate
=
widget
.
initialDate
;
registerForRestoration
(
_entryMode
,
'calendar_entry_mode'
);
_autoValidate
=
false
;
}
}
final
GlobalKey
_calendarPickerKey
=
GlobalKey
();
final
GlobalKey
<
FormState
>
_formKey
=
GlobalKey
<
FormState
>();
void
_handleOk
()
{
void
_handleOk
()
{
if
(
_entryMode
==
DatePickerEntryMode
.
input
||
_entryMod
e
==
DatePickerEntryMode
.
inputOnly
)
{
if
(
_entryMode
.
value
==
DatePickerEntryMode
.
input
||
_entryMode
.
valu
e
==
DatePickerEntryMode
.
inputOnly
)
{
final
FormState
form
=
_formKey
.
currentState
!;
final
FormState
form
=
_formKey
.
currentState
!;
if
(!
form
.
validate
())
{
if
(!
form
.
validate
())
{
setState
(()
=>
_autoValidate
=
true
);
setState
(()
=>
_autoValidate
.
value
=
true
);
return
;
return
;
}
}
form
.
save
();
form
.
save
();
}
}
Navigator
.
pop
(
context
,
_selectedDate
);
Navigator
.
pop
(
context
,
_selectedDate
.
value
);
}
}
void
_handleCancel
()
{
void
_handleCancel
()
{
...
@@ -323,14 +483,14 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -323,14 +483,14 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
void
_handleEntryModeToggle
()
{
void
_handleEntryModeToggle
()
{
setState
(()
{
setState
(()
{
switch
(
_entryMode
)
{
switch
(
_entryMode
.
value
)
{
case
DatePickerEntryMode
.
calendar
:
case
DatePickerEntryMode
.
calendar
:
_autoValidate
=
false
;
_autoValidate
.
value
=
false
;
_entryMode
=
DatePickerEntryMode
.
input
;
_entryMode
.
value
=
DatePickerEntryMode
.
input
;
break
;
break
;
case
DatePickerEntryMode
.
input
:
case
DatePickerEntryMode
.
input
:
_formKey
.
currentState
!.
save
();
_formKey
.
currentState
!.
save
();
_entryMode
=
DatePickerEntryMode
.
calendar
;
_entryMode
.
value
=
DatePickerEntryMode
.
calendar
;
break
;
break
;
case
DatePickerEntryMode
.
calendarOnly
:
case
DatePickerEntryMode
.
calendarOnly
:
case
DatePickerEntryMode
.
inputOnly
:
case
DatePickerEntryMode
.
inputOnly
:
...
@@ -341,12 +501,14 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -341,12 +501,14 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
}
}
void
_handleDateChanged
(
DateTime
date
)
{
void
_handleDateChanged
(
DateTime
date
)
{
setState
(()
=>
_selectedDate
=
date
);
setState
(()
{
_selectedDate
.
value
=
date
;
});
}
}
Size
_dialogSize
(
BuildContext
context
)
{
Size
_dialogSize
(
BuildContext
context
)
{
final
Orientation
orientation
=
MediaQuery
.
of
(
context
).
orientation
;
final
Orientation
orientation
=
MediaQuery
.
of
(
context
).
orientation
;
switch
(
_entryMode
)
{
switch
(
_entryMode
.
value
)
{
case
DatePickerEntryMode
.
calendar
:
case
DatePickerEntryMode
.
calendar
:
case
DatePickerEntryMode
.
calendarOnly
:
case
DatePickerEntryMode
.
calendarOnly
:
switch
(
orientation
)
{
switch
(
orientation
)
{
...
@@ -382,7 +544,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -382,7 +544,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
// layout issues.
// layout issues.
final
double
textScaleFactor
=
math
.
min
(
MediaQuery
.
of
(
context
).
textScaleFactor
,
1.3
);
final
double
textScaleFactor
=
math
.
min
(
MediaQuery
.
of
(
context
).
textScaleFactor
,
1.3
);
final
String
dateText
=
localizations
.
formatMediumDate
(
_selectedDate
);
final
String
dateText
=
localizations
.
formatMediumDate
(
_selectedDate
.
value
);
final
Color
onPrimarySurface
=
colorScheme
.
brightness
==
Brightness
.
light
final
Color
onPrimarySurface
=
colorScheme
.
brightness
==
Brightness
.
light
?
colorScheme
.
onPrimary
?
colorScheme
.
onPrimary
:
colorScheme
.
onSurface
;
:
colorScheme
.
onSurface
;
...
@@ -412,7 +574,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -412,7 +574,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
CalendarDatePicker
calendarDatePicker
()
{
CalendarDatePicker
calendarDatePicker
()
{
return
CalendarDatePicker
(
return
CalendarDatePicker
(
key:
_calendarPickerKey
,
key:
_calendarPickerKey
,
initialDate:
_selectedDate
,
initialDate:
_selectedDate
.
value
,
firstDate:
widget
.
firstDate
,
firstDate:
widget
.
firstDate
,
lastDate:
widget
.
lastDate
,
lastDate:
widget
.
lastDate
,
currentDate:
widget
.
currentDate
,
currentDate:
widget
.
currentDate
,
...
@@ -425,7 +587,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -425,7 +587,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
Form
inputDatePicker
()
{
Form
inputDatePicker
()
{
return
Form
(
return
Form
(
key:
_formKey
,
key:
_formKey
,
autovalidate:
_autoValidate
,
autovalidate:
_autoValidate
.
value
,
child:
Container
(
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
24
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
24
),
height:
orientation
==
Orientation
.
portrait
?
_inputFormPortraitHeight
:
_inputFormLandscapeHeight
,
height:
orientation
==
Orientation
.
portrait
?
_inputFormPortraitHeight
:
_inputFormLandscapeHeight
,
...
@@ -435,7 +597,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -435,7 +597,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
children:
<
Widget
>[
children:
<
Widget
>[
const
Spacer
(),
const
Spacer
(),
InputDatePickerFormField
(
InputDatePickerFormField
(
initialDate:
_selectedDate
,
initialDate:
_selectedDate
.
value
,
firstDate:
widget
.
firstDate
,
firstDate:
widget
.
firstDate
,
lastDate:
widget
.
lastDate
,
lastDate:
widget
.
lastDate
,
onDateSubmitted:
_handleDateChanged
,
onDateSubmitted:
_handleDateChanged
,
...
@@ -457,7 +619,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -457,7 +619,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
final
Widget
picker
;
final
Widget
picker
;
final
Widget
?
entryModeButton
;
final
Widget
?
entryModeButton
;
switch
(
_entryMode
)
{
switch
(
_entryMode
.
value
)
{
case
DatePickerEntryMode
.
calendar
:
case
DatePickerEntryMode
.
calendar
:
picker
=
calendarDatePicker
();
picker
=
calendarDatePicker
();
entryModeButton
=
IconButton
(
entryModeButton
=
IconButton
(
...
@@ -549,6 +711,32 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -549,6 +711,32 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
}
}
}
}
// A restorable [DatePickerEntryMode] value.
//
// This serializes each entry as a unique `int` value.
class
_RestorableDatePickerEntryMode
extends
RestorableValue
<
DatePickerEntryMode
>
{
_RestorableDatePickerEntryMode
(
DatePickerEntryMode
defaultValue
,
)
:
_defaultValue
=
defaultValue
;
final
DatePickerEntryMode
_defaultValue
;
@override
DatePickerEntryMode
createDefaultValue
()
=>
_defaultValue
;
@override
void
didUpdateValue
(
DatePickerEntryMode
?
oldValue
)
{
assert
(
debugIsSerializableForRestoration
(
value
.
index
));
notifyListeners
();
}
@override
DatePickerEntryMode
fromPrimitives
(
Object
?
data
)
=>
DatePickerEntryMode
.
values
[
data
!
as
int
];
@override
Object
?
toPrimitives
()
=>
value
.
index
;
}
/// Re-usable widget that displays the selected date (in large font) and the
/// Re-usable widget that displays the selected date (in large font) and the
/// help text above it.
/// help text above it.
///
///
...
...
packages/flutter/lib/src/widgets/restoration_properties.dart
View file @
594ce21d
...
@@ -358,6 +358,33 @@ class RestorableStringN extends _RestorablePrimitiveValueN<String?> {
...
@@ -358,6 +358,33 @@ class RestorableStringN extends _RestorablePrimitiveValueN<String?> {
RestorableStringN
(
String
?
defaultValue
)
:
super
(
defaultValue
);
RestorableStringN
(
String
?
defaultValue
)
:
super
(
defaultValue
);
}
}
/// A [RestorableValue] that knows how to save and restore [DateTime].
///
/// {@macro flutter.widgets.RestorableNum}.
class
RestorableDateTime
extends
RestorableValue
<
DateTime
>
{
/// Creates a [RestorableDateTime].
///
/// {@macro flutter.widgets.RestorableNum.constructor}
RestorableDateTime
(
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
)
=>
DateTime
.
fromMillisecondsSinceEpoch
(
data
!
as
int
);
@override
Object
?
toPrimitives
()
=>
value
.
millisecondsSinceEpoch
;
}
/// A base class for creating a [RestorableProperty] that stores and restores a
/// A base class for creating a [RestorableProperty] that stores and restores a
/// [Listenable].
/// [Listenable].
///
///
...
...
packages/flutter/test/material/date_picker_test.dart
View file @
594ce21d
...
@@ -198,7 +198,7 @@ void main() {
...
@@ -198,7 +198,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'Invalid format.'
),
findsOneWidget
);
expect
(
find
.
text
(
'Invalid format.'
),
findsOneWidget
);
// Toggle to calend
e
r mode and then back to input mode
// Toggle to calend
a
r mode and then back to input mode
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
calendar_today
));
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
calendar_today
));
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
edit
));
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
edit
));
...
@@ -1125,6 +1125,187 @@ void main() {
...
@@ -1125,6 +1125,187 @@ void main() {
expect
(
tester
.
takeException
(),
isNull
);
expect
(
tester
.
takeException
(),
isNull
);
});
});
});
});
testWidgets
(
'DatePickerDialog is state restorable'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
MaterialApp
(
restorationScopeId:
'app'
,
home:
_RestorableDatePickerDialogTestWidget
(),
),
);
// The date picker should be closed.
expect
(
find
.
byType
(
DatePickerDialog
),
findsNothing
);
expect
(
find
.
text
(
'25/7/2021'
),
findsOneWidget
);
// Open the date picker.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
byType
(
DatePickerDialog
),
findsOneWidget
);
final
TestRestorationData
restorationData
=
await
tester
.
getRestorationData
();
await
tester
.
restartAndRestore
();
// The date picker should be open after restoring.
expect
(
find
.
byType
(
DatePickerDialog
),
findsOneWidget
);
// Tap on the barrier.
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.
expect
(
find
.
byType
(
DatePickerDialog
),
findsNothing
);
expect
(
find
.
text
(
'25/7/2021'
),
findsOneWidget
);
// The date picker should be open after restoring.
await
tester
.
restoreFrom
(
restorationData
);
expect
(
find
.
byType
(
DatePickerDialog
),
findsOneWidget
);
// Select a different date and close the date picker.
await
tester
.
tap
(
find
.
text
(
'30'
));
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
text
(
'OK'
));
await
tester
.
pumpAndSettle
();
// The date picker should be closed, the text value updated to the
// newly selected date.
expect
(
find
.
byType
(
DatePickerDialog
),
findsNothing
);
expect
(
find
.
text
(
'30/7/2021'
),
findsOneWidget
);
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/33615
testWidgets
(
'DatePickerDialog state restoration - DatePickerEntryMode'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
MaterialApp
(
restorationScopeId:
'app'
,
home:
_RestorableDatePickerDialogTestWidget
(
datePickerEntryMode:
DatePickerEntryMode
.
calendarOnly
,
),
),
);
// The date picker should be closed.
expect
(
find
.
byType
(
DatePickerDialog
),
findsNothing
);
expect
(
find
.
text
(
'25/7/2021'
),
findsOneWidget
);
// Open the date picker.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
byType
(
DatePickerDialog
),
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 picker should be open after restoring.
expect
(
find
.
byType
(
DatePickerDialog
),
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
.
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.
expect
(
find
.
byType
(
DatePickerDialog
),
findsNothing
);
expect
(
find
.
text
(
'25/7/2021'
),
findsOneWidget
);
// The date picker should be open after restoring.
await
tester
.
restoreFrom
(
restorationData
);
expect
(
find
.
byType
(
DatePickerDialog
),
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
_RestorableDatePickerDialogTestWidget
extends
StatefulWidget
{
const
_RestorableDatePickerDialogTestWidget
({
Key
?
key
,
this
.
datePickerEntryMode
=
DatePickerEntryMode
.
calendar
,
})
:
super
(
key:
key
);
final
DatePickerEntryMode
datePickerEntryMode
;
@override
_RestorableDatePickerDialogTestWidgetState
createState
()
=>
_RestorableDatePickerDialogTestWidgetState
();
}
class
_RestorableDatePickerDialogTestWidgetState
extends
State
<
_RestorableDatePickerDialogTestWidget
>
with
RestorationMixin
{
@override
String
?
get
restorationId
=>
'scaffold_state'
;
final
RestorableDateTime
_selectedDate
=
RestorableDateTime
(
DateTime
(
2021
,
7
,
25
));
late
final
RestorableRouteFuture
<
DateTime
?>
_restorableDatePickerRouteFuture
=
RestorableRouteFuture
<
DateTime
?>(
onComplete:
_selectDate
,
onPresent:
(
NavigatorState
navigator
,
Object
?
arguments
)
{
return
navigator
.
restorablePush
(
_datePickerRoute
,
arguments:
<
String
,
dynamic
>{
'selectedDate'
:
_selectedDate
.
value
.
millisecondsSinceEpoch
,
'datePickerEntryMode'
:
widget
.
datePickerEntryMode
.
index
,
}
);
},
);
@override
void
restoreState
(
RestorationBucket
?
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
_selectedDate
,
'selected_date'
);
registerForRestoration
(
_restorableDatePickerRouteFuture
,
'date_picker_route_future'
);
}
void
_selectDate
(
DateTime
?
newSelectedDate
)
{
if
(
newSelectedDate
!=
null
)
{
setState
(()
{
_selectedDate
.
value
=
newSelectedDate
;
});
}
}
static
Route
<
DateTime
>
_datePickerRoute
(
BuildContext
context
,
Object
?
arguments
,
)
{
return
DialogRoute
<
DateTime
>(
context:
context
,
builder:
(
BuildContext
context
)
{
final
Map
<
dynamic
,
dynamic
>
args
=
arguments
!
as
Map
<
dynamic
,
dynamic
>;
return
DatePickerDialog
(
restorationId:
'date_picker_dialog'
,
initialEntryMode:
DatePickerEntryMode
.
values
[
args
[
'datePickerEntryMode'
]
as
int
],
initialDate:
DateTime
.
fromMillisecondsSinceEpoch
(
args
[
'selectedDate'
]
as
int
),
firstDate:
DateTime
(
2021
,
1
,
1
),
lastDate:
DateTime
(
2022
,
1
,
1
),
);
},
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
DateTime
selectedDateTime
=
_selectedDate
.
value
;
// Example: "25/7/1994"
final
String
selectedDateTimeString
=
'
${selectedDateTime.day}
/
${selectedDateTime.month}
/
${selectedDateTime.year}
'
;
return
Scaffold
(
body:
Center
(
child:
Column
(
children:
<
Widget
>[
OutlinedButton
(
onPressed:
()
{
_restorableDatePickerRouteFuture
.
present
();
},
child:
const
Text
(
'X'
),
),
Text
(
selectedDateTimeString
),
],
),
),
);
}
}
}
class
_DatePickerObserver
extends
NavigatorObserver
{
class
_DatePickerObserver
extends
NavigatorObserver
{
...
...
packages/flutter/test/widgets/restorable_property_test.dart
View file @
594ce21d
...
@@ -18,6 +18,7 @@ void main() {
...
@@ -18,6 +18,7 @@ void main() {
expect
(()
=>
RestorableStringN
(
'hello'
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableStringN
(
'hello'
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableBoolN
(
true
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableBoolN
(
true
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableTextEditingController
().
value
,
throwsAssertionError
);
expect
(()
=>
RestorableTextEditingController
().
value
,
throwsAssertionError
);
expect
(()
=>
RestorableDateTime
(
DateTime
(
2020
,
4
,
3
)).
value
,
throwsAssertionError
);
expect
(()
=>
_TestRestorableValue
().
value
,
throwsAssertionError
);
expect
(()
=>
_TestRestorableValue
().
value
,
throwsAssertionError
);
});
});
...
@@ -34,6 +35,7 @@ void main() {
...
@@ -34,6 +35,7 @@ void main() {
expect
(
state
.
stringValue
.
value
,
'hello world'
);
expect
(
state
.
stringValue
.
value
,
'hello world'
);
expect
(
state
.
boolValue
.
value
,
false
);
expect
(
state
.
boolValue
.
value
,
false
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2021
,
3
,
16
));
expect
(
state
.
objectValue
.
value
,
55
);
expect
(
state
.
objectValue
.
value
,
55
);
// Modify values.
// Modify values.
...
@@ -44,6 +46,7 @@ void main() {
...
@@ -44,6 +46,7 @@ void main() {
state
.
stringValue
.
value
=
'guten tag'
;
state
.
stringValue
.
value
=
'guten tag'
;
state
.
boolValue
.
value
=
true
;
state
.
boolValue
.
value
=
true
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
state
.
objectValue
.
value
=
53
;
});
});
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -54,6 +57,7 @@ void main() {
...
@@ -54,6 +57,7 @@ void main() {
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2020
,
7
,
4
));
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
});
});
...
@@ -74,6 +78,7 @@ void main() {
...
@@ -74,6 +78,7 @@ void main() {
expect
(
state
.
stringValue
.
value
,
'hello world'
);
expect
(
state
.
stringValue
.
value
,
'hello world'
);
expect
(
state
.
boolValue
.
value
,
false
);
expect
(
state
.
boolValue
.
value
,
false
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2021
,
3
,
16
));
expect
(
state
.
objectValue
.
value
,
55
);
expect
(
state
.
objectValue
.
value
,
55
);
// Modify values.
// Modify values.
...
@@ -84,6 +89,7 @@ void main() {
...
@@ -84,6 +89,7 @@ void main() {
state
.
stringValue
.
value
=
'guten tag'
;
state
.
stringValue
.
value
=
'guten tag'
;
state
.
boolValue
.
value
=
true
;
state
.
boolValue
.
value
=
true
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
state
.
objectValue
.
value
=
53
;
});
});
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -94,6 +100,7 @@ void main() {
...
@@ -94,6 +100,7 @@ void main() {
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2020
,
7
,
4
));
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
...
@@ -109,6 +116,7 @@ void main() {
...
@@ -109,6 +116,7 @@ void main() {
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
stringValue
.
value
,
'guten tag'
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
boolValue
.
value
,
true
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2020
,
7
,
4
));
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
});
});
...
@@ -135,6 +143,7 @@ void main() {
...
@@ -135,6 +143,7 @@ void main() {
state
.
nullableStringValue
.
value
=
'hullo'
;
state
.
nullableStringValue
.
value
=
'hullo'
;
state
.
nullableBoolValue
.
value
=
false
;
state
.
nullableBoolValue
.
value
=
false
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
state
.
objectValue
.
value
=
53
;
});
});
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -155,6 +164,7 @@ void main() {
...
@@ -155,6 +164,7 @@ void main() {
state
.
nullableStringValue
.
value
=
'ni hao'
;
state
.
nullableStringValue
.
value
=
'ni hao'
;
state
.
nullableBoolValue
.
value
=
null
;
state
.
nullableBoolValue
.
value
=
null
;
state
.
controllerValue
.
value
.
text
=
'blub'
;
state
.
controllerValue
.
value
.
text
=
'blub'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
3
,
2
);
state
.
objectValue
.
value
=
20
;
state
.
objectValue
.
value
=
20
;
});
});
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -174,6 +184,7 @@ void main() {
...
@@ -174,6 +184,7 @@ void main() {
expect
(
state
.
nullableStringValue
.
value
,
'hullo'
);
expect
(
state
.
nullableStringValue
.
value
,
'hullo'
);
expect
(
state
.
nullableBoolValue
.
value
,
false
);
expect
(
state
.
nullableBoolValue
.
value
,
false
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'blabla'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2020
,
7
,
4
));
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
state
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
expect
(
state
.
controllerValue
.
value
,
isNot
(
same
(
controller
)));
expect
(
state
.
controllerValue
.
value
,
isNot
(
same
(
controller
)));
...
@@ -191,6 +202,7 @@ void main() {
...
@@ -191,6 +202,7 @@ void main() {
expect
(
state
.
nullableStringValue
.
value
,
null
);
expect
(
state
.
nullableStringValue
.
value
,
null
);
expect
(
state
.
nullableBoolValue
.
value
,
null
);
expect
(
state
.
nullableBoolValue
.
value
,
null
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
controllerValue
.
value
.
text
,
'FooBar'
);
expect
(
state
.
dateTimeValue
.
value
,
DateTime
(
2021
,
3
,
16
));
expect
(
state
.
objectValue
.
value
,
55
);
expect
(
state
.
objectValue
.
value
,
55
);
expect
(
find
.
text
(
'hello world'
),
findsOneWidget
);
expect
(
find
.
text
(
'hello world'
),
findsOneWidget
);
});
});
...
@@ -390,6 +402,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
...
@@ -390,6 +402,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
final
RestorableStringN
nullableStringValue
=
RestorableStringN
(
null
);
final
RestorableStringN
nullableStringValue
=
RestorableStringN
(
null
);
final
RestorableBoolN
nullableBoolValue
=
RestorableBoolN
(
null
);
final
RestorableBoolN
nullableBoolValue
=
RestorableBoolN
(
null
);
final
RestorableTextEditingController
controllerValue
=
RestorableTextEditingController
(
text:
'FooBar'
);
final
RestorableTextEditingController
controllerValue
=
RestorableTextEditingController
(
text:
'FooBar'
);
final
RestorableDateTime
dateTimeValue
=
RestorableDateTime
(
DateTime
(
2021
,
3
,
16
));
final
_TestRestorableValue
objectValue
=
_TestRestorableValue
();
final
_TestRestorableValue
objectValue
=
_TestRestorableValue
();
@override
@override
...
@@ -405,6 +418,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
...
@@ -405,6 +418,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
registerForRestoration
(
nullableStringValue
,
'nullableString'
);
registerForRestoration
(
nullableStringValue
,
'nullableString'
);
registerForRestoration
(
nullableBoolValue
,
'nullableBool'
);
registerForRestoration
(
nullableBoolValue
,
'nullableBool'
);
registerForRestoration
(
controllerValue
,
'controller'
);
registerForRestoration
(
controllerValue
,
'controller'
);
registerForRestoration
(
dateTimeValue
,
'dateTime'
);
registerForRestoration
(
objectValue
,
'object'
);
registerForRestoration
(
objectValue
,
'object'
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment