You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter ListView中添加对应未付款人数的dummy占位组件

Flutter ListView追加未付款用户占位组件实现方案

问题场景

通过JSON API返回的已付款用户列表渲染横向ListView,接口同时返回剩余未付款人数numberOfUserLeft,需要在已付款用户条目后追加dummy占位组件,展示剩余未付款用户状态,参考效果如下:
付款进度列表效果参考

原有实现存在三个核心问题:

  • itemCount仅计算了已付款用户的长度,没有预留占位组件的位置
  • itemBuilder没有分支判断逻辑,无法区分渲染真实用户和占位组件
  • 冗余布局问题:原有itemBuilder中每个条目内重复写了两个完全一致的用户头像组件,会导致单条目显示重复内容

实现步骤

  1. 初始化变量时设置空安全默认值,避免初始加载阶段空报错
// 初始化时给默认值
List data = [];
int numberOfUserLeft = 0;
bool isLoadingActivity = false;
  1. 接口数据解析时把人数转成整数类型,避免接口返回字符串类型导致计算错误
this.setState(() {
  data = json.decode(response.body);
  isLoadingActivity = false;
  // 转成int类型,兼容接口返回字符串数字的场景
  numberOfUserLeft = int.parse(data[0]['userCount'].toString());
});
  1. 修改ListView配置,调整itemCount总长度,在itemBuilder中分支渲染真实用户和占位组件
ListView.builder(
  scrollDirection: Axis.horizontal,
  // 总条目数 = 已付款用户数 + 未付款占位数量
  // 如果只需要1个组件显示总剩余人数,把numberOfUserLeft改成1即可
  itemCount: data.length + numberOfUserLeft,
  itemBuilder: (context, position) {
    // 索引在已付款用户范围内:渲染真实已付款用户组件
    if (position < data.length) {
      return Container(
        padding: EdgeInsets.all(10),
        child: Container(
          padding: EdgeInsets.all(4),
          child: Stack(
            clipBehavior: Clip.none,
            children: [
              CircleAvatar(
                radius: 60,
                backgroundColor: colorGreen,
                child: Container(
                  padding: EdgeInsets.all(4),
                  margin: EdgeInsets.all(3),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    shape: BoxShape.circle,
                  ),
                  child: ClipOval(
                    child: Container(
                      height: 250,
                      width: 250,
                      child: data[position]["profile_img"] != null
                          ? Image.network(
                              data[position]["profile_img"],
                              fit: BoxFit.cover,
                            )
                          : Image.asset("assets/person_icon.png"),
                    ),
                  ),
                ),
              ),
              Positioned(
                bottom: 100,
                right: 45,
                child: InkWell(
                  onTap: () {},
                  child: Center(
                    child: CircleAvatar(
                      backgroundColor: colorGreen,
                      radius: 15.0,
                      child: Icon(
                        Icons.check,
                        color: Colors.white,
                        size: 25,
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      );
    }
    // 索引超出已付款用户范围:渲染占位dummy组件
    else {
      // 判断是否是最后一个占位项,最后一个项显示剩余人数
      bool isLastPlaceHolder = position == data.length + numberOfUserLeft - 1;
      return Container(
        padding: EdgeInsets.all(10),
        child: Container(
          padding: EdgeInsets.all(4),
          child: CircleAvatar(
            radius: 60,
            backgroundColor: Colors.grey[300],
            child: Container(
              padding: EdgeInsets.all(4),
              margin: EdgeInsets.all(3),
              decoration: BoxDecoration(
                color: Colors.white,
                shape: BoxShape.circle,
              ),
              child: ClipOval(
                child: Container(
                  height: 250,
                  width: 250,
                  alignment: Alignment.center,
                  child: isLastPlaceHolder
                      ? Text(
                          "+$numberOfUserLeft",
                          style: TextStyle(
                            fontSize: 32,
                            fontWeight: FontWeight.bold,
                            color: Colors.grey[600],
                          ),
                        )
                      : Image.asset(
                          "assets/person_icon.png",
                          color: Colors.grey[400],
                          fit: BoxFit.cover,
                        ),
                ),
              ),
            ),
          ),
        ),
      );
    }
  },
)

自定义调整说明

  • 如果不需要给每个未付款用户都渲染单独的灰色头像占位,仅需要一个组件显示剩余总人数,直接把itemCount改成data.length + 1即可,不需要判断isLastPlaceHolder,直接渲染带+numberOfUserLeft文字的组件就行
  • 占位组件的样式可以根据设计稿调整尺寸、颜色、文字大小
  • 如果需要给占位组件添加点击交互(比如点击跳转邀请付款页面),直接给占位组件的InkWell添加对应onTap逻辑即可

内容的提问来源于stack exchange,提问作者mideveloper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:48:24