import 'dart:io'; import 'package:flutter/material.dart'; Future routeToPage( BuildContext context, Widget page, { bool forceCustomPageRoute = false, }) { if (Platform.isAndroid || forceCustomPageRoute) { return Navigator.of(context).push( _buildPageRoute(page), ); } else { return Navigator.of(context).push( SwipeableRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) { return page; }, ), ); } } void replacePage(BuildContext context, Widget page) { Navigator.of(context).pushReplacement( _buildPageRoute(page), ); } PageRouteBuilder _buildPageRoute(Widget page) { return PageRouteBuilder( pageBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, ) { return page; }, transitionsBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) { return Align( child: FadeTransition( opacity: animation, child: child, ), ); }, transitionDuration: const Duration(milliseconds: 200), opaque: false, ); } class SwipeableRouteBuilder extends PageRoute { final RoutePageBuilder pageBuilder; final PageTransitionsBuilder matchingBuilder = const CupertinoPageTransitionsBuilder(); // Default iOS/macOS (to get the swipe right to go back gesture) // final PageTransitionsBuilder matchingBuilder = const FadeUpwardsPageTransitionsBuilder(); // Default Android/Linux/Windows SwipeableRouteBuilder({required this.pageBuilder}); @override Null get barrierColor => null; @override Null get barrierLabel => null; @override Widget buildPage( BuildContext context, Animation animation, Animation secondaryAnimation, ) { return pageBuilder(context, animation, secondaryAnimation); } @override bool get maintainState => true; @override Duration get transitionDuration => const Duration( milliseconds: 300, ); // Can give custom Duration, unlike in MaterialPageRoute @override Widget buildTransitions( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) { return matchingBuilder.buildTransitions( this, context, animation, secondaryAnimation, child, ); } @override bool get opaque => false; } class TransparentRoute extends PageRoute { TransparentRoute({ required this.builder, RouteSettings? settings, }) : assert(builder != null), super(settings: settings, fullscreenDialog: false); final WidgetBuilder? builder; @override bool get opaque => false; @override Null get barrierColor => null; @override Null get barrierLabel => null; @override bool get maintainState => true; @override Duration get transitionDuration => const Duration(milliseconds: 200); @override Widget buildPage( BuildContext context, Animation animation, Animation secondaryAnimation, ) { final result = builder!(context); return FadeTransition( opacity: Tween(begin: 0, end: 1).animate(animation), child: Semantics( scopesRoute: true, explicitChildNodes: true, child: result, ), ); } }