iOS-Flutter 可滚动组件-AnimatedList

55 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 29 天,点击查看活动详情

AnimatedList

AnimatedList和ListView功能基本相似,不同的是,AnimatedList可以在列表中插入或删除节点时执行一个动画,在需要添加或删除列表的场景中提高用户体验。

AnimatedList是一个StatefulWidget,对应的State类型为AnimatedListState,添加和删除元素方法位于AnimatedListState中:

//插入函数
void insertItem(int index, { Duration duration = _kDuration });
//删除函数
void removeItem(int index, AnimatedListRemovedItemBuilder builder, { Duration duration = _kDuration }) ;

实例:

class AnimatedListRoute extends StatefulWidget {
  const AnimatedListRoute({Key? key}) : super(key: key);

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

class _AnimatedListRouteState extends State<AnimatedListRoute> {
  var data = <String>[];
  int counter = 5;

  final globalKey = GlobalKey<AnimatedListState>();

  @override
  void initState() {
  //初始化时先把数据加载出来,便于展示
    for (var i = 0; i < counter; i++) {
      data.add('${i + 1}');
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        AnimatedList(
          key: globalKey,
          initialItemCount: data.length,//具体展示的行数
          itemBuilder: (
            BuildContext context,
            int index,
            Animation<double> animation,
          ) {
            //添加列表项时会执行渐显动画,FadeTransition动画类型,可切换为其他动画类型
            return FadeTransition(
              opacity: animation,
              child: buildItem(context, index),//具体展示的单元行
            );
          },
        ),
        buildAddBtn(),
      ],
    );
  }

  // 创建一个 “+” 按钮,点击后会向列表中插入一项
  Widget buildAddBtn() {
    return Positioned(
      child: FloatingActionButton(
        child: Icon(Icons.add),//系统Icon
        onPressed: () {
          // 添加一个列表项;注意一定要先把数据添加到UI的数据源中
          data.add('${++counter}');
          // 告诉列表项有新添加的列表项
          globalKey.currentState!.insertItem(data.length - 1);
          print('添加 $counter');
        },
      ),
      bottom: 30,
      left: 0,
      right: 0,
    );
  }

  // 构建列表项
  Widget buildItem(context, index) {
    String char = data[index];
    return ListTile(
      //数字不会重复,所以作为Key
      key: ValueKey(char),
      title: Text(char),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        // 点击时删除
        onPressed: () => onDelete(context, index),
      ),
    );
  }

  void onDelete(context, index) {
  //删除动画
    setState(() {
    globalKey.currentState!.removeItem(
    index,
    (context, animation) {
      // 删除过程执行的是反向动画,animation.value 会从1变为0
      var item = buildItem(context, index);
      print('删除 ${data[index]}');
      data.removeAt(index);
      // 删除动画是一个合成动画:渐隐 + 缩小列表项告诉
      return FadeTransition(//动画类型渐隐 
        opacity: CurvedAnimation(//指定具体的动画样式
          parent: animation,
          //让透明度变化的更快一些
          curve: const Interval(0.5, 1.0),//动画时间比例
        ),
        // 不断缩小列表项的高度
        child: SizeTransition(//尺寸变化动画:缩小动画
          sizeFactor: animation,
          axisAlignment: 0.0,//缩小至那个位置
          child: item,//具体的item
        ),
      );
    },
    duration: Duration(milliseconds: 200), // 动画时间为 200 ms
  );
});
  }
}

注意:数据是单独维护,调用AnimatedListState的插入和移除方法只是相当于一个通知:什么位置执行插入或删除动画,具体的展示仍然是数据驱动。