Flutter-如何具有任意数量的隐式动画

时间:2018-08-21 09:28:29

标签: animation dart widget flutter state

我制作了一个小部件,其中包含一个children和一个List<double>的{​​{1}}列表,该列表显示了子级以及它们之间的相应间隔。我已经做到了,所以传递新的gaps列表会使小部件从旧间隙动画化为新间隙(更改不支持的间隙数)。

处理间隙动画的最佳方法是什么?

这是我正在寻找的行为:example

2 个答案:

答案 0 :(得分:1)

这是我的解决方案,但是我的代码非常混乱。特别是,我不确定是否有单独的动画,补间,控制器和曲线列表(这就是我现在正在做的)是执行操作的最佳方法。同样在List<int>.generate(widget.gaps.length, (i) => i).forEach函数中执行build似乎是错误的,但是通常的for (var i; i<x; i++)似乎也不是很容易。

是否有更好的方法来处理这两个问题?

class GappedList extends StatefulWidget {
  final List<Widget> children;
  final List<double> gaps;
  GappedList({@required this.children, @required this.gaps}) :
    assert(children != null),
    assert(children.length > 0),
    assert(gaps != null),
    assert (gaps.length >= children.length - 1);
  @override
  GappedListState createState() {
    return new GappedListState();
  }
}

class GappedListState extends State<GappedList> with TickerProviderStateMixin{
  List<Animation> _animations = [];
  List<AnimationController> _controllers = [];
  List<CurvedAnimation> _curves = [];
  List<Tween<double>> _tweens;

  @override
  void initState() {
    super.initState();
    _tweens = widget.gaps.map((g) => Tween(
      begin: g ?? 0.0,
      end: g ?? 0.0,
    )).toList();
    _tweens.forEach((t) {
      _controllers.add(AnimationController(
        value: 1.0,
        vsync: this,
        duration: Duration(seconds: 1),
      ));
      _curves.add(CurvedAnimation(parent: _controllers.last, curve: Curves.ease));
      _animations.add(t.animate(_curves.last));
    });
  }
  @override
  void dispose() {
    _controllers.forEach((c) => c.dispose());
    super.dispose();
  }

  @override
  void didUpdateWidget(GappedList oldWidget) {
    super.didUpdateWidget(oldWidget);
    assert(oldWidget.gaps.length == widget.gaps.length);
    List<Tween<double>> oldTweens = _tweens;
    List<int>.generate(widget.gaps.length, (i) => i).forEach(
      (i) {
        _tweens[i] = Tween<double>(
          begin: oldTweens[i].evaluate(_curves[i]),
          end: widget.gaps[i] ?? 0.0,
        );
        _animations[i] = _tweens[i].animate(_curves[i]);
        if (_tweens[i].begin != _tweens[i].end) {
          _controllers[i].forward(from: 0.0);
        }
      }
    );
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> list = [];
    List<int>.generate(widget.children.length, (i) => i).forEach(
      (i) {
        list.add(widget.children[i]);
        if (widget.children[i] != widget.children.last) {
          list.add(
            AnimatedBuilder(
              animation: _animations[i],
              builder: (context, _) => ConstrainedBox(
                constraints: BoxConstraints.tightForFinite(
                  height: _animations[i].value,
                ),
              ),
            )
          );
        }
      }
    );
    return ListView(
      primary: true,
      shrinkWrap: true,
      children: list,
    );
  }
}

答案 1 :(得分:1)

为避免不必要的重复,可以将补间逻辑移至自定义小部件。

您还可以通过自定义<a href="#" class="button" modal=".modal-1">modal-1</a> <a href="#" class="button" modal=".modal-2">modal-2</a> <div class="side-modal modal-1"> <div class="modal-content"> <h2>modal-1</h2> <div class="close close-side-modal">+</div> </div> </div> <div id="modal2" class="side-modal modal-2"> <div class="modal-content"> <h2>modal-2</h2> <div class="close close-side-modal">+</div> </div> </div>小部件将List<Widget> childrenList<double> gaps融合在一起。

最终,您可以继续通过Gap构造函数使用ListView,并使用我们的自定义separated作为分隔符。


将所有这些考虑在内,最后,您的Gap小部件只是一个具有自定义高度的Gap

AnimatedContainer

然后您可以通过以下方式使用它:

class Gap extends StatelessWidget {
  final double gap;

  const Gap(this.gap, {Key key})
      : assert(gap >= .0),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 250),
      curve: Curves.easeOut,
      height: gap,
    );
  }
}

此处的ListView.separated( itemCount: 42, addAutomaticKeepAlives: true, itemBuilder: (context, index) { return RaisedButton(onPressed: null, child: Text("Foo $index")); }, separatorBuilder: (context, index) { return Gap(10.0); }, ), 用于确保离开然后重新出现的项目不会重置其动画。但这不是必需的。

这是一个动态更改间隙大小的完整示例:

enter image description here

addAutomaticKeppAlives: true