Why it's useless trying to pass 'context' to showDialog? - flutter

I'm using the plugin 'Provider' to get some information and when I'm trying to show a dialog and get a info from my Provider, something weird happened.
The code seems like this:
showDialog(
context: context,
builder: (context) {
return LoadingDialog(); //LoadingDialog is one of my own class ,which extends AlertDialog.
}
);
but it throws a error like:
ProviderNotFoundException (Error: Could not find the correct Provider<LoginState> above this LoadingDialog Widget
So I set a breakpoint and checked the context in the class LoadingDialog, but it's like:
StatelessElement(LoadingDialog(dirty))
So i think the context from the outside page failed to pass into this dialog. So later I read the Flutter API doc and it says:
This function takes a builder which typically builds a Dialog widget. Content below the dialog is dimmed with a ModalBarrier. The widget returned by the builder does not share a context with the location that showDialog is originally called from. Use a StatefulBuilder or a custom StatefulWidget if the dialog needs to update dynamically.
So i tried to use the StatefulBuilder,the code seems like this:
showDialog(
context: context,
builder: (context) {
bool tempLoginState = Provider.of<LoginState>(context, listen: true).getState;//my own method returns true.
return AlertDialog(
content: StatefulBuilder(
builder: (context, setState){
//my dialog detail codes
}
)
)
}
);
But actually, the first line in the builder:
bool tempLoginState = Provider.of<LoginState>(context, listen: true).getState;
it doesn't work, because the context here is also the same as
StatelessElement(LoadingDialog(dirty))
So how can I pass the context to the dialog on earth? Thank you for your ansering.

Related

How do I verify an alert dialog has appeared using mockingjay in a widget test?

Using the mockingjay Flutter package, how do I verify in a widget test that an alert dialog has been displayed after e.g. tapping a button? The alert dialog is displayed using the showDialog function.
This is very similar to verifying page navigation behaviour, the thing that caught me out was the showDialog function must have the parameter useRootNavigator set to false, as documented in the example project. Without it the test will fail to detect that the alert dialog was displayed.
showDialog(
context: context,
useRootNavigator: false, // By default this is true, which won't work with Mockingjay
routeSettings: RouteSettings(name: 'verify_checkout_failure'),
builder: (context) {
return AlertDialog(title: Text('Checkout failed'));
});
Then in your test:
await tester.tap(find.byType(PlaceOrderButton));
verify(
() => mockNavigator.push(
any(
that: isRoute<void>(
whereName: equals('verify_checkout_failure'),
),
),
),
);

Trying to use showDialog()/show Pop up on app startup

What I want to achieve: I want to open a pop up explaining my app when it starts.
My approach: As far as I understand it from googling the issue, I should use the showDialog() method. In its most basic form:
showDialog(
context: context,
builder: (context) {
return Text('data');
});
I tried returning actual dialogs (e.g. AlertDialog) but it doesn't change the behavior so I'm just using Text() with a string as a placeholder for now.
The problem:
No matter where I place the showDialog function, it doesn't work as intended (also see scrennshots below):
Placing it in initState: I get an error message about inherited Widgets being called before the initState is done + an explanation about dependiencies I can barely follow.
Placing it in the build method: I get an error message that setState() or markNeedsBuild() gets called while the app is already buildung widgets.
Placing it in DidChangeAppLifeCycleState(): This is actually working and opening the pop when I pause the app and then resume it. It is not opening on app startup though.
Wrapping it in WidgetsBinding.instance!.addPostFrameCallback(): An idea I picked up here: How to show a popup on app start in Flutter. Doesn't change the outcome of the error messages, neither in initState nor in build.
Any ideas?
Screenshots:
From initState:
From build method:
From DidChangeAppLifecycleState (the "succesful" variant:
Will you please try below code in your init method? I hope this may work.
Future.delayed(Duration.zero, () async {
myFunction();
});
Using WidgetsBinding.instance.addPostFrameCallback inside initState perform its inner task after the 1st frame is complete.
addPostFrameCallback Schedule a callback for the end of this frame.
Next issue arise for not having material. You can directly return AlertDialog on builder or wrap with any material widget like Material, Scaffold..
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showDialog(
context: context,
builder: (context) {
return const AlertDialog(
content: Text('data'),
);
},
);
});
}
If you are running version<3 null safety, use WidgetsBinding.instance?.addPostFrameCallback
One of the methods with WidgetsBinding.instance!.addPostFrameCallback() works fine .
If you show a normal show dialog with the press of a button too it will produce the same result.
Here, you need to wrap the text("data") in a dialog widget such as alertDialog or simpleDialog widget as needed and it will display the dialog within the current scaffold as -
WidgetsBinding.instance!.addPostFrameCallback((_) async {
return await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("data"),
);
});
});
I tried adding this in the init state and the dialog pops up fine when I restart the app
Thanks a lot for your answers. I ficed the issue by rewriting with your suggestions; and it works. I tihnk the issue was that I did not have _ or anything else in my WidgetsBinding code. So I did:
WidgetsBinding.instance?.addPostFrameCallback(() {})
instead of
WidgetsBinding.instance?.addPostFrameCallback((_) {})

