[Android] Fatal exception after adding modal window to navigation stack
Description
After adding a modal popup window to the main page's navigation stack, a fatal exception is generated (Android only):
Steps to Reproduce
- Create a standard .NET MAUI application (v8.0.6).
- Modify the App() constructor in App.xaml.cs to load the MainPage as a navigation page:
MainPage = new NavigationPage(new MainPage());
- Add a map control to the applications main page.
- Add the following to the MainPage() constructor in MainPage.xaml.cs:
Loaded += MainPage_Loaded;
- In the Loaded event handler, push a new page to the modal navigation stack:
Navigation.PushModalAsync(new Page());
The navigation causes a Fatal Exception (See Relevant log output).
Link to public reproduction project repository
No response
Version with bug
8.0.6
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
8.0.3
Affected platforms
Android
Affected platform versions
Android 14 (API 34)
Did you find any workaround?
No.
Relevant log output
[AndroidRuntime] Shutting down VM
[AndroidRuntime] FATAL EXCEPTION: main
[AndroidRuntime] Process: com.promiles.FuelFinder, PID: 2966
[AndroidRuntime] java.lang.ClassCastException: android.widget.ImageView cannot be cast to android.view.ViewGroup
[AndroidRuntime] at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:537)
[AndroidRuntime] at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:272)
[AndroidRuntime] at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1943)
[AndroidRuntime] at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1839)
[AndroidRuntime] at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1782)
[AndroidRuntime] at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:565)
[AndroidRuntime] at android.os.Handler.handleCallback(Handler.java:958)
[AndroidRuntime] at android.os.Handler.dispatchMessage(Handler.java:99)
[AndroidRuntime] at android.os.Looper.loopOnce(Looper.java:230)
[AndroidRuntime] at android.os.Looper.loop(Looper.java:319)
[AndroidRuntime] at android.app.ActivityThread.main(ActivityThread.java:8893)
[AndroidRuntime] at java.lang.reflect.Method.invoke(Native Method)
[AndroidRuntime] at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
[AndroidRuntime] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
@okirkland-promiles I'm not clear from your selections if this worked in 8.0.3 for you or not.
Not sure, did not test other versions
Hi @okirkland-promiles. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md
This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
What if you use a ContentPage instead of a Page? Or a page derived from it? That still works fine in my application on 8.0.6
(in my application I wait until Shell OnAppearing has been fired, then navigate to my ContentPage from App.xaml.cs, but it should be the same?)
What if you use a ContentPage instead of a Page? Or a page derived from it? That still works fine in my application on 8.0.6
(in my application I wait until Shell OnAppearing has been fired, then navigate to my ContentPage from App.xaml.cs, but it should be the same?)
@jkommeren Thanks for the suggestion, I tried that but same result. I'm adding a sample project that demonstrates the issue.
@okirkland-promiles I'm not clear from your selections if this worked in 8.0.3 for you or not.
Not sure, did not test other versions
@PureWeen Yes it did work fine in 8.0.3 (at least). I did not test prior versions.
@okirkland-promiles I ran your sample on 8.0.3 and it crashes for me there as well.
I noticed you're not awaiting the modal push which will sometimes cause errors to get lost. If you await the push modal call for 8.0.3 do you start getting an exception?
private async void MainPage_Loaded(object? sender, EventArgs e)
{
await Navigation.PushModalAsync(new ContentPage(){ Content = new Label(){ Text = "Hello World" }});
}
Hi @okirkland-promiles. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
@okirkland-promiles I ran your sample on 8.0.3 and it crashes for me there as well.
I noticed you're not awaiting the modal push which will sometimes cause errors to get lost. If you await the push modal call for 8.0.3 do you start getting an exception?
private async void MainPage_Loaded(object? sender, EventArgs e) { await Navigation.PushModalAsync(new ContentPage(){ Content = new Label(){ Text = "Hello World" }}); }
@PureWeen I updated the code as you suggested. Same result, no extra details sorry to say. The error originally showed up in one my apps that pushes modal pages on top of the map from time to time, and that code has always used await, it was just missing from my sample project.
@okirkland-promiles can you create a branch with your working 8.0.3 version?
@okirkland-promiles can you create a branch with your working 8.0.3 version?
@PureWeen I tried creating an 8.0.3 version of the test project, and it breaks just like yours. I can't even get a 7.0.101 version of the test project working.
I also ran tests against my main project (FuelFinder) for both of those versions, and it works in both. Both use the same exact frameworks, nuget package versions and Android API versions in each comparison. So it must be more complicated than different package versions only. I do know it was during the transition from 8.0.3 to 8.0.6 in my main project when it broke, but there were other minor changes too. The modal popup does not work in the main project with 8.0.6, even though the code that opens it is just the same.
I'll look into it more as soon as I get time.
Can reproduce on 8.0.3 and 8.0.6.
Duplicate of #20317
Running some tests here, this seems to work in a Shell app. Looking further as to why there is some image view that needs to be cast at all.
We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.
Issue still occurs on 8.0.40 and it's pretty inconvenient since there are no workarounds...
Been struggling with this also (MAUI version 8.0.40)... In my case I'm migrating from Xamarin.Forms. No Shell used, page hierarchy as follows:
- NavigationPage
- TabbedPage
- MapPage : ContentPage
- OtherContentPage: ContentPage
- TabbedPage
In MapPage, i have a element that opens modal (with Navigation.PushModalAsync() ) when tapped.
Crash occures if I call PushModalAsync first thing after app start. If i navigate to OtherContentPage, return to MapPage and request to PushModalAsync(), the modal will open with no issues.
It seems to be related to the maps element, because if I change the OtherContentPage as default, same modal opens flawlessly, and there is nothing maps related in the modal. Only way to trigger fault is when MapPage is first shown when app starts and the modal is being opened within that page. Even when I call OpenModal on separate button outside map it fails.
Update: Navigation.PushAsync() works, and I'm currently using that as workaround.
To me it seems that some type within Google maps api conflicts with MAUI type?🤔
Stack:
java.lang.ClassCastException: com.google.maps.api.android.lib6.impl.as cannot be cast to android.view.ViewGroup at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:537) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:272) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1943) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1839) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1782) at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:565) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Ok, the Navigation.PushAsync did not work, at least in my case (messed up the navigation stack). On further investigation, i found out similar issue with Xamarin Forms / Maps prior version 2.4.0.91020: Spanish SO thread Of course link to original discussion was broken, but SO had enough data for me to do a another work-around.
So basically what I did was to drop the map component from view just before call to PushModalAsync, and restore it when Modal was popped out. No need to dispose the Map component, just take it out from Layout, push modal, and put it back when done.
protected void RemoveMap() {
#if ANDROID
if (MapContainer == null) // MapContainer contains the Map (Grid in my case)
return;
if (MapComponent == null) // MapComponent is a reference to Map
return;
if (MapComponent.Parent != null)
{
try
{
MapContainer.Remove(MapComponent);
}
catch (Exception ex)
{
// Show error to user
}
}
#endif
}
protected void InsertMap() {
#if ANDROID
if (MapContainer == null || MapComponent == null)
return;
if (MapComponent.Parent == null)
{
try
{
MapContainer.Insert(0, MapComponent);
}
catch (Exception ex)
{
// Show error to user
}
}
#endif
}
Call the RemoveMap() before call to Navigation.PushModalAsync(), and InsertMap after Navigation.PopModalAsync().