用Flutter实现一个多级列表

2,104 阅读1分钟

前言

用flutter实现了一个可以展开、关闭的多级列表。借助AnimatedRotationAnimatedAlignClipRect实现展开、关闭动画。

使用

  • 可以在ExpandedListTile的child中添加任意自定义Widget
ExpandedListTile(
  onTap: () => print("onTap."),
  title: "点击展开列表",
  child: ListView.builder(
      physics: NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemCount: 10,
      itemBuilder: (_, index) {
        return ListTile(
          title: Text("这是第$index行"),
        );
      }),
),
  • 嵌套使用ExpandedListTile即可实现多级列表
ExpandedListTile(
  onTap: () => print("onTap."),
  title: "点击展开列表",
  child: ExpandedListTile(
    onTap: () => print("onTap."),
    title: "点击展开列表",
    child: ListView.builder(
        physics: NeverScrollableScrollPhysics(),
        shrinkWrap: true,
        itemCount: 10,
        itemBuilder: (_, index) {
          return ListTile(
            title: Text("这是第$index行"),
          );
        }),
  ),
),

运行

ExpandedListTile.gif

源码

class ExpandedListTile extends StatefulWidget {
  const ExpandedListTile(
      {Key? key, required this.title, this.child, this.onTap})
      : super(key: key);

  final String title;
  final Widget? child;
  final Function()? onTap;

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

class _ExpandedListTileState extends State<ExpandedListTile> {
  bool _isExpanded = false;
  static final Duration _animationDuration = Duration(milliseconds: 200);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        InkWell(
          onTap: () {
            setState(() {
              _isExpanded = !_isExpanded;
            });
            widget.onTap?.call();
          },
          child: Container(
            color: Color(0xFFEEEEEE),
            padding: EdgeInsets.fromLTRB(20, 16, 20, 16),
            child: Row(
              children: [
                Expanded(
                  child: Text(
                    widget.title,
                    style: TextStyle(color: Color(0xFF222222), fontSize: 15),
                  ),
                ),
                AnimatedRotation(
                  turns: _isExpanded ? 0 : -0.5,
                  duration: _animationDuration,
                  child: Icon(Icons.keyboard_arrow_down),
                ),
              ],
            ),
          ),
        ),
        ClipRect(
          child: AnimatedAlign(
            heightFactor: _isExpanded ? 1.0 : 0.0,
            alignment: Alignment.center,
            duration: _animationDuration,
            child: widget.child),
          ),
      ],
    );
  }
}