How can I await modal form dismissal using Xamarin.Forms?

You can do this by triggering an event in your login page and listen for that event before going on, but you want the full TAP support and I second you there. Here's a simple yet working 2 page app that does just this. You'll obviously want to use ContentPage custom subclass and have proper methods instead of my quick Commands, but you get the idea, and it saves me typing.

public static Page GetFormsApp ()
{
    NavigationPage navpage = null;
    return navpage = new NavigationPage (new ContentPage { 
        Content = new Button {
            Text = "Show Login dialog",
            Command = new Command (async o => {
                Debug.WriteLine ("Showing sign in dialog");
                var result = await SignInAsync (navpage);
                Debug.WriteLine (result);
            })
        }
    });
}

static Task<bool> SignInAsync (NavigationPage navpage)
{
    Random rnd = new Random ();
    var tcs = new TaskCompletionSource<bool> ();
    navpage.Navigation.PushModalAsync (new ContentPage {
        Content = new Button {
            Text = "Try login",
            Command = new Command ( o => {
                var result = rnd.Next (2) == 1;
                navpage.Navigation.PopModalAsync ();
                tcs.SetResult (result);
            })
        }
    });
    return tcs.Task;
}

The minor drawback is that the Task<bool> returns before the end of the pop modal animation, but that's:

  1. easy to fix
  2. only an issue if you're awaiting that result to push a new modal Page. Otherwise, meh, just go on.

Override OnAppearing

Firstly, it's worth noting that simply overriding OnAppearing in the calling Page may suffice in many circumstances.

protected override void OnAppearing()
{
    base.OnAppearing();
    ...
    // Handle any change here from returning from a Pushed Page
}

(note that the pushed page's OnDisappearing override is called after the caller's OnAppearing - seems a bit backwards to me!)


AwaitableContentPage

Secondly...this is my take on @Chad Bonthuys answer:

public class AwaitableContentPage : ContentPage
{
    // Use this to wait on the page to be finished with/closed/dismissed
    public Task PageClosedTask { get { return tcs.Task; } }

    private TaskCompletionSource<bool> tcs { get; set; }

    public AwaitableContentPage()
    {
        tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
    }       

    // Either override OnDisappearing 
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        tcs.SetResult(true);
    }

    // Or provide your own PopAsync function so that when you decide to leave the page explicitly the TaskCompletion is triggered
    public async Task PopAwaitableAsync()
    {
        await Navigation.PopAsync();
        tcs.SetResult(true);
    }
}

And then call it thus:

SettingsPage sp = new SettingsPage();
await Navigation.PushAsync(sp);
await sp.PageClosedTask; // Wait here until the SettingsPage is dismissed