【问题标题】:Flutter - Flip animation - Flip a card over its right or left side based on the tap's locationFlutter - 翻转动画 - 根据点击的位置将卡片翻转到其右侧或左侧
【发布时间】:2019-03-02 13:13:45
【问题描述】:

我已经开始使用 Flutter,现在正在思考如何实现卡片翻转动画的最佳方式。

我找到了这个flip_card 包,我正在尝试根据我的需要调整its source code

这是我现在拥有的应用程序:

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

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

class FlipAnimationApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Flip animation"),
        ),
        body: Center(
          child: Container(
            width: 200,
            height: 200,
            child: WidgetFlipper(
              frontWidget: Container(
                color: Colors.green[200],
                child: Center(
                  child: Text(
                    "FRONT side.",
                  ),
                ),
              ),
              backWidget: Container(
                color: Colors.yellow[200],
                child: Center(
                  child: Text(
                    "BACK side.",
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class WidgetFlipper extends StatefulWidget {
  WidgetFlipper({
    Key key,
    this.frontWidget,
    this.backWidget,
  }) : super(key: key);

  final Widget frontWidget;
  final Widget backWidget;

  @override
  _WidgetFlipperState createState() => _WidgetFlipperState();
}

class _WidgetFlipperState extends State<WidgetFlipper> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> _frontRotation;
  Animation<double> _backRotation;
  bool isFrontVisible = true;

  @override
  void initState() {
    super.initState();

    controller = AnimationController(duration: Duration(milliseconds: 500), vsync: this);
    _frontRotation = TweenSequence(
      <TweenSequenceItem<double>>[
        TweenSequenceItem<double>(
          tween: Tween(begin: 0.0, end: pi / 2).chain(CurveTween(curve: Curves.linear)),
          weight: 50.0,
        ),
        TweenSequenceItem<double>(
          tween: ConstantTween<double>(pi / 2),
          weight: 50.0,
        ),
      ],
    ).animate(controller);
    _backRotation = TweenSequence(
      <TweenSequenceItem<double>>[
        TweenSequenceItem<double>(
          tween: ConstantTween<double>(pi / 2),
          weight: 50.0,
        ),
        TweenSequenceItem<double>(
          tween: Tween(begin: -pi / 2, end: 0.0).chain(CurveTween(curve: Curves.linear)),
          weight: 50.0,
        ),
      ],
    ).animate(controller);
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: [
        AnimatedCard(
          animation: _backRotation,
          child: widget.backWidget,
        ),
        AnimatedCard(
          animation: _frontRotation,
          child: widget.frontWidget,
        ),
        _tapDetectionControls(),
      ],
    );
  }

  Widget _tapDetectionControls() {
    return Stack(
      fit: StackFit.expand,
      children: <Widget>[
        GestureDetector(
          onTap: _leftRotation,
          child: FractionallySizedBox(
            widthFactor: 0.5,
            heightFactor: 1.0,
            alignment: Alignment.topLeft,
            child: Container(
              color: Colors.transparent,
            ),
          ),
        ),
        GestureDetector(
          onTap: _rightRotation,
          child: FractionallySizedBox(
            widthFactor: 0.5,
            heightFactor: 1.0,
            alignment: Alignment.topRight,
            child: Container(
              color: Colors.transparent,
            ),
          ),
        ),
      ],
    );
  }

  void _leftRotation() {
    _toggleSide();
  }

  void _rightRotation() {
    _toggleSide();
  }

  void _toggleSide() {
    if (isFrontVisible) {
      controller.forward();
      isFrontVisible = false;
    } else {
      controller.reverse();
      isFrontVisible = true;
    }
  }
}

class AnimatedCard extends StatelessWidget {
  AnimatedCard({
    this.child,
    this.animation,
  });

  final Widget child;
  final Animation<double> animation;

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: animation,
      builder: (BuildContext context, Widget child) {
        var transform = Matrix4.identity();
        transform.setEntry(3, 2, 0.001);
        transform.rotateY(animation.value);
        return Transform(
          transform: transform,
          alignment: Alignment.center,
          child: child,
        );
      },
      child: child,
    );
  }
}

如下所示:

我想要实现的是让卡片在其右半部分被轻敲时翻转其右侧,如果它在其左半部分被轻敲则翻转其左侧。如果在同一半边连续敲击几次,它应该翻转到同一侧(而不是像现在这样来回)。

因此,所需的动画应该与 Quizlet 应用程序中的以下动画相同。

【问题讨论】:

    标签: flutter flutter-animation


    【解决方案1】:

    您应该知道何时点击右侧或左侧来动态更改动画,因为您可以使用标志isRightTap。然后,如果必须旋转到一侧或另一侧,则应反转 Tweens 的值。

    你应该旋转的一侧是:

    • 如果正面可见并且您点击了左侧,则向左旋转,或者,如果背面可见并且您点击了右侧,则由于背面动画反转了
    • 否则向右旋转

    这是我在问题代码中在_WidgetFlipperState 中更改的内容:

    _updateRotations(bool isRightTap) {
      setState(() {
        bool rotateToLeft = (isFrontVisible && !isRightTap) || !isFrontVisible && isRightTap;
        _frontRotation = TweenSequence(
          <TweenSequenceItem<double>>[
            TweenSequenceItem<double>(
              tween: Tween(begin: 0.0, end: rotateToLeft ? (pi / 2) : (-pi / 2))
                  .chain(CurveTween(curve: Curves.linear)),
              weight: 50.0,
            ),
            TweenSequenceItem<double>(
              tween: ConstantTween<double>(rotateToLeft ? (-pi / 2) : (pi / 2)),
              weight: 50.0,
            ),
          ],
        ).animate(controller);
        _backRotation = TweenSequence(
          <TweenSequenceItem<double>>[
            TweenSequenceItem<double>(
              tween: ConstantTween<double>(rotateToLeft ? (pi / 2) : (-pi / 2)),
              weight: 50.0,
            ),
            TweenSequenceItem<double>(
              tween: Tween(begin: rotateToLeft ? (-pi / 2) : (pi / 2), end: 0.0)
                  .chain(CurveTween(curve: Curves.linear)),
              weight: 50.0,
            ),
          ],
        ).animate(controller);
      });
    }
    
    @override
    void initState() {
      super.initState();
      controller =
          AnimationController(duration: Duration(milliseconds: 500), vsync: this);
      _updateRotations(true);
    }
    
    void _leftRotation() {
      _toggleSide(false);
    }
    
    void _rightRotation() {
      _toggleSide(true);
    }
    
    void _toggleSide(bool isRightTap) {
      _updateRotations(isRightTap);
      if (isFrontVisible) {
        controller.forward();
        isFrontVisible = false;
      } else {
        controller.reverse();
        isFrontVisible = true;
      }
    }
    

    【讨论】:

      【解决方案2】:

      试试这个代码 我已经对你的代码做了一些更改,现在GestureDetector 在小部件上的宽度被平均划分,所以当你点击框的左侧时,它会反转动画,如果你点击右侧侧面部分它会转发动画。

       Widget _tapDetectionControls() {
          return Flex(
            direction: Axis.horizontal,
            children: <Widget>[
              Expanded(
                flex: 1,
                child: GestureDetector(
                  onTap: _leftRotation,
                ),
              ),
              Expanded(
                flex: 1,
                child: GestureDetector(
                  onTap: _rightRotation,
                ),
              ),
            ],
          );
        }
      
        void _leftRotation() {
          controller.reverse();
        }
      
        void _rightRotation() {
          controller.forward();
        }
      

      【讨论】:

      • 我尝试过类似的方法,但它不起作用/满足我的需求:案例 1. 如果你点击卡片的右侧,它会翻转它,但如果你点击再次在右侧,它不会做任何事情,因为动画已经处于结束状态。案例2.如果你打开应用程序并点击卡片左侧,if不会翻转,因为动画在它的开始阶段和反转不会生效。有什么建议吗?
      • 好的,那么当我们点击右侧卡片时应该向前翻转,当我们点击左侧时它应该向后翻转,不管是第一个还是最后一个?对吗??
      • 是的,这就是我想要实现的行为。
      • 我试过这个场景,所以在这个场景中,我们需要为两个小部件提供两个动画,应该动态更改。我会尝试破解这个并让你知道。
      • 我也得出了同样的结论,但是我现在还没有足够的 Flutter 技能来优雅地解决它:)
      猜你喜欢
      • 2013-11-22
      • 2020-11-27
      • 2014-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-22
      • 2021-03-12
      相关资源
      最近更新 更多