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