buildTransitions method

  1. @override
Widget buildTransitions(
  1. BuildContext context,
  2. Animation<double> animation,
  3. Animation<double> secondaryAnimation,
  4. Widget child,
)
override

Override this method to wrap the child with one or more transition widgets that define how the route arrives on and leaves the screen.

By default, the child (which contains the widget returned by buildPage) is not wrapped in any transition widgets.

The buildTransitions method, in contrast to buildPage, is called each time the Route's state changes while it is visible (e.g. if the value of canPop changes on the active route).

The buildTransitions method is typically used to define transitions that animate the new topmost route's comings and goings. 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, e.g. because the use pressed the back button, the primary animation runs from 1.0 to 0.0.

{@tool snippet} The following example uses the primary animation to drive a SlideTransition that translates the top of the new route vertically from the bottom of the screen when it is pushed on the Navigator's stack. When the route is popped the SlideTransition translates the route from the top of the screen back to the bottom.

We've used PageRouteBuilder to demonstrate the buildTransitions method here. The body of an override of the buildTransitions method would be defined in the same way.

PageRouteBuilder<void>(
  pageBuilder: (BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
  ) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hello')),
      body: const Center(
        child: Text('Hello World'),
      ),
    );
  },
  transitionsBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child,
   ) {
    return SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(0.0, 1.0),
        end: Offset.zero,
      ).animate(animation),
      child: child, // child is the value returned by pageBuilder
    );
  },
)

{@end-tool}

When the Navigator pushes a route on the top of its stack, the secondaryAnimation can be used to define how the route that was on the top of the stack leaves the screen. Similarly when the topmost route is popped, the secondaryAnimation can be used to define how the route below it reappears on the screen. When the Navigator pushes a new route on the top of its stack, the old topmost route's secondaryAnimation runs from 0.0 to 1.0. When the Navigator pops the topmost route, the secondaryAnimation for the route below it runs from 1.0 to 0.0.

{@tool snippet} The example below adds a transition that's driven by the secondaryAnimation. When this route disappears because a new route has been pushed on top of it, it translates in the opposite direction of the new route. Likewise when the route is exposed because the topmost route has been popped off.

PageRouteBuilder<void>(
  pageBuilder: (BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
  ) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hello')),
      body: const Center(
        child: Text('Hello World'),
      ),
    );
  },
  transitionsBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child,
  ) {
    return SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(0.0, 1.0),
        end: Offset.zero,
      ).animate(animation),
      child: SlideTransition(
        position: Tween<Offset>(
          begin: Offset.zero,
          end: const Offset(0.0, 1.0),
        ).animate(secondaryAnimation),
        child: child,
      ),
     );
  },
)

{@end-tool}

In practice the secondaryAnimation is used pretty rarely.

The arguments to this method are as follows:

  • context: The context in which the route is being built.
  • 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.
  • secondaryAnimation: When the Navigator pushes a new route on the top of its stack, the old topmost route's secondaryAnimation runs from 0.0 to 1.0. When the Navigator pops the topmost route, the secondaryAnimation for the route below it runs from 1.0 to 0.0.
  • child, the page contents, as returned by buildPage.

See also:

  • buildPage, which is used to describe the actual contents of the page, and whose result is passed to the child argument of this method.

Implementation

@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
    Animation<double> secondaryAnimation, Widget child) {
  switch (type) {
    case PageTransitionType.theme:
      return Theme.of(context).pageTransitionsTheme.buildTransitions(
          this, context, animation, secondaryAnimation, child);

    case PageTransitionType.fade:
      if (isIos) {
        var fade = FadeTransition(opacity: animation, child: child);
        return matchingBuilder.buildTransitions(
            this, context, animation, secondaryAnimation, fade);
      }
      return FadeTransition(opacity: animation, child: child);
      // ignore: dead_code
      break;

    /// PageTransitionType.rightToLeft which is the give us right to left transition
    case PageTransitionType.rightToLeft:
      var slideTransition = SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(1, 0),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
      if (isIos) {
        return matchingBuilder.buildTransitions(
            this, context, animation, secondaryAnimation, child);
      }
      return slideTransition;
      // ignore: dead_code
      break;

    /// PageTransitionType.leftToRight which is the give us left to right transition
    case PageTransitionType.leftToRight:
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(-1, 0),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.topToBottom which is the give us up to down transition
    case PageTransitionType.topToBottom:
      if (isIos) {
        var topBottom = SlideTransition(
          position: Tween<Offset>(
            begin: const Offset(0, -1),
            end: Offset.zero,
          ).animate(animation),
          child: child,
        );
        return matchingBuilder.buildTransitions(
            this, context, animation, secondaryAnimation, topBottom);
      }
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(0, -1),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.downToUp which is the give us down to up transition
    case PageTransitionType.bottomToTop:
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(0, 1),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.scale which is the scale functionality for transition you can also use curve for this transition

    case PageTransitionType.scale:
      assert(alignment != null, """
              When using type "scale" you need argument: 'alignment'
              """);
      if (isIos) {
        var scale = ScaleTransition(
          alignment: alignment!,
          scale: animation,
          child: child,
        );
        return matchingBuilder.buildTransitions(
            this, context, animation, secondaryAnimation, scale);
      }
      return ScaleTransition(
        alignment: alignment!,
        scale: CurvedAnimation(
          parent: animation,
          curve: Interval(
            0.00,
            0.50,
            curve: curve,
          ),
        ),
        child: child,
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.rotate which is the rotate functionality for transition you can also use alignment for this transition

    case PageTransitionType.rotate:
      assert(alignment != null, """
              When using type "RotationTransition" you need argument: 'alignment'
              """);
      return new RotationTransition(
        alignment: alignment!,
        turns: animation,
        child: ScaleTransition(
          alignment: alignment!,
          scale: animation,
          child: FadeTransition(
            opacity: animation,
            child: child,
          ),
        ),
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.size which is the rotate functionality for transition you can also use curve for this transition

    case PageTransitionType.size:
      assert(alignment != null, """
              When using type "size" you need argument: 'alignment'
              """);
      return Align(
        alignment: alignment!,
        child: SizeTransition(
          sizeFactor: CurvedAnimation(
            parent: animation,
            curve: curve,
          ),
          child: child,
        ),
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.rightToLeftWithFade which is the fade functionality from right o left

    case PageTransitionType.rightToLeftWithFade:
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(1.0, 0.0),
          end: Offset.zero,
        ).animate(animation),
        child: FadeTransition(
          opacity: animation,
          child: SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(1, 0),
              end: Offset.zero,
            ).animate(animation),
            child: child,
          ),
        ),
      );
      // ignore: dead_code
      break;

    /// PageTransitionType.leftToRightWithFade which is the fade functionality from left o right with curve

    case PageTransitionType.leftToRightWithFade:
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(-1.0, 0.0),
          end: Offset.zero,
        ).animate(
          CurvedAnimation(
            parent: animation,
            curve: curve,
          ),
        ),
        child: FadeTransition(
          opacity: animation,
          child: SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(-1, 0),
              end: Offset.zero,
            ).animate(animation),
            child: child,
          ),
        ),
      );
      // ignore: dead_code
      break;

    case PageTransitionType.rightToLeftJoined:
      assert(childCurrent != null, """
              When using type "rightToLeftJoined" you need argument: 'childCurrent'

              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(-1.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          ),
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(1.0, 0.0),
              end: Offset.zero,
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: child,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.leftToRightJoined:
      assert(childCurrent != null, """
              When using type "leftToRightJoined" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(-1.0, 0.0),
              end: const Offset(0.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: child,
          ),
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(1.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.topToBottomJoined:
      assert(childCurrent != null, """
              When using type "topToBottomJoined" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, -1.0),
              end: const Offset(0.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: child,
          ),
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(0.0, 1.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.bottomToTopJoined:
      assert(childCurrent != null, """
              When using type "bottomToTopJoined" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 1.0),
              end: const Offset(0.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: child,
          ),
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(0.0, -1.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.rightToLeftPop:
      assert(childCurrent != null, """
              When using type "rightToLeftPop" you need argument: 'childCurrent'

              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          child,
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(-1.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          ),
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.leftToRightPop:
      assert(childCurrent != null, """
              When using type "leftToRightPop" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          child,
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(1.0, 0.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.topToBottomPop:
      assert(childCurrent != null, """
              When using type "topToBottomPop" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          child,
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(0.0, 1.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    case PageTransitionType.bottomToTopPop:
      assert(childCurrent != null, """
              When using type "bottomToTopPop" you need argument: 'childCurrent'
              example:
                child: MyPage(),
                childCurrent: this

              """);
      return Stack(
        children: <Widget>[
          child,
          SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0.0, 0.0),
              end: const Offset(0.0, -1.0),
            ).animate(
              CurvedAnimation(
                parent: animation,
                curve: curve,
              ),
            ),
            child: childCurrent,
          )
        ],
      );
      // ignore: dead_code
      break;

    /// FadeTransitions which is the fade transition

    default:
      return FadeTransition(opacity: animation, child: child);
  }
}