likes
comments
collection
share

34、Flutter之AnimatedList详解

作者站长头像
站长
· 阅读数 8

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

下面我们看一个示例:实现下面这样的一个列表,点击底部 + 按钮时向列表追加一个列表项;点击每个列表项后面的删除按钮时,删除该列表项,添加和删除时分别执行指定的动画。

初始的时候有5个列表项,先点击了 + 号按钮,会添加一个 6,添加过程执行渐显动画。然后点击了 4 后面的删除按钮,删除的时候执行了一个渐隐+收缩的合成动画。

下面是实现代码:

import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
import 'dart:math';

/// @Author wywinstonwy
/// @Date 2022/1/20 8:42 上午
/// @Description:

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

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

class _MyAnimatedListState extends State<MyAnimatedList> {
  var data = <ItemModel>[];
  int counter = 5;
  final globalKey = GlobalKey<AnimatedListState>();

  @override
  void initState() {
    for (var i = 0; i < counter; i++) {
      Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256),
          Random().nextInt(256), 1);
      String title = '${i + 1}';
      ItemModel item = ItemModel(backColor: color, title: title);
      data.add(item);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: getAppBar('AnimatedList'),
      body: Stack(
        children: [_buildAnimatedList(), buildAddBtn()],
      ),
    );
  }

  buildAddBtn() {
    return Positioned(
      child: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          //添加一个列表项目
          // data.add(('${++counter}'));
          Color color = Color.fromRGBO(Random().nextInt(256),
              Random().nextInt(256), Random().nextInt(256), 1);
          String title = '${++counter}';
          ItemModel item = ItemModel(backColor: color, title: title);
          data.add(item);
          // 告诉列表项有新添加的列表项
          globalKey.currentState!.insertItem(data.length - 1);
          print('添加 $counter');
        },
      ),
      bottom: 30,
      left: 0,
      right: 0,
    );
  }

  _buildAnimatedList() {
    return AnimatedList(
        key: globalKey,
        initialItemCount: data.length,
        itemBuilder: (context, index, Animation<double> animation) {
          return FadeTransition(
            opacity: animation,
            child: _buildItem(context, index),
          );
        });
  }

  _buildItem(context, index) {
    ItemModel itemModel = data[index];

    return Container(
      padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
      margin: const EdgeInsets.only(bottom: 10),
      color: itemModel.backColor,
      key: ValueKey(itemModel.title),
      child: Row(
        children: [
          Container(
            margin: const EdgeInsets.only(left: 10),
            child: Text(
              itemModel.title,
              style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(child: Container()),
          IconButton(
              onPressed: () {
                onDelete(context, index);
              },
              icon: const Icon(
                Icons.delete,
                color: Colors.red,
                size: 30,
              )),
        ],
      ),
    );
  }

  onDelete(context, index) {
    //
    globalKey.currentState!.removeItem(index, (context, animation) {
      // 删除过程执行的是反向动画,animation.value 会从1变为0
      var item = _buildItem(context, 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,
        ),
      );
    }, duration: const Duration(milliseconds: 200) //动画时间200毫秒
        );
  }
}

class ItemModel extends StatelessWidget {
  const ItemModel({Key? key, required this.backColor, required this.title})
      : super(key: key);
  final Color backColor;
  final String title;

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

运行效果:

34、Flutter之AnimatedList详解 删除的时候需要我们通过AnimatedListState 的 removeItem 方法来应用删除动画。

代码很简单,但我们需要注意,我们的数据是单独在 data 中维护的,调用 AnimatedListState 的插入和移除方法知识相当于一个通知:在什么位置执行插入或移除动画,仍然是数据驱动的(响应式并非命令式)。

官方文档:api.flutter-io.cn/flutter/wid…

34、Flutter之AnimatedList详解

同时需要关注SliverAnimatedList,用法和AnimatedList相同。

SliverAnimatedList, a sliver that animates items when they are inserted or removed from a list.

demo示例地址:gitee.com/wywinstonwy…