Skip to content

Commit 15a2815

Browse files
authored
Revert "[PageTransitionsBuilder] Fix 'ZoomPageTransition' built more than once (flutter#58686)" (flutter#59992)
This reverts commit fe15d1e.
1 parent 8c3b826 commit 15a2815

File tree

5 files changed

+116
-743
lines changed

5 files changed

+116
-743
lines changed

packages/flutter/lib/src/material/page_transitions_theme.dart

+116-191
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,19 @@ class _OpenUpwardsPageTransition extends StatelessWidget {
150150

151151
// Zooms and fades a new page in, zooming out the previous page. This transition
152152
// is designed to match the Android 10 activity transition.
153-
class _ZoomPageTransition extends StatelessWidget {
154-
/// Creates a [_ZoomPageTransition].
155-
///
156-
/// The [animation] and [secondaryAnimation] argument are required and must
157-
/// not be null.
153+
class _ZoomPageTransition extends StatefulWidget {
158154
const _ZoomPageTransition({
159155
Key key,
160-
@required this.animation,
161-
@required this.secondaryAnimation,
156+
this.animation,
157+
this.secondaryAnimation,
162158
this.child,
163-
}) : assert(animation != null),
164-
assert(secondaryAnimation != null),
165-
super(key: key);
159+
}) : super(key: key);
160+
161+
// The scrim obscures the old page by becoming increasingly opaque.
162+
static final Tween<double> _scrimOpacityTween = Tween<double>(
163+
begin: 0.0,
164+
end: 0.60,
165+
);
166166

167167
// A curve sequence that is similar to the 'fastOutExtraSlowIn' curve used in
168168
// the native transition.
@@ -179,207 +179,132 @@ class _ZoomPageTransition extends StatelessWidget {
179179
),
180180
];
181181
static final TweenSequence<double> _scaleCurveSequence = TweenSequence<double>(fastOutExtraSlowInTweenSequenceItems);
182+
static final FlippedTweenSequence _flippedScaleCurveSequence = FlippedTweenSequence(fastOutExtraSlowInTweenSequenceItems);
182183

183-
/// The animation that drives the [child]'s entrance and exit.
184-
///
185-
/// See also:
186-
///
187-
/// * [TransitionRoute.animation], which is the value given to this property
188-
/// when the [_ZoomPageTransition] is used as a page transition.
189184
final Animation<double> animation;
190-
191-
/// The animation that transitions [child] when new content is pushed on top
192-
/// of it.
193-
///
194-
/// See also:
195-
///
196-
/// * [TransitionRoute.secondaryAnimation], which is the value given to this
197-
// property when the [_ZoomPageTransition] is used as a page transition.
198185
final Animation<double> secondaryAnimation;
199-
200-
/// The widget below this widget in the tree.
201-
///
202-
/// This widget will transition in and out as driven by [animation] and
203-
/// [secondaryAnimation].
204186
final Widget child;
205187

206188
@override
207-
Widget build(BuildContext context) {
208-
return DualTransitionBuilder(
209-
animation: animation,
210-
forwardBuilder: (
211-
BuildContext context,
212-
Animation<double> animation,
213-
Widget child,
214-
) {
215-
return _ZoomEnterTransition(
216-
animation: animation,
217-
child: child,
218-
);
219-
},
220-
reverseBuilder: (
221-
BuildContext context,
222-
Animation<double> animation,
223-
Widget child,
224-
) {
225-
return _ZoomExitTransition(
226-
animation: animation,
227-
reverse: true,
228-
child: child,
229-
);
230-
},
231-
child: DualTransitionBuilder(
232-
animation: ReverseAnimation(secondaryAnimation),
233-
forwardBuilder: (
234-
BuildContext context,
235-
Animation<double> animation,
236-
Widget child,
237-
) {
238-
return _ZoomEnterTransition(
239-
animation: animation,
240-
reverse: true,
241-
child: child,
242-
);
243-
},
244-
reverseBuilder: (
245-
BuildContext context,
246-
Animation<double> animation,
247-
Widget child,
248-
) {
249-
return _ZoomExitTransition(
250-
animation: animation,
251-
child: child,
252-
);
253-
},
254-
child: child,
255-
),
256-
);
257-
}
189+
__ZoomPageTransitionState createState() => __ZoomPageTransitionState();
258190
}
259191

