Bloc isn't found in the widget tree - flutter-provider

Because the code is to big I will try and sum it up in words.
This is the latest exception:
Error: Could not find the correct Provider<ProdEntriesSearchCubit> above this BlocListener<ProdEntriesSearchCubit, ProdEntriesSearchState> Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that BlocListener<ProdEntriesSearchCubit, ProdEntriesSearchState> is under your MultiProvider/Provider<ProdEntriesSearchCubit>.
This usually happens when you are creating a provider and trying to read it immediately.
In screen 1 I have the following build method:
Widget build(BuildContext context) {
final entriesState = context.watch<ProdEntriesCubit>().state;
return BlocProvider(
create: (context) => ProdEntriesSearchCubit(
productsRepository: context.watch<ProductsRepository>(),
),
child: Builder(
builder: (context) => SafeScreen(
child: Scaffold(
body: _buildBody(context, entriesState: entriesState),
floatingActionButton: _buildFab(context),
),
),
),
);
}
_buildFab(BuildContext context) {
return FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () async {
await navigatorPush(context, screen: AdminProdEntryScreen());
},
);
}
In AdminProdEntryScreen I do again:
navigatorPush(
context,
screen: EntryProdSearchScreen(),
);
In EntryProdSearchScreen I get the error from above.
Why is the BloC/Cubit not found in the widget tree?
I even used multiple Builder widgets but I am always hit by this exception.

When you provide your BLoC, it has access to the current widget tree, when you navigate to another screen it won't have access to that BLoC.
You can solve this in one of two ways.
1
You wrap your whole app with a bloc (Multi) provider that you can access the bloc no matter the navigation.
The reason that this works is because you are wrapping your navigation within MaterialApp with the bloc provider.
runApp(
MultiBlocProvider(
providers: [
BlocProvider<ProdEntriesSearchCubit>(
create: (context) => ProdEntriesSearchCubit(),
),
],
child: MyApp(),
),
);
2
You can pass an instance of the bloc through the nav route and use BlocProvider.value to provide the same instance of the bloc.
//nav method
Navigator.of(context).pushNamed(
'/entry_prod_search_screen',
arguments: context.read< ProdEntriesSearchCubit >(),
);
//in the navigated route screen
final bloc = ModalRoute.of(context).settings.arguments;
return MultiBlocProvider(
providers: [
BlocProvider.value(
value: bloc,
),
],
child: ...,
);

Related

How to prevent child widget redraw when parent widget perform setState()?

I have a ListView inside many stateFull widget of tree, and i cant able to separate from all parent widget.
Listview build from streambuilder(firebase).
From a large hierarchy of stateFull widget, some of them anytime perform setState widget, then child ListView also redraw and once again it will get data from firebase and also its flickering(blinking)
explain below example
StatefullWidget(
child:StatefullWidget(
child:StatefullWidget(
child:ListView()
),
),
);
There are three parent widget of Listview(), for user friendly app setState called many time in parent widget.
So i want to avoid flickering and redraw of listView(), even if the parent widget redraw(setState())
https://pub.dev/documentation/provider/latest/provider/Selector-class.html
'selector' of Provider is what you find.
But usually optimizing performance is enough for not blinking.
ListView.builder, const Widget, key, etc., https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html#performance-considerations
You should use one of the state management method.
For example with getx package, you can make your widgets stateless and use getx widgets for any updating data.
This is the example of getx usage for default flutter app.
class Home extends StatelessWidget {
final controller = Get.put(Controller());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("counter")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GetBuilder<Controller>(
builder: (_) => Text(
'clicks: ${controller.count}',
)),
ElevatedButton(
child: Text('Next Route'),
onPressed: () {
Get.to(Second());
},
),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: controller.increment(),
),
);
}
}
class Second extends StatelessWidget {
final Controller ctrl = Get.find();
#override
Widget build(context){
return Scaffold(body: Center(child: Text("${ctrl.count}")));
}
}

Why im getting provider error in project?

