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

使用Flutter_bloc返回上一页时显示加载指示器的问题修复咨询

问题描述

点击详情页返回按钮回到首页时,首页显示CircularProgressIndicator而非记录列表,只有点击AppBar的刷新按钮才能重新加载列表。经排查,返回时DatabaseBloc处于SelectedEntryLoaded状态,首页的BlocBuilder未处理该状态,因此进入else分支显示加载指示器。


首页(HomeView)代码

class HomeView extends StatelessWidget {
  const HomeView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<AuthenticationBloc, AuthenticationState>(
      listener: (context, state) {
        if (state is AuthenticationFailure) {
          Navigator.of(context).pushAndRemoveUntil(
              MaterialPageRoute(builder: (context) => const WelcomeView()),
              (Route<dynamic> route) => false);
        }
      },
      buildWhen: ((previous, current) {
        if (current is AuthenticationFailure) {
          return false;
        }
        return true;
      }),
      builder: (context, state) {
        return Scaffold(
            appBar: AppBar(
              automaticallyImplyLeading: false,
              actions: <Widget>[
                IconButton(
                    icon: const Icon(
                      Icons.logout,
                      color: Colors.white,
                    ),
                    onPressed: () async {
                      context
                          .read<AuthenticationBloc>()
                          .add(AuthenticationSignedOut());
                    }),
                IconButton(
                    icon: const Icon(
                      Icons.update,
                      color: Colors.white,
                    ),
                    onPressed: () async {
                      String? displayName = (context.read<AuthenticationBloc>().state
                      as AuthenticationSuccess)
                          .displayName;
                      context.read<DatabaseBloc>().add(DatabaseFetched(displayName));
                    })
              ],
              systemOverlayStyle:
                  const SystemUiOverlayStyle(statusBarColor: Colors.blue),
              title: Text((state as AuthenticationSuccess).displayName!),
            ),
            body: BlocBuilder<DatabaseBloc, DatabaseState>(
              builder: (context, state) {
                String? displayName = (context.read<AuthenticationBloc>().state
                        as AuthenticationSuccess)
                    .displayName;
                if (state is DatabaseSuccess &&
                    displayName !=
                        (context.read<DatabaseBloc>().state as DatabaseSuccess)
                            .displayName) {
                  context.read<DatabaseBloc>().add(DatabaseFetched(displayName));
                }
                if (state is DatabaseInitial) {
                  context.read<DatabaseBloc>().add(DatabaseFetched(displayName));
                  return const Center(child: CircularProgressIndicator());
                } else if (state is DatabaseSuccess) {
                  if (state.listOfEntryData.isEmpty) {
                    return const Center(
                      child: Text(Constants.textNoData),
                    );
                  } else {
                    return Center(
                      child: ListView.builder(
                        itemCount: state.listOfEntryData.length,
                        itemBuilder: (BuildContext context, int index) {
                          return Card(
                            child: ListTile(
                              title: Text(
                                  state.listOfEntryData[index].title!),
                              subtitle:
                              Text(state.listOfEntryData[index].date!),
                              onTap: () {
                                context.read<DatabaseBloc>().add(EntrySelected(index));
                                print('selectedIndex is $index');
                                Navigator.of(context).push(
                                    MaterialPageRoute(builder: (context) => const SelectedEntryView()));
                              },
                            ),
                          );
                        },
                      ),
                    );
                  }
                } else {
                  return const Center(child: CircularProgressIndicator());
                }
              },
            ),
            floatingActionButton: FloatingActionButton (
              onPressed: () {
                Navigator.of(context).pushAndRemoveUntil(
                    MaterialPageRoute(builder: (context) => const NewEntryView()),
                        (Route<dynamic> route) => false);
              },
              backgroundColor: Colors.blue,
              child: const Icon(Icons.add),
            )
        );
      },
    );
  }
}

详情页(SelectedEntryView)代码

