Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 303 Vote(s) - 3.54 Average
  • 1
  • 2
  • 3
  • 4
  • 5
BlocProvider.of() called with a context that does not contain a Bloc - even that it does

#1
First of, I do know how BLoC suppose to work, the idea behind it and I know the difference between `BlocProvider()` and `BlocProvider.value()` constructors.

For simplicity, my application has 3 pages with a widget tree like this:

`App()` => `LoginPage()` => `HomePage()` => `UserTokensPage()`

I want my `LoginPage()` to have access to `UserBloc` because i need to log in user etc. To do that, I wrap `LoginPage()` builder at `App()` widget like this:
```Dart
void main() => runApp(App());

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
),
);
}
}
```

That obviously works just fine. Then, if *User* logs in successfully, he is navigated to `HomePage`. Now, I need to have access to two different blocs at my `HomePage` so I use `MultiBlocProvider` to pass existing `UserBloc` further and create a brand new one named `DataBloc`. I do it like this:

```Dart
@override
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
),
BlocProvider<DataBloc>(
create: (_) => DataBloc(DataRepository()),
),
],
child: HomePage(),
),
),
);
}
},
[...]
```

This also works. Problem happens when from `HomePage` *user* navigates to `UserTokensPage`. At `UserTokensPage` I need my already existing `UserBloc` that I want to pass with `BlocProvider.value()` constructor. I do it like this:

```Dart
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text('My App'),
actions: <Widget>[
CustomPopupButton(),
],
),

[...]

class CustomPopupButton extends StatelessWidget {
const CustomPopupButton({
Key key,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
icon: Icon(Icons.more_horiz),
onSelected: (String choice) {
switch (choice) {
case PopupState.myTokens:
{
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
}
break;
case PopupState.signOut:
{
BlocProvider.of<UserBloc>(context).add(SignOut());
Navigator.of(context).pop();
}
}
},
[...]
```

When I press button to navigate to `MyTokensPage` i get error with message:
```
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Builder(dirty):
BlocProvider.of() called with a context that does not contain a Bloc of type UserBloc.

No ancestor could be found starting from the context that was passed to BlocProvider.of<UserBloc>().

This can happen if:
1. The context you used comes from a widget above the BlocProvider.
2. You used MultiBlocProvider and didn't explicity provide the BlocProvider types.

Good: BlocProvider<UserBloc>(create: (context) => UserBloc())
Bad: BlocProvider(create: (context) => UserBloc()).

The context used was: CustomPopupButton
```

What am I doing wrong? Is it because i have extracted `PopupMenuButton` widget that somehow loses blocs? I don't understand what I can be doing wrong.
Reply

#2
# Solution
## Method A: Access `UserBloc` provider instance directly without passing it
I prefer this solution since it requires less code.
### A.1 Wrap `CustomPopupButton` instance with provider [Consumer<T>][1] so it rebuilds itself whenever UserBloc notifies listeners of value changes.
Change this:
```dart
actions: <Widget>[
CustomPopupButton(),
],
```
To:
```dart
actions: <Widget>[
Consumer<UserBloc>(builder: (BuildContext context, UserBloc userBloc, Widget child) {
return CustomPopupButton(),
});
],
```
### A.2 Change Provider instance invocation inside the stateless widget to disable listening to value changes -- "listening" and resulting "rebuilds" are already done by `Consumer`.
**A.2.1** Change this:
```dart
value: BlocProvider.of<UserBloc>(context),
```
To:
```dart
value: BlocProvider.of<UserBloc>(context, listen: false),
```
**A.2.2** And change this:
```dart
BlocProvider.of<UserBloc>(context).add(SignOut());
```
To:
```dart
BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
```

## Method B: pass `UserBloc` provider instance
Same thing as Method A, but:

- In A.1 you'd pass `userBloc` like this: `return CustomPopupButton(userBloc: userBloc),`.
- You'd declare `final UserBloc userBloc;` member property inside `CustomPopupButton`.
- In A.2 you'd do this: `userBloc.add(SignOut());` instead of `BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());`

# Explanation

`flutter_bloc` is using [`Provider`][2], to be aware what's going on it's better understand Provider. Please refer to my answer [here][3] to understand my answer to your question, and to understand Provider and `listen` flag better.


[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]

Reply

#3
you don't have to use BlocProvider.value() to navigate to another screen, you can just wrap MaterialApp into BlocProvider as a child of it
Reply

#4
You can just wrap the Blocs you need to access through out the app by wrapping it at the entry point of the app like this

```void main() {
runApp(
MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (context) =>
UserBloc(UserRepository()),
),

],
child: App()
)
);
}
```

and you can access this bloc at anywhere of your app by

` BlocProvider.of<UserBloc>(context).add(event of user bloc());`
Reply

#5
**Change name of context in builder whether in bottomSheet or materialPageRoute.**

So that bloc can access parent context through context
unless it's going to take context from builder (bottom sheet). This can lead
to an error which you can't reach the instance of bloc .


showModalBottomSheet(
context: context,
builder: (context2) { ===> change here to context2
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: widgetA(),
),
}

Reply

#6
You need to either decompose your widget into two widgets (which I recommend for testability reasons) or use a Builder widget to get a child context.



class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (_) => TestCubit(), child: MyHomeView(), ); } } class MyHomeView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: RaisedButton(onPressed: () => BlocProvider.of<TestCubit>(context)...) ), ); } }

source: solved by Felix Angelov,

[To see links please register here]


Reply

#7

**EDIT 10/03/2022**

Since this thread became very popular I feel I need to add some comments.

This is valid solution if your goal is to use blocs that are **not** provided above your `MaterialApp` widget, but instead being declared somewhere down the widget tree by wrapping your widget (eg. some page) with `BlocProvider` making it possible for that widget to access the bloc.

It is easier to avoid problems by declaring all your blocs in `MultiBlocProvider` somewhere up the widget tree (like I said before), but this topic was not created with that in mind. Feel free to upvote and use this aproach described in *Amesh Fernando* response but do that knowing the difference.

---
I fixed it. Inside `App` widget i create `LoginPage` with

```dart
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
```
At `LoginPage` I simply wrap `BlocBuilders` one into another

```dart
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: BlocProvider<NewRelicBloc>(
create: (_) => NewRelicBloc(NewRelicRepository()),
child: HomePage(),
),
),
),
);
}
},
[...]
```

`PopupMenuButton` navigates *User* to `TokenPage` with

```dart
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
```
And that solved all my problems.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through