Flutter - Error Could not Find the correct Provider - flutter

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

Related

Could not find the correct Provider above the BlocListener Widget

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...

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
);
},
),
);

What is onUnknownRoute in flutter

I'm new to flutter, In MaterailApp Widget have an attribute called onUnknownRoute. What is the main use of onUnknownRoute?
Thanks for your help!
You can copy paste run full code below
In flutter web, when user manually key a undefine route, it can produce like 404 effect
full code
import 'package:flutter/material.dart';
void main() {
runApp( MaterialApp(
initialRoute: "/screen1",
routes: <String, WidgetBuilder>{
'/screen1': (BuildContext context) => Screen1(),
'/screen2': (BuildContext context) => Screen2(),
'/screen3': (BuildContext context) => Screen3(),
'/screen4': (BuildContext context) => Screen4()
},
onUnknownRoute: (RouteSettings settings) {
return MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) =>
Scaffold(body: Center(child: Text('Not Found'))),
);
},
)
);
}
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen4 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
onUnknownRoute property is used to handle the worst case when the app navigation fails to get a route. If not handled, exception will be generated.
The navigator widget manages a stack of Route objects.
Route objects are nothing but full-screen elements like "screens" or "pages".
The navigator manages navigation between the screens in any app.
The Navigator for any app is only built if routes are provided by one of below items
home
routes
onGenerateRoute
onUnknownRoute
If the app only has one page, then you can specify it using home.
Else all routes will be in routes table including the home
If a route is requested that is not specified in routes table (or by home), then the onGenerateRoute callback is called
when even onGenerateRoute fails to generate a route, the property OnUnknownRoute will be triggered to handle the scenario.
We can handle an unknown route as below,
return MaterialApp(
title: 'Named Routing',
onGenerateRoute: router.generateRoute,
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (context) => UndefinedView(
name: settings.name,
)),
initialRoute: HomeViewRoute,
);
If unknown route event is not handled using the onUnknownRoute property, flutter may throw an exception
The solution is coding in routes -> Example
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
// home: HomePage(),
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => HomePage(),
'alert': (BuildContext context) => AlertPage(),
'avatar': (BuildContext context) => AvatarPage(),
},
***onUnknownRoute: (settings) {},***
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (BuildContext context) => AlertPage());
},
);
}
}

Use Provider to provide BLoCs globally to whole flutter module

https://pub.dev/packages/provider
Can I use Provider to provide blocs globally to all widgets down the tree without MaterialApp? I tried to provide BLoC to a page, but when I'm navigating to the next page, BloC isn't found. So should I provide it whenever I'm navigating to a new page or is there a solution to provide it globally without MaterialApp?
Currently I'm doing it this way
Provider
|_MaterialApp
|_MyPage1 (from which you can navigate to MyPage2...3)
This approach works, and all pages can access provided BLoC.
But if use this approach
Provider
|_MyPage1 (from which you can navigate to MyPage2...3)
MyPage2, MyPage3 can't find provided BLoC. BLoC only can be found on MyPage1
You can achieve this by using the builder property of a MaterialApp.
lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SomeBloc {
final String someValue;
SomeBloc(this.someValue);
void dispose() {}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
SomeBloc _someBloc = SomeBloc("someValue");
#override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, widget) => Provider<SomeBloc>(
create: (_) => _someBloc,
dispose: (_, bloc) => bloc.dispose(),
child: widget,
),
initialRoute: '/first-page',
routes: <String, WidgetBuilder>{
'/first-page': (context) => FirstPage(),
'/second-page': (context) => SecondPage(),
},
);
}
}
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
SomeBloc _someBloc = Provider.of<SomeBloc>(context);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_someBloc.someValue),
RaisedButton(
child: Text("Open second page"),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SecondPage(),
),
),
)
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
SomeBloc _someBloc = Provider.of<SomeBloc>(context);
return Scaffold(
body: Center(
child: Text(_someBloc.someValue),
),
);
}
}
Per the documentation,
home
The difference between using home and using builder is that the home subtree is inserted into the application below a Navigator.
builder
... the widget returned from builder is inserted above the app's Navigator (if any).
If you want your "blocs" to be provided on all of your routes, you have to provide those above the Navigator widget.
That's why this simply works:
return Provider(
create: (_) => _someBloc,
child: MaterialApp(
home: FirstPage(),
),
);
In this setup above, Provider is on top of the Navigator widget.
And this doesn't:
return MaterialApp(
home: Provider(
create: (_) => _someBloc,
child: FirstPage(),
),
);
_someBloc will only be accessible to the FirstPage widget.
Hope this helps.

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.