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

Flutter Bloc模式下拉加载更多后UI未更新问题求助

解决Bloc加载更多数据后UI不更新的问题

我仔细看了你的代码,发现几个核心问题导致加载更多数据后UI没有更新,且加载指示器持续显示:

问题分析

1. UI中硬编码加载更多触发条件

你的ListView.builder里用i >= 5来判断是否显示BottomLoader,这是硬编码的固定值,不管实际列表长度是多少,只要索引超过5就显示加载器。这会导致即使列表已经加载了更多数据,加载器仍然会一直显示,同时无法正确匹配实际的列表长度。

2. Bloc中复用同一List实例导致状态未更新

你在Bloc里维护了一个全局的propertyList,每次加载更多都是往这个List里添加元素,然后直接把这个List传入AllProjectsLoadedState。由于Equatable是通过比较对象引用来判断状态是否变化的,同一个List引用即使内容改变,Equatable会认为状态没有变化,Bloc也就不会通知UI更新。

3. 加载更多时的状态处理不完整

加载更多的过程中没有处理错误情况,且没有利用copyWith方法来创建新状态(虽然你定义了copyWith,但之前的代码没有使用),这也会导致状态更新逻辑不严谨。


修复方案

第一步:修复UI中的加载器判断逻辑

把硬编码的i >=5改成根据列表动态长度判断,只有当索引等于当前列表长度时(也就是最后一个item)才显示加载器:

Flexible(
  child: Container(
    child: BlocConsumer<AllProjectsBloc, AllProjectsState>(
      builder: (context, state) {
        if (state is AllProjectsLoadingState) {
          return Center(
            child: CircularProgressIndicator(),
          );
        } else if (state is AllProjectsLoadedState) {
          return ListView.builder(
            controller: _scrollController,
            itemCount: state.hasReachedMax ? state.allProjectsData.length : state.allProjectsData.length + 1,
            itemBuilder: (context, int i) {
              // 动态判断:当索引等于列表长度时显示加载器(仅当未加载完所有数据时)
              if (i == state.allProjectsData.length) {
                return BottomLoader();
              }
              final project = state.allProjectsData[i];
              return UnitCard(
                price: project.price,
                date: project.title.en,
                image: project.image,
                bathroom: project.bathroom,
                bedroom: project.bedroom,
                area: project.area,
                function: () {
                  Navigator.push(context, MaterialPageRoute(builder: (context)=>DetailedProperty()));
                }
              );
            },
          );
        } 
        // 其他状态判断保持不变
        else if (state is FilteredProjectsLoadedState) {
          return Expanded(
            child: ListView.builder(
              itemCount: state.filteredProjectsData.length,
              itemBuilder: (context, i) {
                return UnitCard(
                  price: 50,
                  date: '20/5/2020',
                  bedroom: 3,
                  bathroom: 2,
                  area: 120,
                  image: 'https://www.propertyturkey.com/uploads/realestate/larg/buyukcekmece_villa_1_8.jpg',
                  function:(){
                    Navigator.push(context, MaterialPageRoute(builder: (context)=>DetailedProperty()));
                  } ,
                );
              },
            ));
        } else if (state is AllProjectsError) {
          return ErrorView(
            errorMessage: state.error.errorMessage,
            retryAction: () {
              BlocProvider.of<AllProjectsBloc>(context)
                .add(state.failedEvent);
            });
        }
        return Container();
      },
    )
  )
)

第二步:修复Bloc中的状态更新逻辑

移除全局的propertyList,每次加载更多时创建新的List实例,确保状态引用变化,同时使用copyWith方法来生成新状态:

class AllProjectsBloc extends Bloc<AllProjectsEvents, AllProjectsState> {
  // 移除全局的propertyList
  AllProjectsBloc() : super(AllProjectsInitialState());
  
  bool _hasReachedMax(AllProjectsState state) => 
      state is AllProjectsLoadedState && state.hasReachedMax;

