Reputation: 5197
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();
}
},
),
);
}
}
Upvotes: 10
Views: 27167
Reputation: 4418
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.
Upvotes: 0
Reputation: 276911
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(),
),
)
Upvotes: 19
Reputation: 437
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())
));
}
Upvotes: 2