颤振过渡出口

时间:2018-10-11 14:03:32

标签: flutter flutter-animation

在Android API上,我们可以使用

overridePendingTransition(int enterAnim, int exitAnim) 

定义进入和退出转换。

如何在Flutter中做到这一点?

我已经实现了这段代码

class SlideLeftRoute extends PageRouteBuilder {
  final Widget enterWidget;
  SlideLeftRoute({this.enterWidget})
      : super(
      pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
        return enterWidget;
      },
      transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
        return SlideTransition(
          position: new Tween<Offset>(
            begin: const Offset(1.0, 0.0),
            end: Offset.zero,
          ).animate(animation),
          child: child
        );
      },

  );
}

,但它仅定义输入转换。如何定义退出转场?

更新

想象一下,我执行时有两个屏幕(Screen1和Screen2)

 Navigator.push(
        context, SlideLeftRoute(enterWidget: Screen2()));

我想将动画应用于Screen1和Screen2而不是不仅应用于Screen2

example

4 个答案:

答案 0 :(得分:3)

好问题,PageRouteBuilder默认使用AnimationController来处理动画过渡,因此,当您关闭视图时,它只是从animationController调用“ reverse”方法,您将看到相同的结果您正在使用的动画,但相反。

如果您要在关闭视图时更改动画,可以检查当前动画的状态并与AnimationStatus.reverse进行比较

这是带有Fade动画的代码,当它反向显示时。

  class SlideLeftRoute extends PageRouteBuilder {
    final Widget enterWidget;
    SlideLeftRoute({this.enterWidget})
        : super(
            pageBuilder: (BuildContext context, Animation<double> animation,
                Animation<double> secondaryAnimation) {
              return enterWidget;
            },
            transitionsBuilder: (BuildContext context,
                Animation<double> animation,
                Animation<double> secondaryAnimation,
                Widget child) {
              if (animation.status == AnimationStatus.reverse) {
                //do your dismiss animation here
                return FadeTransition(
                  opacity: animation,
                  child: child,
                );
              } else {
                return SlideTransition(
                    position: new Tween<Offset>(
                      begin: const Offset(1.0, 0.0),
                      end: Offset.zero,
                    ).animate(animation),
                    child: child);
              }
            },
          );
  }

WORKAROUND

    class SlideLeftRoute extends PageRouteBuilder {
      final Widget enterWidget;
      final Widget oldWidget;

      SlideLeftRoute({this.enterWidget, this.oldWidget})
          : super(
                transitionDuration: Duration(milliseconds: 600),
                pageBuilder: (BuildContext context, Animation<double> animation,
                    Animation<double> secondaryAnimation) {
                  return enterWidget;
                },
                transitionsBuilder: (BuildContext context,
                    Animation<double> animation,
                    Animation<double> secondaryAnimation,
                    Widget child) {
                  return Stack(
                    children: <Widget>[
                      SlideTransition(
                          position: new Tween<Offset>(
                            begin: const Offset(0.0, 0.0),
                            end: const Offset(-1.0, 0.0),
                          ).animate(animation),
                          child: oldWidget),
                      SlideTransition(
                          position: new Tween<Offset>(
                            begin: const Offset(1.0, 0.0),
                            end: Offset.zero,
                          ).animate(animation),
                          child: enterWidget)
                    ],
                  );
                });
    }

用法:

 Navigator.of(context)
              .push(SlideLeftRoute(enterWidget: Page2(), oldWidget: this));

答案 1 :(得分:3)

实现此目的的正确方法是使用PageRouteBuilder对象的transitionBuilder中提供的secondaryAnimation参数。

在这里,您可以在flutter sdk的flutter / lib / src / widgets / routes.dart文件中的文档中了解有关secondaryAnimation参数的更多信息:

///
/// 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.
///
/// 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.
///
/// ```dart
///   transitionsBuilder: (
///       BuildContext context,
///       Animation<double> animation,
///       Animation<double> secondaryAnimation,
///       Widget child,
///   ) {
///     return SlideTransition(
///       position: AlignmentTween(
///         begin: const Offset(0.0, 1.0),
///         end: Offset.zero,
///       ).animate(animation),
///       child: SlideTransition(
///         position: TweenOffset(
///           begin: Offset.zero,
///           end: const Offset(0.0, 1.0),
///         ).animate(secondaryAnimation),
///         child: child,
///       ),
///     );
///   }
/// ```

