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
547b86a9
Unverified
Commit
547b86a9
authored
Oct 22, 2019
by
Shi-Hao Hong
Committed by
GitHub
Oct 22, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Android 10] Activity zoom transition (#41935)
* Android 10 zoom transition
parent
4dd50971
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
266 additions
and
1 deletion
+266
-1
tween_sequence.dart
packages/flutter/lib/src/animation/tween_sequence.dart
+25
-1
page_transitions_theme.dart
...ages/flutter/lib/src/material/page_transitions_theme.dart
+201
-0
page_transitions_theme_test.dart
...es/flutter/test/material/page_transitions_theme_test.dart
+40
-0
No files found.
packages/flutter/lib/src/animation/tween_sequence.dart
View file @
547b86a9
...
...
@@ -88,7 +88,7 @@ class TweenSequence<T> extends Animatable<T> {
return
_evaluateAt
(
t
,
index
);
}
// Should be unreachable.
assert
(
false
,
'TweenSequence.evaluate() could not find a interval for
$t
'
);
assert
(
false
,
'TweenSequence.evaluate() could not find a
n
interval for
$t
'
);
return
null
;
}
...
...
@@ -96,6 +96,30 @@ class TweenSequence<T> extends Animatable<T> {
String
toString
()
=>
'TweenSequence(
${_items.length}
items)'
;
}
/// Enables creating a flipped [Animation] whose value is defined by a sequence
/// of [Tween]s.
///
/// This creates a [TweenSequence] that evaluates to a result that flips the
/// tween both horizontally and vertically.
///
/// This tween sequence assumes that the evaluated result has to be a double
/// between 0.0 and 1.0.
class
FlippedTweenSequence
extends
TweenSequence
<
double
>
{
/// Creates a flipped [TweenSequence].
///
/// The [items] parameter must be a list of one or more [TweenSequenceItem]s.
///
/// There's a small cost associated with building a `TweenSequence` so it's
/// best to reuse one, rather than rebuilding it on every frame, when that's
/// possible.
FlippedTweenSequence
(
List
<
TweenSequenceItem
<
double
>>
items
)
:
assert
(
items
!=
null
),
super
(
items
);
@override
double
transform
(
double
t
)
=>
1
-
super
.
transform
(
1
-
t
);
}
/// A simple holder for one element of a [TweenSequence].
class
TweenSequenceItem
<
T
>
{
/// Construct a TweenSequenceItem.
...
...
packages/flutter/lib/src/material/page_transitions_theme.dart
View file @
547b86a9
...
...
@@ -146,6 +146,168 @@ class _OpenUpwardsPageTransition extends StatelessWidget {
}
}
// Zooms and fades a new page in, zooming out the previous page. This transition
// is designed to match the Android 10 activity transition.
class
_ZoomPageTransition
extends
StatefulWidget
{
const
_ZoomPageTransition
({
Key
key
,
this
.
animation
,
this
.
secondaryAnimation
,
this
.
child
,
})
:
super
(
key:
key
);
// The scrim obscures the old page by becoming increasingly opaque.
static
final
Tween
<
double
>
_scrimOpacityTween
=
Tween
<
double
>(
begin:
0.0
,
end:
0.60
,
);
// A curve sequence that is similar to the 'fastOutExtraSlowIn' curve used in
// the native transition.
static
final
List
<
TweenSequenceItem
<
double
>>
fastOutExtraSlowInTweenSequenceItems
=
<
TweenSequenceItem
<
double
>>[
TweenSequenceItem
<
double
>(
tween:
Tween
<
double
>(
begin:
0.0
,
end:
0.4
)
.
chain
(
CurveTween
(
curve:
const
Cubic
(
0.05
,
0.0
,
0.133333
,
0.06
))),
weight:
0.166666
,
),
TweenSequenceItem
<
double
>(
tween:
Tween
<
double
>(
begin:
0.4
,
end:
1.0
)
.
chain
(
CurveTween
(
curve:
const
Cubic
(
0.208333
,
0.82
,
0.25
,
1.0
))),
weight:
1.0
-
0.166666
,
),
];
static
final
TweenSequence
<
double
>
_scaleCurveSequence
=
TweenSequence
<
double
>(
fastOutExtraSlowInTweenSequenceItems
);
static
final
FlippedTweenSequence
_flippedScaleCurveSequence
=
FlippedTweenSequence
(
fastOutExtraSlowInTweenSequenceItems
);
final
Animation
<
double
>
animation
;
final
Animation
<
double
>
secondaryAnimation
;
final
Widget
child
;
@override
__ZoomPageTransitionState
createState
()
=>
__ZoomPageTransitionState
();
}
class
__ZoomPageTransitionState
extends
State
<
_ZoomPageTransition
>
{
AnimationStatus
_currentAnimationStatus
;
AnimationStatus
_lastAnimationStatus
;
@override
void
initState
()
{
super
.
initState
();
widget
.
animation
.
addStatusListener
((
AnimationStatus
animationStatus
)
{
_lastAnimationStatus
=
_currentAnimationStatus
;
_currentAnimationStatus
=
animationStatus
;
});
}
// This check ensures that the animation reverses the original animation if
// the transition were interruped midway. This prevents a disjointed
// experience since the reverse animation uses different fade and scaling
// curves.
bool
get
_transitionWasInterrupted
{
bool
wasInProgress
=
false
;
bool
isInProgress
=
false
;
switch
(
_currentAnimationStatus
)
{
case
AnimationStatus
.
completed
:
case
AnimationStatus
.
dismissed
:
isInProgress
=
false
;
break
;
case
AnimationStatus
.
forward
:
case
AnimationStatus
.
reverse
:
isInProgress
=
true
;
break
;
}
switch
(
_lastAnimationStatus
)
{
case
AnimationStatus
.
completed
:
case
AnimationStatus
.
dismissed
:
wasInProgress
=
false
;
break
;
case
AnimationStatus
.
forward
:
case
AnimationStatus
.
reverse
:
wasInProgress
=
true
;
break
;
}
return
wasInProgress
&&
isInProgress
;
}
@override
Widget
build
(
BuildContext
context
)
{
final
Animation
<
double
>
_forwardScrimOpacityAnimation
=
widget
.
animation
.
drive
(
_ZoomPageTransition
.
_scrimOpacityTween
.
chain
(
CurveTween
(
curve:
const
Interval
(
0.2075
,
0.4175
))));
final
Animation
<
double
>
_forwardEndScreenScaleTransition
=
widget
.
animation
.
drive
(
Tween
<
double
>(
begin:
0.85
,
end:
1.00
)
.
chain
(
_ZoomPageTransition
.
_scaleCurveSequence
));
final
Animation
<
double
>
_forwardStartScreenScaleTransition
=
widget
.
secondaryAnimation
.
drive
(
Tween
<
double
>(
begin:
1.00
,
end:
1.05
)
.
chain
(
_ZoomPageTransition
.
_scaleCurveSequence
));
final
Animation
<
double
>
_forwardEndScreenFadeTransition
=
widget
.
animation
.
drive
(
Tween
<
double
>(
begin:
0.0
,
end:
1.00
)
.
chain
(
CurveTween
(
curve:
const
Interval
(
0.125
,
0.250
))));
final
Animation
<
double
>
_reverseEndScreenScaleTransition
=
widget
.
secondaryAnimation
.
drive
(
Tween
<
double
>(
begin:
1.00
,
end:
1.10
)
.
chain
(
_ZoomPageTransition
.
_flippedScaleCurveSequence
));
final
Animation
<
double
>
_reverseStartScreenScaleTransition
=
widget
.
animation
.
drive
(
Tween
<
double
>(
begin:
0.9
,
end:
1.0
)
.
chain
(
_ZoomPageTransition
.
_flippedScaleCurveSequence
));
final
Animation
<
double
>
_reverseStartScreenFadeTransition
=
widget
.
animation
.
drive
(
Tween
<
double
>(
begin:
0.0
,
end:
1.00
)
.
chain
(
CurveTween
(
curve:
const
Interval
(
1
-
0.2075
,
1
-
0.0825
))));
return
AnimatedBuilder
(
animation:
widget
.
animation
,
builder:
(
BuildContext
context
,
Widget
child
)
{
if
(
widget
.
animation
.
status
==
AnimationStatus
.
forward
||
_transitionWasInterrupted
)
{
return
Container
(
color:
Colors
.
black
.
withOpacity
(
_forwardScrimOpacityAnimation
.
value
),
child:
FadeTransition
(
opacity:
_forwardEndScreenFadeTransition
,
child:
ScaleTransition
(
scale:
_forwardEndScreenScaleTransition
,
child:
child
,
),
),
);
}
else
if
(
widget
.
animation
.
status
==
AnimationStatus
.
reverse
)
{
return
ScaleTransition
(
scale:
_reverseStartScreenScaleTransition
,
child:
FadeTransition
(
opacity:
_reverseStartScreenFadeTransition
,
child:
child
,
),
);
}
return
child
;
},
child:
AnimatedBuilder
(
animation:
widget
.
secondaryAnimation
,
builder:
(
BuildContext
context
,
Widget
child
)
{
if
(
widget
.
secondaryAnimation
.
status
==
AnimationStatus
.
forward
||
_transitionWasInterrupted
)
{
return
ScaleTransition
(
scale:
_forwardStartScreenScaleTransition
,
child:
child
,
);
}
else
if
(
widget
.
secondaryAnimation
.
status
==
AnimationStatus
.
reverse
)
{
return
ScaleTransition
(
scale:
_reverseEndScreenScaleTransition
,
child:
child
,
);
}
return
child
;
},
child:
widget
.
child
,
),
);
}
}
/// Used by [PageTransitionsTheme] to define a [MaterialPageRoute] page
/// transition animation.
///
...
...
@@ -158,6 +320,8 @@ class _OpenUpwardsPageTransition extends StatelessWidget {
/// * [FadeUpwardsPageTransitionsBuilder], which defines a default page transition.
/// * [OpenUpwardsPageTransitionsBuilder], which defines a page transition
/// that's similar to the one provided by Android P.
/// * [ZoomPageTransitionsBuilder], which defines a page transition similar
/// to the one provided in Android 10.
/// * [CupertinoPageTransitionsBuilder], which defines a horizontal page
/// transition that matches native iOS page transitions.
abstract
class
PageTransitionsBuilder
{
...
...
@@ -191,6 +355,8 @@ abstract class PageTransitionsBuilder {
///
/// * [OpenUpwardsPageTransitionsBuilder], which defines a page transition
/// that's similar to the one provided by Android P.
/// * [ZoomPageTransitionsBuilder], which defines a page transition similar
/// to the one provided in Android 10.
/// * [CupertinoPageTransitionsBuilder], which defines a horizontal page
/// transition that matches native iOS page transitions.
class
FadeUpwardsPageTransitionsBuilder
extends
PageTransitionsBuilder
{
...
...
@@ -216,6 +382,8 @@ class FadeUpwardsPageTransitionsBuilder extends PageTransitionsBuilder {
/// See also:
///
/// * [FadeUpwardsPageTransitionsBuilder], which defines a default page transition.
/// * [ZoomPageTransitionsBuilder], which defines a page transition similar
/// to the one provided in Android 10.
/// * [CupertinoPageTransitionsBuilder], which defines a horizontal page
/// transition that matches native iOS page transitions.
class
OpenUpwardsPageTransitionsBuilder
extends
PageTransitionsBuilder
{
...
...
@@ -238,6 +406,37 @@ class OpenUpwardsPageTransitionsBuilder extends PageTransitionsBuilder {
}
}
/// Used by [PageTransitionsTheme] to define a zooming [MaterialPageRoute] page
/// transition animation that looks like the default page transition used on
/// Android 10.
///
/// See also:
///
/// * [FadeUpwardsPageTransitionsBuilder], which defines a default page transition.
/// * [OpenUpwardsPageTransitionsBuilder], which defines a page transition
/// similar to the one provided by Android P.
/// * [CupertinoPageTransitionsBuilder], which defines a horizontal page
/// transition that matches native iOS page transitions.
class
ZoomPageTransitionsBuilder
extends
PageTransitionsBuilder
{
/// Construct a [ZoomPageTransitionsBuilder].
const
ZoomPageTransitionsBuilder
();
@override
Widget
buildTransitions
<
T
>(
PageRoute
<
T
>
route
,
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
,
)
{
return
_ZoomPageTransition
(
animation:
animation
,
secondaryAnimation:
secondaryAnimation
,
child:
child
,
);
}
}
/// Used by [PageTransitionsTheme] to define a horizontal [MaterialPageRoute]
/// page transition animation that matches native iOS page transitions.
///
...
...
@@ -246,6 +445,8 @@ class OpenUpwardsPageTransitionsBuilder extends PageTransitionsBuilder {
/// * [FadeUpwardsPageTransitionsBuilder], which defines a default page transition.
/// * [OpenUpwardsPageTransitionsBuilder], which defines a page transition
/// that's similar to the one provided by Android P.
/// * [ZoomPageTransitionsBuilder], which defines a page transition similar
/// to the one provided in Android 10.
class
CupertinoPageTransitionsBuilder
extends
PageTransitionsBuilder
{
/// Construct a [CupertinoPageTransitionsBuilder].
const
CupertinoPageTransitionsBuilder
();
...
...
packages/flutter/test/material/page_transitions_theme_test.dart
View file @
547b86a9
...
...
@@ -117,4 +117,44 @@ void main() {
expect
(
findOpenUpwardsPageTransition
(),
findsOneWidget
);
});
testWidgets
(
'pageTranstionsTheme override builds a _ZoomPageTransition for android'
,
(
WidgetTester
tester
)
async
{
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
Material
(
child:
FlatButton
(
child:
const
Text
(
'push'
),
onPressed:
()
{
Navigator
.
of
(
context
).
pushNamed
(
'/b'
);
},
),
),
'/b'
:
(
BuildContext
context
)
=>
const
Text
(
'page b'
),
};
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
android
,
pageTransitionsTheme:
const
PageTransitionsTheme
(
builders:
<
TargetPlatform
,
PageTransitionsBuilder
>{
TargetPlatform
.
android
:
ZoomPageTransitionsBuilder
(),
// creates a _ZoomPageTransition
},
),
),
routes:
routes
,
),
);
Finder
findZoomPageTransition
()
{
return
find
.
descendant
(
of:
find
.
byType
(
MaterialApp
),
matching:
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_ZoomPageTransition'
),
);
}
expect
(
Theme
.
of
(
tester
.
element
(
find
.
text
(
'push'
))).
platform
,
TargetPlatform
.
android
);
expect
(
findZoomPageTransition
(),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'push'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'page b'
),
findsOneWidget
);
expect
(
findZoomPageTransition
(),
findsOneWidget
);
});
}
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