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(), ], ); }, )
关键修改点
- 预计算页码:在
initState中一次性计算所有元素对应的页码,存储在_pageIndices列表中,避免在itemBuilder中修改状态。 - 无状态判断:在
itemBuilder中仅通过索引读取预计算的页码,比较当前与前一个元素的页码决定是否显示分页Tile,逻辑无副作用。 - 避免可变状态:移除了原代码中
pageCounter、oldPageCounter等可变成员变量,彻底解决状态混乱问题。
内容的提问来源于stack exchange,提问作者Damandroid
相关产品推荐
相关产品推荐

