Flutter中Provider加载空列表时函数重复调用问题咨询
问题根源
当前实现的核心问题:
- 当数据库无事件时,
events始终为空,每次调用getAllEvents()后触发notifyListeners()导致页面重建;重建后又因events为空再次调用getAllEvents(),形成无限循环。 getAllEvents()每次调用都会向_events追加数据,后续有数据时会重复添加。- 每次调用都重新实例化
Datenbank,存在资源浪费。
优化后的Provider实现
class EventProvider extends ChangeNotifier { List<Events> _events = []; List<Events> get events => _events; // 加载状态,避免重复发起请求 bool _isLoading = false; bool get isLoading => _isLoading; // 复用数据库实例,建议Datenbank实现单例模式 final Datenbank _db = Datenbank(); Future<void> getAllEvents() async { // 正在加载时直接返回,防止重复请求 if (_isLoading) return; _isLoading = true; notifyListeners(); try { final data = await _db.getAllTermine(); // 清空旧数据再赋值,避免重复添加 _events = List.from(data); } catch (e) { print('加载事件失败: $e'); _events = []; } finally { _isLoading = false; notifyListeners(); } } }
页面实现优化(两种可选方案)
方案1:在StatefulWidget的initState中加载(适合首次加载场景)
class EventPage extends StatefulWidget { const EventPage({super.key}); @override State<EventPage> createState() => _EventPageState(); } class _EventPageState extends State<EventPage> { @override void initState() { super.initState(); // 仅在页面初始化时加载一次事件 Provider.of<EventProvider>(context, listen: false).getAllEvents(); } @override Widget build(BuildContext context) { final provider = Provider.of<EventProvider>(context); final events = provider.events; final isLoading = provider.isLoading; if (isLoading) { return const Center(child: CircularProgressIndicator()); } if (events.isEmpty) { return const Center(child: Text('暂无事件')); } // 渲染事件列表 return ListView.builder( itemCount: events.length, itemBuilder: (context, index) { return ListTile(title: Text(events[index].title)); // 替换为你的事件UI }, ); } }
方案2:使用FutureBuilder(支持手动触发刷新场景)
如果需要下拉刷新等手动触发加载的场景,可结合FutureBuilder实现:
@override Widget build(BuildContext context) { final provider = Provider.of<EventProvider>(context, listen: false); return RefreshIndicator( onRefresh: () => provider.getAllEvents(), child: FutureBuilder( future: provider.getAllEvents(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } final events = provider.events; if (events.isEmpty) { return const Center(child: Text('暂无事件')); } return ListView.builder( itemCount: events.length, itemBuilder: (context, index) { return ListTile(title: Text(events[index].title)); }, ); }, ), ); }
额外注意点
- 确保
Datenbank的getAllTermine()方法在无数据时返回空列表,而非抛出异常。 - 若需支持事件的增删改,可在对应操作后调用
getAllEvents()刷新列表,或直接更新_events并触发notifyListeners()。
内容的提问来源于stack exchange,提问作者Matthias Felder
相关产品推荐
相关产品推荐