  @override
  Stream<AllProjectsState> mapEventToState(AllProjectsEvents event) async* {
    bool isUserConnected = await NetworkUtilities.isConnected();
    if (!isUserConnected) {
      yield AllProjectsError(
        failedEvent: event,
        error: Constants.CONNECTION_TIMEOUT);
      return;
    }
    if (event is FetchAllProjectsData && !_hasReachedMax(state)) {
      yield* _handleFetchingAllProject(event);
    }
    if (event is FetchFilteredProjectsData) {
      yield* _handleFetchingFilteredProject(event);
      return;
    }
  }

  Stream<AllProjectsState> _handleFetchingAllProject(
      FetchAllProjectsData event) async* {
    if (state is AllProjectsInitialState) {
      yield AllProjectsLoadingState();
      final response = await Repository.getAllPropertiesData(1);
      if (response.isSuccess) {
        yield AllProjectsLoadedState(
          allProjectsData: response.responseData ?? [],
          hasReachedMax: false,
        );
      } else {
        // 新增错误处理
        yield AllProjectsError(
          failedEvent: event,
          error: response.errorViewModel,
        );
      }
    } else if (state is AllProjectsLoadedState) {
      final response = await Repository.getAllPropertiesData(2);
      if (response.isSuccess) {
        // 创建新的List实例,合并旧数据和新数据
        final updatedList = List<Data>.from(state.allProjectsData)
          ..addAll(response.responseData ?? []);
        // 根据返回的新数据是否为空,判断是否到达最后一页
        final hasReachedMax = response.responseData?.isEmpty ?? true;
        // 使用copyWith生成新状态
        yield state.copyWith(
          allProjectsData: updatedList,
          hasReachedMax: hasReachedMax,
        );
        print(updatedList.length);
      } else {
        yield AllProjectsError(
          failedEvent: event,
          error: response.errorViewModel,
        );
      }
    }
  }

  Stream<AllProjectsState> _handleFetchingFilteredProject(
      FetchFilteredProjectsData event) async* {
    yield AllProjectsLoadingState();
    final response = await Repository.getAllPropertiesData(1);
    if (response.isSuccess) {
      yield FilteredProjectsLoadedState(response.responseData ?? []);
    } else {
      yield AllProjectsError(
        failedEvent: event,
        error: response.errorViewModel);
    }
  }
}

第三步:确保State类的Equatable配置正确

确认你的AllProjectsState抽象类正确继承Equatable:

abstract class AllProjectsState extends Equatable {
  const AllProjectsState();

  @override
  List<Object> get props => [];
}

你的AllProjectsLoadedState已经正确传递了props到父类,这部分不需要修改:

class AllProjectsLoadedState extends AllProjectsState {
  final List<Data> allProjectsData;
  final bool hasReachedMax ;
  
  AllProjectsLoadedState({this.allProjectsData , this.hasReachedMax}) 
    : super([allProjectsData , hasReachedMax]);

  AllProjectsLoadedState copyWith ({List<Data> allProjectsData , bool hasReachedMax }){
    return AllProjectsLoadedState(
      allProjectsData: allProjectsData ?? this.allProjectsData ,
      hasReachedMax:hasReachedMax ?? this.hasReachedMax
    );
  }
}

修复原理说明

  1. 动态加载器判断:通过i == state.allProjectsData.length来判断是否显示加载器,确保只有当还有更多数据需要加载时,才在列表末尾显示加载器,且不会硬编码固定值。
  2. 新List实例触发状态更新:每次加载更多时创建新的List,确保AllProjectsLoadedState中的allProjectsData是新的引用,Equatable会识别到状态变化,从而通知Bloc更新UI。
  3. 完善错误处理:在加载数据的各个分支都添加错误处理,避免出现未处理的异常导致UI卡住。

内容的提问来源于stack exchange,提问作者AnAs Ezzat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:37:36