Im getting this error when im trying to use provider in my code .
IN new in flutter and a beginner so maybe anyone can explain how to fix I search already but still cannot find something.
The following ProviderNotFoundException was thrown building MeinAccount(dirty, state: _MeinAccountState#e2cbb):
Error: Could not find the correct Provider<User> above this MeinAccount Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that MeinAccount is under your MultiProvider/Provider<User>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was:
MeinAccount file:///Users/myname/StudioProjects/projectname/lib/main.dart:48:41
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:332:7)
#1 Provider.of (package:provider/src/provider.dart:284:30)
#2 _MeinAccountState.build (package:projectname/seitenleiste/meinacount.dart:362:27)
#3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4704:27)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4587:15)
This is my widget UPDATEEE:
Widget _buildPasswordTF() {
final user = Provider.of<User>(context);
return StreamBuilder<UserData>(
stream: DatbaseService(uid:user.uid).userData,
builder: (context, snapshot) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Password',
Maybe anyone can help :
Also here's my main dart UPDATEEE:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context){
return MultiProvider(
providers: [
//DatbaseService(create: (_) => User()), // add your providers like this.
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title:'Appbar Scaffold',
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
primaryColor: Colors.white,
If you need more informations please add a comment. Thanks again.
It's because you haven't registered your provider. You need to register them, before consuming them. Wrap your MaterialApp widget with MultiProvider (if you have multiple providers in your app).
example:
child: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => User()), // add your providers like this.
],
child:MaterialApp(..........)
);

Struggling to load images from Firebase Firestore into ListItem in Flutter app

I am having a bit of issue loading images from my Firebase Firestore database into my Flutter app. I am sort of stuck on this since I don't know what to do currently. I have seen a way to use NetworkImage to download an image on a list tile but it only does one image. I am trying to figure out a way to use a separate image for each item in the Firestore database.
I have hooked up the image URL from the Firebase Storage to Firestore under the imageURL key which is a String. But I am still struggling to figure out how to do this. I have also downloaded all the dependencies such as Firebase and Firestore. If anyone does offer to help or shares a tip on how they have worked with something similar to this, it'd be greatly appreciated! :)
class HomeTab extends StatefulWidget {
#override
_HomeTabState createState() => _HomeTabState();
}
class _HomeTabState extends State<HomeTab> {
#override
Widget build(BuildContext context) {
// This will reference our posts collection in Firebase Firestore NoSQL database.
CollectionReference posts = FirebaseFirestore.instance.collection('posts');
return Center(
child: Container(
width: 250,
child: StreamBuilder<QuerySnapshot>(
// Renders every post from the Firebase Firestore Database.
stream: posts.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
return Card(
margin: EdgeInsets.all(20.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 10,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
// Renders the title on every single listview.
title: new Text(document.data()['title']),
// leading: new NetworkImage(''),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ReaderPage(),
),
);
},
),
],
// Added Column
// In case if things don't work, go back to this.
// child: new ListTile(
// title: new Text(document.data()['text']),
// ),
),
);
}).toList(),
);
},
),
),
);
}
}
Here is my Firebase Storage
My Firestore database
The NetworkImage class is not a flutter widget class. It is responsible for downloading the image from the URL provided. It inherits from the ImageProvider class.
From the flutter docs:
Fetches the given URL from the network, associating it with the given scale.
Use Image.network for a shorthand of an Image widget backed by NetworkImage.
You should be using the Image.network named constructor of the Image widget to create a flutter widget used to display image with help of NetworkImage.
You should replace the NetworkImage in your widget tree with:
new Image.network("https://i.stack.imgur.com/W98kA.png")

Flutter - How to rebuild my page when user logs in using Firebase?

I am trying to hide a Container in my app when a user logs in, but my UI won't change even if I use onAuthStateChanged or setState()
I am using a StreamBuilder in my main.dart and I am using a dependency that is similar to a Hamburger Menu (https://pub.dev/packages/kf_drawer) that passes a variable which decides the Visibility of my Container
return MaterialApp(
home: StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (_, snap) {
if (snap.connectionState == ConnectionState.active) {
if (snap.data != null) {
return DrawerMenu(firebaseUser: snap.data, visibleLogin: false);
} else {
return DrawerMenu(firebaseUser: null, visibleLogin: true);
}
} else {
return CircularProgressIndicator();
}
},
),
);
This is my code where the firebase user first passes to the DrawerMenu() and then to my Home(),
KFDrawerItem.initWithPage(
text: Text(
'Home',
style: TextStyle(color: Colors.white),
),
icon: Icon(Icons.settings, color: Colors.white),
page: Home(
firebaseUser: widget.firebaseUser,
visibleLogin: widget.visibleLogin,
),
),
This is the code for my Container, however my UI won't update as soon as my user logs in but only when I navigate through the other pages in my app, nor does the state of the app get saved when I re-open my app after quitting it.
//Simplified code for example purposes
Widget login(bool visibleLogin) {
return Visibility(
visible: visibleLogin,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(),
),
);
}
I want to achieve something like this, the bottom login Container disappearing as soon as the user logs in.
BEFORE LOGGING IN - https://i.stack.imgur.com/BKfby.png
AFTER LOGGING IN - https://i.stack.imgur.com/5zMHB.png
Something might not be right with your stream, are you sure you are getting stream reacting to change if a stream is fine, you can then look to your hamburger it might somehow not working propper.
create a StreamSubscription from FirebaseAuth.instance.onAuthStateChanged in the initState() function inside the DrawerMenu
to setState((){}) of the DrawerMenu screen depending on the user state;
and remove the StreamBuilder from the MaterialApp

How to handle Firebase login as first access

I'm writing a Flutter application which integrates Firebase Authentication.
The problem
I would like to integrate in the best and most optimal way possible the authentication checking if the user is authenticated in the moment the app is launched. If the user is authenticated the app opens the normal home page, otherwise the authentication page is shown. After the authentication the app should redirect the user to the normal home page. For obvious reasons the user mustn't have the possibility to tap the back button and go back to the authentication page.
What I've done so far
At the moment the application checks in the main() if the user is authenticated, and, if it is so, it creates a MaterialApp with, as home, the main page of the application. In that case, the '/' of the app is the home page. If it is not, the app creates a MaterialApp with, as home, the authentication screen. In that case, however, the '/' is the welcome screen, and so I can't use
Navigator.of(context).popUntil(ModalRoute.withName('/'))
(which, in fact, happens to be quite necessary and useful), because the '/' is not the home page, and, moreover, the user could tap the back button and get back to the welcome screen.
The question
What am I losing? Am I completely wrong, and there is a totally different way of doing what I want to do, or the base is correct? If so, how can I implement what I would like to?
Thanks in advance.
You're looking for Navigator.pushReplacement or Navigator.pushReplacementNamed.
https://docs.flutter.io/flutter/widgets/Navigator-class.html
https://docs.flutter.io/flutter/widgets/Navigator/pushReplacement.html
https://docs.flutter.io/flutter/widgets/Navigator/pushReplacementNamed.html
Here's a quick sample code.
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("First screen"),
RaisedButton(
child: Text("Go to second screen"),
onPressed: () => _goToSecondScreen(context),
),
],
);
}
_goToSecondScreen(context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => SecondScreen()));
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("Second screen"),
RaisedButton(
child: Text("Go to first screen"),
onPressed: () => _goToFirstScreen(context),
),
],
);
}
_goToFirstScreen(context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => FirstScreen()));
}
}

Resources