77  comments

Navigating between routes is quite bland by default. Flutter graciously provides you with the MaterialPageRoute and CupertinoPageRoute classes and, while their transition animations don't look bad, there's certainly something more we can do.

Could we, for example, animate parts of the pushed page independently and even make the animation staggered? Of course we can! We're in Flutter, after all ?

The project we're going to build

By the end of this tutorial, you are going to know how to build a fully custom page transition consisting of translating two Containers. You can then take this approach and apply it to all kinds of crazy effects - anything is possible with the Transform widget.

Exploring the default transition

The starter project contains two pages. After pressing the floating action button in the FirstPage, the following code is executed.

first_page.dart

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => SecondPage(),
  ),
);

We're then taken to the SecondPage while seeing an uninspiring "full page" slide transition. This page contains two simple Containers inside a Column.

second_page.dart

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            child: Container(
              color: Colors.red,
            ),
          ),
          Expanded(
            child: Container(
              color: Colors.green,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          Navigator.of(context).pop();
        },
        label: Text('Navigate Back'),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

The FloatingActionButton held inside a Scaffold is animated for free as a Hero widget. While that livens up the transition quite a bit, we want to go more extreme. Let's animate the Containers.

Goodbye, MaterialPageRoute ?

Fully custom page transition is not possible with the MaterialPageRoute because it hides a whole lot of objects from us. In order to get hold of the transition Animation object, we need to use a PageRouteBuilder.

Animation in Flutter is, in essence, only a value smoothly transitioning between its start and end boundaries. You can brush up on animations in this tutorial.

As with any animation, even page transitions can be configured to have a certain duration. By default, it takes 300 milliseconds but we're going to set the duration to a full second. Lastly, we want to pass the animation parameter of the pageBuilder method to the SecondPage.

first_page.dart

Navigator.of(context).push(
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) {
      return SecondPage(
        transitionAnimation: animation,
      );
    },
    transitionDuration: Duration(seconds: 1),
  ),
);
Consider using named routes in production-grade projects. This animation approach will still work the same if you use PageRouteBuilders inside the onGenerateRoute method.

Slide animation

Now we need to add the transitionAnimation field into the SecondPage class and use it!

The most versatile choice would be to use the Transform widget, in this case, the simplified Transform.translate constructor. With it, Skia is the limit ?

Since we're only going to slide the widgets, we may as well use the SlideTransition because it's a bit simpler to use. The transitionAnimation is passed by reference just like any other object in Dart. This means that it will be updated as usual and we can listen to these updates using an AnimatedBuilder widget. Sprinkle in some Tween and you have things moving on the screen!

second_page.dart

Column(
  children: <Widget>[
    // Expanded remains as the direct child of a Column
    Expanded(
      child: AnimatedBuilder(
        animation: transitionAnimation,
        builder: (context, child) {
          return SlideTransition(
            position: Tween<Offset>(
              // X, Y - Origin (0, 0) is in the upper left corner.
              begin: Offset(1, 0),
              end: Offset(0, 0),
            ).animate(transitionAnimation),
            child: child,
          );
        },
        child: Container(
          color: Colors.red,
        ),
      ),
    ),
    Expanded(
      child: AnimatedBuilder(
        animation: transitionAnimation,
        builder: (context, child) {
          return SlideTransition(
            position: Tween<Offset>(
              begin: Offset(-1, 0),
              end: Offset(0, 0),
            ).animate(transitionAnimation),
            child: child,
          );
        },
        child: Container(
          color: Colors.green,
        ),
      ),
    ),
  ],
),

Apart from the horrible code duplication (which we'll address in just a second), there's something else that doesn't play right. After running the app, we see that the animation is not staggered as in the video above.

Staggered Animation

Making the red container arrive sooner than the green one is very simple. When you think about it, both of the animations are linear, going from 0 to 1. 

What we want to do is to change the curve so that the red animation will arrive at the value 1 first and only then start changing the value of the green animation.

This is achievable by using an Interval curve in conjunction with a CurvedAnimation which will be driven by our transitionAnimation. While we're at it, we can also swap the unexciting linear nature of the animation for something more smooth, for example, Curves.easeOutCubic. This is how the red widget will look like:

second_page.dart

Expanded(
  child: AnimatedBuilder(
    animation: transitionAnimation,
    builder: (context, child) {
      return SlideTransition(
        position: Tween<Offset>(
          begin: Offset(1, 0),
          end: Offset(0, 0),
        ).animate(
          CurvedAnimation(
            curve: Interval(0, 0.5, curve: Curves.easeOutCubic),
            parent: transitionAnimation,
          ),
        ),
        child: child,
      );
    },
    child: Container(
      color: Colors.red,
    ),
  ),
),

Changes to the green widget will be almost identical, only the Interval will go from 0.5 to 1.

second_page.dart

// Green widget
CurvedAnimation(
  curve: Interval(0.5, 1, curve: Curves.easeOutCubic),
  parent: transitionAnimation,
),

Removing duplication with a Provider

We have only two widgets and duplication makes the code hard to maintain even now. We must get rid of it and because we don't want to pass the transitionAnimation through a bunch of constructors, we're going to use the provider package. Let's add it as a dependency.

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.1.2

Inside the FirstPage, we now want to provide the animation to the SecondPage. Because Animation is a subclass of Listenable, we have to use the ListenableProvider in order not to get any errors although we would be fine with a regular Provider in this case too.

first_page.dart

Navigator.of(context).push(
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) {
      return ListenableProvider(
        create: (context) => animation,
        child: SecondPage(),
      );
    },
    transitionDuration: Duration(seconds: 1),
  ),
);

