【问题标题】:Flutter GestureDetector if swiping down to dismiss如果向下滑动以关闭,则颤振 GestureDetector
【发布时间】:2021-05-31 22:46:11
【问题描述】:

我希望能够向下滑动以关闭但使用 Hero-Animation

我尝试像这样使用GestureDetector

  body: GestureDetector(
    onVerticalDragDown: (details) {
      Navigator.pop(context);
    },
    child: 

这个动画看起来不错,但问题是它几乎在任何手势上都是poping。我想要的是它只有在用户实际向下滑动时才会弹出。

应该还有一些finished 属性,这样如果用户没有完全向下滑动,动画就会被取消。这可能吗?如果可以,怎么做?我在这方面找不到任何东西..

期望的结果应该是这样的:

Desired Animation

如您所见,我可以向下滑动,也可以通过不完全向下滑动来取消pop

当前动画:

Screenvideo

通过单击关闭按钮,动画效果很好。 但是如果我开始拖动,动画应该开始,如果我结束它应该pop,或者我也可以取消动画,动画恢复到正常屏幕。

如果有帮助的话,这是我的代码:

 Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onVerticalDragDown: (details) {
          Navigator.pop(context);
        },
        child: Stack(
          children: [
            Hero(
              tag: month.name + 'background',
              // transitionOnUserGestures: true,
              child: Container(
                // color: CustomColors.darkCustom,
                decoration: BoxDecoration(
                  color: CustomColors.darkCustom,
                  borderRadius: BorderRadius.circular(30.0),
                ),
              ),
            ),
            Positioned(
              right: 30,
              top: 15,
              child: SafeArea(
                child: Hero(
                  // transitionOnUserGestures: true,
                  tag: month.name + 'close',
                  child: Container(
                    height: 45,
                    width: 45,
                    child: RawMaterialButton(
                      fillColor: CustomColors.lightGreyCustom,
                      splashColor: Colors.transparent,
                      highlightColor: Colors.transparent,
                      // elevation: 10,
                      shape: CircleBorder(),
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                      child: SvgPicture.asset('assets/images/close.white.svg',
                          height: 25, width: 25),
                    ),
                  ),
                ),
              ),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                SafeArea(
                  bottom: false,
                  child: SizedBox(height: 20),
                ),
                Row(
                  children: [
                    Padding(
                      padding:
                          const EdgeInsets.only(left: 45, top: 45, bottom: 35),
                      child: Hero(
                        // transitionOnUserGestures: true,
                        tag: month.name + 'text',
                        // sized box to prevent flickering bug
                        child: SizedBox(
                          height: 40,
                          width: 200,
                          // material is need for Hero + Text
                          child: Material(
                            color: Colors.transparent,
                            child: Text(
                              month.name,
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 28,
                                fontFamily: Fonts.glossAndBloom,
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
                Hero(
                  tag: month.name + 'frame',
                  child: Container(
                    height: Constants.width(context) - 60,
                    width: Constants.width(context) - 60,
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(10.0),
                      border: Border.all(color: Colors.white, width: 5),
                    ),
                  ),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }

在 Swift 中,我设法使用以下代码实现了动画效果:

@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()
        }
    }
}

【问题讨论】:

    标签: flutter dart flutter-animation gesturedetector


    【解决方案1】:

    我建议使用回调函数的 DragEndDetails。 简单的例子是:

    onVerticalDragEnd: (endDetails) {
                  double velocity = endDetails.primaryVelocity;
                  if (velocity > 0 ){                              
                   Navigator.pop(context);
                  }
                },
    

    在这种情况下,如果您在最后按住拖动手势,它不会弹出,因为速度将等于 0。

    编辑:

    这是一个在拖动细节上实现动画的简单示例。在 DragUpdate 上,容器高度将被调整,但限制为 max:300 到 min:100。在 DragEnd 上取决于您向上或向下滑动,容器高度将设置为最大或最小。

    class AnimatedContainerApp extends StatefulWidget {
      @override
      _AnimatedContainerAppState createState() => _AnimatedContainerAppState();
    }
    
    class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
      double height = 300;
      bool gestureUp = false;
      
      @override
      Widget build(BuildContext context) {
        final maxHeight = 300.0;
        final minHeight = 100.0;
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text('AnimatedContainer Demo'),
            ),
            body: Align(alignment: Alignment.bottomCenter,             
             child:AnimatedContainer(
              color: Colors.red,
                height: height,
                duration: Duration(milliseconds: 200),
                curve: Curves.fastOutSlowIn,
                child: GestureDetector(
                   onVerticalDragUpdate: (details) {
                     setState((){
                      if (0 < details.delta.dy)
                        gestureUp = false;
                      else
                        gestureUp = true;
                      height -= details.delta.dy;
                      if (height > maxHeight)
                          height = maxHeight;
                      else if (height < minHeight)
                          height = minHeight;
                      });     
                    },
                    onVerticalDragEnd: (details) {
                      setState((){
                          if (gestureUp) {
                            height = maxHeight;
                          } else {
                            height = minHeight;
                          }
                        });     
                    },
                )
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 其实你应该使用kMinFlingVelocity或者一些类似的常量
    • ok 这种工作方式是pop 仅在实际应该被调用时才被调用。但是,通过向下拖动,动画不会开始。当完成整个拖动时,它会动画。但这并不是 100% 想要的动画。你明白我的意思吗?
    • @Chris 是的。在这种情况下,您应该使用 onVerticalDragUpdate 回调处理动画
    • @hyobbb 我该怎么做??
    • 这取决于您希望如何使用DragUpdateDetails 实现动画行为。看看这个 api 文档。 api.flutter.dev/flutter/gestures/DragUpdateDetails-class.html
    猜你喜欢
    • 2021-12-28
    • 2022-07-25
    • 2021-01-26
    • 1970-01-01
    • 2020-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多