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

Flutter:ListView.builder特定间隔插自定义Tile及滚动页码异常问题

问题与解决方案

问题

在ListView.builder中实现按内容长度自动插入分页Tile,但滚动列表时,分页Tile上的页码会异常变化。

原代码

int pageCounter = 1;
int oldPageCounter = 0;
final int pageLength = 735;
int pageLengthLeft = 735;

ListView.builder(
  // ... 其他配置
  itemBuilder: (BuildContext context, int index) {
    adjustPageCounter(widget.mapOfProblems.values.elementAt(index), index);
    
    // ... 其他逻辑
    child: (oldPageCounter != pageCounter)
        ? Column(
            children: [
              getPageDivider(),
              MyUsualListTile()
            ])
        : MyUsualListTile()
  }
)

Widget getPageDivider() {
  oldPageCounter = pageCounter;
  return Container(
    padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
    width: double.infinity,
    color: Colors.grey[300],
    child: Align(alignment: Alignment.topRight,child: Text('Page $pageCounter'),),
  );
}

void adjustPageCounter(element, int index) {
  if (element is Note || element is Snag){
    if (pageLengthLeft < 165) resetPageLengthIncCounter();
    pageLengthLeft -= 165;
  }
  if (element is Photos) {
      if (pageLengthLeft < 250) resetPageLengthIncCounter();
      pageLengthLeft -= 250;
  }
}

void resetPageLengthIncCounter() { 
  pageLengthLeft = pageLength; 
  pageCounter++;
}

问题根源

核心问题在于pageCounter、pageLengthLeft等变量是可变的成员变量,而ListView.builder的itemBuilder会在列表滚动、组件重建时反复且无序地调用,每次调用都会修改这些变量,导致页码计算的状态混乱,最终出现滚动时页码跳变的问题。

解决方案

将页码计算逻辑改为预计算纯函数,避免在itemBuilder中修改状态变量,确保每个索引对应的页码结果固定。

修改后的代码

late List<int> _pageIndices;
final int _pageLength = 735;

@override
void initState() {
  super.initState();
  // 预先计算每个元素对应的页码
  _pageIndices = _calculatePageIndices();
}

List<int> _calculatePageIndices() {
  final elements = widget.mapOfProblems.values.toList();
  List<int> pageIndices = [];
  int currentPage = 1;
  int remainingLength = _pageLength;

  for (var element in elements) {
    pageIndices.add(currentPage);
    if (element is Note || element is Snag) {
      if (remainingLength < 165) {
        currentPage++;
        remainingLength = _pageLength;
      }
      remainingLength -= 165;
    } else if (element is Photos) {
      if (remainingLength < 250) {
        currentPage++;
        remainingLength = _pageLength;
      }
      remainingLength -= 250;
    }
  }
  return pageIndices;
}

// ListView.builder 实现
ListView.builder(
  itemCount: widget.mapOfProblems.length,
  itemBuilder: (context, index) {
    final currentPage = _pageIndices[index];
    final showDivider = index > 0 && currentPage != _pageIndices[index - 1];

    return Column(
      children: [
        if (showDivider)
          Container(
            padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
            width: double.infinity,
            color: Colors.grey[300],
            child: Align(
              alignment: Alignment.topRight,
              child: Text('Page $currentPage'),
            ),
          ),
        MyUsualListTile(),
      ],
    );
  },
)

关键修改点

  1. 预计算页码:在initState中一次性计算所有元素对应的页码,存储在_pageIndices列表中,避免在itemBuilder中修改状态。
  2. 无状态判断:在itemBuilder中仅通过索引读取预计算的页码,比较当前与前一个元素的页码决定是否显示分页Tile,逻辑无副作用。
  3. 避免可变状态:移除了原代码中pageCounter、oldPageCounter等可变成员变量,彻底解决状态混乱问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:31:02