Unreliable navigation to root page
Xamarin.Forms Shell navigation seems well documented but either I don't understand the documentation correctly or it isn't accurate.
The part that confuses me is the description of the following two route formats:
//route - The route hierarchy will be searched for the specified route, upwards from the current position. The matching page will replace the navigation stack.
///route - The route hierarchy will be searched for the specified route, downwards from the current position. The matching page will replace the navigation stack.
First of all, I don't understand the difference between the two. They both seem to always work identically for me. But leaving that aside, the documentation clearly states that the requested page will fully replace the navigation stack. But even that doesn't always happen.
I encountered a reproducible scenario in which the following call doesn't open the requested page:
await Shell.Current.GoToAsync($"//{nameof(ItemsPage)}");
The requested ItemsPage
is still the root of the new navigation stack. But on top of it, an instance of ItemDetailPage
is already opened. How can this be reproduced? By following these steps in a newly created project from the Xamarin.Forms Flyout template:
- Open the
ItemsPage
from the flyout menu. - Navigate to the
ItemDetailPage
which opens on top of theItemsPage
on the same navigation stack. - Open the
AboutPage
from the flyout menu. This switches to a new navigation stack with this page as the only one opened. - Open the
ItemsPage
from the flyout menu. This will switch to the previous navigation stack with both theItemsPage
and theItemDetailPage
still opened.
While this could be intentional behavior, I don't think it matches the documentation (as quoted above). It also interfered with the way I wanted navigation to work in my application. Fortunately, I found a workaround with which I could reliably achieve that after following the steps above the ItemsPage
would be the only one opened on its own navigation stack.
For that to work, I had to navigate to the root of the navigation stack before switching to the other navigation stack at step 3 above using the following code:
await Shell.Current.GoToAsync($"///{nameof(ItemsPage)}", false);
await Shell.Current.GoToAsync($"///{nameof(AboutPage)}");
The first call navigates back to the root of the navigation stack if there are any other pages open on top of it (ItemDetailPage
in my case). Its second parameter disables the navigation animation. The second call then navigates to the page we actually want to open.
To try the described behavior yourself, check my GitHub repository. It's a slightly modified project created from the Xamarin.Forms Flyout template with buttons which make it easy to follow both the steps reproducing the issue and the fix for the issue.
Based on my experience, Xamarin.Forms Shell navigation doesn't always work as documented (or at least not as I understand it). In this blog post I described a workaround for the unexpected behavior I encountered in my application. At least in my case it worked great.