skip to Main Content

I have a CustomPageRoute for a fade animation when pushing to another screen. That is working as expected.

However I would love to have a swipe back animation and can not make it work. I tried couple of different things, the most promising was this answer but that ist still sliding in/out the page.

For animation I only want the a fadeAnimation, both for pushing and popping.

This is my code for the FadeTransition:

class FadePageTransition extends Page {
  final Widget page;

  const FadePageTransition({
    required this.page,
    LocalKey? key,
    String? restorationId,
  }) : super(
          key: key,
          restorationId: restorationId,
        );

  @override
  Route createRoute(BuildContext context) {
    return FadeRoute(
      child: page,
      routeSettings: this,
    );
  }
}

class FadeRoute<T> extends PageRoute<T> {
  final Widget child;
  final RouteSettings routeSettings;

  FadeRoute({
    required this.child,
    required this.routeSettings,
  });

  @override
  RouteSettings get settings => routeSettings;

  @override
  Color? get barrierColor => Palette.black;

  @override
  String? get barrierLabel => null;

  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
    return FadeTransition(
      opacity: animation,
      child: child,
    );
  }

  @override
  bool get maintainState => true;

  @override
  Duration get transitionDuration => const Duration(
        milliseconds: transitionDurationInMS,
      );
}

Let me know if you need any more info.

In iOS Swift it is possible, with this code:

// handle swqipe down gesture
@objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
    // calculate the progress based on how far the user moved
    let translation = panGR.translation(in: nil)
    let progress = translation.y / 2 / view.bounds.height
    
    switch panGR.state {
    case .began:
        // begin the transition as normal
        self.dismissView()
        break
    case .changed:
        
        Hero.shared.update(progress)
        
    default:
        // finish or cancel the transition based on the progress and user's touch velocity
        if progress + panGR.velocity(in: nil).y / view.bounds.height > 0.3 {
            self.dismissView()
            Hero.shared.finish()
        } else {
            Hero.shared.cancel()
        }
    }
}

3

Answers


  1. This is my approach for fade in and slide transitions if you can figure out from context if you are adding or removing from route you put condition and that should work.

    Future nextScreenSlideIn(BuildContext context, page) {
      PageRouteBuilder builder = PageRouteBuilder(
          pageBuilder: (_, Animation animation, Animation secondaryAnimation) {
            return page;
          },
          transitionsBuilder: (
            _,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
            Widget child,
          ) {
            return SlideTransition(
              position: Tween<Offset>(
                begin: const Offset(1, 0),
                end: Offset.zero,
              ).animate(animation),
              child: child,
            );
          },
          transitionDuration: const Duration(milliseconds: 200));
    
      return Navigator.push(context, builder);
    }
    
    Future nextScreenFadeIn(BuildContext context, page) {
      PageRouteBuilder builder = PageRouteBuilder(
          pageBuilder:
              (_, Animation<double> animation, Animation secondaryAnimation) {
            return page;
          },
          transitionsBuilder: (
            _,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
            Widget child,
          ) {
            return FadeTransition(
              opacity: Tween<double>(begin: 0, end: 1).animate(animation),
              child: child,
            );
          },
          transitionDuration: const Duration(milliseconds: 200));
    
      return Navigator.push(context, builder);
    }
    

    or

    You can try following package: https://pub.dev/packages/page_transition#do-you-want-use-theme-transitions-

    it has also mentioned similar thing.

    Login or Signup to reply.
  2. you can use the SlideTransition widget and your FadeTransition, for Custom Transition FadeAnimation.
    just like this code

    class FadeRoute<T> extends PageRoute<T> {
      final Widget child;
      final RouteSettings routeSettings;
    
      FadeRoute({
        required this.child,
        required this.routeSettings,
      });
    
      @override
      RouteSettings get settings => routeSettings;
    
      @override
      Color? get barrierColor => Palette.black;
    
      @override
      String? get barrierLabel => null;
    
      @override
      Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
        return SlideTransition(
          position: Tween<Offset>(begin: Offset(-1, 0), end: Offset(0, 0)).animate(animation),
          child: FadeTransition(
            opacity: animation,
            child: child,
          ),
        );
      }
    
      @override
      Widget buildPage(
        BuildContext context,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
      ) {
        return child;
      }
    
      @override
      bool get maintainState => true;
    
      @override
      Duration get transitionDuration => const Duration(
            milliseconds: transitionDurationInMS,
          );
    }
    
    Login or Signup to reply.
  3. I have extended CupertinoPageRoute and used buildTransitions method to get _CupertinoBackGestureDetector object which is responsible for detecting the back swipe.

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Semantics(
          child: MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              useMaterial3: false,
            ),
            onGenerateRoute: (settings) {
              if (settings.name == "/") {
                return FadePageRoute(builder: (context) => const HomePage());
              } else if (settings.name == "/abc") {
                return FadePageRoute(builder: (context) => const SecondPage());
              }
              return null;
            },
          ),
        );
      }
    }
    
    class FadePageRoute<T> extends CupertinoPageRoute<T> {
      FadePageRoute({required super.builder});
    
      @override
      Widget buildTransitions(BuildContext context, Animation<double> animation,
          Animation<double> secondaryAnimation, Widget child) {
        final widget =
            super.buildTransitions(context, animation, secondaryAnimation, child);
        if (widget is CupertinoPageTransition) {
          return FadeTransition(
            opacity: animation,
            child: widget.child,
          );
        } else {
          return widget;
        }
      }
    }
    
    class HomePage extends StatelessWidget {
      const HomePage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: TextButton(
              child: const Text('Next Page',
                  style: TextStyle(
                    fontSize: 48,
                  )),
              onPressed: () {
                Navigator.of(context).pushNamed('/abc');
              },
            ),
          ),
        );
      }
    }
    
    class SecondPage extends StatelessWidget {
      const SecondPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: const Center(
            child: Text(
              '2nd Page',
              style: TextStyle(
                fontSize: 48,
              ),
            ),
          ),
        );
      }
    }
    
    

    Output:
    https://youtube.com/shorts/dQpfyEH9frE

    Please let me know if this is something which you wanted.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search