Search code examples
flutterdartflutter-blocflutter-navigationgorouter

Navigating back with back button is making lose BLoC state


Going back to previous screen by using back button is making lose BLOC State. Flutter

I'm currently using flutter_bloc: ^8.1.3 and go_router: ^7.1.1. I have two screens, one is display items screen and another one is add items screen. In display items screen i'm using initState() to load the items data with the help of bloc and it is working fine. But when i navigate to add items screen and click on back button, the display items screen is showing white screen but it was suppose to show the data. Here is my code.

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 {  // it is running this else condition when navigated using back button
            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,
    );
  }
}

Solution

  • Maybe In your DisplayItemsBloc code some state is triggering. If you provide your bloc code it would be good to understand.

    But you can use buildWhen function in your BlocConsumer to stop building unnecessary states:

    buildWhen: (previous, current) =>
                  current is DisplayItemsLoadingState || current is DisplayItemsLoadedState
    

    and you can make your builder function like this:

    builder: (context, state) {
           if (state is DisplayItemsLoadedState) {
            final categories = state.userProductCategories;
            return DisplayItem(categories: categories); 
          } else { 
            return const LoadingView();
          }
        },