Reputation: 327
I've created an app and I have used MultiProvider but it doesn't work when I use it inside MaterialApp
I want to use it to change app theme color but
it gives me an error:
*Note: when I use posts provider in any other screen it works.
My Code:
import 'package:blog_app/provider/posts.dart';
import 'package:blog_app/provider/settings.dart';
import 'package:blog_app/screens/splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<Posts>(
builder: (context) => Posts(),
),
ChangeNotifierProvider<Settings>(
builder: (context) => Settings(),
),
],
child: MaterialApp(
darkTheme: Provider.of<Settings>(context).darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
debugShowCheckedModeBanner: false,
title: 'Blogy',
theme: ThemeData(
primaryColor: Colors.deepPurple[900],
cursorColor: Colors.deepPurple[900],
accentColor: Colors.deepPurple[900],
fontFamily: 'Ubuntu',
),
home: SplashScreen(),
),
);
}
}
The Error :-
I/flutter ( 9316): The following ProviderNotFoundError was thrown building MyApp(dirty):
I/flutter ( 9316): Error: Could not find the correct Provider<Settings> above this MyApp Widget
I/flutter ( 9316):
I/flutter ( 9316): To fix, please:
I/flutter ( 9316):
I/flutter ( 9316): * Ensure the Provider<Settings> is an ancestor to this MyApp Widget
I/flutter ( 9316): * Provide types to Provider<Settings>
I/flutter ( 9316): * Provide types to Consumer<Settings>
I/flutter ( 9316): * Provide types to Provider.of<Settings>()
Upvotes: 6
Views: 10238
Reputation: 253
This error happens because you are creating your providers and your consumers in the same build
method. This results in them having the same context
, which doesn't have any Provider<Settings>
registered yet. Provider.of<Settings>(context)
is trying to find a Provider<Settings>
above in the widget tree, but there is no such provider there.
Using Consumer
seems like a valid workaround, but recreating the whole MaterialApp on every change might be pretty heavy.
I suggest instead creating separate widgets for providers and the app root:
class AppProviders extends StatelessWidget {
final Widget child;
AppProviders({this.child});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<Posts>(
builder: (context) => Posts(),
),
ChangeNotifierProvider<Settings>(
builder: (context) => Settings(),
),
],
child: child;
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
darkTheme: Provider.of<Settings>(context).darkModeEnabled ? ThemeData.dark() :
ThemeData.light(),
debugShowCheckedModeBanner: false,
title: 'Blogy',
theme: ThemeData(
primaryColor: Colors.deepPurple[900],
cursorColor: Colors.deepPurple[900],
accentColor: Colors.deepPurple[900],
fontFamily: 'Ubuntu',
),
home: SplashScreen(),
);
}
}
Then wrap the MyApp
widget in AppProviders
inside the runApp
function.
void main() {
runApp(
AppProviders(
child: MyApp(),
)
);
}
This ensures that the Providers are registered above your root app widget and that they are visible in its context
.
Alternatively you can declare three widgets where the third one is just AppProviders(child: MyApp())
and call that one in runApp
. Note that creating AppProviders
inside MyApp
's build
method will result in the same error as before, so don't try it that way.
Upvotes: 4
Reputation: 54397
The following test code work without error, you can test with your case
Use Consumer
to wrap MaterialApp
code snippet
return MultiProvider(
providers: [
ChangeNotifierProvider<Posts>(
create: (context) => Posts(),
),
ChangeNotifierProvider<Settings>(
create: (context) => Settings(darkModeEnabled: true),
),
],
child: Consumer<Settings>(builder: (_, settings, child) {
return MaterialApp(
darkTheme:
settings.darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
debugShowCheckedModeBanner: false,
title: 'Blogy',
theme: ThemeData(
primaryColor: Colors.deepPurple[900],
cursorColor: Colors.deepPurple[900],
accentColor: Colors.deepPurple[900],
fontFamily: 'Ubuntu',
),
home: SplashScreen(),
);
}),
);
full test code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class Posts extends ChangeNotifier {}
class Settings extends ChangeNotifier {
bool darkModeEnabled;
Settings({this.darkModeEnabled});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<Posts>(
create: (context) => Posts(),
),
ChangeNotifierProvider<Settings>(
create: (context) => Settings(darkModeEnabled: true),
),
],
child: Consumer<Settings>(builder: (_, settings, child) {
return MaterialApp(
darkTheme:
settings.darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
debugShowCheckedModeBanner: false,
title: 'Blogy',
theme: ThemeData(
primaryColor: Colors.deepPurple[900],
cursorColor: Colors.deepPurple[900],
accentColor: Colors.deepPurple[900],
fontFamily: 'Ubuntu',
),
home: SplashScreen(),
);
}),
);
}
}
class SplashScreen extends StatefulWidget {
SplashScreen({Key key}) : super(key: key);
//final String title;
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Upvotes: 8