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

问题描述 投票:0回答:1

是否可以使用Flutter复制iOS App Store的转场效果?

我尝试通过将两个标签放入两个小部件的根布局中来使用英雄动画,但动画看起来很卡顿或者不是我所期望的。但这样做的好处是,当我使用 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

Example App Store

flutter dart flutter-animation flutter-navigation
1个回答
14
投票

2023/10 更新

我觉得我们可以用OpenContainer来改造它。 比使用 Hero 更流畅 https://pub.dev/packages/animations


这是我的解决方案。

https://i.stack.imgur.com/y3Na2.jpg

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

我自定义了英雄过渡,以尽可能地重新制作应用商店过渡。

      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,
                );
        },
      ),

接下来,我使用

ScaleTransition
onVerticalDragUpdate
来控制弹出动画。

https://i.stack.imgur.com/6iyyR.jpg

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://i.stack.imgur.com/AXHPt.jpg

在我的例子中,我通过

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

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

© www.soinside.com 2019 - 2024. All rights reserved.