在 AnimatedList 中添加或删除列表项时,有没有一种方法可以平滑地调整列表项的位置?

Is there a way to smoothly tween the position of list items when adding or removing from an AnimatedList?

我有一个 AnimatedList,我可以很好地添加和删除项目。我的问题是,当我将一个项目添加到列表顶部时,它下面的项目会跳下来为它制作 space,而不是平滑过渡。

我发现 this answer 它提供了一种解决方案,可以在移除项目时平滑地动画化项目,但我无法使用相同的解决方案来插入项目。

这就是我目前所拥有的。当项目被添加到列表的顶部时,它下面的元素会平滑地移动。然而,新元素的缩放过渡也遵循这种转变。

class Foo extends StatefulWidget {
  Foo({Key key}) : super(key: key);

  final kDuration = const Duration(seconds: 1);

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

class _FooState extends State<Foo> {
  final _key = GlobalKey<AnimatedListState>();
  List<Color> _items;
  int _counter;

  @override
  void initState() {
    super.initState();
    _items = <Color>[];
    _counter = 0;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Animated List Spike'),
      ),
      body: AnimatedList(
        key: _key,
        initialItemCount: _items.length,
        itemBuilder: (context, index, animation) {
          return Stack(
            children: [
              // This transition creates the smooth shift-down effect.
              SizeTransition(
                sizeFactor: animation.drive(Tween(begin: 0, end: 1)),
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    height: 64,
                    color: Colors.transparent,
                  ),
                ),
              ),
              // This is the content we actually want to show.
              Align(
                alignment: Alignment.topCenter,
                heightFactor: 0,
                child: SlideTransition(
                  position: Tween(
                    begin: Offset(0, -0.5),
                    end: Offset(0, 0),
                  ).animate(animation),
                  child: ScaleTransition(
                    scale: CurveTween(curve: Curves.easeOutBack)
                        .animate(animation),
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Card(
                        color: _items[index],
                        child: Container(height: 64),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: addItem,
        child: const Icon(Icons.add),
      ),
    );
  }

  void addItem() {
    _items.insert(0, Colors.primaries[_counter++ % Colors.primaries.length]);
    _key.currentState.insertItem(0, duration: widget.kDuration);
  }
}

有没有办法让元素平滑移动,然后然后让新元素缩放?

解决方案是使用 Interval 曲线。通过让一条曲线在 0.5 标记处结束,另一条曲线从那里开始,我能够使元素向下滑动,然后让我的新元素缩放。

CurveTween(
  curve: Interval(
    0.5, // Start halfway through the animation.
    1.0, // Stop at the end of the animation.
    curve: Curves.easeOutBack,
  ),
).animate(animation),

问题的完整解决方案:

itemBuilder: (context, index, animation) {
          return Stack(
            children: [
              SizeTransition(
                sizeFactor: animation.drive(
                  CurveTween(
                    curve: Interval(
                      0.0,
                      0.5,
                      curve: Curves.linear,
                    ),
                  ),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    height: 64,
                    color: Colors.transparent,
                  ),
                ),
              ),
              Align(
                alignment: Alignment.topCenter,
                heightFactor: 0,
                child: ScaleTransition(
                  scale: CurveTween(
                    curve: Interval(
                      0.5,
                      1.0,
                      curve: Curves.easeOutBack,
                    ),
                  ).animate(animation),
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Card(
                      color: _items[index],
                      child: Container(height: 64),
                    ),
                  ),
                ),
              ),
            ],
          );