I have a problem using Flutter Provider... My flow is like this: After login user id is passed to new widget -> from there it preforms save to db and then it redirects to new widget (Dashboard).
And this is a code of a widget after Login:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
This works:
user.savePreference(user.user.id, "Something");
But this is causing a problem:
user.navigateToNewPage(Dashboard(), context);
In Dashboard widget I am creating this:
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
And in UserRepository I have this:
class UserRepository with ChangeNotifier {
User user;
Status _status = Status.Uninitialized;
Status get status => _status;
User get getUser => user;
UserRepository.instance();
Future<void> navigateToNewPage(Widget page, BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
}
I know this topic has already solved questions, but could not find anything suitable to my problem.
MaterialApp
> provider(Screen A)
> Screen B
If Provider
is instantiated in Screen A, it won't be accessible in Screen B after a Navigator.push
from A → B.
Because Provider
is an InheritedWidget
and Navigator
uses MaterialApp context
outside its Screen A context
scope. (See Details below.)
Moving Provider
up to a common-parent, MaterialApp context
, allows both Screen A and B to inherit its state/context.
provider(MaterialApp)
> Screen A
> Screen B
Example
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// wrap MaterialApp in Provider widget
return ChangeNotifierProvider(
create: (context) => ColorModel(), // ← create/init your state model
child: MaterialApp(
home: ScreenA()
),
);
}
}
Provider
is based on InheritedWidget
. Only child widgets can inherit parent widget's state.
Provider
needs to be the root widget for any widget tree that wants access to your "provided" state object.Navigator.push(context)
on Screen A doesn't use the context
from Screen A.
context
from MaterialApp
.Navigator.push(context)
is actually Navigator.of(context).push
Navigator.of(context)
means: search up this context hierarchy until you find a context that instantiated a Navigator
Navigator
is instantiated in MaterialApp
.Navigator
, you're using the default.Navigator
's context
is that of MaterialApp
.MaterialApp
), not the context of Screen A.
context
A.MaterialApp
context
, not of Screen A context
.Provider
context
scope, if defined in Screen A, doesn't cover Screen BScreen A → B
Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenB()))
is actually:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ScreenB()))
which is like:
Navigator.of(MaterialApp).push(
MaterialPageRoute(builder: (MaterialAppContext) => ScreenB())
)
So Screen B is under MaterialApp context
, not under Screen A context
and therefore has no access to Screen A Provider
and its context
.
See this answer for a code sample to a similar question about Provider
.
Parameter context
for Provider.of(context) must be a child of you defined provider.
@override
Widget build(BuildContext context) {
// !important here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
Official sample: https://api.flutter.dev/flutter/widgets/BuildContext-class.html
This is my code:
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider<SomeModel>(
create: (context){
return SomeModel();
},
),],
child: Builder(builder: (BuildContext context){
BuildContext rootContext = context;
return Container(
//Here to use rootContext is safe
//Provider.of<SomeModel>(rootContext, listen: false);
);
}),
);
}
This is how we used with multi providers
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => GeneralProvider(),
),
],
child: MaterialApp(
home: InitPage(),
onGenerateRoute: MyRouter.generateRoute,
initialRoute: '/InitPage',
debugShowCheckedModeBanner: false,
title: 'Eray',
),
);
}
}
Try extracting part of your widget tree that lies below Scaffold to a separate widget. The context you are using now is used to build your top level widget which does not Navigator yet.
The resulting code should look like that:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: LoginWidget()
class LoginWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
...
}
}
I imported my Provider from the wrong path:
import 'file:///D:/Projects/Flutter/library_app/lib/MyStateManagement/local/temp_management.dart';
And when I change the path's import its work for me:
import 'package:library_app/MyStateManagement/local/temp_management.dart';
And this issue happened to me when I changed the folder of the Provider file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With