Flutter中如何为ListView.builder添加加载Spinner?
解决Flutter ListView懒加载同步计算的加载状态显示问题
核心问题
你的同步计算逻辑阻塞了主线程,导致EasyLoading的UI更新被卡住——主线程被耗时计算占用时,无法处理渲染请求,所以加载提示根本显示不出来。
解决方案
把耗时的同步计算移到**Isolate(隔离区)**中执行,让主线程专注于UI渲染。Flutter提供了compute函数简化Isolate的使用,无需手动管理隔离区通信。同时添加加载状态标记,防止重复触发计算。
修改后的完整代码
import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; class Matches extends StatefulWidget { // 替换为你的Iterator生成逻辑,这里用数据源列表示例(可根据实际情况调整) final List<String> dataSource; Matches(this.dataSource); @override MatchesState createState() => MatchesState(dataSource); } class MatchesState extends State<Matches> { final List<String> _dataSource; final _matches = <String>[]; int _currentOffset = 0; bool _isLoading = false; MatchesState(this._dataSource); Future<void> _next() async { // 防止重复加载或已加载完毕 if (_isLoading || _currentOffset >= _dataSource.length) return; _isLoading = true; EasyLoading.show(status: 'Searching...'); // 计算本次要加载的数量 final takeCount = 10; final endOffset = _currentOffset + takeCount; final targetItems = _dataSource.sublist( _currentOffset, endOffset > _dataSource.length ? _dataSource.length : endOffset ); // 使用compute将耗时计算移到Isolate执行 final newMatches = await compute(_processItems, targetItems); setState(() { _matches.addAll(newMatches); _currentOffset += newMatches.length; _isLoading = false; }); // 更新加载提示状态 if (_currentOffset >= _dataSource.length) { EasyLoading.showSuccess('搜索完成'); } else { EasyLoading.dismiss(animation: false); } } // 顶层/静态函数:用于在Isolate中执行耗时同步计算(不能是类实例方法) static List<String> _processItems(List<String> items) { final results = <String>[]; for (var item in items) { // 这里替换为你原来的耗时同步计算逻辑 // 示例模拟耗时操作 final processedItem = item.toUpperCase(); sleep(const Duration(milliseconds: 100)); results.add(processedItem); } return results; } Widget _buildMatches() { return ListView.builder( padding: const EdgeInsets.all(16.0), itemBuilder: (context, i) { if (i.isOdd) return const Divider(); final index = i ~/ 2; // 滚动到列表末尾时触发加载 if (index >= _matches.length && !_isLoading) { _next(); } // 加载中且已到现有列表末尾,显示本地加载指示器(可选) if (index >= _matches.length) { return const Center(child: CircularProgressIndicator()); } return ListTile(title: Text(_matches[index])); }, // 动态设置列表长度,让ListView知道有更多元素待加载 itemCount: _matches.length * 2 + (_isLoading ? 2 : 0), ); } @override Widget build(BuildContext context) { return FlutterEasyLoading( child: Scaffold( appBar: AppBar( title: const Text('Matches'), ), body: _buildMatches(), ), ); } }
关键调整点
- Isolate执行计算:用
compute把耗时的同步计算移到后台,主线程不阻塞,EasyLoading能正常显示。 - 加载状态控制:添加
_isLoading标记,避免滚动时重复触发加载请求。 - 列表长度动态化:
itemCount包含加载状态对应的占位元素,确保ListView能滚动到触发加载的位置。 - 兼容原有逻辑:
_processItems函数完全复用你原来的同步计算代码,无需修改核心逻辑。
内容的提问来源于stack exchange,提问作者Graham Wheeler
相关产品推荐
相关产品推荐

