Xamarin Forms Navigation: The Current State

My most recent blog post centered around my discoveries within Forms, relating primarily to Appearance and Navigation.  Truthfully, I felt the Navigation system had to be improved beyond what 1.3 provided for the platform to be viable.

After speaking with members of the Forms team, I decided to dig deeper into Navigation to try and get a better sense of where the team was.  Here are some of my findings:

Do I Push or PushModal?

Understanding the difference between these two methods is core to creating efficient navigation systems with Forms. Separating the conversation from the visual notion (which WinPhone and Android have no concept of), we have to ask the question, what does it mean for something to be modal?

In terms of Forms its all about the NavigationStack.  When we Push a new page onto our stack, it is considered part of the primary flow of the application.  This means, Push a page will give you soft back functionality in Android and iPhone (Windows Phone has no concept of us, so its irrelevant).  However, when we Push a Modal, it is considered a new root page, thus a new Navigation context.

However, while this is without a “gotcha” on iOS, both Windows Phone and Android must contend with a hardware back button which can dismiss the modal.  This is important if your modal is going to hide a main page until login is accepted, a common paradigm on all platforms.

In general, if you intend to navigate to one page from another in a non-modal fashion you will want to wrap your page in a NavigationPage.  If you only intend to show one page, you can dispense with this, although on iOS it may cause the screen to look funny due to being too close to the status bar.

What does it mean to be a root page?

The simple answer is, the first page in the stack is the root.  It is a page that the user either cannot go back from or doing so closes the application (Windows Phone, Android).  In Forms, the method PopToRootAsync will remove all pages except for the first page in the Navigation stack.  Remember, if you are within a Modal, it has a separate Navigation context than your main app.

Sometimes, you may need to alter the root page of your application.  This is a common practice on both Windows Phone and Android to support backing out of an application. To support this, the InsertPageBefore method exists.  This allows you to insert a new page before another page.

Navigation.InsertPageBefore(new FourthPage(), Navigation.NavigationStack.First());
Navigation.PopToRootAsync(true);

In this case, we are inserting FourthPage as the first page in the application (root), and then doing a PopToRoot call which will land us on this new page and alter our root.

Note: At the time of this writing this line was no universally supported as I will explain later

Can I remove a Page?

Yes, but there are some restrictions.  Oddly, this is one area of the Navigation that is very odd.  You would think you should be able to do something like this:

Navigation.PushAsync(new ThirdPage());
var page = Navigation.NavigationStack.OfType().FirstOrDefault();
Navigation.RemovePage(page);

But this causes an unexplained crash on Android, though it does work on iOS and Windows Phone.  The way that I have found around it is to perform the removal on the actual action, like so:

Page page = Navigation.NavigationStack.OfType().First();
Navigation.RemovePage(page);


Navigation.PopAsync();

But, it perplexes me why Android seems unable to do this.  The obvious problem this creates is removal logic is now on a page which could be used elsewhere when the page in question would not be in the stack.

The role of NavigationPage

The NavigationPage is what provides the soft navigation at the top that is essential to iOS apps and complimentary in Android apps.  In general, you will see many applications start off like this:

public App()
{

     MainPage = new NavigationPage(new FirstPage());
}

This is quite necessary on both iOS and Android since it will allow to push additional pages onto the Navigation stack; otherwise you get an exception.  Windows Phone is the only one capable of operating without it.

Oddly, using NavigationPage at all renders InsertPageBefore completely useless on Windows Phone, as it will throw an exception whenever invoked; as of this writing I have yet to make this call work cross platform as a means redefine my root page.

Thinking about Navigation with Forms

When it comes to writing applications with Forms, Navigation is going to have to be something you consider.  In my opinion, you need to first understand how NavigationContext changes between simply pushing a new page onto the stack versus opening that same page in a modal view.

While calling PushModal does work well it fails to disallow the back button to be used to dismiss these modals on Android and Windows Phone; rendering it unfit to show a Login screen.  At this point in time, it may be wise to consider approaches which change the root view of your application; but this has cross platform implications as it may not work on Android/iOS or may not work on WinPhone.

Remember, Forms is always about generalization, and that needs to be considered in deciding whether Forms is appropriate for your app, as well as how its various features will be affected.

Missing Features

I really think some additional methods need to be defined to aid developers in manipulating the NavigationStack in common ways.  For example, changing the root page in a NavigationConext.  Or marking a page as closed after leaving (similar to NoHistory) in Android.

Another feature which would be helpful is chaining.  My colleague Greg came up with a great example:

Page A –> Page B –> open Page C modally –> Page D –> Page E –> Page A

While this can be done in the existing infrastructure, it requires a solid understanding of what Forms is doing under the hood. The code below, when executed from Page E will return us to Page A.

private void ButtonClick(object sender, EventArgs ev)
{
    var modalStack = Navigation.ModalStack;
    var parentNavigation = modalStack.First().Navigation;

    parentNavigation.PopToRootAsync(true);
	Navigation.PopModalAsync();
}

The thing, again, to understand is this idea of context that the Forms team has created.  Think of each Modal as a navigation context; this means that each application will always have at least one Modal, and it does.

The Navigation property associated with a ContentPage is associated with the navigation context the page resides within, so, if we can get access to this property within that context we can affect the navigation inside that context; hence the code above first removes all Pages from our parent navigation stack until only the root remains, then we we pop off the child modal.

While this is workable it is not 100% clear.  I think it would be better if we were allowed to do some sort of chaining with these calls; I think the code would be a lot cleaner this way.

Continuing Problems

My problems continue to be focused around organizing navigation so that it makes sense on the various platforms being targeted.  The simple truth is, the way the user interacts with the device and navigates through the app may not be something that can be completely generalized, but with these methods it does appear that we neednt worry about dropping into the platform specific code, as my previous post suggested.

I am looking forward to the next major release to see what additional enhancements the team has made in this area.

Advertisements

11 thoughts on “Xamarin Forms Navigation: The Current State

    • Yup, I noticed that as well. I think its becasue, PopToRoot implies a stack architecture, which Android and iOS both use. Windows Phone uses a global navigation concept, so there isnt really a stack per se. At least that is my impression

      Like

  1. Hi. Very interesting sample. I have a problem when I try to do something like
    Page A –> Page B –> open Page C modally –> Page D

    If I am in Page C modally and try to do a Navigation.PushAsync, I have the

    “PushAsync is not supported globally” error, in iOS and Android… I am not able to find a workaround

    Like

    • Whenever you intend to use Push navigation you must make sure your page is wrapped with a NavigationPage otherwise you will get the error you mentioned. Once you wrap it, you can customize the look if you so choose to drop the NavigationBar. However, take care doing this as iOS relies on this to allow the user to navigate back – you will have to make sure such a mechanism is available if you choose to hide the NavBar.

      Cheers,

      Jason

      Like

  2. Hi Jason, is this still the current state of play in Xamarin Forms navigation? The navigation is currently confusing the heck out of me, but if what you’re saying in this article is still current then I clearly have totally misunderstood the model up ’til now!

    Like

  3. Navigation.PushAsync(new MainPage());
    PushAsync is not supported globally on iOS, please use a NavigationPage”
    im getting this error in visualStidio 2015 please some1 help

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s