Skip to content

Commit bed13e9

Browse files
committed
🚸 Using zoom page transition for viewer.
1 parent 1de4319 commit bed13e9

File tree

2 files changed

+205
-12
lines changed

2 files changed

+205
-12
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
///
2+
/// [Author] Alex (https://github.com/AlexVincent525)
3+
/// [Date] 2020-05-31 11:23
4+
///
5+
import 'package:flutter/material.dart';
6+
7+
// Zooms and fades a new page in, zooming out the previous page. This transition
8+
// is designed to match the Android 10 activity transition.
9+
class ZoomPageTransition extends StatefulWidget {
10+
const ZoomPageTransition({
11+
Key key,
12+
this.animation,
13+
this.secondaryAnimation,
14+
this.child,
15+
}) : super(key: key);
16+
17+
// The scrim obscures the old page by becoming increasingly opaque.
18+
static final Tween<double> _scrimOpacityTween = Tween<double>(
19+
begin: 0.0,
20+
end: 0.60,
21+
);
22+
23+
// A curve sequence that is similar to the 'fastOutExtraSlowIn' curve used in
24+
// the native transition.
25+
static final List<TweenSequenceItem<double>>
26+
fastOutExtraSlowInTweenSequenceItems = <TweenSequenceItem<double>>[
27+
TweenSequenceItem<double>(
28+
tween: Tween<double>(begin: 0.0, end: 0.4)
29+
.chain(CurveTween(curve: const Cubic(0.05, 0.0, 0.133333, 0.06))),
30+
weight: 0.166666,
31+
),
32+
TweenSequenceItem<double>(
33+
tween: Tween<double>(begin: 0.4, end: 1.0)
34+
.chain(CurveTween(curve: const Cubic(0.208333, 0.82, 0.25, 1.0))),
35+
weight: 1.0 - 0.166666,
36+
),
37+
];
38+
static final TweenSequence<double> _scaleCurveSequence =
39+
TweenSequence<double>(fastOutExtraSlowInTweenSequenceItems);
40+
static final FlippedTweenSequence flippedScaleCurveSequence =
41+
FlippedTweenSequence(fastOutExtraSlowInTweenSequenceItems);
42+
43+
final Animation<double> animation;
44+
final Animation<double> secondaryAnimation;
45+
final Widget child;
46+
47+
@override
48+
_ZoomPageTransitionState createState() => _ZoomPageTransitionState();
49+
}
50+
51+
class _ZoomPageTransitionState extends State<ZoomPageTransition> {
52+
AnimationStatus _currentAnimationStatus;
53+
AnimationStatus _lastAnimationStatus;
54+
55+
@override
56+
void initState() {
57+
super.initState();
58+
widget.animation.addStatusListener((AnimationStatus animationStatus) {
59+
_lastAnimationStatus = _currentAnimationStatus;
60+
_currentAnimationStatus = animationStatus;
61+
});
62+
}
63+
64+
// This check ensures that the animation reverses the original animation if
65+
// the transition were interruped midway. This prevents a disjointed
66+
// experience since the reverse animation uses different fade and scaling
67+
// curves.
68+
bool get _transitionWasInterrupted {
69+
bool wasInProgress = false;
70+
bool isInProgress = false;
71+
72+
switch (_currentAnimationStatus) {
73+
case AnimationStatus.completed:
74+
case AnimationStatus.dismissed:
75+
isInProgress = false;
76+
break;
77+
case AnimationStatus.forward:
78+
case AnimationStatus.reverse:
79+
isInProgress = true;
80+
break;
81+
}
82+
switch (_lastAnimationStatus) {
83+
case AnimationStatus.completed:
84+
case AnimationStatus.dismissed:
85+
wasInProgress = false;
86+
break;
87+
case AnimationStatus.forward:
88+
case AnimationStatus.reverse:
89+
wasInProgress = true;
90+
break;
91+
}
92+
return wasInProgress && isInProgress;
93+
}
94+
95+
@override
96+
Widget build(BuildContext context) {
97+
final Animation<double> _forwardScrimOpacityAnimation = widget.animation
98+
.drive(ZoomPageTransition._scrimOpacityTween
99+
.chain(CurveTween(curve: const Interval(0.2075, 0.4175))));
100+
101+
final Animation<double> _forwardEndScreenScaleTransition = widget.animation
102+
.drive(Tween<double>(begin: 0.85, end: 1.00)
103+
.chain(ZoomPageTransition._scaleCurveSequence));
104+
105+
final Animation<double> _forwardStartScreenScaleTransition =
106+
widget.secondaryAnimation.drive(Tween<double>(begin: 1.00, end: 1.05)
107+
.chain(ZoomPageTransition._scaleCurveSequence));
108+
109+
final Animation<double> _forwardEndScreenFadeTransition = widget.animation
110+
.drive(Tween<double>(begin: 0.0, end: 1.00)
111+
.chain(CurveTween(curve: const Interval(0.125, 0.250))));
112+
113+
final Animation<double> _reverseEndScreenScaleTransition =
114+
widget.secondaryAnimation.drive(Tween<double>(begin: 1.00, end: 1.10)
115+
.chain(ZoomPageTransition.flippedScaleCurveSequence));
116+
117+
final Animation<double> _reverseStartScreenScaleTransition =
118+
widget.animation.drive(Tween<double>(begin: 0.9, end: 1.0)
119+
.chain(ZoomPageTransition.flippedScaleCurveSequence));
120+
121+
final Animation<double> _reverseStartScreenFadeTransition = widget.animation
122+
.drive(Tween<double>(begin: 0.0, end: 1.00)
123+
.chain(CurveTween(curve: const Interval(1 - 0.2075, 1 - 0.0825))));
124+
125+
return AnimatedBuilder(
126+
animation: widget.animation,
127+
builder: (BuildContext context, Widget child) {
128+
if (widget.animation.status == AnimationStatus.forward ||
129+
_transitionWasInterrupted) {
130+
return Container(
131+
color:
132+
Colors.black.withOpacity(_forwardScrimOpacityAnimation.value),
133+
child: FadeTransition(
134+
opacity: _forwardEndScreenFadeTransition,
135+
child: ScaleTransition(
136+
scale: _forwardEndScreenScaleTransition,
137+
child: child,
138+
),
139+
),
140+
);
141+
} else if (widget.animation.status == AnimationStatus.reverse) {
142+
return ScaleTransition(
143+
scale: _reverseStartScreenScaleTransition,
144+
child: FadeTransition(
145+
opacity: _reverseStartScreenFadeTransition,
146+
child: child,
147+
),
148+
);
149+
}
150+
return child;
151+
},
152+
child: AnimatedBuilder(
153+
animation: widget.secondaryAnimation,
154+
builder: (BuildContext context, Widget child) {
155+
if (widget.secondaryAnimation.status == AnimationStatus.forward ||
156+
_transitionWasInterrupted) {
157+
return ScaleTransition(
158+
scale: _forwardStartScreenScaleTransition,
159+
child: child,
160+
);
161+
} else if (widget.secondaryAnimation.status ==
162+
AnimationStatus.reverse) {
163+
return ScaleTransition(
164+
scale: _reverseEndScreenScaleTransition,
165+
child: child,
166+
);
167+
}
168+
return child;
169+
},
170+
child: widget.child,
171+
),
172+
);
173+
}
174+
}

lib/src/widget/asset_picker_viewer.dart

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import 'package:flutter/services.dart';
1212
import 'package:extended_image/extended_image.dart';
1313
import 'package:photo_manager/photo_manager.dart';
1414
import 'package:provider/provider.dart';
15-
import 'package:wechat_assets_picker/src/widget/builder/audio_page_builder.dart';
1615
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
1716

1817
import '../constants/constants.dart';
18+
import '../constants/zoom_page_transition.dart';
19+
import 'builder/audio_page_builder.dart';
1920
import 'builder/fade_image_builder.dart';
2021
import 'builder/image_page_builder.dart';
2122
import 'builder/video_page_builder.dart';
@@ -65,19 +66,37 @@ class AssetPickerViewer extends StatefulWidget {
6566
AssetPickerProvider selectorProvider,
6667
}) async {
6768
try {
68-
final WidgetBuilder viewer = (BuildContext _) => AssetPickerViewer(
69-
currentIndex: currentIndex,
70-
assets: assets,
71-
themeData: themeData,
72-
selectedAssets: selectedAssets,
73-
selectorProvider: selectorProvider,
69+
final Widget viewer = AssetPickerViewer(
70+
currentIndex: currentIndex,
71+
assets: assets,
72+
themeData: themeData,
73+
selectedAssets: selectedAssets,
74+
selectorProvider: selectorProvider,
75+
);
76+
final PageRouteBuilder<List<AssetEntity>> pageRoute =
77+
PageRouteBuilder<List<AssetEntity>>(
78+
pageBuilder: (
79+
BuildContext context,
80+
Animation<double> animation,
81+
Animation<double> secondaryAnimation,
82+
) {
83+
return viewer;
84+
},
85+
transitionsBuilder: (
86+
BuildContext context,
87+
Animation<double> animation,
88+
Animation<double> secondaryAnimation,
89+
Widget child,
90+
) {
91+
return ZoomPageTransition(
92+
animation: animation,
93+
secondaryAnimation: secondaryAnimation,
94+
child: child,
7495
);
75-
final List<AssetEntity> result =
76-
await Navigator.of(context).push<List<AssetEntity>>(
77-
Platform.isAndroid
78-
? MaterialPageRoute<List<AssetEntity>>(builder: viewer)
79-
: CupertinoPageRoute<List<AssetEntity>>(builder: viewer),
96+
},
8097
);
98+
final List<AssetEntity> result =
99+
await Navigator.of(context).push<List<AssetEntity>>(pageRoute);
81100
return result;
82101
} catch (e) {
83102
realDebugPrint('Error when calling assets picker viewer: $e');

0 commit comments

Comments
 (0)