class SelectedEntryView extends StatelessWidget {
  const SelectedEntryView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<DatabaseBloc, DatabaseState>(
      builder: (context, state) {
        if (state is SelectedEntryLoaded) {
         final int selectedIndex = state.selectedIndex;
         return Scaffold(
            body: SafeArea(
              child: Column(
                children: [
                  Text(state.listOfEntryData[selectedIndex].title!),
                  Text(state.listOfEntryData[selectedIndex].text!),
                  Text(state.listOfEntryData[selectedIndex].date!)
                ],
              ),
            ),
          );
        } else {
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

解决方案

有两种实用修复方式,任选其一即可:

方式一:在首页直接处理SelectedEntryLoaded状态

SelectedEntryLoaded状态已包含完整的记录列表,无需重新请求,直接复用数据渲染即可。修改首页的BlocBuilder逻辑:

body: BlocBuilder<DatabaseBloc, DatabaseState>(
  builder: (context, state) {
    String? displayName = (context.read<AuthenticationBloc>().state
            as AuthenticationSuccess)
        .displayName;
    if (state is DatabaseSuccess &&
        displayName !=
            (context.read<DatabaseBloc>().state as DatabaseSuccess)
                .displayName) {
      context.read<DatabaseBloc>().add(DatabaseFetched(displayName));
    }
    if (state is DatabaseInitial) {
      context.read<DatabaseBloc>().add(DatabaseFetched(displayName));
      return const Center(child: CircularProgressIndicator());
    } else if (state is DatabaseSuccess || state is SelectedEntryLoaded) {
      // 统一从两种状态中获取数据列表
      final list = state is DatabaseSuccess 
          ? state.listOfEntryData 
          : (state as SelectedEntryLoaded).listOfEntryData;
      
      if (list.isEmpty) {
        return const Center(
          child: Text(Constants.textNoData),
        );
      } else {
        return Center(
          child: ListView.builder(
            itemCount: list.length,
            itemBuilder: (BuildContext context, int index) {
              return Card(
                child: ListTile(
                  title: Text(list[index].title!),
                  subtitle: Text(list[index].date!),
                  onTap: () {
                    context.read<DatabaseBloc>().add(EntrySelected(index));
                    print('selectedIndex is $index');
                    Navigator.of(context).push(
                        MaterialPageRoute(builder: (context) => const SelectedEntryView()));
                  },
                ),
              );
            },
          ),
        );
      }
    } else {
      return const Center(child: CircularProgressIndicator());
    }
  },
),

方式二:返回首页时重置Bloc状态

通过监听详情页的返回事件,触发Bloc回到DatabaseSuccess状态:

步骤1:新增重置事件

在DatabaseBloc的事件类中添加DatabaseReset:

abstract class DatabaseEvent extends Equatable {
  const DatabaseEvent();

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

// 保留原有事件...
class DatabaseReset extends DatabaseEvent {}

步骤2:处理重置事件

在DatabaseBloc的状态转换逻辑中添加处理:

@override
Stream<DatabaseState> mapEventToState(DatabaseEvent event) async* {
  if (event is DatabaseFetched) {
    // 原有逻辑不变
  } else if (event is EntrySelected) {
    // 原有逻辑不变,确保SelectedEntryLoaded保留列表数据
    yield SelectedEntryLoaded(
      selectedIndex: event.index,
      listOfEntryData: (state as DatabaseSuccess).listOfEntryData,
      displayName: (state as DatabaseSuccess).displayName,
    );
  } else if (event is DatabaseReset) {
    if (state is SelectedEntryLoaded) {
      // 从选中状态转回成功状态
      yield DatabaseSuccess(
        listOfEntryData: state.listOfEntryData,
        displayName: state.displayName,
      );
    }
  }
}

步骤3:在详情页监听返回

修改SelectedEntryView添加返回监听:

class SelectedEntryView extends StatelessWidget {
  const SelectedEntryView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        // 返回首页前触发重置事件
        context.read<DatabaseBloc>().add(DatabaseReset());
        return true;
      },
      child: BlocBuilder<DatabaseBloc, DatabaseState>(
        builder: (context, state) {
          if (state is SelectedEntryLoaded) {
            final int selectedIndex = state.selectedIndex;
            return Scaffold(
              appBar: AppBar(
                leading: IconButton(
                  icon: const Icon(Icons.arrow_back),
                  onPressed: () {
                    context.read<DatabaseBloc>().add(DatabaseReset());
                    Navigator.pop(context);
                  },
                ),
              ),
              body: SafeArea(
                child: Column(
                  children: [
                    Text(state.listOfEntryData[selectedIndex].title!),
                    Text(state.listOfEntryData[selectedIndex].text!),
                    Text(state.listOfEntryData[selectedIndex].date!)
                  ],
                ),
              ),
            );
          } else {
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:24:49