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
05eae425
Unverified
Commit
05eae425
authored
Feb 05, 2020
by
chunhtai
Committed by
GitHub
Feb 05, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix initial routes do not run secondary animation when pops (#47476)
parent
374b55cc
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
100 additions
and
6 deletions
+100
-6
routes.dart
packages/flutter/lib/src/widgets/routes.dart
+63
-6
routes_test.dart
packages/flutter/test/widgets/routes_test.dart
+37
-0
No files found.
packages/flutter/lib/src/widgets/routes.dart
View file @
05eae425
...
...
@@ -256,42 +256,99 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
super
.
didChangeNext
(
nextRoute
);
}
// A callback method that disposes existing train hopping animation and
// removes its listener.
//
// This property is non-null if there is a train hopping in progress, and the
// caller must reset this property to null after it is called.
VoidCallback
_trainHoppingListenerRemover
;
void
_updateSecondaryAnimation
(
Route
<
dynamic
>
nextRoute
)
{
// There is an existing train hopping in progress. Unfortunately, we cannot
// dispose current train hopping animation until we replace it with a new
// animation.
final
VoidCallback
previousTrainHoppingListenerRemover
=
_trainHoppingListenerRemover
;
_trainHoppingListenerRemover
=
null
;
if
(
nextRoute
is
TransitionRoute
<
dynamic
>
&&
canTransitionTo
(
nextRoute
)
&&
nextRoute
.
canTransitionFrom
(
this
))
{
final
Animation
<
double
>
current
=
_secondaryAnimation
.
parent
;
if
(
current
!=
null
)
{
final
Animation
<
double
>
currentTrain
=
current
is
TrainHoppingAnimation
?
current
.
currentTrain
:
current
;
final
Animation
<
double
>
nextTrain
=
nextRoute
.
_animation
;
if
(
currentTrain
.
value
==
nextTrain
.
value
)
{
if
(
currentTrain
.
value
==
nextTrain
.
value
||
nextTrain
.
status
==
AnimationStatus
.
completed
||
nextTrain
.
status
==
AnimationStatus
.
dismissed
)
{
_setSecondaryAnimation
(
nextTrain
,
nextRoute
.
completed
);
}
else
{
// Two trains animate at different values. We have to do train hopping.
// There are three possibilities of train hopping:
// 1. We hop on the nextTrain when two trains meet in the middle using
// TrainHoppingAnimation.
// 2. There is no chance to hop on nextTrain because two trains never
// cross each other. We have to directly set the animation to
// nextTrain once the nextTrain stops animating.
// 3. A new _updateSecondaryAnimation is called before train hopping
// finishes. We leave a listener remover for the next call to
// properly clean up the existing train hopping.
TrainHoppingAnimation
newAnimation
;
void
_jumpOnAnimationEnd
(
AnimationStatus
status
)
{
switch
(
status
)
{
case
AnimationStatus
.
completed
:
case
AnimationStatus
.
dismissed
:
// The nextTrain has stopped animating without train hopping.
// Directly sets the secondary animation and disposes the
// TrainHoppingAnimation.
_setSecondaryAnimation
(
nextTrain
,
nextRoute
.
completed
);
if
(
_trainHoppingListenerRemover
!=
null
)
{
_trainHoppingListenerRemover
();
_trainHoppingListenerRemover
=
null
;
}
break
;
case
AnimationStatus
.
forward
:
case
AnimationStatus
.
reverse
:
break
;
}
}
_trainHoppingListenerRemover
=
()
{
nextTrain
.
removeStatusListener
(
_jumpOnAnimationEnd
);
newAnimation
?.
dispose
();
};
nextTrain
.
addStatusListener
(
_jumpOnAnimationEnd
);
newAnimation
=
TrainHoppingAnimation
(
currentTrain
,
nextTrain
,
onSwitchedTrain:
()
{
assert
(
_secondaryAnimation
.
parent
==
newAnimation
);
assert
(
newAnimation
.
currentTrain
==
nextRoute
.
_animation
);
// We can hop on the nextTrain, so we don't need to listen to
// whether the nextTrain has stopped.
_setSecondaryAnimation
(
newAnimation
.
currentTrain
,
nextRoute
.
completed
);
newAnimation
.
dispose
();
if
(
_trainHoppingListenerRemover
!=
null
)
{
_trainHoppingListenerRemover
();
_trainHoppingListenerRemover
=
null
;
}
},
);
_setSecondaryAnimation
(
newAnimation
,
nextRoute
.
completed
);
}
if
(
current
is
TrainHoppingAnimation
)
{
current
.
dispose
();
}
}
else
{
_setSecondaryAnimation
(
nextRoute
.
_animation
,
nextRoute
.
completed
);
}
}
else
{
_setSecondaryAnimation
(
kAlwaysDismissedAnimation
);
}
// Finally, we dispose any previous train hopping animation because it
// has been successfully updated at this point.
if
(
previousTrainHoppingListenerRemover
!=
null
)
{
previousTrainHoppingListenerRemover
();
}
}
void
_setSecondaryAnimation
(
Animation
<
double
>
animation
,
[
Future
<
dynamic
>
disposed
])
{
_secondaryAnimation
.
parent
=
animation
;
// Release the reference to the next route's animation when that route
// Release
s
the reference to the next route's animation when that route
// is disposed.
disposed
?.
then
((
dynamic
_
)
{
if
(
_secondaryAnimation
.
parent
==
animation
)
{
...
...
packages/flutter/test/widgets/routes_test.dart
View file @
05eae425
...
...
@@ -788,6 +788,43 @@ void main() {
expect
(
trainHopper2
.
currentTrain
,
isNull
);
// Has been disposed.
});
testWidgets
(
'secondary animation is triggered when pop initial route'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
NavigatorState
>
navigator
=
GlobalKey
<
NavigatorState
>();
Animation
<
double
>
secondaryAnimationOfRouteOne
;
Animation
<
double
>
primaryAnimationOfRouteTwo
;
await
tester
.
pumpWidget
(
MaterialApp
(
navigatorKey:
navigator
,
onGenerateRoute:
(
RouteSettings
settings
)
{
return
PageRouteBuilder
<
void
>(
settings:
settings
,
pageBuilder:
(
_
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
)
{
if
(
settings
.
name
==
'/'
)
secondaryAnimationOfRouteOne
=
secondaryAnimation
;
else
primaryAnimationOfRouteTwo
=
animation
;
return
const
Text
(
'Page'
);
},
);
},
initialRoute:
'/a'
,
)
);
// The secondary animation of the bottom route should be chained with the
// primary animation of top most route.
expect
(
secondaryAnimationOfRouteOne
.
value
,
1.0
);
expect
(
secondaryAnimationOfRouteOne
.
value
,
primaryAnimationOfRouteTwo
.
value
);
// Pops the top most route and verifies two routes are still chained.
navigator
.
currentState
.
pop
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
30
));
expect
(
secondaryAnimationOfRouteOne
.
value
,
0.9
);
expect
(
secondaryAnimationOfRouteOne
.
value
,
primaryAnimationOfRouteTwo
.
value
);
await
tester
.
pumpAndSettle
();
expect
(
secondaryAnimationOfRouteOne
.
value
,
0.0
);
expect
(
secondaryAnimationOfRouteOne
.
value
,
primaryAnimationOfRouteTwo
.
value
);
});
testWidgets
(
'showGeneralDialog uses root navigator by default'
,
(
WidgetTester
tester
)
async
{
final
DialogObserver
rootObserver
=
DialogObserver
();
final
DialogObserver
nestedObserver
=
DialogObserver
();
...
...
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