Ishwor
Ishwor

Reputation: 101

Slide child widget in Row layout container from its current position

I have a Row that consists x number of Button widgets. So when user clicks on one of the Button I need to fade out the other buttons and the slide the clicked button to the starting position(0,0) of the Row layout. I managed to get the fade out animation to work.

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

However I have been unsuccessful to using Animation classes and widgets to slide the child widget to the start position of the Row. Basically I am not sure what kind of classes could be useful to achieve such animation. I can use the Tween and set the end position to (0, 0) and use Translation to achieve it, however I am not sure how to set the begin position of the Animation when I initialise it as the position of a child is not known.

Upvotes: 0

Views: 1728

Answers (1)

dreambit.io dreambitio
dreambit.io dreambitio

Reputation: 1902

Try this solution. I thing it is almost what you need. You just have to add SizeTransition widget. Ask if you have any question.

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

}

UPDATE 1

Check one more way to do what you need. There are few different things in code below.

  1. Change SingleTickerProviderStateMixin to TickerProviderStateMixin.
  2. Use two animation controllers and animations (for fading out of unselected buttons and sliding left selected button).
  3. Run animations sequentially (see animation tweens listeners).
  4. Add bool _buttonSelected to track completion of animations.
  5. Use _buttonSelected to build correct widget (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();
  }
}

Upvotes: 1

Related Questions