Implementando el framework XF.Material Library en XF

Que onda estimados lectores, para esta entrada y aportando al calendario #AdvientoXamarin he decidido que realicemos una app con una vista principal de Login y una vista secundaria para un Sign up.

Se preguntarán que tiene de especial este login, la idea es utilizar un nuget llamado XF.Material Library el cual estiliza nuestros controles como Material design, hay una gran gama de controles y de personalización, en nuestro caso nos enfocaremos en:
  • MaterialTextField
  • MaterialButton
  • Material Dialogs
  • MaterialSwitch
  • Entre otras cosillas que le den una mejor visualización a nuestra app
En este ejemplo estaremos implementando además:
  • MVVM (sin framework jejeje eso lo dejamos para otra entrada con PRISM)
  • Comandos para la comunicación de acciones
  • Tips de clean code

1- Creación del proyecto y actualización de nugets

Comencemos por crear el proyecto Archivo>Nuevo>Nuevo proyecto con esto podras ver la siguiente ventana, vamos a ubicarnos en el menú lateral y la opción Cross Platform una vez ahí seleccionaremos la opción Mobile App (Xamarin Forms), damos el nombre a la app en este caso yo le puse XamarinAdvientoApp, por último presionamos el botón de OK.


Ahora pasamos a la siguiente imagen en donde debes establecer el alcance que tendrá en cuanto a plataformas android, ios, uwp de momento y para este ejemplo solo no seleccionaremos uwp, nuestra aplicación será en blanco y por último la estrategía de código compartido será por .NET Standard



Ahora, es cuestión de esperar unos momentos... ¿Un café para la espera?

Si todo va bien e intentas correr la app, deberás poder algo así

Antes de continuar...
Hay que darle orden a nuestro proyecto como parte de un clean code, en mi caso he creado dos carpetas una para los ViewModels y otra para las Vistas, también he movido mi vista que trae por default el proyecto a la carpeta Views


2- Actualización de nugets en nuestra solución
Como buena práctica, antes de comenzar a codificar acostumbra a actualizar tus nugets, sé lo que te digo, después puede ser un dolor de cabeza. Para ello basta con un click derecho en la solución y después la opción de administrar nugets de la solución.



Aquí basta con ir actualizando cada nuget en cada proyecto, en mi caso tengo que actualizar 6 paquetes entre ellos Xamarin.Forms

Procura que antes de actualizar el nuget de Xamarin.Forms primero actualices manualmente los nuget android, ese es consejo :) 
Al final de todo podrás ver algo así

Aprovechando que estamos en los nugets, realiza la busqueda del nuestro XF.Material Library, recuerda instalarlo en los 3 proyectos de nuestra solución (por si acaso tú debes elegir el que tiene más de 30k de descargas).

3- Configuración del nuget en los 3 proyectos
Es necesario realizar la inicialización del nuget en cada proyecto de la solución, comencemos por el App.cs

Para el caso de android , primero debes compilar con la última version de android disponible, en mi caso lo tengo con Android 9, de otra manera el espacio de nombre Droid no esta disponible y no podrás terminar la inicialización en esta plataforma.



Para terminar y a pesar que no es el alcance de esta entrada, configuremos iOS, en el archivo AppDelegate

Hasta ahora lo más tedioso es la actualización de los nugets, si te fijas lo demás es pan comido :) continuemos.

4- Diseño de interfaz XAML
Comencemos por implementar nuestra pantalla de login, en la que tendremos 5 controles:
  • Una caja de texto para el usuario
  • Una caja de texto para la contraseña
  • Un botón para iniciar sesión
  • Un botón para el signup
Para las cajas de texto usaremos MaterialTextField.
Para el botón usaremos MaterialButton.
Para el botón de signup usaremos MaterialChip

4.1- Lo primero es agregar el espacio de nombre con el cual accederemos a los controles, deberás asignar un nombre (en nuestro caso ui) al espacio de nombres, el cual es el siguiente:



xmlns:ui="clr-namespace:XF.Material.Forms.UI;assembly=XF.Material"
       