这是一个使用secondaryAnimation参数的工作示例:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      onGenerateRoute: (RouteSettings settings) {
        if (settings.name == '/') {
          return PageRouteBuilder<dynamic>(
            pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Page1(),
            transitionsBuilder: (
              BuildContext context,
              Animation<double> animation,
              Animation<double> secondaryAnimation,
              Widget child,
            ) {
              final Tween<Offset> offsetTween = Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0));
              final Animation<Offset> slideOutLeftAnimation = offsetTween.animate(secondaryAnimation);
              return SlideTransition(position: slideOutLeftAnimation, child: child);
            },
          );
        } else {
          // handle other routes here
          return null;
        }
      },
    );
  }
}

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page 1")),
      backgroundColor: Colors.blue,
      body: Center(
        child: RaisedButton(
          onPressed: () => Navigator.push(
            context,
            PageRouteBuilder<dynamic>(
              pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Page2(),
              transitionsBuilder: (
                BuildContext context,
                Animation<double> animation,
                Animation<double> secondaryAnimation,
                Widget child,
              ) {
                final Tween<Offset> offsetTween = Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0));
                final Animation<Offset> slideInFromTheRightAnimation = offsetTween.animate(animation);
                return SlideTransition(position: slideInFromTheRightAnimation, child: child);
              },
            ),
          ),
          child: Text("Go to Page 2"),
        ),
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page 2"), backgroundColor: Colors.green),
      backgroundColor: Colors.green,
      body: Center(child: RaisedButton(onPressed: () => Navigator.pop(context), child: Text("Back to Page 1"))),
    );
  }
}

结果:

enter image description here

答案 2 :(得分:2)

我使用了不同的方式,但是diegodeveloper提供了类似的逻辑

屏幕截图:

enter image description here

完整代码:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Page1(),
    );
  }
}

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 1"),
        leading: Icon(Icons.menu),
      ),
      body: Container(
        color: Colors.grey,
        child: Center(
          child: RaisedButton(
            onPressed: () => Navigator.push(context, MyCustomPageRoute(previousPage: this, builder: (context) => Page2())),
            child: Text("2nd Page"),
          ),
        ),
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page 2")),
      body: Container(
        color: Colors.blueGrey,
        child: Center(
          child: RaisedButton(
            onPressed: () => Navigator.pop(context),
            child: Text("Back"),
          ),
        ),
      ),
    );
  }
}

class MyCustomPageRoute extends MaterialPageRoute {
  final Widget previousPage;
  MyCustomPageRoute({this.previousPage, WidgetBuilder builder, RouteSettings settings}) : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget currentPage) {
    Animation<Offset> _slideAnimationPage1 = Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0)).animate(animation);
    Animation<Offset> _slideAnimationPage2 = Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0)).animate(animation);
    return Stack(
      children: <Widget>[
        SlideTransition(position: _slideAnimationPage1, child: previousPage),
        SlideTransition(position: _slideAnimationPage2, child: currentPage),
      ],
    );
  }
}

答案 3 :(得分:0)

还有另一种方法。 initState()在oldWidget中被调用的问题不再存在。

void main() => runApp(MaterialApp(theme: ThemeData.dark(), home: HomePage()));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page 1")),
      body: RaisedButton(
        child: Text("Next"),
        onPressed: () {
          Navigator.push(
            context,
            PageRouteBuilder(
              pageBuilder: (c, a1, a2) => Page2(),
              transitionsBuilder: (context, anim1, anim2, child) {
                return SlideTransition(
                  position: Tween<Offset>(end: Offset(0, 0), begin: Offset(1, 0)).animate(anim1),
                  child: Page2(),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page 2")),
      body: RaisedButton(
        child: Text("Back"),
        onPressed: () => Navigator.pop(context),
      ),
    );
  }
}