Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在FLBFlutterViewContainer内调用Navigator.push方法,第一个页面push到第二个页面动画有问题。 #466

Closed
liugang1104 opened this issue Nov 8, 2019 · 19 comments

Comments

@liugang1104
Copy link

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';

void main(){
  print("flutter main");
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    ///register page widget builders,the key is pageName
    FlutterBoost.singleton.registerPageBuilders({
      'pageone': (pageName, params, _) => FirstRouteWidget(),
    });

  }

  @override
  Widget build(BuildContext context) => MaterialApp(
      title: 'Flutter Boost example',
      builder: FlutterBoost.init(), ///init container manager
      home: Container());
}

//第一个页面
class FirstRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("pageone"),
      ),
      body: Center(
        child: GestureDetector(
          onTap: (){
            //跳转到第二个flutter页面
            Navigator.push(context, MaterialPageRoute(builder: (context) => SecondRouteWidget()));
          },
          child: Text("push next"),
        ),
      ),
    );
  }
}

class SecondRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("page two"),
      ),
      body: Center(
        child: GestureDetector(
          onTap: (){
            Navigator.push(context, MaterialPageRoute(builder: (context) => ThirdRouteWidget()));
          },
          child: Text("push next"),
        ),
      ),
    );
  }
}

class ThirdRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("page three"),
      ),
      body: Center(
        child: GestureDetector(
          onTap: (){
            Navigator.push(context, MaterialPageRoute(builder: (context) => ForthRouteWidget()));
          },
          child: Text("push next"),
        ),
      ),
    );
  }
}

class ForthRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("page four"),
      ),
      body: Center(
        child: GestureDetector(
          onTap: (){
            // Navigator.push(context, MaterialPageRoute(builder: (context) => enterPage));
          },
          child: Text("push next"),
        ),
      ),
    );
  }
}

A push B正常动画是A页面有一个向左移动的动画,但是从gif看pageone -> pagetwo没有这个动画。

@liugang1104
Copy link
Author

QQ20191108-155505-HD

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

这个效果实际上由 CupertinoPageTransition 的三个组合动画构成

CupertinoPageTransition({
    Key key,
    @required Animation<double> primaryRouteAnimation,
    @required Animation<double> secondaryRouteAnimation,
    @required this.child,
    @required bool linearTransition,
  }) : assert(linearTransition != null),
       _primaryPositionAnimation =
           (linearTransition
             ? primaryRouteAnimation
             : CurvedAnimation(
                 parent: primaryRouteAnimation,
                 curve: Curves.linearToEaseOut,
                 reverseCurve: Curves.easeInToLinear,
               )
           ).drive(_kRightMiddleTween),
       _secondaryPositionAnimation =
           (linearTransition
             ? secondaryRouteAnimation
             : CurvedAnimation(
                 parent: secondaryRouteAnimation,
                 curve: Curves.linearToEaseOut,
                 reverseCurve: Curves.easeInToLinear,
               )
           ).drive(_kMiddleLeftTween),
       _primaryShadowAnimation =
           (linearTransition
             ? primaryRouteAnimation
             : CurvedAnimation(
                 parent: primaryRouteAnimation,
                 curve: Curves.linearToEaseOut,
               )
           ).drive(_kGradientShadowTween),
       super(key: key);

第一页进行 page transition 的时候 _secondaryPositionAnimation 的值并未正确发生改变,怀疑是动画的控制器未启动动画,还需验证。

之前推测 FlutterBoost 自定义的 BoostPageRoute 中根据平台使用了 CupertinoPageRoute,查验后发现并没有使用。昨天根据链路记录了一些可能与这个 issue 相关的类与方法,我先写在下面:

在 TransitionRoute 中创建的 _secondaryAnimation

final ProxyAnimation _secondaryAnimation = ProxyAnimation(kAlwaysDismissedAnimation);

    AnimationController createAnimationController() {
    assert(!_transitionCompleter.isCompleted, 'Cannot reuse a $runtimeType after disposing it.');
    final Duration duration = transitionDuration;
    assert(duration != null && duration >= Duration.zero);
    return AnimationController(
      duration: duration,
      debugLabel: debugLabel,
      vsync: navigator,
    );
  }

调用中的 PageRoute 有判断是否为初始 Route

abstract class PageRoute<T> extends ModalRoute<T> {
  
