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
Expand all
Show 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
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/restoration_properties.dart
View file @
594ce21d
...
...
@@ -358,6 +358,33 @@ class RestorableStringN extends _RestorablePrimitiveValueN<String?> {
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
/// [Listenable].
///
...
...
packages/flutter/test/material/date_picker_test.dart
View file @
594ce21d
...
...
@@ -198,7 +198,7 @@ void main() {
await
tester
.
pumpAndSettle
();
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
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
edit
));
...
...
@@ -1125,6 +1125,187 @@ void main() {
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
{
...
...
packages/flutter/test/widgets/restorable_property_test.dart
View file @
594ce21d
...
...
@@ -18,6 +18,7 @@ void main() {
expect
(()
=>
RestorableStringN
(
'hello'
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableBoolN
(
true
).
value
,
throwsAssertionError
);
expect
(()
=>
RestorableTextEditingController
().
value
,
throwsAssertionError
);
expect
(()
=>
RestorableDateTime
(
DateTime
(
2020
,
4
,
3
)).
value
,
throwsAssertionError
);
expect
(()
=>
_TestRestorableValue
().
value
,
throwsAssertionError
);
});
...
...
@@ -34,6 +35,7 @@ void main() {
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
.
objectValue
.
value
,
55
);
// Modify values.
...
...
@@ -44,6 +46,7 @@ void main() {
state
.
stringValue
.
value
=
'guten tag'
;
state
.
boolValue
.
value
=
true
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
});
await
tester
.
pump
();
...
...
@@ -54,6 +57,7 @@ void main() {
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
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
});
...
...
@@ -74,6 +78,7 @@ void main() {
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
.
objectValue
.
value
,
55
);
// Modify values.
...
...
@@ -84,6 +89,7 @@ void main() {
state
.
stringValue
.
value
=
'guten tag'
;
state
.
boolValue
.
value
=
true
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
});
await
tester
.
pump
();
...
...
@@ -94,6 +100,7 @@ void main() {
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
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
...
...
@@ -109,6 +116,7 @@ void main() {
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
.
objectValue
.
value
,
53
);
expect
(
find
.
text
(
'guten tag'
),
findsOneWidget
);
});
...
...
@@ -135,6 +143,7 @@ void main() {
state
.
nullableStringValue
.
value
=
'hullo'
;
state
.
nullableBoolValue
.
value
=
false
;
state
.
controllerValue
.
value
.
text
=
'blabla'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
7
,
4
);
state
.
objectValue
.
value
=
53
;
});
await
tester
.
pump
();
...
...
@@ -155,6 +164,7 @@ void main() {
state
.
nullableStringValue
.
value
=
'ni hao'
;
state
.
nullableBoolValue
.
value
=
null
;
state
.
controllerValue
.
value
.
text
=
'blub'
;
state
.
dateTimeValue
.
value
=
DateTime
(
2020
,
3
,
2
);
state
.
objectValue
.
value
=
20
;
});
await
tester
.
pump
();
...
...
@@ -174,6 +184,7 @@ void main() {
expect
(
state
.
nullableStringValue
.
value
,
'hullo'
);
expect
(
state
.
nullableBoolValue
.
value
,
false
);
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
)));
...
...
@@ -191,6 +202,7 @@ void main() {
expect
(
state
.
nullableStringValue
.
value
,
null
);
expect
(
state
.
nullableBoolValue
.
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
);
});
...
...
@@ -390,6 +402,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
final
RestorableStringN
nullableStringValue
=
RestorableStringN
(
null
);
final
RestorableBoolN
nullableBoolValue
=
RestorableBoolN
(
null
);
final
RestorableTextEditingController
controllerValue
=
RestorableTextEditingController
(
text:
'FooBar'
);
final
RestorableDateTime
dateTimeValue
=
RestorableDateTime
(
DateTime
(
2021
,
3
,
16
));
final
_TestRestorableValue
objectValue
=
_TestRestorableValue
();
@override
...
...
@@ -405,6 +418,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
registerForRestoration
(
nullableStringValue
,
'nullableString'
);
registerForRestoration
(
nullableBoolValue
,
'nullableBool'
);
registerForRestoration
(
controllerValue
,
'controller'
);
registerForRestoration
(
dateTimeValue
,
'dateTime'
);
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