Using Provider in the same widget in flutter - flutter

I have declared MultipleProviders in my widget and i want to use it to change the color of the App by assaining the variable to the ThemeData Primary swatch but it's giving me this error related to provider . and i have use in other widgets and it's working .i think i am getting this error bacause i am using it in the same widget how i can solve it ?
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
var u = Provider.of<prov>(context);
return MultiProvider(
providers: [ChangeNotifierProvider(create: (_)=>prov())],
child: GetMaterialApp(
theme: ThemeData(primarySwatch: u.col),
title: 'Material App',
home: f(),
),
);
}
}
this is the error
Error: Could not find the correct Provider above this MyApp 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 MyApp 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>()),
),
}
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>()),
}
),
}

You're getting the error because the context you're using does not have access to the provider.
The solution is like it says in the error message: you can use a builder instead of a child property for your provider. That creates a new context that reads the provider created.
You should change your build method to this.
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider(create: (_)=>prove())],
//From here is where you make the change
builder: (context, child) {
var u = Provider.of<prov>(context);
return GetMaterialApp(
theme: ThemeData(primarySwatch: u.col),
title: 'Material App',
home: f(),
),
);
}

Related

StreamProvider: Error: Could not find the correct Provider<User> above this App Widget

I'm using StreamProvider from the provider package for auth functionality in my flutter-firebase app, just like it is explained in this tutorial https://www.youtube.com/watch?v=j_SJ7XmT2MM&list=PL4cUxeGkcC9j--TKIdkb3ISfRbJeJYQwC&index=9.
When trying to run my app, I get an error message, with a suggestion how to do it correctly, but my code IS written in the way that is suggested.
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(FirebaseWrapper());
runApp(App());
}
class FirebaseWrapper extends StatelessWidget {
// Create the initialization Future outside of build():
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
// final Future<void> _initSharedPrefs = SharedPrefsHelper().initSharedPrefsInstance();
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return FutureBuilder(
// from: https://firebase.flutter.dev/docs/overview/#initializing-flutterfire
future: _initialization,
// future: Future.wait([_initialization, _initSharedPrefs]),
builder: (context, snapshot) {
if (snapshot.hasError) return ErrorPage(); //TODO better error pages
if (snapshot.connectionState == ConnectionState.done) return FirebaseAuthWrapper();
return Loading(); //waiting
},
);
}
}
class FirebaseAuthWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: Auth().userStream,
initialData: null,
child: App(),
);
}
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
print('yeet');
return MaterialApp(
key: UniqueKey(),
title: 'Wanderapp',
theme: ThemeData(primarySwatch: Colors.blue),
initialRoute: (user == null) ? '/signIn' : '/',
routes: (user == null)
? {
'/signIn': (context) => SignIn(),
'/register': (context) => Register(),
// '/forgotPassword': (context) => ForgotPassword(),
}
: {
'/': (context) => Home(),
//...
},
);
}
}
the error message:
Error: Could not find the correct Provider<User> above this App 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 App 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>()),
}
),
}
```
I'm user the same "User" class from Firebase for StreamProvider and Provider.of, the hierarchy/scope also seems to be correct in my code, but it doesn't work.
Does anyone know what my mistake is? Thank you very much.
In this link about runApp it says:
Calling runApp again will detach the previous root widget from the
screen and attach the given widget in its place.
So, you just need to remove the second runApp, as App is being called anyway from the StreamProvider: child: App(),.
Solution:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(FirebaseWrapper());
runApp(App()); //*** Remove this line ***///
}

How to add multi notifier provider in flutter

I am working on Food delivery app and I am using Provider as state management architecture. Problem is when i add a second provider to my app it is giving error.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (_) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (_) => OTPViewModel()),
],
child: GPS(),
),
);
}
Error is
Error: Could not find the correct Provider<OTPViewModel> above this MobileOTP Widget
In MobileOTP i am accessing the provider like this in init state method
#override
void initState() {
super.initState();
Provider.of<OTPViewModel>(context, listen: false).
verifyMobileNumber(widget.phone,verificationCompleted,verificationFailed,codeSent,codeAutoRetrievalTimeout);
}
The Full error trace is like this
Error: Could not find the correct Provider<OTPViewModel> above this MobileOTP 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 MobileOTP is under your MultiProvider/Provider<OTPViewModel>.
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>()),
}
),
}
What i am doing wrong ?
So basically problem was "Provider is based on InheritedWidget. Only child widgets can inherit parent widget's state.". I was trying to access it otherwise, so it was giving me error. I swap the Material App with Multi provider and it fixes the problem.
Code now becomes
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (_) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (context) => OTPViewModel()),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GPS(),
),
);
}
Thats it !!!
Do not ignore the context, use it while you define them, like this:
MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (ctx) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (ctx) => OTPViewModel()),
],

Could not find the correct Provider<HomeBloc> above this RestoreLocalBackupPage Widget, how to solve this issue in a simpler manner than what I did?

I'm trying to build a Notes app with backup and restore functionality. I have a home page that shows up when the app is opened. This page has a Scaffold as it's body, which in turn has a drawer that has ListTiles for backup and restore. I use a HomeBloc object to interact with the database where I save the notes, hence I used Provider to get access to it everywhere.
The ListTiles open a MaterialPageRoute to new screens where the user is prompted to choose the file, enter passwords etc.
When I tap on the Restore ListTile in the drawer, I get this error:
The following ProviderNotFoundException was thrown building RestoreLocalBackupPage(dirty, state: _RestoreLocalBackupPageState#4f937):
Error: Could not find the correct Provider<HomeBloc> above this RestoreLocalBackupPage Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice.
This is my main.dart, where I wrap the Home page in a Provider:
void main() {
runApp(
MyApp()
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Notes',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Provider(
create: (_) => HomeBloc(),
child: HomePage(),
)
);
}
}
This is the build method of my HomePage:
Widget build(BuildContext context) {
homeBloc = Provider.of<HomeBloc>(context);
return Scaffold(
backgroundColor: Color.fromRGBO(219, 243, 250, 1),
appBar: AppBar(...),
body: StreamBuilder<List<Note>>(...),
floatingActionButton: FloatingActionButton(...),
drawer: HomeDrawer(),
);
}
The HomeDrawer's build method returns a Drawer, which has a ListView as it's child. Here's the code for the ListTile that launches the Restore Backup page:
ListTile(
title: Text('Local Backup',
style: GoogleFonts.sourceSansPro(
textStyle: TextStyle(fontWeight: FontWeight.w500),
fontSize: 16)),
onTap: () async {
// Update the state of the app
// ...
// Then close the drawer
bool permissionGranted = await _getPermissions(context);
if (permissionGranted) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => CreateLocalBackupPage(
currentBackupDirectory: currentBackupDirectory
),
)
);
}
},
)
This is the error that I get when I tap on the above ListTile:
The following ProviderNotFoundException was thrown building RestoreLocalBackupPage(dirty, state: _RestoreLocalBackupPageState#4f937):
Error: Could not find the correct Provider<HomeBloc> above this RestoreLocalBackupPage Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice.
HomeDrawer()'s BuildContext does have access to the HomeBloc object I need. Hence, wrapping the RestoreLocalBackupPage widget inside another Provider works:
HomeBloc homebloc = Provider.of<HomeBloc>(context);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Provider(
create: (_) => homebloc,
child: RestoreLocalBackupPage(currentBackupDirectory: currentBackupDirectory),
)
)
);
I wanted to know if there's a simpler, more elegant way of getting access to HomeBloc inside RestoreLocalBackupPage using Provider. Dependency Injection via the constructor works but that sort of defeats the purpose of using Provider in the first place, right?
Wrapping the MaterialApp in main.dart with a Provider solved my issue. I have found the solution here. Check rrousselGit's answer.
After doing that, main.dart now becomes:
void main() {
runApp(
MyApp()
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
LicenseRegistry.addLicense(() async* {
final license = await rootBundle.loadString('google_fonts/Cabin_OFL.txt');
yield LicenseEntryWithLineBreaks(['google_fonts_Cabin'], license);
});
LicenseRegistry.addLicense(() async* {
final license = await rootBundle.loadString('google_fonts/SSP_OFL.txt');
yield LicenseEntryWithLineBreaks(['google_fonts_SSP'], license);
});
return Provider(
create: (_) => HomeBloc(),
child: MaterialApp(
title: 'Notes',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
),
);
}
}
Try to use provider one level up and wrap MaterialApp with Provider.

how to dispose the provider that is wrapping the entire MaterialApp in Flutter

When I wrap the widget that is the home of the MaterialApp with MultiProvider
it works fine but when I want to navigate to another page that already contains widgets that depend on the provider,
a message shows up that tell me
"Could not find the correct Provider above .... et cetera"
but when I wrap the entire MaterialApp, It works fine
but the problem is
even when I remove all Widget tree and insert a new page
the provider still has its data and I need it to dispose
cause I can access the data of the provider from inside the newly inserted page after removing all the previous pages from the navigator stack
how can I force the disposing of the provider that is already wrapping the MaterialApp
here is the sample code
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<CommonWidgetsStateProvider>(
create: (context) => CommonWidgetsStateProvider(),
),
ChangeNotifierProvider<CollegePostSignUpState>(
create: (context) => CollegePostSignUpState()),
ChangeNotifierProvider<SchoolStudentPostSignupState>(
create: (context) => SchoolStudentPostSignupState()),
ChangeNotifierProvider(create: (context) => ExecutionState())
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: LandingPage(),
routes: NavigatorServices.navigatorRoutes),
);
}
}```

Using provider in ancestor

I am using provider package to manage state in Flutter.
This is main.dart:
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Application>(
builder: (_) => Application(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: HomeScreen(),
initialRoute: AppRoutes.home
),
);
}
}
Now, how can I use the application provider in main.dart? I tried doing final app = Provider.of<Application>(context).app; in build function. However, it throws the following error:
Error: Could not find the correct Provider<Application> above this MyApp Widget
To fix, please:
* Ensure the Provider<Application> is an ancestor to this MyApp Widget
* Provide types to Provider<Application>
* Provide types to Consumer<Application>
* Provide types to Provider.of<Application>()
* Always use package imports. Ex: `import 'package:my_app/my_code.dart';
* Ensure the correct `context` is being used.
I know that the provider can be accessed by the children but is there any way to access in ancestor/main.dart as well? I need to manage an app wide state.
That's what Consumer is here for.
You can use it to do:
Widget build(BuildContext context) {
return Provider<Whatever>(
builder: (_) => Whatever(),
child: Consumer<Whatever>(
builder: (_, whatever, __) {
// todo: use `whatever`
},
),
);
}