@@ -153,6 +153,185 @@ void main() {
153153 },
154154 );
155155
156+ testWidgets (
157+ 'showModal builds a new route with specified barrier properties '
158+ 'with default configuration(FadeScaleTransitionConfiguration)' ,
159+ (WidgetTester tester) async {
160+ await tester.pumpWidget (
161+ MaterialApp (
162+ home: Scaffold (
163+ body: Builder (builder: (BuildContext context) {
164+ return Center (
165+ child: RaisedButton (
166+ onPressed: () {
167+ showModal <void >(
168+ context: context,
169+ builder: (BuildContext context) {
170+ return const _FlutterLogoModal ();
171+ },
172+ );
173+ },
174+ child: const Icon (Icons .add),
175+ ),
176+ );
177+ }),
178+ ),
179+ ),
180+ );
181+ await tester.tap (find.byType (RaisedButton ));
182+ await tester.pumpAndSettle ();
183+
184+ // New route containing _FlutterLogoModal is present.
185+ expect (find.byType (_FlutterLogoModal ), findsOneWidget);
186+ final ModalBarrier topModalBarrier = tester.widget <ModalBarrier >(
187+ find.byType (ModalBarrier ).at (1 ),
188+ );
189+
190+ // Verify new route's modal barrier properties are correct.
191+ expect (topModalBarrier.color, Colors .black54);
192+ expect (topModalBarrier.barrierSemanticsDismissible, true );
193+ expect (topModalBarrier.semanticsLabel, 'Dismiss' );
194+ },
195+ );
196+
197+ testWidgets (
198+ 'showModal forwards animation '
199+ 'with default configuration(FadeScaleTransitionConfiguration)' ,
200+ (WidgetTester tester) async {
201+ final GlobalKey key = GlobalKey ();
202+ await tester.pumpWidget (
203+ MaterialApp (
204+ home: Scaffold (
205+ body: Builder (builder: (BuildContext context) {
206+ return Center (
207+ child: RaisedButton (
208+ onPressed: () {
209+ showModal <void >(
210+ context: context,
211+ builder: (BuildContext context) {
212+ return _FlutterLogoModal (key: key);
213+ },
214+ );
215+ },
216+ child: const Icon (Icons .add),
217+ ),
218+ );
219+ }),
220+ ),
221+ ),
222+ );
223+
224+ // Start forwards animation
225+ await tester.tap (find.byType (RaisedButton ));
226+ await tester.pump ();
227+
228+ // Opacity duration: First 30% of 150ms, linear transition
229+ double topFadeTransitionOpacity = _getOpacity (key, tester);
230+ double topScale = _getScale (key, tester);
231+ expect (topFadeTransitionOpacity, 0.0 );
232+ expect (topScale, 0.80 );
233+
234+ // 3/10 * 150ms = 45ms (total opacity animation duration)
235+ // 1/2 * 45ms = ~23ms elapsed for halfway point of opacity
236+ // animation
237+ await tester.pump (const Duration (milliseconds: 23 ));
238+ topFadeTransitionOpacity = _getOpacity (key, tester);
239+ expect (topFadeTransitionOpacity, closeTo (0.5 , 0.05 ));
240+ topScale = _getScale (key, tester);
241+ expect (topScale, greaterThan (0.80 ));
242+ expect (topScale, lessThan (1.0 ));
243+
244+ // End of opacity animation.
245+ await tester.pump (const Duration (milliseconds: 22 ));
246+ topFadeTransitionOpacity = _getOpacity (key, tester);
247+ expect (topFadeTransitionOpacity, 1.0 );
248+ topScale = _getScale (key, tester);
249+ expect (topScale, greaterThan (0.80 ));
250+ expect (topScale, lessThan (1.0 ));
251+
252+ // 100ms into the animation
253+ await tester.pump (const Duration (milliseconds: 55 ));
254+ topScale = _getScale (key, tester);
255+ expect (topScale, greaterThan (0.80 ));
256+ expect (topScale, lessThan (1.0 ));
257+
258+ // Get to the end of the animation
259+ await tester.pump (const Duration (milliseconds: 50 ));
260+ topScale = _getScale (key, tester);
261+ expect (topScale, 1.0 );
262+
263+ await tester.pump (const Duration (milliseconds: 1 ));
264+ expect (find.byType (_FlutterLogoModal ), findsOneWidget);
265+ },
266+ );
267+
268+ testWidgets (
269+ 'showModal reverse animation '
270+ 'with default configuration(FadeScaleTransitionConfiguration)' ,
271+ (WidgetTester tester) async {
272+ final GlobalKey key = GlobalKey ();
273+ await tester.pumpWidget (
274+ MaterialApp (
275+ home: Scaffold (
276+ body: Builder (builder: (BuildContext context) {
277+ return Center (
278+ child: RaisedButton (
279+ onPressed: () {
280+ showModal <void >(
281+ context: context,
282+ builder: (BuildContext context) {
283+ return _FlutterLogoModal (key: key);
284+ },
285+ );
286+ },
287+ child: const Icon (Icons .add),
288+ ),
289+ );
290+ }),
291+ ),
292+ ),
293+ );
294+
295+ // Start forwards animation
296+ await tester.tap (find.byType (RaisedButton ));
297+ await tester.pumpAndSettle ();
298+ expect (find.byType (_FlutterLogoModal ), findsOneWidget);
299+
300+ // Tap on modal barrier to start reverse animation.
301+ await tester.tapAt (Offset .zero);
302+ await tester.pump ();
303+
304+ // Opacity duration: Linear transition throughout 75ms
305+ // No scale animations on exit transition.
306+ double topFadeTransitionOpacity = _getOpacity (key, tester);
307+ double topScale = _getScale (key, tester);
308+ expect (topFadeTransitionOpacity, 1.0 );
309+ expect (topScale, 1.0 );
310+
311+ await tester.pump (const Duration (milliseconds: 25 ));
312+ topFadeTransitionOpacity = _getOpacity (key, tester);
313+ topScale = _getScale (key, tester);
314+ expect (topFadeTransitionOpacity, closeTo (0.66 , 0.05 ));
315+ expect (topScale, 1.0 );
316+
317+ await tester.pump (const Duration (milliseconds: 25 ));
318+ topFadeTransitionOpacity = _getOpacity (key, tester);
319+ topScale = _getScale (key, tester);
320+ expect (topFadeTransitionOpacity, closeTo (0.33 , 0.05 ));
321+ expect (topScale, 1.0 );
322+
323+ // End of opacity animation
324+ await tester.pump (const Duration (milliseconds: 25 ));
325+ topFadeTransitionOpacity = _getOpacity (key, tester);
326+ expect (topFadeTransitionOpacity, 0.0 );
327+ topScale = _getScale (key, tester);
328+ expect (topScale, 1.0 );
329+
330+ await tester.pump (const Duration (milliseconds: 1 ));
331+ expect (find.byType (_FlutterLogoModal ), findsNothing);
332+ },
333+ );
334+
156335 testWidgets (
157336 'State is not lost when transitioning' ,
158337 (WidgetTester tester) async {
@@ -290,6 +469,17 @@ double _getOpacity(GlobalKey key, WidgetTester tester) {
290469 });
291470}
292471
472+ double _getScale (GlobalKey key, WidgetTester tester) {
473+ final Finder finder = find.ancestor (
474+ of: find.byKey (key),
475+ matching: find.byType (ScaleTransition ),
476+ );
477+ return tester.widgetList (finder).fold <double >(1.0 , (double a, Widget widget) {
478+ final ScaleTransition transition = widget;
479+ return a * transition.scale.value;
480+ });
481+ }
482+
293483class _FlutterLogoModal extends StatefulWidget {
294484 const _FlutterLogoModal ({
295485 Key key,
0 commit comments