Flutter实现购物车动画

最近项目中有一个类似购物车的动画,然后我用android实现之后就想到自己再学flutter,就写了一个flutter的版本,效果上跟原生差不多,如图

因为有了android实现思路的支持,我就想到用同样的思路来实现,但是上来就遇到了一个问题,在android中咱们可以创建一个那个移动的view动态的添加到父布局中,然后开始执行动画,但是flutter我不知道怎么用父widget动态的添加widget。

所以只能换思路了,查看flutter的widget,看到了一个stack,这个类似android中的FrameLayout,然后就想到父widget用stack,stack的也正好接收的是children。

在点击view的时候我要获取到这个widget的大小和位置,所以写了一个widget

class ItemChildWidget extends StatelessWidget {
  final int position;
  final Widget child;
  final ItemClickCallBack callBack;

  ItemChildWidget({this.position, this.child, this.callBack});

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return _getItemChildWidget(context);
  }

  Widget _getItemChildWidget(BuildContext context) {
    return GestureDetector(
      child: child,
      onTap: () {
        RenderBox box = context.findRenderObject();
        callBack(box);
      },
    );
  }
}

这个widget响应点击和获取位置大小的作用,然后再定义一个移动widget所在的stack,和定义一个移动的widget添加相应的动画

class AnimationItemMove extends StatefulWidget {
  final List children;
  final bool show;
  final Offset offset;
  final Size size;
  final double defferentX;
  final double defferentY;

  AnimationItemMove(
      {this.children,
      @required this.show,
      @required this.offset,
      @required this.size,
      @required this.defferentX,
      @required this.defferentY});

  @override
  State createState() => AnimationItemMoveState();
}

class AnimationItemMoveState extends State
    with SingleTickerProviderStateMixin {
  // Animation _animation;
  // AnimationController _controller;
  var newChildren = List();

  @override
  Widget build(BuildContext context) => _getRootWidget(context);

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

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }

  @override
  void didUpdateWidget(AnimationItemMove oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget");
    // _controller.reset();
    // _controller.forward();
  }

  Widget _getRootWidget(BuildContext context) {
    return Offstage(
      offstage: widget.show,
      child: Stack(
        children: _getChild(),
      ),
    );
  }

  List _getChild() {
    for (var item in widget.children) {
      var itemChild = AnimaPositioned(
        child: item,
        show: widget.show,
        offset: widget.offset,
        size: widget.size,
        defferentX: widget.defferentX,
        defferentY: widget.defferentY,
        yOffset: _getYdifferet(),
        callBack: (parent){
          // newChildren.remove(parent);
          newChildren.removeAt(0);
          setState(() {
            
          });
        },
      );
      newChildren.add(itemChild);
    }
    return newChildren;
  }

  double _getYdifferet() {
    RenderBox box = context.findRenderObject();
    if (box != null) {
      Offset offset = box.localToGlobal(Offset.zero);
      return offset.dy;
    } else {
      return 0;
    }
  }


  // Widget _getStackView() {
  //   var parent = Stack();
  //   parent.children.add();
  // }
}
class AnimaPositioned extends StatefulWidget{

  final Widget child;
  final bool show;
  final Offset offset;
  final Size size;
  final double defferentX;
  final double defferentY;
  final double yOffset;
  final AnimationFinshCallBack callBack;

  AnimaPositioned({
    this.child,
    this.defferentX,
    this.defferentY,
    this.offset,
    this.show,
    this.size,
    this.yOffset,
    this.callBack,
  });

  @override
  State createState() => AnimaPositionedState();

}

class AnimaPositionedState extends State with SingleTickerProviderStateMixin{

  Animation _animation;
  AnimationController _controller;

  @override
  Widget build(BuildContext context) => _getRootWidget();

  @override
  void initState() { 
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: 1000),
      vsync: this,
    );
    _animation = Tween(begin: 1.0, end: 0.0).animate(_controller)
      ..addListener(() {
        setState(() {});
      })
      ..addStatusListener((state){
        if(state == AnimationStatus.completed){
          widget.callBack(widget);
        }
      });
      var widgetsbinding = WidgetsBinding.instance;
      widgetsbinding.addPostFrameCallback((callback){
        _controller.forward();
      });
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    
  }
  @override
  void didUpdateWidget (AnimaPositioned oldWidget) {
    super.didUpdateWidget(oldWidget);
    
  }

  Widget _getRootWidget(){
    return Positioned(
        top: _getOffset().dy -
            widget.yOffset +
            widget.defferentY * (1 - _animation.value),
        // top: _getOffset().dy + widget.defferentY * (1 - _animation.value),
        left: _getOffset().dx + widget.defferentX * (1 - _animation.value),
        height: _getSize().height * _animation.value,
        width: _getSize().width * _animation.value,
        child: Opacity(
          opacity: _animation.value,
          child: widget.child,
        ),
      );
  }

  Offset _getOffset() {
    if (widget.offset != null) {
      // print(widget.offset.dy);
      return widget.offset;
    } else {
      return Offset(0, 0);
    }
  }

  Size _getSize() {
    if (widget.size != null) {
      return widget.size;
    } else {
      return Size(0, 0);
    }
  }

  

}

AnimaPositionedState这个类是主要的移动widget,里面执行了相应的移动、缩小、透明度的动画,里面要注意

var widgetsbinding = WidgetsBinding.instance;
      widgetsbinding.addPostFrameCallback((callback){
        _controller.forward();
      });

这段代码是在移动的widget渲染结束后被调用的,也正是咱们需要开始执行动画的时候。

大致思路就是这么多,其实当我做完之后又看到flutter里面自带的hero,才感觉动画很相似,然后就发现它里面的实现方式更好,有用到Overlay,这个东西能够更好和方便的实现咱们这个动画,所以下次我会再优化的

这个是整个界面的widget的文件地址https://download.csdn.net/download/bb3413830/11214826(我不会修改下载积分,要下载的也可以私信。。。)

你可能感兴趣的:(自定义)