Implementing Custom Route Transitions in Flutter

Flutter is a powerful UI toolkit that allows developers to create natively compiled applications for mobile, web, and desktop from a single codebase. One of the standout features of Flutter is its rich set of customizable components and widgets, allowing for highly tailored user experiences. Route transitions are a key aspect of user experience, providing visual feedback and enhancing the navigation flow of an application. In Flutter, you can implement custom route transitions to create unique and engaging experiences.

What are Route Transitions?

Route transitions, also known as page transitions or navigation animations, are visual effects that occur when navigating between different screens (routes) in an application. These transitions help users understand the change in the application’s state and make the navigation feel more natural and intuitive.

Why Implement Custom Route Transitions?

  • Enhance User Experience: Custom transitions can make the application feel more polished and professional.
  • Brand Consistency: Implementing custom transitions allows you to align the app’s navigation style with your brand identity.
  • Improved Visual Feedback: Custom animations provide better feedback to the user, making navigation more intuitive.
  • Differentiation: Unique transitions can set your application apart from others.

How to Implement Custom Route Transitions in Flutter

Flutter provides several ways to implement custom route transitions, including using the PageRouteBuilder class and the AnimatedBuilder widget.

Method 1: Using PageRouteBuilder

The PageRouteBuilder class is the most common way to create custom route transitions. It allows you to define the animation used when pushing or popping a route.

Step 1: Create a Custom Route

Create a new route using PageRouteBuilder, defining the transitionsBuilder to specify the animation.


import 'package:flutter/material.dart';

class CustomPageRoute extends PageRouteBuilder {
  final Widget child;
  final AxisDirection direction;

  CustomPageRoute({
    required this.child,
    this.direction = AxisDirection.right,
  }) : super(
    transitionDuration: const Duration(milliseconds: 500),
    reverseTransitionDuration: const Duration(milliseconds: 500),
    pageBuilder: (context, animation, secondaryAnimation) => child,
    transitionsBuilder: (context, animation, secondaryAnimation, child) =>
        SlideTransition(
          position: Tween(
            begin: getBeginOffset(direction),
            end: Offset.zero,
          ).animate(animation),
          child: child,
        ),
  );

  Offset getBeginOffset(AxisDirection direction) {
    switch (direction) {
      case AxisDirection.up:
        return const Offset(0, 1);
      case AxisDirection.down:
        return const Offset(0, -1);
      case AxisDirection.left:
        return const Offset(1, 0);
      case AxisDirection.right:
        return const Offset(-1, 0);
    }
  }
}

Explanation:

  • CustomPageRoute: This class extends PageRouteBuilder and takes the destination child and transition direction as parameters.
  • transitionDuration & reverseTransitionDuration: Specifies the duration for the transition animation.
  • pageBuilder: Builds the content of the route (the page to be displayed).
  • transitionsBuilder: Defines the transition animation using SlideTransition.
  • SlideTransition: Animates the route by sliding it into view from the specified direction.
  • getBeginOffset: A helper function that returns the appropriate starting offset based on the given AxisDirection.
Step 2: Use the Custom Route

To use this custom route, call Navigator.push with an instance of CustomPageRoute:


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Route Transition Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  const FirstScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('Go to Second Screen'),
          onPressed: () {
            Navigator.push(
              context,
              CustomPageRoute(
                child: const SecondScreen(),
                direction: AxisDirection.left, // Transition from the left
              ),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Screen'),
      ),
      body: const Center(
        child: Text('This is the Second Screen'),
      ),
    );
  }
}

Method 2: Using AnimatedBuilder

The AnimatedBuilder widget can be used to create more complex and dynamic route transitions. This method is particularly useful when you want to create transitions that depend on more than just a simple slide.

Step 1: Create a Custom Transition Widget

Implement a widget that uses AnimatedBuilder to define the transition.


import 'package:flutter/material.dart';

class FadeTransitionRoute extends PageRouteBuilder {
  final Widget child;

  FadeTransitionRoute({required this.child})
      : super(
    transitionDuration: const Duration(milliseconds: 500),
    pageBuilder: (context, animation, secondaryAnimation) => child,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return FadeTransition(opacity: animation, child: child);
    },
  );
}
Step 2: Usage

Here’s how to use the FadeTransitionRoute in your navigation:


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Route Transition Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  const FirstScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('Go to Second Screen'),
          onPressed: () {
            Navigator.push(
              context,
              FadeTransitionRoute(child: const SecondScreen()),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Screen'),
      ),
      body: const Center(
        child: Text('This is the Second Screen'),
      ),
    );
  }
}

Custom Transition Examples

Flutter offers a plethora of opportunities to design your route transitions as unique as your imagination allows you. We will focus on four main examples that could inspire a great UX/UI app developer to come up with much more.

In these samples, you would only need to adjust transitionsBuilder portion:

  • Scale Transition

ScaleTransition(
  scale: animation,
  child: child,
),
  • Rotation Transition

RotationTransition(
  turns: animation,
  child: child,
),
  • Size Transition

SizeTransition(
    sizeFactor: animation,
    axis: Axis.horizontal,
    axisAlignment: -1,
    child: child,
),
  • Fade Transition

FadeTransition(
   opacity: animation,
   child: child,
),

Conclusion

Implementing custom route transitions in Flutter is a great way to enhance the user experience and set your application apart. By using PageRouteBuilder or AnimatedBuilder, you can create a wide variety of transitions to match your app’s style and branding. Experiment with different animations and techniques to find the perfect transition that provides intuitive and visually appealing navigation.