We can now extract the duplicated widget (all the way from AnimatedBuilder) from the SecondPage. We'll call it SlidingContainer and make initialOffsetX, intervalStart, intervalEnd, and color configurable. The transition animation will not be passed through any constructor. Instead, it will be gotten directly inside the SlidingContainer widget as highlighted below.

second_page.dart

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            child: SlidingContainer(
              color: Colors.red,
              initialOffsetX: 1,
              intervalStart: 0,
              intervalEnd: 0.5,
            ),
          ),
          Expanded(
            child: SlidingContainer(
              color: Colors.green,
              initialOffsetX: -1,
              intervalStart: 0.5,
              intervalEnd: 1,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          Navigator.of(context).pop();
        },
        label: Text('Navigate Back'),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

class SlidingContainer extends StatelessWidget {
  final double initialOffsetX;
  final double intervalStart;
  final double intervalEnd;
  final Color color;

  const SlidingContainer({
    Key key,
    this.initialOffsetX,
    this.intervalStart,
    this.intervalEnd,
    this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final animation = Provider.of<Animation<double>>(context, listen: false);

    return AnimatedBuilder(
      animation: animation,
      builder: (context, child) {
        return SlideTransition(
          position: Tween<Offset>(
            begin: Offset(initialOffsetX, 0),
            end: Offset(0, 0),
          ).animate(
            CurvedAnimation(
              curve: Interval(
                intervalStart,
                intervalEnd,
                curve: Curves.easeOutCubic,
              ),
              parent: animation,
            ),
          ),
          child: child,
        );
      },
      child: Container(
        color: color,
      ),
    );
  }
}

And there you have it! A fully custom staggered page transition animation in Flutter. Now that you know the principles, make sure to apply them in the most ingenious of ways!

About the author 

Matt Rešetár

Matt is an app developer with a knack for teaching others. Working as a Flutter Developer at LeanCode and a developer educator, he is set on helping other people succeed in their Flutter app development career.

You may also like

Flutter UI Testing with Patrol

Flutter UI Testing with Patrol
  • obviously like your web-site but you need to test the spelling on quite a few of your posts. Several of them are rife with spelling problems and I to find it very troublesome to inform the reality on the other hand I’ll certainly come back again.

  • I loved as much as you will receive carried out right here. The sketch is tasteful, your authored subject matter stylish. nonetheless, you command get got an edginess over that you wish be delivering the following. unwell unquestionably come further formerly again as exactly the same nearly very often inside case you shield this hike.

  • I do believe all the ideas you’ve presented for your post. They are really convincing and will certainly work. Nonetheless, the posts are too short for novices. May just you please lengthen them a little from subsequent time? Thanks for the post.

  • I just could not leave your web site before suggesting that I really enjoyed the standard information a person supply to your visitors? Is gonna be again steadily in order to check up on new posts.

  • you are in reality a just right webmaster. The site loading velocity is incredible. It seems that you are doing any unique trick. In addition, The contents are masterwork. you have performed a wonderful task on this topic!

  • Thanks, I have just been looking for information about this subject for a long time and yours is the best I’ve discovered till now. However, what in regards to the bottom line? Are you certain in regards to the supply?

  • Techarp This is really interesting, You’re a very skilled blogger. I’ve joined your feed and look forward to seeking more of your magnificent post. Also, I’ve shared your site in my social networks!

  • Fiberglass and Resin Pipes in Iraq ElitePipe Factory in Iraq is proud to be a leading producer of high-quality fiberglass and resin pipes, delivering superior performance and durability for various industrial applications. Our fiberglass reinforced plastic (FRP) pipes, also known as GRP pipes, offer excellent corrosion resistance, lightweight properties, and long-lasting service life. These attributes make them ideal for use in demanding environments such as chemical processing, water treatment, and oil and gas industries. With our commitment to innovation and quality, ElitePipe Factory ensures that every pipe meets rigorous standards, establishing us as one of the best and most reliable suppliers in Iraq. For more information, visit our website at elitepipeiraq.com.

  • PEX Pipes in Iraq Elite Pipe Factory in Iraq is renowned for its high-quality PEX pipes, designed for both residential and commercial plumbing systems. Our PEX pipes offer excellent flexibility, durability, and resistance to temperature fluctuations, making them a versatile choice for various applications. As one of the best and most reliable manufacturers in Iraq, Elite Pipe Factory ensures that our PEX pipes are produced to the highest standards, delivering exceptional performance and reliability. For detailed information about our PEX pipes, visit elitepipeiraq.com.

  • الأنابيب المتخصصة في العراق في مصنع إيليت بايب في العراق، نفتخر بتقديم مجموعة متنوعة من الأنابيب المتخصصة لتلبية الاحتياجات الصناعية والعلمية المختلفة. أنابيب الزجاج الخاصة بنا، المثالية للاستخدام في المختبرات، تم تصنيعها بدقة لضمان الوضوح والمتانة. هذه الأنابيب مثالية للتعامل مع ومراقبة التفاعلات الكيميائية في ظروف محكمة. يشتهر مصنع إيليت بايب بالجودة والموثوقية، حيث يحدد معايير إنتاج أنابيب الزجاج في العراق. لمزيد من المعلومات، قم بزيارة موقعنا: elitepipeiraq.com.

  • Bwer Company is a top supplier of weighbridge truck scales in Iraq, providing a complete range of solutions for accurate vehicle load measurement. Their services cover every aspect of truck scales, from truck scale installation and maintenance to calibration and repair. Bwer Company offers commercial truck scales, industrial truck scales, and axle weighbridge systems, tailored to meet the demands of heavy-duty applications. Bwer Company’s electronic truck scales and digital truck scales incorporate advanced technology, ensuring precise and reliable measurements. Their heavy-duty truck scales are engineered for rugged environments, making them suitable for industries such as logistics, agriculture, and construction. Whether you’re looking for truck scales for sale, rental, or lease, Bwer Company provides flexible options to match your needs, including truck scale parts, accessories, and software for enhanced performance. As trusted truck scale manufacturers, Bwer Company offers certified truck scale calibration services, ensuring compliance with industry standards. Their services include truck scale inspection, certification, and repair services, supporting the long-term reliability of your truck scale systems. With a team of experts, Bwer Company ensures seamless truck scale installation and maintenance, keeping your operations running smoothly. For more information on truck scale prices, installation costs, or to learn about their range of weighbridge truck scales and other products, visit Bwer Company’s website at bwerpipes.com.

  • filtro de aire 109043 para audi : Proper maintenance is essential to guarantee your vehicle’s performance and longevity, especially when it comes to high-end cars like Audi. One of the most crucial components in this regard is the air filter, and the filtro de aire 109043 Audi is an excellent option for those who want to keep their vehicles in top shape. This air filter is specifically designed for Audi models, ensuring clean and efficient airflow to the engine, which translates to better performance and increased engine durability.

  • In addition to specific medicines, homeopathy offers combination remedies tailored to address various aspects of piles. These may include formulations targeting pain relief, inflammation reduction, and overall rectal health. Combination remedies provide piles homeopathic remedy comprehensive support for individuals suffering from piles, promoting faster healing and symptom relief.

  • The effectiveness of homeopathy in managing piles varies from person to person. While some individuals may experience significant improvements in symptoms and overall well-being with homeopathic treatment alone, others may require a combination of homeopathy and conventional approaches homeopathic medicine for piles for optimal results. It is essential to work closely with a qualified homeopath to determine the most suitable treatment plan for individual needs and monitor progress regularly.

  • In the fast-paced world of global finance, the DAX 40 (formerly known as DAX 30) stands out as a critical barometer of economic performance, especially within the European Union. As Germany’s premier stock market index, the fintechzoom.com dax40 reflects the health of 40 major companies listed on the Frankfurt Stock Exchange. FintechZoom.com has emerged as a reliable resource for understanding the intricacies of the DAX 40, providing in-depth analysis, expert opinions, and real-time updates.

  • In the digital era, where every brand and individual strives to leave their mark online, understanding unique and intriguing topics becomes essential. One such captivating subject is Blog hitlmila. But what exactly is BlogHitlmila? Why is it gaining popularity? And how can it impact your digital presence? Let’s dive in!

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >