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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:47:06