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

Flutter ListView图片加载崩溃及无限滚动分页失效问题求助

修复Flutter中Firebase图片分页加载崩溃问题

原代码核心问题

  • 每次调用initSliderImages都会全量拉取galerytab1集合的所有图片链接,一次性加载过多图片直接导致内存溢出崩溃
  • 同时使用ScrollController的addListener和NotificationListener,造成加载逻辑重复触发
  • 没有利用Firestore的分页能力,完全没实现真正的分页加载逻辑

修复后的完整代码

class _CategoryTab1State extends State<CategoryTab1> {
  List<String> images = [];
  late ScrollController controller;
  final scaffoldKey = GlobalKey<ScaffoldState>();
  
  // 分页相关变量
  int _pageSize = 6; // 每页加载6张图片,可根据需求调整
  DocumentSnapshot? _lastDocument; // 记录最后一条文档,用于分页游标
  bool _isLoading = false; // 避免重复发起请求

  @override
  void initState() {
    super.initState();
    controller = ScrollController()..addListener(_scrollListener);
    _loadInitialImages(); // 加载第一页数据
    if (mounted) {
      context.read<CategoryTab1Bloc>().data.isNotEmpty
          ? print('data already loaded')
          : context.read<CategoryTab1Bloc>().getData(mounted, widget.category);
    }
  }

  @override
  void dispose() {
    controller.removeListener(_scrollListener);
    super.dispose();
  }

  void _scrollListener() {
    // 当滚动到距离底部200px以内时触发加载
    if (controller.position.extentAfter < 200 && !_isLoading) {
      _loadMoreImages();
    }
  }

  // 加载初始第一页数据
  Future<void> _loadInitialImages() async {
    setState(() => _isLoading = true);
    try {
      var query = FirebaseFirestore.instance
          .collection('galerytab1')
          .limit(_pageSize);
      
      var snapshot = await query.get();
      _processSnapshot(snapshot);
    } catch (e) {
      print('加载初始图片失败: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  // 加载更多分页数据
  Future<void> _loadMoreImages() async {
    if (_lastDocument == null) return; // 没有更多数据时直接返回
    setState(() => _isLoading = true);
    try {
      var query = FirebaseFirestore.instance
          .collection('galerytab1')
          .startAfterDocument(_lastDocument!)
          .limit(_pageSize);
      
      var snapshot = await query.get();
      _processSnapshot(snapshot);
    } catch (e) {
      print('加载更多图片失败: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  // 处理Firestore返回的快照,提取图片链接并追加到列表
  void _processSnapshot(QuerySnapshot snapshot) {
    if (snapshot.docs.isEmpty) return;
    
    List<String> newImages = snapshot.docs.map((doc) {
      return doc.data()?['url'] as String;
    }).toList();
    
    setState(() {
      images.addAll(newImages);
      _lastDocument = snapshot.docs.last; // 更新最后一条文档游标
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: controller,
      key: widget.key,
      itemCount: images.length + (_isLoading ? 1 : 0), // 加载时多显示一个加载项
      itemExtent: 250,
      itemBuilder: (context, index) {
        if (index == images.length) {
          // 加载中的占位widget
          return Center(child: CircularProgressIndicator());
        }
        return CachedNetworkImage(
          imageUrl: images[index],
          fit: BoxFit.cover,
          key: ValueKey(images[index]),
          // 可选:添加加载错误占位图
          errorWidget: (context, url, error) => Icon(Icons.error),
        );
      },
    );
  }
}

关键修改说明

  • 分页核心逻辑:使用Firestore的limit控制每页加载数量,startAfterDocument基于上次最后一条文档作为游标,实现真正的分页加载,避免一次性拉取全部数据
  • 请求防抖:通过_isLoading状态变量,避免滚动时重复发起请求
  • 滚动监听优化:仅保留ScrollController的监听,设置合理的触发阈值(200px),避免过早或频繁触发加载
  • 加载状态提示:在列表末尾添加加载指示器,提升用户体验
  • 错误处理:添加try-catch捕获请求异常,避免崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:25:12