Flutter中使用返回键返回时丢失BLoC状态的问题求助
问题描述
使用flutter_bloc: ^8.1.3和go_router: ^7.1.1开发Flutter应用,包含「展示条目」和「添加条目」两个页面。首次进入「展示条目」页面时,通过initState()调用BLoC加载数据,页面能正常展示内容;但跳转到「添加条目」页面后,点击返回键回到「展示条目」页面时,页面显示空白。排查发现此时BlocConsumer执行了else分支,返回SizedBox.shrink()。
相关代码
display_items_screen.dart
class DisplayItemsScreen extends StatefulWidget { const DisplayItemsScreen({super.key}); @override State<DisplayItemsScreen> createState() => _DisplayItemsScreenState(); } class _DisplayItemsScreenState extends State<DisplayItemsScreen> { @override void initState() { super.initState(); context.read<DisplayItemsBloc>().add(DisplayItemsLoadEvent()); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => context.push("/add-items"), child: const Icon(Icons.add), ), appBar: AppBar( title: const Text("Display Items"), ), body: BlocConsumer<DisplayItemsBloc, DisplayItemsState>( listener: (context, state) { if (state is DisplayItemsErrorState) { showSnackBar("Something went wrong! please try again."); } }, builder: (context, state) { if (state is DisplayItemsLoadingState) { return const LoadingView(); } else if (state is DisplayItemsLoadedState) { final categories = state.userProductCategories; return DisplayItem(categories: categories); // stateless widget GridView.builder() } else { // 返回时执行此分支 return SizedBox.shrink(); } }, ), ); } }
add_items_screen.dart
class AddItemsScreen extends StatefulWidget { const AddItemsScreen({super.key}); @override State<AddItemsScreen> createState() => _AddItemsScreenState(); } class _AddItemsScreenState extends State<AddItemsScreen> { @override void initState() { super.initState(); context.read<AddItemsBloc>().add(AddItemsLoadEvent()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Add Items"), ), body: BlocConsumer<AddItemsBloc, AddItemsState>( listener: (context, state) { if (state is AddItemsErrorState) { showSnackBar("Something went wrong! please try again."); } }, builder: (context, state) { if (state is AddItemsLoadingState) { return const LoadingView(); } else if (state is AddItemsLoadedState) { final items = state.loadedItems; return AddItem(categories: items); } else { return SizedBox.shrink(); } }, ), ); } }
router.dart
final navigatorKey = GlobalKey<NavigatorState>(); class AppRoutes { static routes() { return GoRouter( initialLocation: "/", navigatorKey: navigatorKey, routes: <RouteBase>[ GoRoute( path: "/display-items", pageBuilder: (BuildContext _, GoRouterState state) { return CupertinoPage( child: const DisplayItemsScreen(), key: state.pageKey, restorationId: state.pageKey.value, ); }, ), GoRoute( path: "/add-items", pageBuilder: (BuildContext _, GoRouterState state) { return CupertinoPage( child: const AddItemsScreen(), key: state.pageKey, restorationId: state.pageKey.value, ); }, ) ], ); } }
bloc_providers.dart
class BlocProviders { final DisplayItemsRepository _displayItemsRepository = DisplayItemsRepository(); final AddItemsRepository _addItemsRepository = AddItemsRepository(); blocs() { return [ BlocProvider<DisplayItemsBloc>( create: (context) => DisplayItemsBloc(repository: _displayItemsRepository), ), BlocProvider<AddItemsBloc>( create: (context) => AddItemsBloc(repository: _addItemsRepository), ), ]; } }
main.dart
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Hive.initFlutter(); final BlocProviders blocProviders = BlocProviders(); runApp( MultiBlocProvider( providers: blocProviders.blocs(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { MyApp({super.key}); final _router = AppRoutes.routes(); @override Widget build(BuildContext context) { return MaterialApp.router( scaffoldMessengerKey: snackbarKey, debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: ThemeMode.light, routerConfig: _router, // routeInformationParser: AppRoutes.routes().routeInformationParser, // routeInformationProvider: AppRoutes.routes().routeInformationProvider, // routerDelegate: AppRoutes.routes().routerDelegate, ); } }
问题原因与解决办法
原因
返回「展示条目」页面时,页面未重新触发数据加载,且BLoC回到了未定义的初始状态,导致BlocConsumer进入else分支返回空白。
解决办法
1. 监听页面可见状态重新加载数据
给_DisplayItemsScreenState混入WidgetsBindingObserver,监听页面恢复可见时重新触发加载事件:
class _DisplayItemsScreenState extends State<DisplayItemsScreen> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); context.read<DisplayItemsBloc>().add(DisplayItemsLoadEvent()); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { // 页面重新可见时加载数据 context.read<DisplayItemsBloc>().add(DisplayItemsLoadEvent()); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } // 原build方法保持不变 }
2. 为BLoC设置初始状态
在DisplayItemsBloc构造函数中初始化状态,避免进入else分支:
class DisplayItemsBloc extends Bloc<DisplayItemsEvent, DisplayItemsState> { final DisplayItemsRepository repository; DisplayItemsBloc({required this.repository}) : super(DisplayItemsLoadingState()) { on<DisplayItemsLoadEvent>((event, emit) async { // 原有加载逻辑 }); } }
内容的提问来源于stack exchange,提问作者Bhuvana Chandra
相关产品推荐
相关产品推荐