Asi mismo el siguiente código XAML es el nos dará como resultado nuestro login
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XamarinAdvientoApp.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:XamarinAdvientoApp"
    xmlns:ui="clr-namespace:XF.Material.Forms.UI;assembly=XF.Material"
    BackgroundImageSource="background.jpg">

    <StackLayout>
        <ui:MaterialTextField
            AlwaysShowUnderline="True"
            BackgroundColor="Transparent"
            ErrorText="{Binding Usuario.MensajeError}"
            HasError="{Binding Usuario.Error}"
            HelperText="Campo obligatorio"
            InputType="Text"
            Placeholder="Usuario"
            Text="{Binding Usuario.Nick}" />
        <ui:MaterialTextField
            AlwaysShowUnderline="True"
            BackgroundColor="Transparent"
            ErrorText="{Binding Usuario.MensajeError}"
            HasError="{Binding Usuario.Error}"
            HelperText="Campo obligatorio"
            InputType="Password"
            Placeholder="Contraseña"
            Text="{Binding Usuario.Pass}" />
        <ui:MaterialButton
            BackgroundColor="#EAEAEA"
            Command="{Binding LoginCommand}"
            HorizontalOptions="Center"
            Text="LOGIN"
            TextColor="Black"
            VerticalOptions="Center" />
        <ui:MaterialChip
            ActionImageTappedCommand="{Binding SignupCommand}"
            BackgroundColor="Green"
            HorizontalOptions="Center"
            Image="signup.png"
            Text="Registrarme"
            TextColor="White" />
    </StackLayout>

</ContentPage>     

Para el apartado del viewmodel estamos haciendo el binding ammm como decirlo nativo, sin framework, esto significa que en el archivo MainPage.xaml.cs se debe agrega la linea de código que hace la unión entre la vista y su viewmodel.
using Xamarin.Forms;
using XamarinAdvientoApp.ViewModels;

namespace XamarinAdvientoApp
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            BindingContext = new MainPageViewModel();
            InitializeComponent();            
        }
    }
}        

A nuestra carpeta de ViewModels hay que agregar una nueva clase llamada MainPageViewModel, la cual contendrá la siguiente lógica de negocios (código) observemos que implementa la interfaz INotifyPropertyChanged para reflejar los cambios en las propiedades que se bindean con nuestros controles en el login.


using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinAdvientoApp.Dtos;

namespace XamarinAdvientoApp.ViewModels
{    
    public class MainPageViewModel: INotifyPropertyChanged
    {
        #region PropiedadesVariables
        string nickDummie = "ArmandoCl", passDummie="ACL2019";
        public ICommand LoginCommand { get; set; }
        public DtoUsuario Usuario { get; set; } = new DtoUsuario();
        #endregion
        #region Metodos
        public MainPageViewModel()
        {
            LoginCommand = new Command(Login);
        }

