How to use build context outside of Flutter widgets
Tags: flutter, mobile, tutorial, showdev
Published: 2022-12-16 16:00:28

## Short answer:

If you need to use `BuildContext` outside of Flutter widgets use the package created based on ideas of this article:

[https://pub.dev/packages/build_context_provider](https://pub.dev/packages/build_context_provider)

## Long answer:

When I started working with Flutter one of the most confusing things for me were inability to use methods that required `BuildContext` outside of flutter widgets. 

Lets say you want to have a reusable class that navigates to a certain page of the app. You might want to use that class without a "context" argument. For example, in Clean Architecture use cases must never rely on any framework or library specific code. Since `BuildContext` is specific to Flutter framework this code is invalid in Clean Architecture:

```dart
class NavigateToProfileUseCase {
  call(BuildContext context) {
    Navigator.of(context).pushNamed('/profile');
  }
}
```

### What is the solution?

*Note: the explanation of the solution is going to sound somewhat confusing. Please bear with me. It is going to get clearer by the end of the article.*

In order to fix this problem we must change the way we call these functions. Instead calling functions directly we must pass these functions to the special widget that is going to run in UI layer.

Here is what we must do:

1. We must create a special class which is going to hold a function. Let’s call it a `Publisher`. This can be a Cubit/Bloc, a `Stream` or a `ChangeNotifier` or any other reactive class.
2. We must create a special widget that is going to listen to the changes of the `Publisher`.
3. This widget must invoke functions pushed to the `Publisher` with the latest `BuildContext` instance.

Let’s take a look at an example:

```dart
class NavigateToProfileUseCase {
	call() { // <- notice how we don't pass BuildContext anymore
		functionRunnerCubit.runFunction(
	    (context) => Navigator.of(context).pushNamed('/profiile'),
	  );
	}
}

final functionRunnerCubit = FunctionRunnerCubit();

class FunctionRunnerCubit extends Cubit<FunctionRunnerState> {
  FunctionRunnerCubit() : super(FunctionRunnerWithNoFunctionsToRun());

  void runFunction(Function(BuildContext context) VoidCallback functionToRun) => emit(
        FunctionRunnerWithFunctionToRun({function: functionToRun}),
      );
}

class FunctionRunner extends StatelessWidget {
  const FunctionRunner({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocListener<FunctionRunnerCubit, ContextListenerState>(
        listener: (buildContext, state) {
          if (state is FunctionRunnerWithFunctionToRun) {
            state.callback(buildContext);
          }
        },
        child: SizedBox(),
    );
  }
}
```

Explanation of the code:

Inside of the `UseCase` we pass a function that we want to run with a `BuildContext` to the `Cubit`.

The `Cubit` publishes this function to the listeners.

`FunctionRunner` widget listens to the changes of the `Cubit`.

The `BlocListener`(think of it simply as a `Listener` if you don’t know the “bloc” library) invokes the function with a latest instance of the `BuildContext`.

## In conclusion

This simple combination of a Publisher and a Listener will allow you to run your Flutter specific code anywhere. Such freedom will allow you to write simpler, cleaner and more maintainable code.