Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f0644f0
:sparkles: Add `DualTransitionBuilder`.
AlexV525 Jun 4, 2020
02f808e
:recycle: Refactored `_ZoomPageTransition` with `DualTransitionBuilder`.
AlexV525 Jun 4, 2020
955ee43
:page_facing_up: :speech_balloon: :art: Updated license & documentati…
AlexV525 Jun 5, 2020
b80465b
:label: Reword `Tween`s.
AlexV525 Jun 5, 2020
ced9cfd
:label: Reword more properties.
AlexV525 Jun 5, 2020
ba8fe04
:rocket: Make reverse animation works normally.
AlexV525 Jun 5, 2020
7bbe91b
:art: Use `kAlwaysCompleteAnimation` and `kAlwaysDismissedAnimation`.
AlexV525 Jun 5, 2020
dd835df
:pencil2: Fix typos and formats.
AlexV525 Jun 5, 2020
32421f9
:white_check_mark: Add build times count test.
AlexV525 Jun 5, 2020
0781f5e
:art: Split files import.
AlexV525 Jun 5, 2020
33d4fa2
:bulb: Explain the animation status in more detail.
AlexV525 Jun 5, 2020
8b6a997
:art: Add key parameter to widgets.
AlexV525 Jun 5, 2020
075b214
:rocket: Hide `DualTransitionBuilder`.
AlexV525 Jun 5, 2020
ca86a62
:ok_hand: `isCompleted` -> `isNotForward`, `isDismissed` -> `isNotRev…
AlexV525 Jun 5, 2020
7af2498
:white_check_mark: Add dual transition builder test.
AlexV525 Jun 5, 2020
17894ad
Fix scale and fade transitions
shihaohong Jun 5, 2020
1e9a04a
Fix up transitions to use DualTransitionBuilder
shihaohong Jun 5, 2020
1a28846
Fix import
shihaohong Jun 5, 2020
afe0d55
Code formatting cleanup
shihaohong Jun 5, 2020
0d45454
Merge pull request #1 from shihaohong/AlexVincent525-fix-zoom-page-tr…
AlexV525 Jun 6, 2020
23271f0
:rewind: Revert "Fix import"
AlexV525 Jun 6, 2020
38ebf2f
:label: Reword transitions.
AlexV525 Jun 7, 2020
3143465
:art: Add new line at the end of line.
AlexV525 Jun 7, 2020
01cb825
:label: Add key property for transitions.
AlexV525 Jun 7, 2020
5cf2342
:art: Update assertions & indentation for transitions.
AlexV525 Jun 9, 2020
67ab739
:zap: Using `AnimatedBuilder` to ensure the container gets rebuild.
AlexV525 Jun 9, 2020
27af148
:art: Adjust widgets import.
AlexV525 Jun 9, 2020
54417be
:speech_balloon: Rewords comment and definition.
AlexV525 Jun 10, 2020
3ce561a
Merge branch 'master' into fix-zoom-page-transition-build
AlexV525 Jun 12, 2020
3ddb0ca
:alien: nnbd updates.
AlexV525 Jun 15, 2020
07ba509
:speech_balloon: Reword description for zoom forward transition.
AlexV525 Jun 15, 2020
26345f1
:pencil: Complete documentation for the transition.
AlexV525 Jun 16, 2020
bfafedb
:ok_hand: Update comment for the constructor.
AlexV525 Jun 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 191 additions & 116 deletions packages/flutter/lib/src/material/page_transitions_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,19 @@ 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 {
class _ZoomPageTransition extends StatelessWidget {
/// Creates a [_ZoomPageTransition].
///
/// The [animation] and [secondaryAnimation] argument are required and must
/// not be null.
const _ZoomPageTransition({
Key key,
this.animation,
this.secondaryAnimation,
@required this.animation,
@required 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,
);
}) : assert(animation != null),
assert(secondaryAnimation != null),
super(key: key);

// A curve sequence that is similar to the 'fastOutExtraSlowIn' curve used in
// the native transition.
Expand All @@ -179,132 +179,207 @@ class _ZoomPageTransition extends StatefulWidget {
),
];
static final TweenSequence<double> _scaleCurveSequence = TweenSequence<double>(fastOutExtraSlowInTweenSequenceItems);
static final FlippedTweenSequence _flippedScaleCurveSequence = FlippedTweenSequence(fastOutExtraSlowInTweenSequenceItems);

/// The animation that drives the [child]'s entrance and exit.
///
/// See also:
///
/// * [TransitionRoute.animation], which is the value given to this property
/// when the [_ZoomPageTransition] is used as a page transition.
final Animation<double> animation;

/// The animation that transitions [child] when new content is pushed on top
/// of it.
///
/// See also:
///
/// * [TransitionRoute.secondaryAnimation], which is the value given to this
// property when the [_ZoomPageTransition] is used as a page transition.
final Animation<double> secondaryAnimation;

/// The widget below this widget in the tree.
///
/// This widget will transition in and out as driven by [animation] and
/// [secondaryAnimation].
final Widget child;

@override
__ZoomPageTransitionState createState() => __ZoomPageTransitionState();
Widget build(BuildContext context) {
return DualTransitionBuilder(
animation: animation,
forwardBuilder: (
BuildContext context,
Animation<double> animation,
Widget child,
) {
return _ZoomEnterTransition(
animation: animation,
child: child,
);
},
reverseBuilder: (
BuildContext context,
Animation<double> animation,
Widget child,
) {
return _ZoomExitTransition(
animation: animation,
reverse: true,
child: child,
);
},
child: DualTransitionBuilder(
animation: ReverseAnimation(secondaryAnimation),
forwardBuilder: (
BuildContext context,
Animation<double> animation,
Widget child,
) {
return _ZoomEnterTransition(
animation: animation,
reverse: true,
child: child,
);
},
reverseBuilder: (
BuildContext context,
Animation<double> animation,
Widget child,
) {
return _ZoomExitTransition(
animation: animation,
child: child,
);
},
child: child,
),
);
}
}

class __ZoomPageTransitionState extends State<_ZoomPageTransition> {
AnimationStatus _currentAnimationStatus;
AnimationStatus _lastAnimationStatus;

@override
void initState() {
super.initState();
widget.animation.addStatusListener((AnimationStatus animationStatus) {
_lastAnimationStatus = _currentAnimationStatus;
_currentAnimationStatus = animationStatus;
});
}
class _ZoomEnterTransition extends StatelessWidget {
const _ZoomEnterTransition({
Key key,
@required this.animation,
this.reverse = false,
this.child,
}) : assert(animation != null),
assert(reverse != null),
super(key: key);

// This check ensures that the animation reverses the original animation if
// the transition were interrupted 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;
}
final Animation<double> animation;
final Widget child;
final bool reverse;

@override
Widget build(BuildContext context) {
final Animation<double> _forwardScrimOpacityAnimation = widget.animation.drive(
_ZoomPageTransition._scrimOpacityTween
.chain(CurveTween(curve: const Interval(0.2075, 0.4175))));
static final Animatable<double> _fadeInTransition = Tween<double>(
begin: 0.0,
end: 1.00,
).chain(CurveTween(curve: const Interval(0.125, 0.250)));

final Animation<double> _forwardEndScreenScaleTransition = widget.animation.drive(
Tween<double>(begin: 0.85, end: 1.00)
.chain(_ZoomPageTransition._scaleCurveSequence));
static final Animatable<double> _scaleDownTransition = Tween<double>(
begin: 1.10,
end: 1.00,
).chain(_ZoomPageTransition._scaleCurveSequence);

final Animation<double> _forwardStartScreenScaleTransition = widget.secondaryAnimation.drive(
Tween<double>(begin: 1.00, end: 1.05)
.chain(_ZoomPageTransition._scaleCurveSequence));
static final Animatable<double> _scaleUpTransition = Tween<double>(
begin: 0.85,
end: 1.00,
).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))));
static final Animatable<double> _scrimOpacityTween = Tween<double>(
begin: 0.0,
end: 0.60,
).chain(CurveTween(curve: const Interval(0.2075, 0.4175)));

final Animation<double> _reverseEndScreenScaleTransition = widget.secondaryAnimation.drive(
Tween<double>(begin: 1.00, end: 1.10)
.chain(_ZoomPageTransition._flippedScaleCurveSequence));
@override
Widget build(BuildContext context) {
double opacity = 0;
// The transition's scrim opacity only increases on the forward transition. In the reverse
// transition, the opacity should always be 0.0.
//
// Therefore, we need to only apply the scrim opacity animation when the transition
// is running forwards.
//
// The reason that we check that the animation's status is not `completed` instead
// of checking that it is `forward` is that this allows the interrupted reversal of the
// forward transition to smoothly fade the scrim away. This prevents a disjointed
// removal of the scrim.
if (!reverse && animation.status != AnimationStatus.completed) {
opacity = _scrimOpacityTween.evaluate(animation);
}

final Animation<double> _reverseStartScreenScaleTransition = widget.animation.drive(
Tween<double>(begin: 0.9, end: 1.0)
.chain(_ZoomPageTransition._flippedScaleCurveSequence));
final Animation<double> fadeTransition = reverse
? kAlwaysCompleteAnimation
: _fadeInTransition.animate(animation);

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))));
final Animation<double> scaleTransition = (reverse
? _scaleDownTransition
: _scaleUpTransition
).animate(animation);

return AnimatedBuilder(
animation: widget.animation,
animation: 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;
return Container(
color: Colors.black.withOpacity(opacity),
child: 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,
child: FadeTransition(
opacity: fadeTransition,
child: ScaleTransition(
scale: scaleTransition,
child: child,
),
),
);
}
}

class _ZoomExitTransition extends StatelessWidget {
const _ZoomExitTransition({
Key key,
@required this.animation,
this.reverse = false,
this.child,
}) : assert(animation != null),
assert(reverse != null),
super(key: key);

final Animation<double> animation;
final bool reverse;
final Widget child;

static final Animatable<double> _fadeOutTransition = Tween<double>(
begin: 1.0,
end: 0.0,
).chain(CurveTween(curve: const Interval(0.0825, 0.2075)));

static final Animatable<double> _scaleUpTransition = Tween<double>(
begin: 1.00,
end: 1.05,
).chain(_ZoomPageTransition._scaleCurveSequence);

static final Animatable<double> _scaleDownTransition = Tween<double>(
begin: 1.00,
end: 0.90,
).chain(_ZoomPageTransition._scaleCurveSequence);

@override
Widget build(BuildContext context) {
final Animation<double> fadeTransition = reverse
? _fadeOutTransition.animate(animation)
: kAlwaysCompleteAnimation;
final Animation<double> scaleTransition = (reverse
? _scaleDownTransition
: _scaleUpTransition
).animate(animation);

return FadeTransition(
opacity: fadeTransition,
child: ScaleTransition(
scale: scaleTransition,
child: child,
),
);
}
Expand Down
Loading