260-
class _ZoomEnterTransition extends StatelessWidget {
261-
const _ZoomEnterTransition({
262-
Key key,
263-
@required this.animation,
264-
this.reverse = false,
265-
this.child,
266-
}) : assert(animation != null),
267-
assert(reverse != null),
268-
super(key: key);
269-
270-
final Animation<double> animation;
271-
final Widget child;
272-
final bool reverse;
273-
274-
static final Animatable<double> _fadeInTransition = Tween<double>(
275-
begin: 0.0,
276-
end: 1.00,
277-
).chain(CurveTween(curve: const Interval(0.125, 0.250)));
278-
279-
static final Animatable<double> _scaleDownTransition = Tween<double>(
280-
begin: 1.10,
281-
end: 1.00,
282-
).chain(_ZoomPageTransition._scaleCurveSequence);
192+
class __ZoomPageTransitionState extends State<_ZoomPageTransition> {
193+
AnimationStatus _currentAnimationStatus;
194+
AnimationStatus _lastAnimationStatus;
283195

284-
static final Animatable<double> _scaleUpTransition = Tween<double>(
285-
begin: 0.85,
286-
end: 1.00,
287-
).chain(_ZoomPageTransition._scaleCurveSequence);
196+
@override
197+
void initState() {
198+
super.initState();
199+
widget.animation.addStatusListener((AnimationStatus animationStatus) {
200+
_lastAnimationStatus = _currentAnimationStatus;
201+
_currentAnimationStatus = animationStatus;
202+
});
203+
}
288204

289-
static final Animatable<double> _scrimOpacityTween = Tween<double>(
290-
begin: 0.0,
291-
end: 0.60,
292-
).chain(CurveTween(curve: const Interval(0.2075, 0.4175)));
205+
// This check ensures that the animation reverses the original animation if
206+
// the transition were interrupted midway. This prevents a disjointed
207+
// experience since the reverse animation uses different fade and scaling
208+
// curves.
209+
bool get _transitionWasInterrupted {
210+
bool wasInProgress = false;
211+
bool isInProgress = false;
212+
213+
switch (_currentAnimationStatus) {
214+
case AnimationStatus.completed:
215+
case AnimationStatus.dismissed:
216+
isInProgress = false;
217+
break;
218+
case AnimationStatus.forward:
219+
case AnimationStatus.reverse:
220+
isInProgress = true;
221+
break;
222+
}
223+
switch (_lastAnimationStatus) {
224+
case AnimationStatus.completed:
225+
case AnimationStatus.dismissed:
226+
wasInProgress = false;
227+
break;
228+
case AnimationStatus.forward:
229+
case AnimationStatus.reverse:
230+
wasInProgress = true;
231+
break;
232+
}
233+
return wasInProgress && isInProgress;
234+
}
293235

294236
@override
295237
Widget build(BuildContext context) {
296-
double opacity = 0;
297-
// The transition's scrim opacity only increases on the forward transition. In the reverse
298-
// transition, the opacity should always be 0.0.
299-
//
300-
// Therefore, we need to only apply the scrim opacity animation when the transition
301-
// is running forwards.
302-
//
303-
// The reason that we check that the animation's status is not `completed` instead
304-
// of checking that it is `forward` is that this allows the interrupted reversal of the
305-
// forward transition to smoothly fade the scrim away. This prevents a disjointed
306-
// removal of the scrim.
307-
if (!reverse && animation.status != AnimationStatus.completed) {
308-
opacity = _scrimOpacityTween.evaluate(animation);
309-
}
238+
final Animation<double> _forwardScrimOpacityAnimation = widget.animation.drive(
239+
_ZoomPageTransition._scrimOpacityTween
240+
.chain(CurveTween(curve: const Interval(0.2075, 0.4175))));
310241

311-
final Animation<double> fadeTransition = reverse
312-
? kAlwaysCompleteAnimation
313-
: _fadeInTransition.animate(animation);
242+
final Animation<double> _forwardEndScreenScaleTransition = widget.animation.drive(
243+
Tween<double>(begin: 0.85, end: 1.00)
244+
.chain(_ZoomPageTransition._scaleCurveSequence));
314245

315-
final Animation<double> scaleTransition = (reverse
316-
? _scaleDownTransition
317-
: _scaleUpTransition
318-
).animate(animation);
246+
final Animation<double> _forwardStartScreenScaleTransition = widget.secondaryAnimation.drive(
247+
Tween<double>(begin: 1.00, end: 1.05)
248+
.chain(_ZoomPageTransition._scaleCurveSequence));
319249

320-
return AnimatedBuilder(
321-
animation: animation,
322-
builder: (BuildContext context, Widget child) {
323-
return Container(
324-
color: Colors.black.withOpacity(opacity),
325-
child: child,
326-
);
327-
},
328-
child: FadeTransition(
329-
opacity: fadeTransition,
330-
child: ScaleTransition(
331-
scale: scaleTransition,
332-
child: child,
333-
),
334-
),
335-
);
336-
}
337-
}
250+
final Animation<double> _forwardEndScreenFadeTransition = widget.animation.drive(
251+
Tween<double>(begin: 0.0, end: 1.00)
252+
.chain(CurveTween(curve: const Interval(0.125, 0.250))));
338253

339-
class _ZoomExitTransition extends StatelessWidget {
340-
const _ZoomExitTransition({
341-
Key key,
342-
@required this.animation,
343-
this.reverse = false,
344-
this.child,
345-
}) : assert(animation != null),
346-
assert(reverse != null),
347-
super(key: key);
254+
final Animation<double> _reverseEndScreenScaleTransition = widget.secondaryAnimation.drive(
255+
Tween<double>(begin: 1.00, end: 1.10)
256+
.chain(_ZoomPageTransition._flippedScaleCurveSequence));
348257

349-
final Animation<double> animation;
350-
final bool reverse;
351-
final Widget child;
352-
353-
static final Animatable<double> _fadeOutTransition = Tween<double>(
354-
begin: 1.0,
355-
end: 0.0,
356-
).chain(CurveTween(curve: const Interval(0.0825, 0.2075)));
357-
358-
static final Animatable<double> _scaleUpTransition = Tween<double>(
359-
begin: 1.00,
360-
end: 1.05,
361-
).chain(_ZoomPageTransition._scaleCurveSequence);
258+
final Animation<double> _reverseStartScreenScaleTransition = widget.animation.drive(
259+
Tween<double>(begin: 0.9, end: 1.0)
260+
.chain(_ZoomPageTransition._flippedScaleCurveSequence));
362261

363-
static final Animatable<double> _scaleDownTransition = Tween<double>(
364-
begin: 1.00,
365-
end: 0.90,
366-
).chain(_ZoomPageTransition._scaleCurveSequence);
262+
final Animation<double> _reverseStartScreenFadeTransition = widget.animation.drive(
263+
Tween<double>(begin: 0.0, end: 1.00)
264+
.chain(CurveTween(curve: const Interval(1 - 0.2075, 1 - 0.0825))));
367265

368-
@override
369-
Widget build(BuildContext context) {
370-
final Animation<double> fadeTransition = reverse
371-
? _fadeOutTransition.animate(animation)
372-
: kAlwaysCompleteAnimation;
373-
final Animation<double> scaleTransition = (reverse
374-
? _scaleDownTransition
375-
: _scaleUpTransition
376-
).animate(animation);
377-
378-
return FadeTransition(
379-
opacity: fadeTransition,
380-
child: ScaleTransition(
381-
scale: scaleTransition,
382-
child: child,
266+
return AnimatedBuilder(
267+
animation: widget.animation,
268+
builder: (BuildContext context, Widget child) {
269+
if (widget.animation.status == AnimationStatus.forward || _transitionWasInterrupted) {
270+
return Container(
271+
color: Colors.black.withOpacity(_forwardScrimOpacityAnimation.value),
272+
child: FadeTransition(
273+
opacity: _forwardEndScreenFadeTransition,
274+
child: ScaleTransition(
275+
scale: _forwardEndScreenScaleTransition,
276+
child: child,
277+
),
278+
),
279+
);
280+
} else if (widget.animation.status == AnimationStatus.reverse) {
281+
return ScaleTransition(
282+
scale: _reverseStartScreenScaleTransition,
283+
child: FadeTransition(
284+
opacity: _reverseStartScreenFadeTransition,
285+
child: child,
286+
),
287+
);
288+
}
289+
return child;
290+
},
291+
child: AnimatedBuilder(
292+
animation: widget.secondaryAnimation,
293+
builder: (BuildContext context, Widget child) {
294+
if (widget.secondaryAnimation.status == AnimationStatus.forward || _transitionWasInterrupted) {
295+
return ScaleTransition(
296+
scale: _forwardStartScreenScaleTransition,
297+
child: child,
298+
);
299+
} else if (widget.secondaryAnimation.status == AnimationStatus.reverse) {
300+
return ScaleTransition(
301+
scale: _reverseEndScreenScaleTransition,
302+
child: child,
303+
);
304+
}
305+
return child;
306+
},
307+
child: widget.child,
383308
),
384309
);
385310
}

0 commit comments

Comments
 (0)