使用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
相关产品推荐
相关产品推荐

