How to create a custom pop-up in Xamarin with MvvmCross in the right way
Developers often create a [Dialog/Alert/Interaction]Service
to display pop-ups in Xamarin cross-platform apps. This way is suitable and clean if we need to show an info message or ask the user simple questions with positive and negative answers. But what if we need some complicated UX like a login experience?
Of course, the existing pop-up service could be extended to have inputs and actions:
But think about the complexity of this service’s usage, especially if we need some validation that should be tested somehow.
How can we simplify these pop-ups?
We can use a regular ViewModel with custom platform-dependent presentation that uses UIAlertController
on iOS and DialogFragment
on Android.
This approach is clean and has some advantages:
- The ViewModel layer does not care about the pop-up presentation.
- The ViewModel with pop-up presentation can be tested like any other ViewModel.
- The ViewModel with pop-up presentation can have much more space to implement custom functionality, as it does not depend on some generic service.
I will implement a simple login flow to demonstrate the pop-ups in Xamarin using the MvvmCross framework: when the Login button is clicked, the native pop-up should be presented so the user can enter a login and password.
Let’s create a custom pop-up with the following steps:
- ViewModel implementation
- Android View implementation
- iOS View implementation
ViewModel Implementation
On the ViewModel layer, we have a simple ViewModel that represents an authorization screen. This LoginViewModel
has Username and Password text and Close and Login commands:
There is no actual authentication logic added as this post’s goal is to demonstrate the implementation of the pop-up presentation only. But here are some highlights:
- The Login command returns the text from the Login property as a navigation result and closes the ViewModel
- The Cancel command simply closes this ViewModel
Next, we move on to the Android implementation.
Android View Implementation
Unlike iOS, we can apply the default tools of MvvmCross to display a dialog on the Android platform.
We need a DialogFragment with a custom view that contains Login and Password text fields:
Important things to know when implementing such dialog:
MvxDialogFragmentPresentationAttribute
is applied, so MvvmCross knows how to present this particular View. See the documentation for the additional information.- The
AlertDialog
is created and configured in theOnCreateDialog
method. We useAlertDialog.Builder
to configure positive and negative buttons, title, description, etc. here. The custom content view should be set later, as we apply bindings. - It is impossible to bind the dialog buttons and command in the right way, so we execute the needed commands in button click listeners.
- As soon as the
OnActivityCreated
method is called, we know that our custom view has been inflated, and bindings have been created. Now we set theView
for theAlertDialog
.
The layout for the LoginDialogFragment
contains two TextInputs and binding descriptions to connect the entered text with properties of the LoginViewModel
:
The complete pop-up presentation of the LoginViewModel
should look like this on the Android platform:
iOS View Implementation
The iOS part is a bit more complicated, and a custom presentation should be applied, but the idea is straightforward:
- Implement a custom
MvxAlertPresentationAttribute
that will be handled by the presenter. - Create a base
UIViewController
-derived class that encapsulatesUIAlertController
creation. - Extend the default
MvxIosViewPresenter
to process the new presentation attribute and display the View. - Implement the
LoginViewController
.
1. MvxAlertPresentationAttribute
This custom attribute contains all the needed information to configure the UIAlertController
:
The PreferredStyle
is mandatory and should be set before the alert is presented. The default preferred style is ActionSheet
. Title
and Message
properties are optional and can be assigned later if the alert content is dynamic or some localization mechanism is applied.
2. MvxAlertViewController
The next step is to declare the MvxAlertViewController
that encapsulates the alert creation. We will derive our LoginViewController
from this class later.
The MvxAlertViewController
inherits the MvxViewController
and is responsible for two main features: it creates a UIAlertController
and provides a convenient API to configure this alert.
Technically it sets itself up as the content of the UIAlertController
and configures the right content size:
It is important to set the current view controller as a contentViewController
because it allows us to use its view as the alert content and, for instance, display some validation later.
The PrefferedContentSize
should be set to a small value, or the view controller will take the default content size, and we will have space at the center of the alert:
The custom MvxIosViewPresenter
will take the result of Wrap
method and present the created UIAlertController
.
Now we could take care of users of our MvxAlertViewController
and implement some convenience methods that allow them to configure alert and add text fields and actions.
First of all, the Title
and Message
properties of UIAlertController
should be exposed and made bindable:
public override string Title
{
get => _alertController.Title;
set => _alertController.Title = value;
}public string Message
{
get => _alertController.Message;
set => _alertController.Message = value;
}
Then we need an ability to add text fields:
protected UITextField AddTextField()
{
if (_alertController.PreferredStyle != UIAlertControllerStyle.Alert)
{
return null;
} var textField = default(UITextField); _alertController.AddTextField(view => textField = view); return textField;
}
Finally, here is a method to add actions:
The most interesting part of this method is the call of the CloseModalViewController
method: we should explicitly tell MvvmCross to remove our modal view controller from the navigation stack, or the navigation will be broken.
Note: text fields added to the alert controller can also close it when the Return button is pressed, so be sure to handle it.
The whole source code for this view class and the configuration methods and properties is available here.
3. MvxAlertIosViewPresenter
The new presenter should know how to show and close the view with MvxAlertPresentationAttribute
, so we need to register this attribute in the constructor:
AttributeTypesToActionsDictionary
.Register<MvxAlertPresentationAttribute(
ShowAlertView,
CloseAlertView
);
Then implement the ShowAlertView
and CloseAlertView
methods:
The ShowAlertView
method is the interesting one, we create the ‘real’ view controller using the default mechanism and wrap it into the alert.
Note: we present the alert controller, not the original view controller.
The whole source code for this presenter class is available here.
4. LoginViewController
Finally, we set all the things up, and the LoginViewController
could be implemented:
As you can see, the implementation of the final view controller is pretty simple. So all you need is to:
- Define the
MvxAlertPresentation
attribute with the right preferred style - Bind or set
Title
andMessage
properties - Create and bind text fields
- Create actions
Unfortunately, we cannot bind UIAlertActions
properly without implementing custom binders. These are not the topic of this post, but text fields, title, and message could easily be bound.
The complete pop-up presentation of the LoginViewModel
should look like this on the iOS platform:
In this post, I described how to implement cross-platform, maintainable, and testable pop-ups using Xamarin and MvvmCross.
Of course, this is not a complete login flow even from the UI perspective, and we need to add some validation and error displaying. But that is the topic for the next post, so stay tuned!
Finally, you can download and run a complete solution on GitHub: