Visitar URL original
Material Fade Transition Configuration; showModal implementation (#60) · flutter/packages@228ab96 · GitHub
Skip to content

Commit 228ab96

Browse files
author
Shi-Hao Hong
authored
Material Fade Transition Configuration; showModal implementation (#60)
* showModal implementation * Material Fade Transition Configuration
1 parent ac4ca3e commit 228ab96

5 files changed

Lines changed: 1288 additions & 0 deletions

File tree

‎packages/animations/lib/animations.dart‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// found in the LICENSE file.
44

55
export 'src/fade_through_transition.dart';
6+
export 'src/fade_transition.dart';
7+
export 'src/modal.dart';
68
export 'src/open_container.dart';
79
export 'src/page_transition_switcher.dart';
810
export 'src/shared_axis_transition.dart';
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
import 'modal.dart';
8+
import 'utils/curves.dart';
9+
10+
/// The modal transition configuration for a Material fade transition.
11+
///
12+
/// The fade pattern is used for UI elements that enter or exit from within
13+
/// the screen bounds. Elements that enter use a quick fade in and scale from
14+
/// 80% to 100%. Elements that exit simply fade out. The scale animation is
15+
/// only applied to entering elements to emphasize new content over old.
16+
///
17+
/// ```dart
18+
/// /// Sample widget that uses [showModal] with [FadeTransitionConfiguration].
19+
/// class MyHomePage extends StatelessWidget {
20+
/// @override
21+
/// Widget build(BuildContext context) {
22+
/// return Scaffold(
23+
/// body: Center(
24+
/// child: RaisedButton(
25+
/// onPressed: () {
26+
/// showModal(
27+
/// context: context,
28+
/// configuration: FadeTransitionConfiguration(),
29+
/// builder: (BuildContext context) {
30+
/// return _CenteredFlutterLogo();
31+
/// },
32+
/// );
33+
/// },
34+
/// child: Icon(Icons.add),
35+
/// ),
36+
/// ),
37+
/// );
38+
/// }
39+
/// }
40+
///
41+
/// /// Displays a centered Flutter logo with size constraints.
42+
/// class _CenteredFlutterLogo extends StatelessWidget {
43+
/// const _CenteredFlutterLogo();
44+
///
45+
/// @override
46+
/// Widget build(BuildContext context) {
47+
/// return Center(
48+
/// child: SizedBox(
49+
/// width: 250,
50+
/// height: 250,
51+
/// child: const Material(
52+
/// child: Center(
53+
/// child: FlutterLogo(size: 250),
54+
/// ),
55+
/// ),
56+
/// ),
57+
/// );
58+
/// }
59+
/// }
60+
/// ```
61+
class FadeTransitionConfiguration extends ModalConfiguration {
62+
/// Creates the Material fade transition configuration.
63+
///
64+
/// [barrierDismissible] configures whether or not tapping the modal's
65+
/// scrim dismisses the modal. [barrierLabel] sets the semantic label for
66+
/// a dismissible barrier. [barrierDismissible] cannot be null. If
67+
/// [barrierDismissible] is true, the [barrierLabel] cannot be null.
68+
FadeTransitionConfiguration({
69+
Color barrierColor = Colors.black54,
70+
bool barrierDismissible = true,
71+
Duration transitionDuration = const Duration(milliseconds: 150),
72+
Duration reverseTransitionDuration = const Duration(milliseconds: 75),
73+
String barrierLabel = 'Dismiss',
74+
}) : super(
75+
barrierColor: barrierColor,
76+
barrierDismissible: barrierDismissible,
77+
barrierLabel: barrierLabel,
78+
transitionDuration: transitionDuration,
79+
reverseTransitionDuration: reverseTransitionDuration,
80+
);
81+
82+
@override
83+
Widget transitionBuilder(
84+
BuildContext context,
85+
Animation<double> animation,
86+
Animation<double> secondaryAnimation,
87+
Widget child,
88+
) {
89+
return _MaterialFadeTransition(
90+
animation: animation,
91+
secondaryAnimation: secondaryAnimation,
92+
child: child,
93+
);
94+
}
95+
}
96+
97+
class _MaterialFadeTransition extends StatefulWidget {
98+
const _MaterialFadeTransition({
99+
Key key,
100+
@required this.animation,
101+
@required this.secondaryAnimation,
102+
this.child,
103+
}) : assert(animation != null),
104+
assert(secondaryAnimation != null),
105+
super(key: key);
106+
107+
/// The animation that drives the [child]'s entrance and exit.
108+
///
109+
/// See also:
110+
///
111+
/// * [TransitionRoute.animate], which is the value given to this property
112+
/// when it is used as a page transition.
113+
final Animation<double> animation;
114+
115+
/// The animation that transitions [child] when new content is pushed on top
116+
/// of it.
117+
///
118+
/// See also:
119+
///
120+
/// * [TransitionRoute.secondaryAnimation], which is the value given to this
121+
/// property when the it is used as a page transition.
122+
final Animation<double> secondaryAnimation;
123+
124+
/// The widget below this widget in the tree.
125+
///
126+
/// This widget will transition in and out as driven by [animation] and
127+
/// [secondaryAnimation].
128+
final Widget child;
129+
130+
@override
131+
_MaterialFadeTransitionState createState() => _MaterialFadeTransitionState();
132+
}
133+
134+
class _MaterialFadeTransitionState extends State<_MaterialFadeTransition> {
135+
AnimationStatus _effectiveAnimationStatus;
136+
137+
@override
138+
void initState() {
139+
super.initState();
140+
_effectiveAnimationStatus = widget.animation.status;
141+
widget.animation.addStatusListener(_animationListener);
142+
}
143+
144+
void _animationListener(AnimationStatus animationStatus) {
145+
_effectiveAnimationStatus = _calculateEffectiveAnimationStatus(
146+
lastEffective: _effectiveAnimationStatus,
147+
current: animationStatus,
148+
);
149+
}
150+
151+
// When a transition is interrupted midway we just want to play the ongoing
152+
// animation in reverse. Switching to the actual reverse transition would
153+
// yield a disjoint experience since the forward and reverse transitions are
154+
// very different.
155+
AnimationStatus _calculateEffectiveAnimationStatus({
156+
@required AnimationStatus lastEffective,
157+
@required AnimationStatus current,
158+
}) {
159+
assert(current != null);
160+
assert(lastEffective != null);
161+
switch (current) {
162+
case AnimationStatus.dismissed:
163+
case AnimationStatus.completed:
164+
return current;
165+
case AnimationStatus.forward:
166+
switch (lastEffective) {
167+
case AnimationStatus.dismissed:
168+
case AnimationStatus.completed:
169+
case AnimationStatus.forward:
170+
return current;
171+
case AnimationStatus.reverse:
172+
return lastEffective;
173+
}
174+
break;
175+
case AnimationStatus.reverse:
176+
switch (lastEffective) {
177+
case AnimationStatus.dismissed:
178+
case AnimationStatus.completed:
179+
case AnimationStatus.reverse:
180+
return current;
181+
case AnimationStatus.forward:
182+
return lastEffective;
183+
}
184+
break;
185+
}
186+
return null; // unreachable
187+
}
188+
189+
void _updateAnimationListener(
190+
Animation<double> oldAnimation,
191+
Animation<double> animation,
192+
) {
193+
if (oldAnimation != animation) {
194+
oldAnimation.removeStatusListener(_animationListener);
195+
animation.addStatusListener(_animationListener);
196+
_animationListener(animation.status);
197+
}
198+
}
199+
200+
@override
201+
void didUpdateWidget(_MaterialFadeTransition oldWidget) {
202+
super.didUpdateWidget(oldWidget);
203+
_updateAnimationListener(
204+
oldWidget.animation,
205+
widget.animation,
206+
);
207+
}
208+
209+
@override
210+
void dispose() {
211+
widget.animation.removeStatusListener(_animationListener);
212+
super.dispose();
213+
}
214+
215+
@override
216+
Widget build(BuildContext context) {
217+
return AnimatedBuilder(
218+
animation: widget.animation,
219+
builder: (BuildContext context, Widget child) {
220+
assert(_effectiveAnimationStatus != null);
221+
switch (_effectiveAnimationStatus) {
222+
case AnimationStatus.forward:
223+
return _EnterTransition(
224+
animation: widget.animation,
225+
child: child,
226+
);
227+
case AnimationStatus.dismissed:
228+
case AnimationStatus.reverse:
229+
case AnimationStatus.completed:
230+
return FadeTransition(
231+
opacity: widget.animation,
232+
child: child,
233+
);
234+
}
235+
return null; // unreachable
236+
},
237+
child: widget.child,
238+
);
239+
}
240+
}
241+
242+
class _EnterTransition extends StatelessWidget {
243+
const _EnterTransition({
244+
this.animation,
245+
this.child,
246+
});
247+
248+
final Animation<double> animation;
249+
final Widget child;
250+
251+
static Animatable<double> fadeInTransition = CurveTween(
252+
curve: const Interval(0.0, 0.3),
253+
);
254+
static Animatable<double> scaleInTransition = Tween<double>(
255+
begin: 0.80,
256+
end: 1.00,
257+
).chain(CurveTween(curve: decelerateEasing));
258+
259+
@override
260+
Widget build(BuildContext context) {
261+
return FadeTransition(
262+
opacity: fadeInTransition.animate(animation),
263+
child: ScaleTransition(
264+
scale: scaleInTransition.animate(animation),
265+
child: child,
266+
),
267+
);
268+
}
269+
}

0 commit comments

Comments
 (0)