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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:55:17