   AnimationController createAnimationController() {
    final AnimationController controller = super.createAnimationController();
    if (settings.isInitialRoute)
      controller.value = 1.0;
    return controller;
  }

由于第一个页面初始化的时候我们并不需要路由转换动画,所以 controller.value 就直接设为 1.0,是否有可能判断这个在 push 之前,这样就还是认为当前的 route 为initial route,从而误设置 controller。

供参考。

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

经验证 settings.isInitialRoute 中的判断没有问题

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

经验证 AnimationController 的行为也是正常的

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

定位到问题:

abstract class TransitionRoute<T> extends OverlayRoute<T> {

 void _updateSecondaryAnimation(Route<dynamic> nextRoute) {

 if (nextRoute is TransitionRoute<dynamic> && canTransitionTo(nextRoute) && nextRoute.canTransitionFrom(this)) {
   

此处的判断应该为 true ,第一次动画时判断为 false,导致后续

final Animation<double> current = _secondaryAnimation.parent;

_secondaryAnimation.parent = TrainHoppingAnimation(current, nextRoute._animation);

未执行,动画未成功初始化。

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

void _updateSecondaryAnimation(Route nextRoute)

在第一个页面 push 第二个页面的时候,此方法被调用了两次,

第一次:

flutter: nextRoute is TransitionRoute:false
flutter: canTransitionTo(nextRoute):false

transition (PageTransitionsTheme::buildTransitions)没有执行

第二次:

flutter: nextRoute is TransitionRoute:true
flutter: canTransitionTo(nextRoute):true

nextRoute.canTransitionFrom(this) 为 false

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

若去掉 nextRoute.canTransitionFrom(this) 这个问题可以被解决,但还不清楚会造成什么样的影响

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

默认 bool canTransitionFrom(TransitionRoute<dynamic> previousRoute) => true;

但是这个可以背重写,调用该方法的 route 为传入的 route,推测为 CupertinoPageRoute

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

CupertinoPageRoute 的 canTransitionFrom 判断应该有问题:

@override
  bool canTransitionFrom(TransitionRoute<dynamic> previousRoute) {
    return previousRoute is CupertinoPageRoute;
  }

这里判断的前一个 Route 是否为 CupertinoPageRoute,若前一个 Route 并不是 CupertinoPageRoute,而当前 push 的 Route 为 CupertinoPageRoute 那么这个方法依然会返回 false。

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

MaterialPageRoute 不会出现这个问题,参考 MaterialPageRoute 更改 CupertinoPageRoute 中 canTransitionFrom

@override
  bool canTransitionFrom(TransitionRoute<dynamic> previousRoute) {
    return previousRoute is MaterialPageRoute || previousRoute is CupertinoPageRoute;
  }

验证可以修复 CupertinoPageRoute 中的第一页 push page 页面动画不一致的问题。

@liugang1104
Copy link
Author

哇,非常感谢你的帮助。经过验证你说的方法确实是可行的。
为你的精神点赞👍👍👍👍

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

FlutterBoost 问题点好像有些不同,即使使用 MaterialPageRoute 也会出现这个现象,这个还要继续跟踪下;)

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

刚测试了下,FlutterBoost 的也被解决了

@liugang1104
Copy link
Author

刚测试了下,FlutterBoost 的也被解决了

你是用的什么跳转方式,我这边用MaterialPageRoute还是存在这个问题。

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

Navigator.of(context).push(MaterialPageRoute(builder: (context)=>FirstRouteWidget()));

就是普通 MaterialPageRoute,Cupertino 那个你改了之后检查下是否 import material package,否则会编译失败。

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 13, 2019

FlutterBoost 中 BoostPageRoute 的 buildTransitions 重写有问题

@override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return child;
  }

这里只 return child 将会丢失动画的信息,应该返回父类的结果,改成以下内容:

@override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return super.buildTransitions(context, animation, secondaryAnimation, child);
  }

经验证,可以解决 FlutterBoost 页面跳转动画问题

@Vadaski
Copy link
Collaborator

Vadaski commented Nov 14, 2019

see also: flutter/flutter#44864

@Vadaski
Copy link
Collaborator

Vadaski commented Dec 3, 2019

该 issue 已在最新 flutter master 版本中被 fix 掉!感谢你的反馈!

@Vadaski Vadaski closed this as completed Dec 3, 2019
@Vadaski
Copy link
Collaborator

Vadaski commented Dec 3, 2019

最终解决方案与该 issue 讨论有所不同,我们选择了去掉 CupertinoPageRouteMaterialPageRoute 中重写的 canTransitionFrom 方法。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants