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
39a46bed
Unverified
Commit
39a46bed
authored
Aug 13, 2020
by
Michael Goderbauer
Committed by
GitHub
Aug 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove decommission from RestorationBuckets (#63687)
parent
06c3de32
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
459 additions
and
308 deletions
+459
-308
restoration.dart
packages/flutter/lib/src/services/restoration.dart
+40
-82
restoration.dart
packages/flutter/lib/src/widgets/restoration.dart
+95
-74
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+2
-2
restoration.dart
packages/flutter/test/services/restoration.dart
+7
-0
restoration_bucket_test.dart
packages/flutter/test/services/restoration_bucket_test.dart
+0
-53
restoration_test.dart
packages/flutter/test/services/restoration_test.dart
+64
-9
restorable_property_test.dart
packages/flutter/test/widgets/restorable_property_test.dart
+1
-1
restoration_mixin_test.dart
packages/flutter/test/widgets/restoration_mixin_test.dart
+1
-45
restoration_scope_test.dart
packages/flutter/test/widgets/restoration_scope_test.dart
+0
-39
restoration_scopes_moving_test.dart
.../flutter/test/widgets/restoration_scopes_moving_test.dart
+238
-0
root_restoration_scope_test.dart
...ges/flutter/test/widgets/root_restoration_scope_test.dart
+2
-2
restoration.dart
packages/flutter_test/lib/src/restoration.dart
+8
-0
restoration_test.dart
packages/flutter_test/test/restoration_test.dart
+1
-1
No files found.
packages/flutter/lib/src/services/restoration.dart
View file @
39a46bed
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/restoration.dart
View file @
39a46bed
...
...
@@ -104,7 +104,7 @@ class _RestorationScopeState extends State<RestorationScope> with RestorationMix
String
get
restorationId
=>
widget
.
restorationId
;
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
// Nothing to do.
// The bucket gets injected into the widget tree in the build method.
}
...
...
@@ -648,7 +648,7 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
/// String get restorationId => widget.restorationId;
///
/// @override
/// void restoreState(RestorationBucket oldBucket) {
/// void restoreState(RestorationBucket oldBucket
, bool initialRestore
) {
/// // All restorable properties must be registered with the mixin. After
/// // registration, the counter either has its old value restored or is
/// // initialized to its default value.
...
...
@@ -783,7 +783,7 @@ mixin RestorationMixin<S extends StatefulWidget> on State<S> {
/// [bucket].
@mustCallSuper
@protected
void
restoreState
(
RestorationBucket
oldBucket
);
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
);
/// Called when [bucket] switches between null and non-null values.
///
...
...
@@ -804,8 +804,8 @@ mixin RestorationMixin<S extends StatefulWidget> on State<S> {
@mustCallSuper
@protected
void
didToggleBucket
(
RestorationBucket
oldBucket
)
{
// When
restore is pending, restoreState must be
called instead.
assert
(
!
restorePending
);
// When
a bucket is replaced, must `restoreState` is
called instead.
assert
(
_bucket
?.
isReplacing
!=
true
);
}
// Maps properties to their listeners.
...
...
@@ -900,8 +900,22 @@ mixin RestorationMixin<S extends StatefulWidget> on State<S> {
/// [restorationId] was caused by an updated widget.
@protected
void
didUpdateRestorationId
()
{
if
(
_bucket
?.
restorationId
!=
restorationId
&&
!
restorePending
)
{
_updateBucketIfNecessary
();
// There's nothing to do if:
// - We don't have a parent to claim a bucket from.
// - Our current bucket already uses the provided restoration ID.
// - There's a restore pending, which means that didUpdateDependencies
// will be called and we handle the rename there.
if
(
_currentParent
==
null
||
_bucket
?.
restorationId
==
restorationId
||
restorePending
)
{
return
;
}
final
RestorationBucket
oldBucket
=
_bucket
;
assert
(!
restorePending
);
final
bool
didReplaceBucket
=
_updateBucketIfNecessary
(
parent:
_currentParent
,
restorePending:
false
);
if
(
didReplaceBucket
)
{
assert
(
oldBucket
!=
_bucket
);
assert
(
_bucket
==
null
||
oldBucket
==
null
);
oldBucket
?.
dispose
();
}
}
...
...
@@ -923,98 +937,105 @@ mixin RestorationMixin<S extends StatefulWidget> on State<S> {
/// While this is true, [bucket] will also still return the old bucket with
/// the old restoration data. It will update to the new bucket with the new
/// data just before [restoreState] is invoked.
bool
get
restorePending
=>
_restorePending
;
bool
_restorePending
=
true
;
bool
get
restorePending
{
if
(
_firstRestorePending
)
{
return
true
;
}
if
(
restorationId
==
null
)
{
return
false
;
}
final
RestorationBucket
potentialNewParent
=
RestorationScope
.
of
(
context
);
return
potentialNewParent
!=
_currentParent
&&
potentialNewParent
?.
isReplacing
==
true
;
}
List
<
RestorableProperty
<
Object
>>
_debugPropertiesWaitingForReregistration
;
bool
get
_debugDoingRestore
=>
_debugPropertiesWaitingForReregistration
!=
null
;
bool
_firstRestorePending
=
true
;
RestorationBucket
_currentParent
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
RestorationBucket
oldBucket
;
if
(
_restorePending
)
{
oldBucket
=
_bucket
;
// Throw away the old bucket so [_updateBucketIfNecessary] will claim a
// new one with the new restoration data.
_bucket
=
null
;
}
_updateBucketIfNecessary
();
if
(
_restorePending
)
{
_restorePending
=
false
;
assert
(()
{
_debugPropertiesWaitingForReregistration
=
_properties
.
keys
.
toList
();
return
true
;
}());
restoreState
(
oldBucket
);
assert
(()
{
if
(
_debugPropertiesWaitingForReregistration
.
isNotEmpty
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'Previously registered RestorableProperties must be re-registered in "restoreState".'
,
),
ErrorDescription
(
'The RestorableProperties with the following IDs were not re-registered to
$this
when '
'"restoreState" was called:'
,
),
...
_debugPropertiesWaitingForReregistration
.
map
((
RestorableProperty
<
Object
>
property
)
=>
ErrorDescription
(
' *
${property._restorationId}
'
,
)),
]);
}
_debugPropertiesWaitingForReregistration
=
null
;
return
true
;
}());
final
RestorationBucket
oldBucket
=
_bucket
;
final
bool
needsRestore
=
restorePending
;
_currentParent
=
RestorationScope
.
of
(
context
);
final
bool
didReplaceBucket
=
_updateBucketIfNecessary
(
parent:
_currentParent
,
restorePending:
needsRestore
);
if
(
needsRestore
)
{
_doRestore
(
oldBucket
);
}
if
(
didReplaceBucket
)
{
assert
(
oldBucket
!=
_bucket
);
oldBucket
?.
dispose
();
}
}
void
_markNeedsRestore
()
{
_restorePending
=
true
;
// [didChangeDependencies] will be called next because our bucket can only
// become invalid if our parent bucket ([RestorationScope.of]) is replaced
// with a new one.
void
_doRestore
(
RestorationBucket
oldBucket
)
{
assert
(()
{
_debugPropertiesWaitingForReregistration
=
_properties
.
keys
.
toList
();
return
true
;
}());
restoreState
(
oldBucket
,
_firstRestorePending
);
_firstRestorePending
=
false
;
assert
(()
{
if
(
_debugPropertiesWaitingForReregistration
.
isNotEmpty
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'Previously registered RestorableProperties must be re-registered in "restoreState".'
,
),
ErrorDescription
(
'The RestorableProperties with the following IDs were not re-registered to
$this
when '
'"restoreState" was called:'
,
),
...
_debugPropertiesWaitingForReregistration
.
map
((
RestorableProperty
<
Object
>
property
)
=>
ErrorDescription
(
' *
${property._restorationId}
'
,
)),
]);
}
_debugPropertiesWaitingForReregistration
=
null
;
return
true
;
}());
}
void
_updateBucketIfNecessary
()
{
if
(
restorationId
==
null
)
{
_setNewBucketIfNecessary
(
newBucket:
null
);
// Returns true if `bucket` has been replaced with a new bucket. It's the
// responsibility of the caller to dispose the old bucket when this returns true.
bool
_updateBucketIfNecessary
({
@required
RestorationBucket
parent
,
@required
bool
restorePending
,
})
{
if
(
restorationId
==
null
||
parent
==
null
)
{
final
bool
didReplace
=
_setNewBucketIfNecessary
(
newBucket:
null
,
restorePending:
restorePending
);
assert
(
_bucket
==
null
);
return
;
return
didReplace
;
}
final
RestorationBucket
newParent
=
RestorationScope
.
of
(
context
);
if
(
newParent
==
null
)
{
_setNewBucketIfNecessary
(
newBucket:
null
);
assert
(
_bucket
==
null
);
return
;
}
if
(
_bucket
==
null
)
{
assert
(
newParent
!=
null
);
assert
(
restorationId
!=
null
);
final
RestorationBucket
newBucket
=
newParent
.
claimChild
(
restorationId
,
debugOwner:
this
)
..
addListener
(
_markNeedsRestore
);
assert
(
restorationId
!=
null
);
assert
(
parent
!=
null
);
if
(
restorePending
||
_bucket
==
null
)
{
final
RestorationBucket
newBucket
=
parent
.
claimChild
(
restorationId
,
debugOwner:
this
);
assert
(
newBucket
!=
null
);
_setNewBucketIfNecessary
(
newBucket:
newBucket
);
final
bool
didReplace
=
_setNewBucketIfNecessary
(
newBucket:
newBucket
,
restorePending:
restorePending
);
assert
(
_bucket
==
newBucket
);
return
;
return
didReplace
;
}
// We have an existing bucket, make sure it has the right parent and id.
assert
(
_bucket
!=
null
);
assert
(
newParent
!=
null
);
assert
(
restorationId
!=
null
);
assert
(!
restorePending
);
_bucket
.
rename
(
restorationId
);
newParent
.
adoptChild
(
_bucket
);
parent
.
adoptChild
(
_bucket
);
return
false
;
}
void
_setNewBucketIfNecessary
({
@required
RestorationBucket
newBucket
})
{
// Returns true if `bucket` has been replaced with a new bucket. It's the
// responsibility of the caller to dispose the old bucket when this returns true.
bool
_setNewBucketIfNecessary
({
@required
RestorationBucket
newBucket
,
@required
bool
restorePending
})
{
if
(
newBucket
==
_bucket
)
{
return
;
return
false
;
}
assert
(
newBucket
==
null
||
_bucket
==
null
);
final
RestorationBucket
oldBucket
=
_bucket
;
_bucket
=
newBucket
;
if
(!
restorePending
)
{
...
...
@@ -1024,7 +1045,7 @@ mixin RestorationMixin<S extends StatefulWidget> on State<S> {
}
didToggleBucket
(
oldBucket
);
}
oldBucket
?.
dispose
()
;
return
true
;
}
void
_updateProperty
(
RestorableProperty
<
Object
>
property
)
{
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
39a46bed
...
...
@@ -402,11 +402,11 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
}
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
_persistedScrollOffset
,
'offset'
);
assert
(
position
!=
null
);
if
(
_persistedScrollOffset
.
value
!=
null
)
{
position
.
restoreOffset
(
_persistedScrollOffset
.
value
,
initialRestore:
oldBucket
==
null
);
position
.
restoreOffset
(
_persistedScrollOffset
.
value
,
initialRestore:
initialRestore
);
}
}
...
...
packages/flutter/test/services/restoration.dart
View file @
39a46bed
...
...
@@ -50,9 +50,16 @@ class MockRestorationManager extends TestRestorationManager {
Future
<
RestorationBucket
>
_rootBucket
;
set
rootBucket
(
Future
<
RestorationBucket
>
value
)
{
_rootBucket
=
value
;
_isRestoring
=
true
;
ServicesBinding
.
instance
.
addPostFrameCallback
((
Duration
_
)
{
_isRestoring
=
false
;
});
notifyListeners
();
}
@override
bool
get
isReplacing
=>
_isRestoring
;
bool
_isRestoring
;
@override
Future
<
void
>
sendToEngine
(
Uint8List
encodedData
)
{
...
...
packages/flutter/test/services/restoration_bucket_test.dart
View file @
39a46bed
...
...
@@ -541,60 +541,12 @@ void main() {
expect
(
rawData
[
childrenMapKey
][
'child1'
][
childrenMapKey
][
'child2'
][
valuesMapKey
][
'hello'
],
'world'
);
});
test
(
'decommission drops itself from parent and notifies all listeners'
,
()
{
final
MockRestorationManager
manager
=
MockRestorationManager
();
final
Map
<
String
,
dynamic
>
rawData
=
_createRawDataSet
();
final
RestorationBucket
root
=
RestorationBucket
.
root
(
manager:
manager
,
rawData:
rawData
);
final
RestorationBucket
child1
=
root
.
claimChild
(
'child1'
,
debugOwner:
'owner1'
);
final
RestorationBucket
child2
=
root
.
claimChild
(
'child2'
,
debugOwner:
'owner1'
);
final
RestorationBucket
childOfChild1
=
child1
.
claimChild
(
'child1.1'
,
debugOwner:
'owner1'
);
final
RestorationBucket
childOfChildOfChild1
=
childOfChild1
.
claimChild
(
'child1.1.1'
,
debugOwner:
'owner1'
);
expect
(
manager
.
updateScheduled
,
isTrue
);
manager
.
doSerialization
();
expect
(
manager
.
updateScheduled
,
isFalse
);
bool
rootDecommissioned
=
false
;
root
.
addListener
(()
{
rootDecommissioned
=
true
;
});
bool
child1Decommissioned
=
false
;
child1
.
addListener
(()
{
child1Decommissioned
=
true
;
});
bool
child2Decommissioned
=
false
;
child2
.
addListener
(()
{
child2Decommissioned
=
true
;
});
bool
childOfChild1Decommissioned
=
false
;
childOfChild1
.
addListener
(()
{
childOfChild1Decommissioned
=
true
;
});
bool
childOfChildOfChild1Decommissioned
=
false
;
childOfChildOfChild1
.
addListener
(()
{
childOfChildOfChild1Decommissioned
=
true
;
});
expect
(
rawData
[
childrenMapKey
].
containsKey
(
'child1'
),
isTrue
);
child1
.
decommission
();
expect
(
rootDecommissioned
,
isFalse
);
expect
(
child2Decommissioned
,
isFalse
);
expect
(
child1Decommissioned
,
isTrue
);
expect
(
childOfChild1Decommissioned
,
isTrue
);
expect
(
childOfChildOfChild1Decommissioned
,
isTrue
);
expect
(
rawData
[
childrenMapKey
].
containsKey
(
'child1'
),
isFalse
);
});
test
(
'throws when used after dispose'
,
()
{
final
RestorationBucket
bucket
=
RestorationBucket
.
empty
(
restorationId:
'foo'
,
debugOwner:
null
);
bucket
.
dispose
();
expect
(()
=>
bucket
.
debugOwner
,
throwsFlutterError
);
expect
(()
=>
bucket
.
restorationId
,
throwsFlutterError
);
expect
(()
=>
bucket
.
decommission
(),
throwsFlutterError
);
expect
(()
=>
bucket
.
read
<
int
>(
'foo'
),
throwsFlutterError
);
expect
(()
=>
bucket
.
write
(
'foo'
,
10
),
throwsFlutterError
);
expect
(()
=>
bucket
.
remove
<
int
>(
'foo'
),
throwsFlutterError
);
...
...
@@ -605,11 +557,6 @@ void main() {
expect
(()
=>
bucket
.
rename
(
'bar'
),
throwsFlutterError
);
expect
(()
=>
bucket
.
dispose
(),
throwsFlutterError
);
});
test
(
'cannot serialize without manager'
,
()
{
final
RestorationBucket
bucket
=
RestorationBucket
.
empty
(
restorationId:
'foo'
,
debugOwner:
null
);
expect
(()
=>
bucket
.
write
(
'foo'
,
10
),
throwsAssertionError
);
});
}
Map
<
String
,
dynamic
>
_createRawDataSet
()
{
...
...
packages/flutter/test/services/restoration_test.dart
View file @
39a46bed
...
...
@@ -126,26 +126,21 @@ void main() {
final
RestorationBucket
child
=
rootBucket
.
claimChild
(
'child1'
,
debugOwner:
null
);
expect
(
child
.
read
<
int
>(
'another value'
),
22
);
bool
rootDecommissioned
=
false
;
bool
childDecommissioned
=
false
;
bool
rootReplaced
=
false
;
RestorationBucket
newRoot
;
rootBucket
.
addListener
(()
{
root
Decommission
ed
=
true
;
manager
.
addListener
(()
{
root
Replac
ed
=
true
;
manager
.
rootBucket
.
then
((
RestorationBucket
bucket
)
{
newRoot
=
bucket
;
});
// The new bucket is available synchronously.
expect
(
newRoot
,
isNotNull
);
});
child
.
addListener
(()
{
childDecommissioned
=
true
;
});
// Send new Data.
await
_pushDataFromEngine
(
_createEncodedRestorationData2
());
expect
(
rootDecommissioned
,
isTrue
);
expect
(
childDecommissioned
,
isTrue
);
expect
(
rootReplaced
,
isTrue
);
expect
(
newRoot
,
isNot
(
same
(
rootBucket
)));
child
.
dispose
();
...
...
@@ -234,6 +229,60 @@ void main() {
manager
.
flushData
();
expect
(
callsToEngine
,
hasLength
(
1
));
});
testWidgets
(
'isReplacing'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
Map
<
dynamic
,
dynamic
>>
result
=
Completer
<
Map
<
dynamic
,
dynamic
>>();
SystemChannels
.
restoration
.
setMockMethodCallHandler
((
MethodCall
call
)
{
return
result
.
future
;
});
final
TestRestorationManager
manager
=
TestRestorationManager
();
expect
(
manager
.
isReplacing
,
isFalse
);
RestorationBucket
rootBucket
;
manager
.
rootBucket
.
then
((
RestorationBucket
bucket
)
{
rootBucket
=
bucket
;
});
result
.
complete
(
_createEncodedRestorationData1
());
await
tester
.
idle
();
expect
(
rootBucket
,
isNotNull
);
expect
(
rootBucket
.
isReplacing
,
isFalse
);
expect
(
manager
.
isReplacing
,
isFalse
);
tester
.
binding
.
scheduleFrame
();
await
tester
.
pump
();
expect
(
manager
.
isReplacing
,
isFalse
);
expect
(
rootBucket
.
isReplacing
,
isFalse
);
manager
.
receiveDataFromEngine
(
enabled:
true
,
data:
null
);
RestorationBucket
rootBucket2
;
manager
.
rootBucket
.
then
((
RestorationBucket
bucket
)
{
rootBucket2
=
bucket
;
});
expect
(
rootBucket2
,
isNotNull
);
expect
(
rootBucket2
,
isNot
(
same
(
rootBucket
)));
expect
(
manager
.
isReplacing
,
isTrue
);
expect
(
rootBucket2
.
isReplacing
,
isTrue
);
await
tester
.
idle
();
expect
(
manager
.
isReplacing
,
isTrue
);
expect
(
rootBucket2
.
isReplacing
,
isTrue
);
tester
.
binding
.
scheduleFrame
();
await
tester
.
pump
();
expect
(
manager
.
isReplacing
,
isFalse
);
expect
(
rootBucket2
.
isReplacing
,
isFalse
);
manager
.
receiveDataFromEngine
(
enabled:
false
,
data:
null
);
RestorationBucket
rootBucket3
;
manager
.
rootBucket
.
then
((
RestorationBucket
bucket
)
{
rootBucket3
=
bucket
;
});
expect
(
rootBucket3
,
isNull
);
expect
(
manager
.
isReplacing
,
isFalse
);
await
tester
.
idle
();
expect
(
manager
.
isReplacing
,
isFalse
);
tester
.
binding
.
scheduleFrame
();
await
tester
.
pump
();
expect
(
manager
.
isReplacing
,
isFalse
);
});
});
test
(
'debugIsSerializableForRestoration'
,
()
{
...
...
@@ -305,3 +354,9 @@ Map<dynamic, dynamic> _packageRestorationData({bool enabled = true, Map<dynamic,
'data'
:
encoded
==
null
?
null
:
encoded
.
buffer
.
asUint8List
(
encoded
.
offsetInBytes
,
encoded
.
lengthInBytes
)
};
}
class
TestRestorationManager
extends
RestorationManager
{
void
receiveDataFromEngine
({
@required
bool
enabled
,
@required
Uint8List
data
})
{
handleRestorationUpdateFromEngine
(
enabled:
enabled
,
data:
data
);
}
}
packages/flutter/test/widgets/restorable_property_test.dart
View file @
39a46bed
...
...
@@ -353,7 +353,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
final
_TestRestorableValue
objectValue
=
_TestRestorableValue
();
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
numValue
,
'num'
);
registerForRestoration
(
doubleValue
,
'double'
);
registerForRestoration
(
intValue
,
'int'
);
...
...
packages/flutter/test/widgets/restoration_mixin_test.dart
View file @
39a46bed
...
...
@@ -356,50 +356,6 @@ void main() {
expect
(
rawData
[
childrenMapKey
].
containsKey
(
'moving-child'
),
isTrue
);
});
testWidgets
(
'decommission claims new bucket with data'
,
(
WidgetTester
tester
)
async
{
final
MockRestorationManager
manager
=
MockRestorationManager
();
RestorationBucket
root
=
RestorationBucket
.
root
(
manager:
manager
,
rawData:
<
String
,
dynamic
>{});
await
tester
.
pumpWidget
(
UnmanagedRestorationScope
(
bucket:
root
,
child:
const
_TestRestorableWidget
(
restorationId:
'child1'
,
),
),
);
manager
.
doSerialization
();
final
_TestRestorableWidgetState
state
=
tester
.
state
(
find
.
byType
(
_TestRestorableWidget
));
expect
(
state
.
bucket
.
restorationId
,
'child1'
);
expect
(
state
.
property
.
value
,
10
);
// Initialized to default.
expect
(
state
.
bucket
.
read
<
int
>(
'foo'
),
10
);
final
RestorationBucket
bucket
=
state
.
bucket
;
state
.
property
.
log
.
clear
();
state
.
restoreStateLog
.
clear
();
// Replace root bucket.
root
..
decommission
()..
dispose
();
root
=
RestorationBucket
.
root
(
manager:
manager
,
rawData:
_createRawDataSet
());
await
tester
.
pumpWidget
(
UnmanagedRestorationScope
(
bucket:
root
,
child:
const
_TestRestorableWidget
(
restorationId:
'child1'
,
),
),
);
// Bucket has been replaced.
expect
(
state
.
bucket
,
isNot
(
same
(
bucket
)));
expect
(
state
.
bucket
.
restorationId
,
'child1'
);
expect
(
state
.
property
.
value
,
22
);
// Restored value.
expect
(
state
.
bucket
.
read
<
int
>(
'foo'
),
22
);
expect
(
state
.
restoreStateLog
.
single
,
bucket
);
expect
(
state
.
toogleBucketLog
,
isEmpty
);
expect
(
state
.
property
.
log
,
<
String
>[
'fromPrimitives'
,
'initWithValue'
]);
});
testWidgets
(
'restartAndRestore'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
...
...
@@ -711,7 +667,7 @@ class _TestRestorableWidgetState extends State<_TestRestorableWidget> with Resto
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
restoreStateLog
.
add
(
oldBucket
);
registerForRestoration
(
property
,
'foo'
);
if
(
_rerigisterAdditionalProperty
&&
additionalProperty
!=
null
)
{
...
...
packages/flutter/test/widgets/restoration_scope_test.dart
View file @
39a46bed
...
...
@@ -323,45 +323,6 @@ void main() {
expect
(
rawData
[
childrenMapKey
][
'fixed'
],
isEmpty
);
expect
(
rawData
[
childrenMapKey
].
containsKey
(
'moving-child'
),
isTrue
);
});
testWidgets
(
'decommission claims new bucket with data'
,
(
WidgetTester
tester
)
async
{
final
MockRestorationManager
manager
=
MockRestorationManager
();
RestorationBucket
root
=
RestorationBucket
.
root
(
manager:
manager
,
rawData:
<
String
,
dynamic
>{});
await
tester
.
pumpWidget
(
UnmanagedRestorationScope
(
bucket:
root
,
child:
const
RestorationScope
(
restorationId:
'child1'
,
child:
BucketSpy
(),
),
),
);
manager
.
doSerialization
();
final
BucketSpyState
state
=
tester
.
state
(
find
.
byType
(
BucketSpy
));
expect
(
state
.
bucket
.
restorationId
,
'child1'
);
expect
(
state
.
bucket
.
read
<
int
>(
'foo'
),
isNull
);
// Does not exist.
final
RestorationBucket
bucket
=
state
.
bucket
;
// Replace root bucket.
root
..
decommission
()..
dispose
();
root
=
RestorationBucket
.
root
(
manager:
manager
,
rawData:
_createRawDataSet
());
await
tester
.
pumpWidget
(
UnmanagedRestorationScope
(
bucket:
root
,
child:
const
RestorationScope
(
restorationId:
'child1'
,
child:
BucketSpy
(),
),
),
);
// Bucket has been replaced.
expect
(
state
.
bucket
,
isNot
(
same
(
bucket
)));
expect
(
state
.
bucket
.
restorationId
,
'child1'
);
expect
(
state
.
bucket
.
read
<
int
>(
'foo'
),
22
);
});
});
}
...
...
packages/flutter/test/widgets/restoration_scopes_moving_test.dart
0 → 100644
View file @
39a46bed
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'widget moves scopes during restore'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
RootRestorationScope
(
restorationId:
'root'
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
TestWidgetWithCounterChild
(),
),
));
expect
(
tester
.
state
<
TestWidgetWithCounterChildState
>(
find
.
byType
(
TestWidgetWithCounterChild
)).
restoreChild
,
true
);
expect
(
find
.
text
(
'Counter: 0'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Counter: 0'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Counter: 1'
),
findsOneWidget
);
final
TestRestorationData
dataWithChild
=
await
tester
.
getRestorationData
();
tester
.
state
<
TestWidgetWithCounterChildState
>(
find
.
byType
(
TestWidgetWithCounterChild
)).
restoreChild
=
false
;
await
tester
.
pump
();
expect
(
tester
.
state
<
TestWidgetWithCounterChildState
>(
find
.
byType
(
TestWidgetWithCounterChild
)).
restoreChild
,
false
);
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Counter: 2'
),
findsOneWidget
);
final
TestRestorationData
dataWithoutChild
=
await
tester
.
getRestorationData
();
// Child moves from outside to inside scope.
await
tester
.
restoreFrom
(
dataWithChild
);
expect
(
find
.
text
(
'Counter: 1'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Counter: 2'
),
findsOneWidget
);
// Child stays inside scope.
await
tester
.
restoreFrom
(
dataWithChild
);
expect
(
find
.
text
(
'Counter: 1'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
tap
(
find
.
text
(
'Counter: 1'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Counter: 6'
),
findsOneWidget
);
// Child moves from inside to outside scope.
await
tester
.
restoreFrom
(
dataWithoutChild
);
expect
(
find
.
text
(
'Counter: 6'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Counter: 6'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Counter: 7'
),
findsOneWidget
);
// Child stays outside scope.
await
tester
.
restoreFrom
(
dataWithoutChild
);
expect
(
find
.
text
(
'Counter: 7'
),
findsOneWidget
);
expect
(
tester
.
state
<
TestWidgetWithCounterChildState
>(
find
.
byType
(
TestWidgetWithCounterChild
)).
toggleCount
,
0
);
});
testWidgets
(
'restoration is turned on later'
,
(
WidgetTester
tester
)
async
{
tester
.
binding
.
restorationManager
.
disableRestoration
();
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
restorationId:
'root-child'
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
TestWidget
(
restorationId:
'foo'
,
),
),
));
final
TestWidgetState
state
=
tester
.
state
<
TestWidgetState
>(
find
.
byType
(
TestWidget
));
expect
(
find
.
text
(
'hello'
),
findsOneWidget
);
expect
(
state
.
buckets
.
single
,
isNull
);
expect
(
state
.
flags
.
single
,
isTrue
);
expect
(
state
.
bucket
,
isNull
);
state
.
buckets
.
clear
();
state
.
flags
.
clear
();
await
tester
.
restoreFrom
(
TestRestorationData
.
empty
);
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
restorationId:
'root-child'
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
TestWidget
(
restorationId:
'foo'
,
),
),
));
expect
(
find
.
text
(
'hello'
),
findsOneWidget
);
expect
(
state
.
buckets
.
single
,
isNull
);
expect
(
state
.
flags
.
single
,
isFalse
);
expect
(
state
.
bucket
,
isNotNull
);
expect
(
state
.
toggleCount
,
0
);
});
}
class
TestWidgetWithCounterChild
extends
StatefulWidget
{
@override
State
<
TestWidgetWithCounterChild
>
createState
()
=>
TestWidgetWithCounterChildState
();
}
class
TestWidgetWithCounterChildState
extends
State
<
TestWidgetWithCounterChild
>
with
RestorationMixin
{
final
RestorableBool
childRestorationEnabled
=
RestorableBool
(
true
);
int
toggleCount
=
0
;
@override
void
didToggleBucket
(
RestorationBucket
oldBucket
)
{
super
.
didToggleBucket
(
oldBucket
);
toggleCount
++;
}
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
childRestorationEnabled
,
'childRestorationEnabled'
);
}
bool
get
restoreChild
=>
childRestorationEnabled
.
value
;
set
restoreChild
(
bool
value
)
{
if
(
value
==
childRestorationEnabled
.
value
)
{
return
;
}
setState
(()
{
childRestorationEnabled
.
value
=
value
;
});
}
@override
void
dispose
()
{
childRestorationEnabled
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Counter
(
restorationId:
restoreChild
?
'counter'
:
null
,
);
}
@override
String
get
restorationId
=>
'foo'
;
}
class
Counter
extends
StatefulWidget
{
const
Counter
({
this
.
restorationId
});
final
String
restorationId
;
@override
State
<
Counter
>
createState
()
=>
CounterState
();
}
class
CounterState
extends
State
<
Counter
>
with
RestorationMixin
{
final
RestorableInt
count
=
RestorableInt
(
0
);
@override
String
get
restorationId
=>
widget
.
restorationId
;
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
count
,
'counter'
);
}
@override
void
dispose
()
{
count
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
OutlinedButton
(
onPressed:
()
{
setState
(()
{
count
.
value
++;
});
},
child:
Text
(
'Counter:
${count.value}
'
,
),
);
}
}
class
TestWidget
extends
StatefulWidget
{
const
TestWidget
({
@required
this
.
restorationId
});
final
String
restorationId
;
@override
State
<
TestWidget
>
createState
()
=>
TestWidgetState
();
}
class
TestWidgetState
extends
State
<
TestWidget
>
with
RestorationMixin
{
List
<
RestorationBucket
>
buckets
=
<
RestorationBucket
>[];
List
<
bool
>
flags
=
<
bool
>[];
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
buckets
.
add
(
oldBucket
);
flags
.
add
(
initialRestore
);
}
int
toggleCount
=
0
;
@override
void
didToggleBucket
(
RestorationBucket
oldBucket
)
{
super
.
didToggleBucket
(
oldBucket
);
toggleCount
++;
}
@override
String
get
restorationId
=>
widget
.
restorationId
;
@override
Widget
build
(
BuildContext
context
)
{
return
const
Text
(
'hello'
);
}
}
packages/flutter/test/widgets/root_restoration_scope_test.dart
View file @
39a46bed
...
...
@@ -296,8 +296,8 @@ void main() {
};
final
RestorationBucket
secondRoot
=
RestorationBucket
.
root
(
manager:
binding
.
restorationManager
,
rawData:
secondRawData
);
binding
.
restorationManager
.
rootBucket
=
SynchronousFuture
<
RestorationBucket
>(
secondRoot
);
firstRoot
..
decommission
()..
dispose
();
await
tester
.
pump
();
firstRoot
.
dispose
();
expect
(
state
.
bucket
,
isNot
(
same
(
firstBucket
)));
expect
(
state
.
bucket
.
read
<
int
>(
'foo'
),
22
);
...
...
@@ -362,8 +362,8 @@ void main() {
expect
(
state
.
bucket
,
isNotNull
);
binding
.
restorationManager
.
rootBucket
=
SynchronousFuture
<
RestorationBucket
>(
null
);
root
..
decommission
()..
dispose
();
await
tester
.
pump
();
root
.
dispose
();
expect
(
binding
.
restorationManager
.
rootBucketAccessed
,
2
);
expect
(
find
.
text
(
'Hello'
),
findsOneWidget
);
...
...
packages/flutter_test/lib/src/restoration.dart
View file @
39a46bed
...
...
@@ -55,6 +55,14 @@ class TestRestorationManager extends RestorationManager {
handleRestorationUpdateFromEngine
(
enabled:
true
,
data:
data
.
binary
);
}
/// Disabled state restoration.
///
/// To turn restoration back on call [restoreFrom].
void
disableRestoration
()
{
_restorationData
=
null
;
handleRestorationUpdateFromEngine
(
enabled:
false
);
}
@override
Future
<
void
>
sendToEngine
(
Uint8List
encodedData
)
async
{
_restorationData
=
TestRestorationData
.
_
(
encodedData
);
...
...
packages/flutter_test/test/restoration_test.dart
View file @
39a46bed
...
...
@@ -88,7 +88,7 @@ class _RestorableWidgetState extends State<_RestorableWidget> with RestorationMi
double
doubleValue
=
1.0
;
// Not restorable.
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
stringValue
,
'string'
);
registerForRestoration
(
intValue
,
'int'
);
}
...
...
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