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

Flutter中ListView Builder无法正常渲染无限滚动数据问题求助

Flutter Provider无限滚动分页不实时渲染问题修复

问题

使用Provider做状态管理实现无限滚动分页时,出现以下异常:

  • 应用启动后,API成功加载数据,但ListView不会立即展示内容,需切换到其他页面再返回才显示
  • 滚动到底部加载更多数据后,新数据同样需要切换页面才能渲染

核心原因

  1. 页面未监听Provider状态变化:HomePage在initState中获取Provider实例后,直接在build里使用该实例,没有通过Consumer或带listen: true的Provider.of监听状态更新,导致Provider调用notifyListeners()时页面不会重建。
  2. Provider状态更新逻辑不严谨:
    • loadMoreData中开始加载时未通知状态,加载完成后的通知逻辑存在遗漏场景
    • refreshData中直接调用listOfUserData.clear()未触发通知,且方法标记为async却无异步操作
  3. ListView的itemCount计算错误:未考虑加载更多时的加载提示项,导致无法显示加载状态

修复代码

1. 修改HomePage,添加状态监听

用Consumer包裹ListView,确保状态更新时页面自动重建:

import 'package:end_point/providers/user_info_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _scrollController = ScrollController();
  late UserInfoProvider userInfoProvider;

  @override
  void initState() {
    super.initState();
    userInfoProvider = Provider.of<UserInfoProvider>(context, listen: false);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      userInfoProvider.loadMoreData();
    });
    _scrollController.addListener(_onScroll);
  }

  Future<void> _onScroll() async {
    if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent * 0.9 &&
        !userInfoProvider.isLoading &&
        userInfoProvider.hasMore) {
      await userInfoProvider.loadMoreData();
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  Future refresh() async {
    userInfoProvider.refreshData();
    await userInfoProvider.loadMoreData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("END POINT LEGGO")),
      body: Consumer<UserInfoProvider>(
        builder: (context, provider, child) {
          return RefreshIndicator(
            key: UniqueKey(),
            onRefresh: refresh,
            child: ListView.builder(
              key: const PageStorageKey<String>('HP'),
              itemCount: provider.listOfUserData.length + (provider.hasMore && !provider.isLoading ? 1 : 0),
              controller: _scrollController,
              shrinkWrap: true,
              itemBuilder: (context, index) {
                if (index < provider.listOfUserData.length) {
                  final id = provider.listOfUserData[index].id;
                  return Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: Container(
                      height: 50,
                      width: 200,
                      decoration: const BoxDecoration(
                        borderRadius: BorderRadius.all(Radius.circular(20)),
                        color: Colors.red,
                      ),
                      child: ListTile(title: Text('$id')),
                    ),
                  );
                } else {
                  return Padding(
                    padding: const EdgeInsets.symmetric(vertical: 32),
                    child: Center(
                      child: provider.isLoading
                          ? const CircularProgressIndicator()
                          : const Text('No more data to load'),
                    ),
                  );
                }
              },
            ),
          );
        },
      ),
    );
  }
}

2. 修正UserInfoProvider的状态更新逻辑

import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:end_point/models/UserInfo.dart';
import 'package:end_point/api/user_info_api.dart' as UserInfoAPI;
import 'package:end_point/models/API/user_info_response.dart';

class UserInfoProvider with ChangeNotifier {
  int _page = 1;
  int get page => _page;

  bool _isLoading = false;
  bool get isLoading => _isLoading;

  List<UserInfo> _listOfUserData = [];
  List<UserInfo> get listOfUserData => _listOfUserData;
  set listOfUserData(List<UserInfo> listOfUserData) {
    _listOfUserData = listOfUserData;
    notifyListeners();
  }

  bool _hasMore = true;
  bool get hasMore => _hasMore;

  Future<void> loadMoreData() async {
    if (_isLoading || !_hasMore) return;

    _isLoading = true;
    notifyListeners(); // 通知开始加载

    try {
      ListOfUserInfoResponse response =
          await UserInfoAPI.getListOfUserInfo(_page);
      _listOfUserData.addAll(response.listOfUserInfo);
      _page++;
      // API的_limit是15,所以判断长度小于15时表示没有更多数据
      if (response.listOfUserInfo.length < 15) {
        _hasMore = false;
      }
    } catch (e) {
      debugPrint('加载失败: $e');
    } finally {
      _isLoading = false;
      notifyListeners(); // 通知加载完成
    }
  }

  void refreshData() {
    _isLoading = false;
    _hasMore = true;
    _page = 1;
    _listOfUserData.clear();
    notifyListeners(); // 手动通知列表清空
  }
}

关键修复点说明

  • 添加状态监听:通过Consumer让HomePage感知Provider的状态变化,确保数据更新时自动刷新UI
  • 完善状态通知:在loadMoreData的加载开始、结束阶段都调用notifyListeners(),同时处理异常场景
  • 修正itemCount:增加加载提示项的计数,让ListView能显示加载状态
  • 优化滚动触发逻辑:提前90%位置触发加载,提升用户体验

内容的提问来源于stack exchange,提问作者Lim Jiexian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 21:20:33