可以使用 Flutter 复制 iOS App Store 过渡吗?

Posted

技术标签:

【中文标题】可以使用 Flutter 复制 iOS App Store 过渡吗?【英文标题】:Possible to copy iOS App Store transition using Flutter? 【发布时间】:2020-10-15 21:29:32 【问题描述】:

是否可以使用 Flutter 复制 ios App Store 的过渡效果?

我尝试通过在两个小部件的根布局中放置两个标签来使用 Hero Animation,但动画看起来很卡,或者不是我预期的那样。但这样做的好处是我能够在使用 MaterialPageRoute 时将 iOS 向后滑动。

来源

Hero(
   tag: 'heroTag_destinationScreen',
   transitionOnUserGestures: true,
   flightShuttleBuilder: (BuildContext flightContext,
     Animation<double> animation,
     HeroFlightDirection flightDirection,
     BuildContext fromHeroContext,
     BuildContext toHeroContext,) 
        final Hero toHero = toHeroContext.widget;
            return ScaleTransition(
              scale: animation,
              child: toHero,
            );
          ,
          child: GestureDetector(
            onTap: () 
              Navigator.of(context).push(
                MaterialPageRoute<void>(
                  builder: (BuildContext context) 
                    return DestinationScreen()
                  ,
                ),
              );
            ,
            child: Card(
                ...someCardContent
            ),
      ),
)

目标屏幕

@override
Widget build(BuildContext context) 
    return Hero(
       tag: 'heroTag_destinationScreen',
       child: Scaffold(
           appBar: ...someAppBar
            body: ...someMainBodyContent
          ),
      )
    

然后我环顾四周,有一个由 Flutter 团队创建的包,可以使用容器变换来模拟这种效果。我实现了它,效果很棒,但是我无法从左侧滑动 iOS 以返回并将布局缩小到卡片视图。

https://pub.dev/packages/animations

【问题讨论】:

卡顿很可能是由于应用程序的调试版本造成的。在配置文件或发布模式下重试 嗨,对不起,我的意思是 jank 没有我想要的过渡动画,即打开卡片,如显示效果并在初始化时反弹目标屏幕 我设法模拟了向后滑动并缓慢缩放脚手架,但在导航器弹出时脚手架突然放大到全屏 @FadhliS 你能分享你在动画包中使用的代码吗? 【参考方案1】:

这是我的解决方案。

https://imgur.com/2WYn6TX

(对不起我的名誉,我无法发布图片。)

我自定义了英雄转场,尽可能地重做应用商店转场。

      child: Hero(
        tag: widget.product.id,
        child: Image.asset(widget.product.image, fit: BoxFit.cover),
        flightShuttleBuilder:
            (flightContext, animation, direction, fromcontext, toContext) 
          final Hero toHero = toContext.widget;
          // Change push and pop animation.
          return direction == HeroFlightDirection.push
              ? ScaleTransition(
                  scale: animation.drive(
                    Tween<double>(
                      begin: 0.75,
                      end: 1.02,
                    ).chain(
                      CurveTween(
                          curve: Interval(0.4, 1.0, curve: Curves.easeInOut)),
                    ),
                  ),
                  child: toHero.child,
                )
              : SizeTransition(
                  sizeFactor: animation,
                  child: toHero.child,
                );
        ,
      ),

接下来,我使用ScaleTransitiononVerticalDragUpdate来控制pop动画。

https://imgur.com/a/xEMYOPr

double _initPoint = 0;
double _pointerDistance = 0;
GestureDetector(
  onVerticalDragDown: (detail) 
    _initPoint = detail.globalPosition.dy;
  ,
  onVerticalDragUpdate: (detail) 
    _pointerDistance = detail.globalPosition.dy - _initPoint;
    if (_pointerDistance >= 0 && _pointerDistance < 200) 
        // scroll up
        double _scaleValue = double.parse((_pointerDistance / 100).toStringAsFixed(2));
        if (_pointerDistance < 100) 
          _closeController.animateTo(_scaleValue,
          duration: Duration(milliseconds: 300),
          curve: Curves.linear);
        
     else if (_pointerDistance >= 260) 
      if (_pop) 
        _pop = false;
        _closeController.fling(velocity: 1).then((_) 
          setState(() 
          _heightController.reverse();
          );
          Timer(Duration(milliseconds: 100), () 
            Navigator.of(context).pop();
          );
        );
      
     else 
      // scroll down
    
  ,
  onVerticalDragEnd: (detail) 
    if (_pointerDistance >= 550) 
      if (_pop) 
        _closeController.fling(velocity: 1).then((_) 
          setState(() 
            _heightController.reverse();
          );
          Timer(Duration(milliseconds: 100), () 
            Navigator.of(context).pop();
          );
        );
      
     else 
    _closeController.fling(velocity: -1);
    
  ,
  child: Hero(
    tag: _product.id,
    child: Image.asset(
      _product.image,
      fit: BoxFit.cover,
      height: 300,
    ),
  ),
),

如果使用Hero作为动画,则需要自定义文本部分的过渡。

这里:https://imgur.com/a/gyD6tiZ

在我的例子中,我通过 Sizetransition 控制文本部分的转换。

// horizontal way and vertical way.
SizeTransition(
  axis: Axis.horizontal,
  sizeFactor: Tween<double>(begin: 0.5, end: 1).animate(
    CurvedAnimation(
        curve: Curves.easeInOut, parent: _widthController),
  ),
  child: SizeTransition(
    sizeFactor: Tween<double>(begin: 0, end: 1).animate(
      CurvedAnimation(
          curve: Curves.easeInOut, parent: _heightController),
    ),
    child: Container(
      padding: EdgeInsets.only(
          left: 20, right: 20, top: 50, bottom: 30),
      width: double.infinity,
      color: Colors.white,
      constraints: BoxConstraints(
        minHeight: 650,
      ),
      child: Column(
        // title and text
        children: <Widget>[
          Text('Title', style: TextStyle(fontSize: 18)),
          SizedBox(height: 30),
          Text(_text,
              style: TextStyle(
                fontSize: 15,
              )),
        ],
      ),
    ),
  ),
),

虽然和App Store不一样,但希望对你有所帮助。

源码:https://github.com/HelloJunWei/app_store_transition

如果您有任何建议,请随时反馈或创建拉取请求。 :)

【讨论】:

您好,非常感谢您的努力!我设法使它也能正常工作,但我认为您的解决方案更好。我认为在关闭时仍然可以使边缘变圆作为您的阻力。也许我可以在你的 git 上创建一个拉取请求 嗨@FadhliS,我已经更新了我的项目。 demo gif 嗨@Neil,干得好!我面临着类似的问题,无法完成......看起来你真的很了解你的东西,所以你能看看吗? :) ***.com/questions/66438581/…

以上是关于可以使用 Flutter 复制 iOS App Store 过渡吗?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter objectbox:我可以用于生产 APP(仅限移动设备 Android 和 iOS)

Flutter Add-to-app 我可以在 iOS 视图控制器中添加半屏 Flutter 小部件吗

Flutter APP开发学习概要

Flutter--在浏览器打开URL

Flutter:Runner.app/Info.plist 不存在。 Flutter“精简二进制”构建阶段必须在“复制捆绑资源”之后运行

Flutter 依赖项,iOS 问题(未找到 Flutter.h 文件)