flutterdartarchitectureblocgorouter

Using router to navigate directly instead of using context in async callback?


Here's how my project is build:

I have a router.dart file where the routing is managed

class MainRouter {

GoRouter router = GoRouter {
//...
}
}

I pass this GoRouter object into my MaterialApp.router-Widget:

class MyApp extends StatelessWidget {
  MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
routerConfig: MainRouter.router
//..
)
}

I have the BuildContext as an attribute at my AppEvent.

class AppEvent extends Equatable {
  final BuildContext context;

  const AppEvent(this.context);

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

Now, I have an asynchronous callback function in my AppBloc where i need to delete something from the database first and then navigate to the home screen.

 void _onAsyncFunction(AppEvent event, Emitter<AppState> emit) async {
    
    await _deleteDatabase();

    event.context.go("/home");
  }

The problem is, I get following lint issue in my project saying: "Don't use 'BuildContext's across async gaps."

I know, according to this question that i can just check with event.context.mounted to be safe the BuildContext is mounted, but i still have that lint issue.

I tried to access the MainRouter.router directly and call MainRouter.router.go("/home") and that worked, but would that violate any architectural rules either of the GoRouter or the Bloc-Architecture?

Thanks for your insights!


Solution

  • Option 1

    You could use a BlocConsumer to listen for specific BLoC states, and define a state that would indicate you should go to that route.

    class AppPopState extends AppState {
      const AppPopState()
    }
    

    An then in your BLoC return that state:

     void _onAsyncFunction(AppEvent event, Emitter<AppState> emit) async {
        
        await _deleteDatabase();
    
        emit(const AppPopState());
      }
    

    An listen to that state in your widget:

    Widget build(context) {
     return BlocConsumer(
       listenWhen: (_, state) => state is AppPopState,
       listener: (ctx, statte) {
           context.go("/home");
         },
       ...
    
    

    Option 2

    Defined a global key for your GoRouter, and use it to navigate from your BLoC.

    /// Root navigator key.
    final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
    
    /// Navigation router.
    final GoRouter kRouter = new GoRouter(
      navigatorKey: rootNavigatorKey,
      ...
    
     void _onAsyncFunction(AppEvent event, Emitter<AppState> emit) async {
        
        await _deleteDatabase();
    
        rootNavigatorKey.currentContext!.go("/home");
      }