Provider in different route Flutter

I wanted to upload a picture inside Firebase Storage when I saw this error :
The provider you are trying to read is in a different route.
So I started study what is this Provider and now I understood that I need to include the page that I'm using to upload the picture inside the same route of the ancestor Provider of type Database.
So I had the issue inside "Registration_form" that is created by "LogInPage" through a Navigator like this :
void navigateToRegistrationForm(context) {
Navigator.push(context,
MaterialPageRoute<void>(builder: (BuildContext context) {
return Reg_Form();
}));
return;
}
Now I'm changing the code that is something like :
void navigateToRegistrationForm(context) {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) => Provider<Database>(
create: (_) => FirestoreDatabase(),
builder: (context, child) => Reg_Form(),
)));
return;
}
But I don't understand what I should pass to the create method...I just want to create Reg_Formin the right route to call Firebase functions to put the file inside firebase storage so I think that FirebaseDatabase isn't what suits my needs.
If I understand your issue correctly, you can either move your ChangeNotifierProvider for your Database class higher up in the widget tree by wrapping your MaterialApp like so:
ChangeNotifierProvider<Database> (
builder: (context) => Database(),
child: MaterialApp(...
Or just change the create in what you provided to
create: (_) => Database(),
Either way should give you access to the correct Provider/BuildContext in your Registration form with standard context.read<Database>()...

Dispatch first bloc event or cubit method, on page start inside StatelessWidget

I have 10 buttons in main menu of my app and each of them contains BlocBuilder inside them.
So when I click on those buttons to open a new page, I want to dispatch the first event, but I don't know how. I can change all classes to stateful widget and then call bloc.dispatch(event) inside initialState() function, but I would like to discover another way, and not sure whether it's the best way
In order to trigger the first event/method call inside BlocBuilder I had to add bloc argument and give parameter provided my BlocProvider, only after this I managed to call my method.
BlocBuilder<MyCubit, MyState>(
bloc: BlocProvider.of<MyCubit>(context)..myFunction(),
builder: (BuildContext context, state) {
//Your code...
}
you can use .. operator to add event while declaring like
BlocProvider(
create: (context) => FirstBloc()..add(InitialiEvent()), // <-- first event,
child: BlocBuilder<FirstBloc, FirstState>(
builder: (BuildContext context, state) {
...
},
),
or you can do it inside the initState method as well

Pass context to navigator push to access inherited widget data

I'm trying to access data from an inherited widget placed under the MaterialApp in my tree from a MaterialPageRoute.
When I try to access the data from this route UserModel.of(context).data it say it's null
I get that this is because the UserModel provider should be placed above the whole MaterialApp for it to share the context but it's impossible in my case.
I therefore tried to pass the context from the navigator builder in hope it would give me access to it:
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingsScreen(
appContext: context)),
);
}
and then in SettingScreen
UserModel.of(appContext).data
But it didn't work either...
Any idea on how to do this ?
You can just declare a constructor of SettingsScreen and pass your data in constructor. :)