        private async void Login()
        {
            if (Usuario.Nick.Equals(nickDummie)&&Usuario.Pass.Equals(passDummie))
            {
                Usuario.Error = false;
                Usuario.MensajeError = "";
                await Application.Current.MainPage.DisplayAlert("Bienvenido "+Usuario.Nick, "Su inicio de sesión fue correcto", "Ok");
            }
            else
            {
                Usuario.Error = true;
                Usuario.MensajeError = "Usuario o contraseña incorrectas";
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

Del lado de la página que fungirá como SignUp tenemos el siguiente código XAML, con su ViewModel (tendrás que agregar una nueva vista y un nuevo ViewModel)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XamarinAdvientoApp.Views.SignUpPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:ui="clr-namespace:XF.Material.Forms.UI;assembly=XF.Material"
    Title="Registro"
    BackgroundImageSource="background.jpg">
    <ContentPage.Content>
        <StackLayout>
            <ui:MaterialTextField
                AlwaysShowUnderline="True"
                BackgroundColor="Transparent"
                ErrorText="{Binding Usuario.CorreoMensajeError}"
                HasError="{Binding Usuario.CorreoError}"
                HelperText="Campo obligatorio"
                InputType="Text"
                Placeholder="Usuario"
                Text="{Binding Usuario.Correo}" />
            <ui:MaterialTextField
                AlwaysShowUnderline="True"
                BackgroundColor="Transparent"
                ErrorText="{Binding Usuario.MensajeError}"
                HasError="{Binding Usuario.Error}"
                HelperText="Campo obligatorio"
                InputType="Text"
                Placeholder="Usuario"
                Text="{Binding Usuario.Nick}" />
            <ui:MaterialTextField
                AlwaysShowUnderline="True"
                BackgroundColor="Transparent"
                ErrorText="{Binding Usuario.MensajeError}"
                HasError="{Binding Usuario.Error}"
                HelperText="Campo obligatorio"
                InputType="Password"
                Placeholder="Contraseña"
                Text="{Binding Usuario.Pass}" />
            <ui:MaterialButton
                BackgroundColor="#EAEAEA"
                Command="{Binding RegistrarCommand}"
                HorizontalOptions="Center"
                Text="Registrar"
                TextColor="Black"
                VerticalOptions="Center" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>      
En el ViewModel nos quedaría lo siguiente

using System;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinAdvientoApp.Dtos;
using XF.Material.Forms.UI.Dialogs;

namespace XamarinAdvientoApp.ViewModels
{
    public class SignUpPageViewModel : INotifyPropertyChanged
    {
        #region Propiedades

        public DtoUsuario Usuario { get; set; } = new DtoUsuario();
        public ICommand RegistrarCommand { get; set; }

        #endregion
        #region Metodos

        public SignUpPageViewModel()
        {
            RegistrarCommand = new Command(Registrar);
        }

        private async void Registrar()
        {
            try
            {
                if (!string.IsNullOrEmpty(Usuario.Correo) && !string.IsNullOrEmpty(Usuario.Nick) && !string.IsNullOrEmpty(Usuario.Pass))
                {
                    if (!IsValidEmail(Usuario.Correo))
                    {
                        Usuario.CorreoError = true;
                        Usuario.CorreoMensajeError = "El correo no es válido";
                        Usuario.Error = false;
                        Usuario.MensajeError = "";
                    }
                    else
                    {
                        Usuario.CorreoError = false;
                        Usuario.CorreoMensajeError = "";
                        Usuario.Error = false;
                        Usuario.MensajeError = "";
                        using (IMaterialModalPage dialog = await MaterialDialog.Instance.LoadingDialogAsync(message: "Espere un momento..."))
                        {
                            await Task.Delay(5000);
                            dialog.MessageText = "Estamos guardando sus datos";
                            await Task.Delay(5000);
                        }
                        await MaterialDialog.Instance.SnackbarAsync(message: "Gracias por registrarse");
                    }
                }
                else if (string.IsNullOrEmpty(Usuario.Nick) || string.IsNullOrEmpty(Usuario.Pass))
                {
                    Usuario.Error = true;
                    Usuario.MensajeError = "El nick y la contraseña son obligatorios";
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                await MaterialDialog.Instance.SnackbarAsync(message: "Algo no salió bien :(", actionButtonText: "OK");
            }
        }
        private bool IsValidEmail(string email)
        {
            // Return true if strIn is in valid e-mail format.
            return Regex.IsMatch(email, @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
        }
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}      
Para complementar he agregados 3 propiedades más al DtoUsuario, para poder manejar los errores y almacenar el dato del correo.

Sé que el ejemplo es muy simple, pero también sé que hay veces que con algo tan simple y con ver una implementación podemos crear cosas increíbles, o salir de dudas muy fácil, al final de todo deberás poder ver un resultado como el siguiente:




Todo el proyecto que vemos aquí lo podrás encontrar en mi repositorio de GitHub, espero que esta entrada te haya sido útil, puedes complementar el blog comentando que otros frameworks de fácil uso e instalación conoces :)

Comentarios

Entradas populares de este blog

Syncfusion para dummies en Xamarin.Forms

Clean code, ¿Qué es? ¿Cómo usarlo?