基于Animation实现带动画的展开列表组件

封装带文字的小图标

smallIconText(IconData iconData, var text) {
  var style = TextStyle(fontSize: 12, color: Colors.grey);
  if (text is int) {
    text = countFormat(text);
  }
  return [
    Icon(iconData, color: Colors.grey, size: 12,),
    Text('$text', style: style,)
  ]
}

带动画的展开列表组件

expandable_content.dart


// 可展开的 Widget
class ExpandbleContent extends StatefulWidget {
  final VideoMo mo;

  const ExpandbleContent({Key key, this.mo}) : super(key: key);

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

class _ExpandbleContentState extends State<ExpandbleContent>
    with TickerProviderStateMixin {
  static final Animatable<double> _easeInTween =
      CurveTween(curve: Curves.easeIn);

  // 是否展开
  bool _expand = false;

  // 用来管理 Animation
  AnimationController _controller;

  // 生成动画高度的值
  Animation<double> _heightFactor;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(duration: Duration(milliseconds: 200), vsync: this);
    _heightFactor = _controller.drive(_easeInTween);
    _controller.addListener(() {
      // 监听动画值的变化
      print(_heightFactor.value);
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(left: 15, right: 15, top: 5),
      child: Column(
        children: [
          _buildTitle(),
          Padding(padding: EdgeInsets.only(bottom: 8)),
          _buildInfo(),
          _buildDes()
        ],
      ),
    );
  }

  _buildTitle() {
    return InkWell(
      onTap: _toggleExpand,
      child: Row(
        // 主轴两端对齐
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        // 交叉轴左对齐
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Expanded: 获得最大宽度,以便显示省略号
          Expanded(
              child: Text(widget.mo.title,
                  maxLines: 1, overflow: TextOverflow.ellipsis)),
          Padding(padding: EdgeInsets.only(left: 15)),
          Icon(
            _expand
                ? Icons.keyboard_arrow_up_sharp
                : Icons.keyboard_arrow_down_sharp,
            color: Colors.grey,
            size: 16,
          )
        ],
      ),
    );
  }

  void _toggleExpand() {
    setState(() {
      _expand = !_expand;
      if (_expand) {
        // 执行动画
        _controller.forward();
      } else {
        // 反向执行动画
        _controller.reverse();
      }
    });
  }

  _buildInfo() {
    var style = TextStyle(fontSize: 12, color: Colors.grey);
    var dateStr = widget.mo.createTime.length > 10
        ? widget.mo.createTime.substring(5, 10)
        : widget.mo.createTime;
    return Row(
      children: [
        ...smallIconText(Icons.ondemand_video, widget.mo.view),
        Padding(padding: EdgeInsets.only(left: 10)),
        ...smallIconText(Icons.ondemand_video, widget.mo.view),
        Text(
          '  $dateStr',
          style: style,
        )
      ],
    );
  }

  _buildDes() {
    var child = _expand
        ? Text(widget.mo.desc,
            style: TextStyle(fontSize: 12, color: Colors.grey))
        : null;
    return AnimatedBuilder(
        animation: _controller.view,
        builder: (BuildContext context, Widget child) {
          return Align(
            heightFactor: _heightFactor.value,
            // fix 从布局之上的位置开始展开
            alignment: Alignment.topCenter,
            child: Container(
              // 撑满宽度后,让内容对齐
              alignment: Alignment.topLeft,
              padding: EdgeInsets.only(top: 8),
              child: child,
            ),
          );
        });
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值