在 Row 布局容器中从当前位置滑动 child 小部件

Slide child widget in Row layout container from its current position

我有一行包含 x 个按钮小部件。因此,当用户单击其中一个按钮时,我需要淡出其他按钮并将单击的按钮滑动到行布局的起始位置 (0,0)。我设法使淡出动画起作用。

final _opacityTween = Tween<double>(begin: 1, end: 0);
_opacityAnimation = _opacityTween.animate(CurvedAnimation(
        parent: _controller,
        curve: new Interval(0.00, 0.50, curve: Curves.linear)
        // curve: Curves.ease
        ));

// And using the opacityAnimation value in Opacity widget.

return Opacity(
        opacity: _opacityAnimation.value,
        child: child,
      );

然而,我一直未能成功使用动画 类 和小部件将 child 小部件滑动到行的起始位置。基本上我不确定什么样的 类 可以用来实现这样的动画。我可以使用 Tween 并将结束位置设置为 (0, 0) 并使用 Translation 来实现它,但是当我将动画初始化为 [=16= 的位置时,我不确定如何设置动画的开始位置] 未知。

试试这个解决方案。我认为这几乎就是您所需要的。您只需添加 SizeTransition 小部件。 问你有什么问题。

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {

    int _pressedButton;

    Animation<double> _animation;
    AnimationController _controller;

    @override
    void initState() {
        super.initState();
        _controller = AnimationController(
            duration: const Duration(milliseconds: 500),
            vsync: this,
        );
        _animation = Tween<double>(begin: 1.0, end: 0.0)
            .animate(_controller)
            ..addListener(() {
                setState(() { });
            });
    }

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text("Test"),
            ),
            body: Column(
                children: <Widget>[
                    Row(
                        children: <Widget>[
                            _animatedButton(1),
                            _animatedButton(2),
                            _animatedButton(3),
                            _animatedButton(4)
                        ]
                    ),
                    Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
                    RaisedButton(
                        child: Text("Reset"),
                        onPressed: () {
                            _controller.reset();
                        }
                    )
                ]
            )
        );
    }

    Widget _animatedButton(int button) {
        if (button != _pressedButton) {
            return SizeTransition(
                sizeFactor: _animation,
                axis: Axis.horizontal,
                child: Opacity(
                    opacity: _animation.value,
                    child: _button(button)
                )
            );
        } else {
            return _button(button);
        }
    }

    Widget _button(int button) {
        return RaisedButton(
            onPressed: () => onButtonClick(button),
            child: Text("Button $button")
        );
    }

    void onButtonClick(int button) {
        setState(() {
            _pressedButton = button;
        });
        _controller.forward();
    }

}

更新 1

检查另一种方法来完成您需要的操作。下面的代码中有一些不同的地方。

  1. 将 SingleTickerProviderStateMixin 更改为 TickerProviderStateMixin。
  2. 使用两个动画控制器和动画(用于淡出未选中的按钮和向左滑动选中的按钮)。
  3. 运行 个动画顺序(请参阅动画补间侦听器)。
  4. 添加 bool _buttonSelected 以跟踪动画的完成情况。
  5. 使用 _buttonSelected 构建正确的小部件 (Widget _buttonsWidget())
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  int _pressedButton = -1;
  bool _buttonSelected = false;

  Animation<double> _animationFadeOut;
  AnimationController _controllerFadeOut;
  Animation<double> _animationSlideLeft;
  AnimationController _controllerSlideLeft;

  @override
  void initState() {
    super.initState();
    _initFadeOutAnimation();
    _initSlideLeftAnimation();
  }

  void _initFadeOutAnimation() {
    _controllerFadeOut = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
      );
    _animationFadeOut = Tween<double>(begin: 1.0, end: 0.0)
        .animate(_controllerFadeOut)
      ..addListener(() {
        setState(() {
          if (_controllerFadeOut.isCompleted && !_controllerSlideLeft.isAnimating) {
            _controllerSlideLeft.forward();
          }
        });
      });
  }

  void _initSlideLeftAnimation() {
    _controllerSlideLeft = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
      );
    _animationSlideLeft = Tween<double>(begin: 1.0, end: 0.0)
        .animate(_controllerSlideLeft)
      ..addListener(() {
        setState(() {
          if (_controllerSlideLeft.isCompleted) {
            _buttonSelected = true;
          }
        });
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Test"),
          ),
        body: Column(
            children: <Widget>[
              _buttonsWidget(),
              Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
              RaisedButton(
                  child: Text("Reset"),
                  onPressed: () {
                    _controllerFadeOut.reset();
                    _controllerSlideLeft.reset();
                    _pressedButton = -1;
                    _buttonSelected = false;
                  }
                  )
            ]
            )
        );
  }

  Widget _buttonsWidget() {
    if (_buttonSelected) {
      return Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[_buttonWidget(_pressedButton)]
          );
    } else {
      return Row(
          children: <Widget>[
            _animatedButtonWidget(1),
            _animatedButtonWidget(2),
            _animatedButtonWidget(3),
            _animatedButtonWidget(4)
          ]
          );
    }
  }

  Widget _animatedButtonWidget(int button) {
    if (button == _pressedButton) {
      return _buttonWidget(button);
    } else {
      return SizeTransition(
          sizeFactor: _animationSlideLeft,
          axis: Axis.horizontal,
          child: Opacity(
              opacity: _animationFadeOut.value,
              child: _buttonWidget(button)
              )
          );
    }
  }

  Widget _buttonWidget(int button) {
    return RaisedButton(
        onPressed: () => _onButtonClick(button),
        child: Text("Button $button")
        );
  }

  void _onButtonClick(int button) {
    setState(() {
      _pressedButton = button;
    });
    _controllerFadeOut.forward();
  }
}