Could not find the correct Provider above the BlocListener Widget - flutter

I'm trying to use Bloc provider for user authentication in my flutter app. When I try to access the data i'm always getting this error even though I double checked all the files.
This is the error i'm getting:
Error: Could not find the correct Provider<StateStreamable<Object?>> above this
BlocListener<StateStreamable<Object?>, Object?> Widget
This happens because you used a `BuildContext` that does not include the provider
main.dart:
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthBloc(LoginInitState(), AuthRepository()))
],
child: MaterialApp(
title: 'Flutter app',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity),
home: const LoginPage(),
),
);
}
}
parts from login.dart:
#override
void initState() {
authBloc = BlocProvider.of<AuthBloc>(context);
super.initState();
}
######################################################
return Scaffold(
backgroundColor: Colors.grey[300],
body: BlocListener(
listener: (context, state) {
if (state is UserLoginSuccessState) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const HomeScreen()));
}
},
child: SafeArea...
I'm still new to flutter and struggling with the state management part, I'd be glad if anybody can help!

In your BlocListener you're missing the State and the Bloc
Here's what I mean
BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
if (state is UserLoginSuccessState) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const HomeScreen()));
}
},
child: SafeArea...

Related

Flutter - Error Could not Find the correct Provider

im new on flutter, and i experience some troubles after i add a new provider into my main.
this is my main code:
import 'package:catalo_gate/providers/providers.dart';
import 'package:catalo_gate/services/auth_service.dart';
import 'package:catalo_gate/services/products_service.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/screens.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const AppState());
}
class AppState extends StatelessWidget {
const AppState({super.key});
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ProductsService()),
ChangeNotifierProvider(
create: (_) => LoginProvider(authService: AuthService())),
],
child: const MyApp(),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'CataloGate',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: 'login',
routes: {
'login': (BuildContext context) => const LoginScreen(),
'register': (BuildContext context) => const RegisterScreen(),
'checking': (BuildContext context) => const CheckAuthScreen(),
'home': (BuildContext context) => const HomeScreen(),
'product': (BuildContext context) => const ProductScreen(),
'productsList': (BuildContext context) => const ProductsListScreen(),
//SubScreens
'listaReposicion': (BuildContext context) =>
const ListaDiasReposicionScreen(),
'entregas': (BuildContext context) => const DiasDeEntregaScreen(),
'unidadesPorBulto': (BuildContext context) =>
const UnidadesPorBultoScreen(),
},
);
}
}
everytime i try to make login i receive this message:
ProviderNotFoundException (Error: Could not find the correct Provider above this HomeScreen 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 HomeScreen is under your MultiProvider/Provider.
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>().toString()),
);
}
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, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
)
Can someone Help me??
i'd try a lot of options, changing the way to use the ChangeNotifierProvider for example

Using more than one provider in a flutter app

I have started using PROVIDER to manage state in my app. I followed tutorials and wrapped my Material app with ChangeNotifierProvider.
Here's the code :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => ListsProvider(),
child: MaterialApp(
title: 'WordsApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: StartingPage.id,
routes: {
StartingPage.id: (context) => StartingPage(),
RegistrationScreen.id: (context) => RegistrationScreen(),
},
),
);
}
}
This provider called "ListsProvider" takes care of "providing" lists that need to be displayed on different screens.
I have now created a second provider which I called "user_data_provider" and I now need to add it to my app too. It will take care of providing user data to many different screens.
How can I do that ?
To achive this you can use Multiprovider as shown below
Add this to the top of your app. If you need these obj everywhere.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<user_data_provider>(
create: (_) => user_data_provider(),
),
ChangeNotifierProvider<ListsProvider>(
create: (_) => ListsProvider(),
),
],
child: Builder(
builder: (BuildContext context) {
return MaterialApp(
//YOur code goes here
);
},
),
);

Flutter state management

