How to switch the interface of flutter to achieve some special effects

Time:2020-9-20

background

We know that if you switch between pages directly, it will be hard, and it will make users feel very abrupt, and the user experience is not very good.

Therefore, in general, in order to achieve smooth transition between pages, animation will be added.

In addition, sometimes we don’t like the default animation of the system and want to be able to customize the animation.

Based on this, this article mainly talks about how to add custom animation to the page switching of flutter.

Default effect

First, let’s see what the default effect looks like?

It looks good. The code is as follows:


import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  home: MyApp(),
 ));

class MyApp extends StatelessWidget {
 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
 return _getCenterWidget(RaisedButton(
  child: Text('Go to next page->'),
  onPressed: () {
   Navigator.of(context).push(_createRoute());
  }));
 }
}

Route _createRoute() {
 return MaterialPageRoute(builder: (BuildContext context) => Page2());
}

class Page2 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return _getCenterWidget(Text('Page2'));
 }
}

Widget _getCenterWidget(Widget child) {
 return Scaffold(
 appBar: AppBar(),
 body: Center(
  child: child,
 ),
 );
}

You can see that two pages, myapp and Page2, have been created.

The first page, myapp, has a button, and the second page, Page2, has a text.

The key switch is here_ Create route() in the route creation method.

We click in the source code of material pageroute, and you can see it


 @override
 Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
 final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
 return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
 }

With the comments at the beginning, you can see that this is the default interface transition effect.


/// See also:
///
/// * [PageTransitionsTheme], which defines the default page transitions used
/// by [MaterialPageRoute.buildTransitions].

In addition, we can see that the default animation duration is 300ms, and we can’t customize it.


 @override
 Duration get transitionDuration => const Duration(milliseconds: 300);

Let’s talk about how to customize the transition effect of our interface and customize the animation duration.

Custom animation

1. Set pageroutebuilder

From the above analysis, we know that the most critical place is to create routing methods_ Create route().

So let’s change it first. Instead of using the default material pageroute, we use pageroutebuilder.


Route _createRoute() {
 return PageRouteBuilder(
  pageBuilder: (context, animation, secondaryAnimation) => Page2(),
  transitionsBuilder:(context, animation, secondaryAnimation, child) {
  return child;
  }
 );
}

You can see that we specify the routing page through pagebuilder, and specify the page transition effect through transitions builder.

In addition, we don’t have to memorize the parameters here by rote. We can see the source code as follows:


/// Signature for the function that builds a route's primary contents.
/// Used in [PageRouteBuilder] and [showGeneralDialog].
///
/// See [ModalRoute.buildPage] for complete definition of the parameters.
typedef RoutePageBuilder = Widget Function(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation);

/// Signature for the function that builds a route's transitions.
/// Used in [PageRouteBuilder] and [showGeneralDialog].
///
/// See [ModalRoute.buildTransitions] for complete definition of the parameters.
typedef RouteTransitionsBuilder = Widget Function(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child);

If we run the code, because it directly returns to child, it should have no animation effect. After running, the effect is as follows:

2. Add tween and slidetransition

The default transition effect is from the right to the left, and the custom demo effect here will transition from the bottom to the top.

It is important to understand that tween is a linear interpolator between the start and end values.

In addition, if we follow the above transitions builder, we can see that its first animation parameter values are 0.0 to 1.0.

Our side is from bottom to top, so the offset of Y axis is from 1.0 to 0.0, which means that there is no offset from the top of the page vertically (already at the top).

Therefore, with regard to tween and animation, we can get:


var begin = Offset(0.0, 1.0);
var end = Offset(0.0, 0.0);
var tween = Tween(begin: begin, end: end);
var offsetAnimation = animation.drive(tween);

Because we want to realize sliding, the determined offset animation is processed and returned through slidetransition. The modified routing code is as follows:


Route _createRoute() {
 return PageRouteBuilder(
 transitionDuration: Duration(seconds: 5),
  pageBuilder: (context, animation, secondaryAnimation) => Page2(),
  transitionsBuilder:(context, animation, secondaryAnimation, child) {
  var begin = Offset(0.0, 1.0);
  var end = Offset(0.0, 0.0);
  var tween = Tween(begin: begin, end: end);
  var offsetAnimation = animation.drive(tween);

  return SlideTransition(
   position: offsetAnimation,
   child: child,
  );
  }
 );
}

The results are as follows:

If you see the above effect, you may have doubts about it.

Question 1: I can understand that you open the page from bottom to top, but why is the return from top to bottom?

We can see the source code of transitions builder


 /// Used to build the route's transitions.
 ///
 /// See [ModalRoute.buildTransitions] for complete definition of the parameters.
 final RouteTransitionsBuilder transitionsBuilder;

We can follow (by clicking) the buildtransition in the comment to see the description of animation as follows:


 /// * [animation]: When the [Navigator] pushes a route on the top of its stack,
 /// the new route's primary [animation] runs from 0.0 to 1.0. When the [Navigator]
 /// pops the topmost route this animation runs from 1.0 to 0.0.

We can see that the animation effect of stack in and out is opposite, and this is in line with our cognition.

Question 2: now the effect is from bottom to top. If I want to achieve from top to bottom, can I exchange the offset of begin and end?

In fact, if you understand what I said above

Our side is from bottom to top, so the offset of Y axis is from 1.0 to 0.0, which means that there is no offset from the top of the page vertically (already at the top).

You’ll know what it would be like to change from 0.0 to 1.0.

Let’s change it and show it through the actual demonstration effect.

The modified code is as follows:


Route _createRoute() {
 return PageRouteBuilder(
  pageBuilder: (context, animation, secondaryAnimation) => Page2(),
  transitionsBuilder:(context, animation, secondaryAnimation, child) {
  var begin = Offset(0.0, 0.0);
  var end = Offset(0.0, 1.0);
  var tween = Tween(begin: begin, end: end);
  var offsetAnimation = animation.drive(tween);

  return SlideTransition(
   position: offsetAnimation,
   child: child,
  );
  }
 );
}

Only the offset of begin and end is exchanged.

The operation effect is as follows:

Although we can see some clues, as we said earlier, the default animation duration is 300 ms, so it is relatively fast, which is not easy to analyze.

We can set the duration of the animation through the transitionduration property of pageroutebuilder.

Let’s set 3S to see the effect. The code is as follows:


Route _createRoute() {
 return PageRouteBuilder(
  transitionDuration: Duration(seconds: 3),
  pageBuilder: (context, animation, secondaryAnimation) => Page2(),
  transitionsBuilder:(context, animation, secondaryAnimation, child) {
  var begin = Offset(0.0, 0.0);
  var end = Offset(0.0, 1.0);
  var tween = Tween(begin: begin, end: end);
  var offsetAnimation = animation.drive(tween);

  return SlideTransition(
   position: offsetAnimation,
   child: child,
  );
  }
 );
}

The operation effect is as follows:

See, the reverse is true, from 0.0 to 1.0 (100%) from the top.

So what if I want to go from top to bottom?

We give you a picture. I believe you will understand after reading it.

We know from this graph that if we go from bottom to top, y should go from 1.0 to 0.0. If you want to go from top to bottom, y should go from – 1.0 to 0.0.

So we modify the code as follows:


Route _createRoute() {
 return PageRouteBuilder(
   transitionDuration: Duration(seconds: 3),
   pageBuilder: (context, animation, secondaryAnimation) => Page2(),
   transitionsBuilder:(context, animation, secondaryAnimation, child) {
    var begin = Offset(0.0, -1.0);
    var end = Offset(0.0, 0.0);
    var tween = Tween(begin: begin, end: end);
    var offsetAnimation = animation.drive(tween);

    return SlideTransition(
     position: offsetAnimation,
     child: child,
    );
   }
 );
}

The operation effect is as follows:

3. Point acceleration through curvetween

When we set the animation duration to 3S, we can clearly see that our animation speed seems to be uniform.

So if I want to change the speed of the animation, such as fast in and slow in ending, can I?

The answer is yes.

We can achieve this requirement through curvetween.

The focus is on the choice of curve, so what kind of curve should we choose?

In fact, one of my favorite points about flutter is that the code comments are detailed and demo demo is also available.

We enter the curves source code to Curves.ease For example:


 /// A cubic animation curve that speeds up quickly and ends slowly.
 ///
 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
 static const Cubic ease = Cubic(0.25, 0.1, 0.25, 1.0);

The note says that the start-up is fast and the end is slow. In addition, there is an mp4 link. Click to see the following effect:

We can see the change trend of it. Through the slope, we can see that the early stage is fast and the later stage is slow. Moreover, there are four kinds of animation effect preview on the right.

We set curvetween code as follows:


var curveTween = CurveTween(curve: Curves.ease);

As you can see, it’s easy to choose a trend you want.

4. Combine tween and curvetween

This is also relatively simple. You can use the chain method provided by Tween, as follows:


var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: Curves.ease));

In addition, general offset (0.0, 0.0) can be written as Offset.zero 。

The modified code is as follows:


Route _createRoute() {
 return PageRouteBuilder(
   transitionDuration: Duration(seconds: 3),
   pageBuilder: (context, animation, secondaryAnimation) => Page2(),
   transitionsBuilder:(context, animation, secondaryAnimation, child) {
    var begin = Offset(0.0, 1.0);
    var end = Offset.zero;
    var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: Curves.ease));

    return SlideTransition(
     position: animation.drive(tween),
     child: child,
    );
   }
 );
}

The operation effect is as follows:

5. Complete examples

With the above foundation, we can add animation effects in all four directions. Of course, there is no delay on our side. In addition, for the convenience of demonstration, we will open it directly and return to delay 1s.

The code is as follows:


import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
   home: MyApp(),
  ));

class MyApp extends StatelessWidget {
 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
  return _getCenterWidget(Column(
   mainAxisAlignment: MainAxisAlignment.center,
   children: <Widget>[
    _getBtn(context, 'right in',
      Tween(begin: Offset(1.0, 0.0), end: Offset.zero)),
    _getBtn(context, 'left in',
      Tween(begin: Offset(-1.0, 0.0), end: Offset.zero)),
    _getBtn(context, 'bottom in',
      Tween(begin: Offset(0.0, 1.0), end: Offset.zero)),
    _getBtn(context, 'top in',
      Tween(begin: Offset(0.0, -1.0), end: Offset.zero)),
   ],
  ));
 }
}

Widget _getBtn(BuildContext context, String textContent, Tween<Offset> tween) {
 return RaisedButton(
   child: Text(textContent),
   onPressed: () {
    Navigator.of(context).push(_createRoute(tween));
   });
}

Route _createRoute(Tween<Offset> tween) {
 return PageRouteBuilder(
   pageBuilder: (context, animation, secondaryAnimation) => Page2(),
   transitionsBuilder: (context, animation, secondaryAnimation, child) {
    return SlideTransition(
     position:
       animation.drive(tween.chain(CurveTween(curve: Curves.ease))),
     child: child,
    );
   });
}

class Page2 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  Future.delayed(Duration(seconds: 1), () {
   Navigator.of(context).pop();
  });
  return _getCenterWidget(Text('Page2'));
 }
}

Widget _getCenterWidget(Widget child) {
 return Scaffold(
  appBar: AppBar(),
  body: Center(
   child: child,
  ),
 );
}

The results are as follows:

epilogue

At this point, the transition between the flutter interfaces is basically clarified.

Other such as rotation, scaling, transparency and even combination animation, I believe that with the above foundation, you can also do DIY by yourself.

The effect of scaling is as follows:

For specific code, see GitHub:

flutter_page_transition

Reference link:

  • Animate a page route transition
  • Tween class

Well, the above is the whole content of this article, I hope the content of this article has a certain reference learning value for your study or work, thank you for your support to developeppaer.