Reputation: 5207
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.
Upvotes: 44
Views: 87287
Reputation: 4253
I change ChangeNotifierProvider to the parent context of the material app and my Navigator.pop(context, true); and Navigator.pushNamed(context, "/home"); started to work
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MasterMonitor>(
create: (context) => MasterMonitor(),
child: MaterialApp(
title: 'My APP',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: LoginPage(),
routes: {
'/login': (BuildContext context) => LoginPage(),
'/home': (BuildContext context) => HomePage(
key: GlobalKey(),
//'/about': (BuildContext context) => AboutPage(key: GlobalKey()),
)
}));
}
}
Upvotes: 0
Reputation: 21
//My main.dart
void main() {
final MultiBlocProvider multiBlocProvider = MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>(
create: (context) => AuthBloc(),
),
BlocProvider<LoadCategoriesBloc>(
create: (context) => LoadCategoriesBloc(),
),
BlocProvider<LoadCustomersBloc>(
create: (context) => LoadCustomersBloc(),
),
],
child: const MyApp(),
);
runApp(multiBlocProvider);
}
//Use above bloc providers for load data.
//quotationCreate.dart
BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthSuccessState) {
context.read<LoadCategoriesBloc>().add(LoadCategoriesAction());
context.read<LoadCustomersBloc>().add(LoadCustomersAction());
} else if (state is AuthFailureState) {
Navigator.pushReplacementNamed(context, '/signin');
}
},
),
I used MultiBlocProvider and with authorication for my code and, then I used providers like the above.
Upvotes: 0
Reputation: 1
enter image description here As i am using navigator.push widget in my login page when i run the dart file the error n which the message on screen is written as:Error: Could not find the correct Provider«FavoriteModel above this Consumer Widget This happens because you used a "BuildContext, that does not include the provider Eror: Could not find the correct Provider«FavoriteModels above this Consumer«FavoriteModels Widget This happens because you used a 'BuildContext that does not include. If anyone knows about it please suggest me the solution.
void main() {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
);
APICallService.getProduct();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => FavoriteModel(),
),
ChangeNotifierProvider(
create: (context) => CartProvider(),
),
],
child: const App(),
),
);
}
Upvotes: -1
Reputation: 1643
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.
Upvotes: 5
Reputation: 783
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',
),
);
}
}
Upvotes: 6
Reputation: 76
maybe answer in DetailScreen
.
I don't know whether you resolved this problem.but I just have resolved using a parameter.
frist my problem is , I have a list screen
, a detail screen
, and a listBloc
. I provided listBloc
in list screen
. like this :
class ListScreen extends StatelessWidget {
static const String routeName = '/list_screen';
ListScreen({Key key}) : super(key: key);
static Route route(Map<String, dynamic> args) {
return MaterialPageRoute(
builder: (_) => ListScreen(),
settings: const RouteSettings(name: routeName),
);
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>ListBloc(),
)
]
child: DetailScreen(),
);
}
}
and , i need to use listBloc
in detail screen
's BlocBuilder
.
class DetailScreen extends StatelessWidget {
// you need a context
final BuildContext context;
// get context
const DetailScreen({
Key key,
@required this.context,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// get bloc using your context
final bloc = this.context.read<ListBloc>();
return BlocBuilder<ListBloc, ListState>(
// provide your bloc
bloc: bloc,
builder: (context, state) {}
);
}
}
Upvotes: 0
Reputation: 28060
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
.
Upvotes: 141
Reputation: 1406
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);
);
}),
);
}
Upvotes: 11
Reputation: 2711
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);
},
...
}
}
Upvotes: 8