If I only want a widget and its children have specific Provider but not in whole app, how do I achieve that ?
// not this
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CartModel()),
Provider(create: (context) => SomeOtherClass()),
],
child: MyApp(),
),
);
}
You just need to wrap MultiProvider to the Widget you want.
Like this:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [Provider(create: (context) => TestModel(index: 1)),],
child: MyHomePage(title: 'Flutter Demo Home Page')
),
);
}
}
You can wrap this widget with the provider and can be used as
final _myProvider=Provider.of<MyProvider>(context, listen: false);
or using
Consumer<MyProvider>(
builder: (BuildContext context, MyProvider myProvider, Widget child) {
return child;
),

Flutter authentification with streams

apparently I'm doing something fundamentally wrong here... After I login, I send new bool value (T) to isAuthorized stream. StreamBuilder reruns and ends up executing correct if-else branch, but for some reason Login Widget remains rendered on the screen?
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
theme: appTheme,
initialRoute: '/',
home: StreamBuilder<bool>(
stream: getIt.get<SessionBloc>().isAuthorized,
initialData: false,
builder: (context, snapshot) {
if (snapshot.data) {
return Home();
} else {
return Login();
}
},
),
routes: {
'/': (context) => Home(),
'/profile': (context) => Profile(),
'/login': (context) => Login(),
'/register': (context) => Register(),
},
),
);
}
}
Because I don't know what's your bloc doing, I created a simple stream that mimics the bloc. It is working fine with this code snippet. If the bloc gives you what you want and goes appropriate if-else block, then you should consider deleting '/': (context) => Home().
This was the exception that I had when I have home route:
If the home property is specified, the routes table cannot include an entry for "/", since it would be redundant.
class Authorization {
final _authorization = StreamController<bool>();
StreamSink<bool> get send => _authorization.sink;
Stream<bool> get isAuthorized => _authorization.stream;
get close => _authorization.close();
}
class MyApp extends StatelessWidget {
Authorization a = new Authorization();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
home: StreamBuilder<bool>(
stream: a.isAuthorized,
initialData: false,
builder: (context, snapshot) {
if (snapshot.data) {
return Home(auth: a);
} else {
return Login(auth: a);
}
},
),
routes: {
'/login': (context) => Login(auth: a),
},
);
}
}
class Home extends StatelessWidget {
final Authorization _auth;
Home({#required Authorization auth})
: assert(auth != null),
_auth = auth;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[Text("Home"), RaisedButton(
onPressed: () {
_auth.send.add(false);
},
)],
),
);
}
}
class Login extends StatelessWidget {
final Authorization _auth;
Login({#required Authorization auth})
: assert(auth != null),
_auth = auth;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[Text("Login"), RaisedButton(
onPressed: () {
_auth.send.add(true);
},
)],
),
);
}
}

Flutter problem with finding provider context

I have a problem with Flutter Provider pattern. After user is redirected to a new screen, the provider could not be found.
Following my previous question (Could not find the correct provider above this widget)
I wrote this code:
class NewRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'Tap to select';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: NewRouteBody()
));
}
}
class NewRouteBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
return ListView(...)
I did same thing but I get again the error which says that it could not find the correct provider above this widget (NewRouteBody).
Tried to fix it somehow, Googled the answer for a few hours but without success...
Any help is appreciated.
EDIT
This is UserRepository which contains pattern:
class UserRepository with ChangeNotifier {
User user;
Status _status = Status.Uninitialized;
Status get status => _status;
User get getUser => user;
...}
EDIT 2:
Code snippet with ChangeNotifier:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserRepository>(
builder: (context) => UserRepository.instance(),
child: Consumer<UserRepository>(
builder: (context, UserRepository userRepository, _) {
switch (userRepository.status) {
case Status.Uninitialized:
return Login();
case Status.Unauthenticated:
return Login();
case Status.Authenticating:
case Status.Authenticated:
if(userRepository.getUser.isPrefSet == 0){
return Selection();
}
return Dashboard();
}
},
),
);
}
}
The issue is:
Your ChangeNotifierProvider is located inside Home, but you are trying to access it outside Home.
Providers are scoped. Which means that if it's located inside a widget tree, only its descendants can access it. As such, in your code, only Home can read from the provider.
To fix that, move the provider above MaterialApp:
ChangeNotifierProvider<UserRepository> (
builder: (context) => UserRepository(),
child: MaterialApp(
home: Home(),
),
)
You first need to create the Provider and place in the tree above the usage.
for example, in your case:
Widget build(BuildContext context) {
final title = 'Tap to select';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Provider<UserRepository> (
builder: (context) => UserRepository(),
dispose: (context, val) => val.dispose(),
child: NewRouteBody())
));
}
When the application reports such an error, it can be from many reasons. In my case, I was trying to read data from a context that was not wrapped by the BlocProvider from its ancestor.
// In my Child Widget
Navigator.push(context, MaterialPageRoute(
builder: (_) => MultiBlocProvider(providers: [
BlocProvider.value(
value: SaveJobsCubit()),
BlocProvider.value(
value: context.read<OnlineCompaniesCubit>()),
BlocProvider.value(
value: context.read<ApplyJobsCubit>()),
],
child: AttractiveJobsScreen(),
)
// But in Parent Widget, I create MultiBlocProvider with case have access_token
AuthRepo.accessToken != null
? RepositoryProvider(
create: (context) => OnlineCompaniesRepo(),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => SaveJobsCubit(),
),
BlocProvider(
create: (context) => OnlineCompaniesCubit(context.read<OnlineCompaniesRepo>()),
),
BlocProvider(
lazy: false,
create: (context) => ApplyJobsCubit(),
),
],
child: current,
),
)
: RepositoryProvider(
create: (context) => OnlineCompaniesRepo(),
child: BlocProvider(
create: (context) => OnlineCompaniesCubit(context.read<OnlineCompaniesRepo>()),
child: current,
),
);
This causes an error in case there is no access_token, then the child screen will not have SaveJobsCubit and cause the above error.
Hope this helps someone.