[Xamarin.Forms] C# Markup Extensions

Introducción

En Xamarin.Forms 4.6 han llegado las extensiones de marcado de C#. Se trata de un conjunto (opcional) de métodos de extensión y clases auxiliares para simplificar la creación de interfaces de usuario en Xamarin.Forms usando C#. En este artículo, vamos a crear la misma interfaz usando XAML, C# y las nuevas extensiones de marcado de C# para conocer las posibilidades de las extensiones añadidas.

Crear UI con XAML

Comenzamos creando una pantalla de Login usando XAML:

<Grid
    Padding="12">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Image
        Grid.Row="0"
        BackgroundColor="Gray"
        HorizontalOptions="Center"
        VerticalOptions="Center"
        HeightRequest="100"
        WidthRequest="100"/>
    <Entry
        Grid.Row="1"
        Text="{Binding Username, Mode=TwoWay}"
        Placeholder="Username"/>
    <Entry
        Grid.Row="2"
        Text="{Binding Password, Mode=TwoWay}"
        IsPassword="True"
        Placeholder="Password"/>
    <Label
        Grid.Row="3"
        Text="Forgot Password?"
        FontSize="Micro"
        TextDecorations="Underline"
        HorizontalOptions="End"/>
    <Grid
        Grid.Row="4">
        <Button
            BackgroundColor="White"
            CornerRadius="24"
            Text="LOGIN"
            HeightRequest="60"
            VerticalOptions="Center"
            Command="{Binding SignInCommand}"
            Margin="24, 12"/>
    </Grid>
</Grid>

Crear UI con C#

Vamos a crear lo mismo usando C#:

public class LoginCSharpView : ContentPage
{
    public LoginCSharpView()
    {
        BindingContext = new LoginViewModel();
        BackgroundColor = Color.LightGray;

        var grid = new Grid
        {
            Padding = 12
        };

        grid.RowDefinitions.Add(new RowDefinition());
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition());

        var logo = new Image
        {
            BackgroundColor = Color.Gray,
            HeightRequest = 100,
            WidthRequest = 100,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };

        grid.Children.Add(logo);
        Grid.SetRow(logo, 0);

        var usernameEntry = new Entry
        {
            Placeholder = "Username"
        };
        usernameEntry.SetBinding(Entry.TextProperty, "Username");
        grid.Children.Add(usernameEntry);
        Grid.SetRow(usernameEntry, 1);

        var passwordEntry = new Entry
        {
            IsPassword = true,
            Placeholder = "Password"
        };
        passwordEntry.SetBinding(Entry.TextProperty, "Password");
        grid.Children.Add(passwordEntry);
        Grid.SetRow(passwordEntry, 2);

        var forgotPassword = new Label
        {
            FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
            Text = "Forgot Password?",
            TextDecorations = TextDecorations.Underline,
            HorizontalOptions = LayoutOptions.End
        };
        grid.Children.Add(forgotPassword);
        Grid.SetRow(forgotPassword, 3);

        var signInGrid = new Grid();

        var signInButton = new Button
        {
            BackgroundColor = Color.White,
            CornerRadius = 24,
            HeightRequest = 60,
            Margin = new Thickness(24, 12),
            VerticalOptions = LayoutOptions.Center,
            Text = "LOGIN"
        };
        signInGrid.Children.Add(signInButton);

        grid.Children.Add(signInGrid);
        Grid.SetRow(signInGrid, 4);

        Content = grid;
    }
}

C# Markup Extensions

Pasamos a ver las novedades aportadas por las extensiones añadidas:

public class LoginCSharpMarkupView : ContentPage
{
    enum Row { Logo, Username, Password, Forgot, SignIn };

    public LoginCSharpMarkupView()
    {
        var vm = new LoginViewModel();
        BindingContext = vm;
        BackgroundColor = Color.LightGray;

        Content = new Grid
        {
            Padding = 12,
            RowDefinitions = Rows.Define(
                (Row.Logo, GridLength.Star),
                (Row.Username, GridLength.Auto),
                (Row.Password, GridLength.Auto),
                (Row.Forgot, GridLength.Auto),
                (Row.SignIn, GridLength.Star)),
            Children =
            {
                new Image
                {
                    BackgroundColor = Color.Gray,
                    HeightRequest = 100,
                    WidthRequest = 100,
                    HorizontalOptions = LayoutOptions.Center,
                    VerticalOptions = LayoutOptions.Center
                }.Row(Row.Logo),
                new Entry
                {
                    Placeholder = "Username"
                }.Row(Row.Username).Bind(nameof(vm.Username)),
                new Entry
                {
                    IsPassword = true,
                    Placeholder = "Password"
                }.Row(Row.Password).Bind(nameof(vm.Password)),
                new Label
                {
                    FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
                    Text = "Forgot Password?",
                    TextDecorations = TextDecorations.Underline,
                    HorizontalOptions = LayoutOptions.End
                }.Row(Row.Forgot),
                new Button
                {
                    BackgroundColor = Color.White,
                    CornerRadius = 24,
                    HeightRequest = 60,
                    Margin = new Thickness(24, 12),
                    VerticalOptions = LayoutOptions.Center,
                    Text = "LOGIN"
                }.Row(Row.SignIn).Bind(nameof(vm.SignInCommand))
            }
        };
    }
}

Antes de profundizar en detalles, las extensiones de marcado C# añadidas en Xamarin.Forms 4.6 se encuentran en fase experimental:

Device.SetFlags(new string[]{ "Markup_Experimental" });

Las extensiones para crear la interfaz de usuario usando C# de forma  fluida se encuentran en el espacio de nombres Xamarin.Forms.Markup.

Enlace a datos

Contamos con el método de extension Bind que cuenta con varias sobrecargas para crear un enlace de datos entre la propiedad de una vista y una propiedad especificada.

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Bind(nameof(vm.Password));

Layouts

Al usar un Grid, podemos usar una enumeración para definir filas y columnas, en lugar de usar directamente números. Es necesario utilizar el namespace:

using static Xamarin.Forms.Markup.GridRowsColumns;

Veamos el uso:

var grid = new Grid { 
     RowDefinitions = 
          Rows.Define( (Row.Logo, GridLength.Star), (Row.Username, GridLength.Auto), (Row.Password, GridLength.Auto), (Row.Forgot, GridLength.Auto), (Row.SignIn, GridLength.Star))
};

Además, se puede definir de forma sencilla filas y columnas usando la enumeración:

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Row(Row.Password);

Estilos

Los estilos implícitos se pueden utilizar cargándolos en el diccionario de recursos de la aplicación:

public App()
{
     Resources = Styles.Implicit;
     ...
}

Mientras que los estilos explícitos se pueden usar utilizando el método de extensión Style.

var password = new Entry { IsPassword = true, Placeholder = "Password" }.Style (PasswordStyle);

Otros

Aunque no lo hemos visto en el ejemplo de Login, tenemos bastantes mas extensiones para trabajar con fuentes, efectos, gestos, etc.

Para trabajar con gestos podemos usar el método de extensión BindTapGesture:

var tapLabel = new Label { Text = "Tap Me" } .BindTapGesture (nameof(vm.TapCommand));

Podemos adjuntar efectos usando el método de extensión Effects:

var effectButton = new Button { Text = "Tap Me" }.Effects (new ButtonBordeless());

Para ver más ejemplos y detalles relacionados con las extensiones de marcado, puedes leer la documentación oficial.

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

¿Qué te parecen las extensiones de marcado C#?, ¿creas la interfaz de usuario en Xamarin.Forms usando XAML o C#). Recuerda, puedes dejar cualquier duda o comentario en la entrada!.

Más información

[Build 2020] Streaming en Twitch para ver novedades presentadas

El //Build 2020

Estos días está celebrando el Build 2020, el mayor evento de Microsoft centrado en el desarrollo del año.

//Build 2020

Este año es un encuentro virtual que puedes seguir online y de forma gratuita. Sin duda alguna, hay una enorme cantidad de novedades, así que, ¿algo mejor que verlas con calma juntos?.

Streaming en Twitch

Suelo hacer streamings con código en directo en Twitch de forma habitual. Al ser habituales no suelo anunciarlos en el Blog, solo en redes sociales. Sin embargo, en esta ocasión, tendremos un streaming algo más especial donde vamos a ver con detalles las novedades presentadas por Xamarin.Forms en el Build. El streaming será el próximo Jueves 21 de Mayo desde las 18:30h.

¿Te unes?

Más información

Novedades en Xamarin.Forms presentadas en el Build 2020

El //Build 2020

En estos días, del 19 al 21 de Mayo se está celebrando el Build 2020, el mayor evento de Microsoft centrado en el desarrollo del año.

//Build 2020

Y se han presentado una gran variedad de novedades relacionadas con Xamarin.Forms.

Novedades anunciadas para Xamarin.Forms

Shell 2.0

Shell llegó en Xamarin.Forms con el objetivo de simplificar la creación de la estructura de la aplicación. Tener un Flyout, pestañas, etc.

Shell hacía muchas cosas bien, como simplificar la creación de estructuras, simplificar la navegación o la gestión de la barra de búsqueda. Sin embargo, sabemos que también hay cosas que se debían mejorar. Por poner algunos ejemplos: la abstracción de nombres hacía que aprender Shell fuese más complejo de lo necesario, había escenarios no cubiertos, etc. Con la llegada de Shell 2.0 se busca mejorar todas estas opciones.

<App>
    <App.Routes>
        <Route = "Foo">
            <TabBar Location="Bottom"  IsNavigationRoot="true">
                <TabBar Location="Top" Name="Your Library">
                    <TabBar Location="Top" Name="Music" Route = "Music">
                        <Tab Name="PlayLists" Route="Login">
                        <Tab Name="Artists">
                        <Tab Name="Albums">
                        <NavigationButton Route="Foo">
                    </TabBar>
                    <TabBar Location="Top" Name="Podcasts" Route = "Podcasts">
                        <Tab Name="Episodes">
                        <Tab Name="Downloads">
                        <Tab Name="Shows">
                    </TabBar>
                </TabBar>
                <Tab Name="Home" />
                <Tab Name="Search" />
            </TabBar>
        </Route>
        <Route="Login">
            <MyLoginPage />
        </Route>
    </App.Routes>
    <FlyoutNavigation />
</App>

Shapes

Con la evolución de Xamarin.Forms las posibilidades creando interfaces de usuario o controles personalizados ha ido incrementando. Sin embargo, no tenemos aún la posibilidad de dibujar formas básicas (rectángulo, línea o círculo). Llegan Shapes a Xamarin.Forms.

  • Rectangle: Es una forma con cuatro lados cuyos lados opuestos son iguales. Para crear un rectángulo básico, se debe especificar las propiedades WidthRequest, HeightRequest y Fill.
  • Ellipse: Una elipse es una forma con un perímetro curvo. Para crear una elipse básica, hay que especificar el WidthRequest, HeightRequest y Fill.
  • Line: Permite dibujar una línea entre dos puntos.
  • Polyline: es similar a un polígono ya que el límite de la forma está definido por un conjunto de puntos, pero hay que tener en cuenta que el último punto de la polilínea no está conectado al primero.
  • Polygon: es una forma con un límite definida por un número de puntos arbitrario. El límite se crea conectando una línea desde un punto al siguiente, con el último punto conectado al primero.
  • Path: es ña figura más versátil, ya que se puede usar para definir una geometría arbitraria.

Ejemplo:

<Path
     HeightRequest="100"
     WidthRequest="100"
     Fill="Red"
     Stroke="Red"
     StrokeThickness="3">
     <Path.Data>
          <GeometryGroup>
               <RectangleGeometry
                    Rect="480, 96, 192, 192" />
               <RectangleGeometry
                    Rect="576, 192, 192, 192" />
          </GeometryGroup>
     </Path.Data>
</Path>

Brushes

Un gradiente es la mezcla gradual de un color a otro. En diseño móvil, es un recurso habitual por lo que contar con soporte en Xamarin.Forms era necesario.

En Xamarin.Forms, todas las Views (esto incluye páginas, layouts y vistas) tendrán soporte a Brushes.

Tendremos tres tipos de Brushes:

  • SolidColorBrush: Pinta un colo sólido.
  • LinearGradientBrush: Pinta un degradado que se define a lo largo de una línea. Esta línea se llama eje de gradiente. Puede especificar los colores del degradado y sus ubicaciones a lo largo del eje del degradado utilizando objetos GradientStop.
  • RadialGradientBrush: Pinta un área con un degradado radial que tiene un círculo, junto con un punto focal, para definir el comportamiento del degradado. El punto focal define el centro del gradiente y tiene el valor predeterminado 0.0.

Veamos un ejemplo usando XAML:

<Grid>
    <Grid.Background>
        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Color="Yellow" Offset="0.0" />
            <GradientStop Color="Red" Offset="0.25" />
            <GradientStop Color="Blue" Offset="0.75" />
            <GradientStop Color="LimeGreen" Offset="1.0" />
        </LinearGradientBrush>
    </Grid.Background>
<Grid>

También se podrá utilizar CSS para definir gradientes:

#RootContainer{
    background: linear-gradient(45deg, rgba(218, 64, 244, 0.26) 0%, rgba(218, 64, 244, 0.26) 3%,rgba(184, 81, 207, 0.26) 3%, rgba(184, 81, 207, 0.26) 26%,rgba(149, 97, 169, 0.26) 26%, rgba(149, 97, 169, 0.26) 27%,rgba(115, 114, 132, 0.26) 27%, rgba(115, 114, 132, 0.26) 46%,rgba(80, 130, 94, 0.26) 46%, rgba(80, 130, 94, 0.26) 87%,rgba(46, 147, 57, 0.26) 87%, rgba(46, 147, 57, 0.26) 100%),linear-gradient(0deg, rgba(247, 80, 105, 0.26) 0%, rgba(247, 80, 105, 0.26) 1%,rgba(223, 84, 119, 0.26) 1%, rgba(223, 84, 119, 0.26) 11%,rgba(199, 88, 133, 0.26) 11%, rgba(199, 88, 133, 0.26) 46%,rgba(174, 91, 147, 0.26) 46%, rgba(174, 91, 147, 0.26) 54%,rgba(150, 95, 161, 0.26) 54%, rgba(150, 95, 161, 0.26) 73%,rgba(126, 99, 175, 0.26) 73%, rgba(126, 99, 175, 0.26) 100%),linear-gradient(90deg, rgb(74, 13, 231) 0%, rgb(74, 13, 231) 18%,rgb(96, 13, 230) 18%, rgb(96, 13, 230) 21%,rgb(119, 13, 229) 21%, rgb(119, 13, 229) 26%,rgb(141, 13, 228) 26%, rgb(141, 13, 228) 32%,rgb(163, 12, 226) 32%, rgb(163, 12, 226) 44%,rgb(185, 12, 225) 44%, rgb(185, 12, 225) 56%,rgb(208, 12, 224) 56%, rgb(208, 12, 224) 64%,rgb(230, 12, 223) 64%, rgb(230, 12, 223) 100%)
}

Usando Brushes

TabView

Podemos tener pestañas usando Shell. Sin embargo, ¿qué sucede si queremos tener pestañas anidadas dentro de una sección específica (Ejemplo: dentro de un Grid)?, ¿qué pasa si queremos personalizar completamente cada pestaña?. En estos casos, necesitaríamos un Custom Renderer, hasta ahora …

TabView es una forma de mostrar un conjunto de pestañas, útil para mostrar varios contenidos al tiempo que ofrece al usuario la capacidad de personalizar todo.

Las características principales de este nuevo control son:

  • Posibilidad de personalizar cada pestaña, el tabstrip y el contenido.
  • Poder personalizar transiciones entre pestañas, etc.
  • Pestañas cíclicas.
  • Lazy loading.
  • Soporte a Badge.
  • Etc.

Veamos algunos ejemplos.

Pestañas básicas

<TabView 
    TabStripPlacement="Bottom"
    TabStripBackgroundColor="Blue">
    <TabViewItem
        Icon="triangle.png"
        Text="Tab 1">
        <Grid 
            BackgroundColor="Gray">
            <Label
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Text="TabContent1" />
        </Grid>
    </TabViewItem>
    <TabViewItem
        Icon="circle.png"
        Text="Tab 2">
        <Grid>
            <Label    
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Text="TabContent2" />
        </Grid>
    </TabViewItem>
</TabView>

Pestañas básicas

Usando TabItemsSource

Se pueden tener Tabs desde una fuente de información directamente:

<TabView
    TabItemsSource="{Binding Monkeys}"
    TabViewItemDataTemplate="{StaticResource TabViewItemTemplate}"
    TabContentDataTemplate="{StaticResource TabContentTemplate}" />

TabItemsSource

Pestañas personalizadas

<ControlTemplate
    x:Key="TabItemTemplate">
    <Grid>
    ...
    </Grid>
</ControlTemplate>

<TabView>
    <TabViewItem
        Text="Tab 1"
        ControlTemplate="{StaticResource TabItemTemplate}">
    </TabViewItem>
</TabView>

Personalizar pestañas

Pestañas cíclicas

<TabView
    IsCyclical="True">
    ...
</TabView>

Pestañas cíclicas

Se pueden personalizar transiciones, animaciones, estados visuales, etc.

<TabView>
    <TabView.TabTransition>
        <local:CustomTabTransition />
    </TabView.TabTransition>
    <TabViewItem
        Text="Tab 1">      
            <TabViewItem.TabAnimation>
                <local:CustomTabViewItemAnimation />
            </TabViewItem.TabAnimation>
        <Grid 
            BackgroundColor="LawnGreen">
            <Label
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Text="TabContent1" />
        </Grid>
    </TabViewItem>
    ...
</TabView>

Personaliza animaciones, transiciones, etc

AppBar

Una AppBar consiste en una barra de navegación y potencialmente otras vistas además de poder exponer una o más acciones.

El principal beneficio de usar AppBar son las opciones de personalización como:

  • Altura de barra personalizada.
  • Incluir cualquier contenido sin restricciones ni limitaciones (márgenes, tamaño, etc.).
  • Barra transparente.
  • Etc.

Veamos un ejemplo:

<AppBar 
     BarHeight="120"
     BarBackgroundColor="Transparent">
     <AppBar.TitleView>
     ...
     </AppBar.TitleView>
</AppBar>

Personalizar la barra de navegación

Roadmap

Se continuarán lanzando nuevas releases cada 6 semanas (como hasta ahora). De modo que:

  • Xamarin.Forms 4.7 llegará en Junio 2020
  • Xamarin.Forms 4.8 llegará en Agosto de 2020
  • Xamarin.Forms 4.9 llegará en Septiembre 2020

Introducción de  .NET Multi-platform App UI

Introducción de  .NET Multi-platform App UI

Partiendo de la evolución de Xamarin.Forms llega a .NET, Multi-platform App UI, MAUI. Se trata de un framework, evolución de Xamarin.Forms, que permitirá crear interfaces de usuario nativas para escritorio y dispositivos móviles usando una base de código común y un único proyecto.

En .NET podrás encontrar:

  • System.Maui: Evolución de lo que puedes encontrar hoy día en Xamarin.Forms.
  • System.Devices: Evolución de lo que puedes encontrar hoy día en Xamarin.Essentials.

Un vistazo a MAUI

Se podrá usar Visual Studio para Windows, Visual Studio para macOS y también Visual Studio Code para crear proyectos y trabajar con MAUI. De igual forma, llega el soporte CLI (command line interface):

dotnet new maui

Las claves

Las claves de MAUI son:

  • Interfaz de usuario multi-plataforma nativa.
  • Evolución de Xamarin.Forms.
  • Base de código común, un único proyecto.
  • Usará .NET 6 (previews a final de 2021).

Los objetivos fundamentales son:

  • Mejorar el rendimiento.
  • Mejorar las posibilidades a la hora de extender controles. Extender de forma más sencilla.
  • Habilitar otras opciones como Model-View-Update (MVU).

La UI

Podrás definir la UI en MAUI usando XAML o C#.

Desarrollo más versátil y simplificado

Otro cambio importante que llegará con MAUI es la simplificación del desarrollo. Pasaremos a tener un único proyecto:

Proyecto único

Que tendrá la siguiente pinta:

Configuración del proyecto

Podremos elegir el despliegue entre diferentes dispositivos o emuladores aunque tengamos un único proyecto:

Despliegue

Probablemente te estarás preguntando, ¿y que ocurre con los recursos de la aplicación como imágenes, etc?.

Gestión de recursos simplificada

El tooling gestionará fuentes compartidas en cada plataforma así como la gestión y creación de imágenes adaptadas a cada plataforma.

¿Y si necesito código específico por cada plataforma?. De igual forma, será posible gestionar de forma sencilla código específico:

Código por plataforma

¿Quieres ver un pequeño ejemplo de todo?

Proyecto único

Xamarin.Forms vs MAUI

¿Cuáles son las diferencias entre Xamarin.Forms y MAUI?.

Xamarin.Forms – MAUI

De la tabla anterior podemos ver diferencias básicas como:

  • Maui tendrá soporte oficial a macOS y Windows!.
  • Soporte a MVU oficial.
  • Soporte a multi-ventana.
  • Soporte a multi-targeting.
  • Proyecto único!.
  • Soporte a utilizar Visual Studio Code y CLI.

Nueva arquitectura de renderers

Uno de los cambios más importantes que llegará en MAUI es una nueva arquitectura de renderers simplificada que se beneficia del multi-targeting y la nueva funcionalidad de proyecto único.

Veamos un ejemplo.

EntryRenderer

public partial class EntryRenderer {
   public static PropertyMapper<IView> ViewMapper = new PropertyMapper<IView> {
	
     // Add your own method to map to any property         
     [nameof(IView.BackgroundColor)] = MapBackgroundColor

   };
}

EntryRenderer.iOS.cs

// You don’t need to register a new renderer.
public partial class EntryRenderer
{
     // You know what method to call because you named it!
   public static void MapBackgroundColor (IViewRenderer renderer, IView view)
     {
        // You don’t need to call any base methods here or worry about order.
   
        // Every renderer is consistent; you know where the native view is.
          var nativeView = (NativeView)renderer.NativeView;
          var color = view.BackgroundColor;

          if (color != null) {

            // Phew! That was easy!	        
            nativeView.BackgroundColor = UIColor.FromRGB (204, 153, 255);
          }
     }
}

Novedades en Shell

Shell llegó en Xamarin.Forms con el objetivo de simplificar la creación de la estructura de la aplicación. Tener un Flyout, pestañas, etc.

Shell hacía muchas cosas bien, como simplificar la creación de estructuras, simplificar la navegación o la gestión de la barra de búsqueda. Sin embargo, sabemos que también hay cosas que se debían mejorar. Por poner algunos ejemplos: la abstracción de nombres hacía que aprender Shell fuese más complejo de lo necesario, había escenarios no cubiertos, etc. Con la llegada de Shell 2.0 se busca mejorar todas estas opciones.

Novedades en animaciones

Se añade IDispatcher en IAnimatable lo que hará las animaciones compatibles con multi-ventana, etc.

DI

La gestión de dependencias se alineará con la experiencia Microsoft siendo igual a ASP.NET Core por ejemplo. Se usará Microsoft.Extensions.DependencyInjection.

Mejoras en rendimiento

La nueva arquitectura de renderers simplificada es uno de los cambios que ayudará a mejorar el rendimiento (sin eventos, etc.).

Por otro lado, llegarán cambios a vistas actuales con un impacto alto en el rendimiento. Un ejemplo claro ocurre en la gestión de imágenes. En Android se usará GlideX:

Comparativa entre Image de Xamarin.Forms y GlideX

Más opciones para personalizar la UI!

Probablemente conoces Visual de Xamarin.Forms. Cuando utilizamos Visual, se utiliza el renderizado personalizado en cada plataforma en lugar del renderizado predeterminado. Podemos conseguir utilizar Material de forma sencilla. En Maui llegarán novedades para especificar diferentes comportamientos de una View usando Visual. Ejemplo: Un botón Outline (con bordes) o de tipo Text (solo texto, sin bordes).

Puedes leer más acerca de las novedades de Visual en este enlace.

Además MAUI tendrá:

  • Opciones para personalizar bordes: Poder definir color o grosor de bordes en vistas como Frame o un Picker por ejemplo.
  • De igual forma, se podrán crear bordes redondeados.
  • Soporte a Brushes: poder crear gradientes en cualquier View.
  • Soporte a Shadows (sombras).
  • Podremos crear formas básicas o hacer Clipping en vistas usando una forma específica.
  • Se podrá personalizar con plantillas pestañas o la barra de navegación.
  • Tendremos transiciones!.

Otras novedades

  • Soporte oficial a macOS y Windows (Desktop).
  • Mejoras en triggers y behaviors.
  • Mejoras en Layouts.
  • Más gestos.
  • Etc.

Roadmap

El roadmap básico de MAUI es el siguiente:

  • MAUI tendrá previews desde Q4 de 2020 hasta el Q3 de 2021.
  • MAUI Release Candidate en Septiembre de 2021.
  • MAUI disponible en Noviembre de 2021.

Queremos tu ayuda para mejorar!

Sí, puedes ayudarnos!. ¿Qué cómo nos podrías ayudar?, puedes revisar todas las Specs publicadas y darnos tu feedback, etc.

¿Y qué ocurre con Xamarin.Forms?

Xamarin.Forms continuará evolucionando y mejorando en las sucesivas versiones 4.7, 4.8, etc hasta el lanzamiento de MAUI con .NET 6. Después de eso, Xamarin.Forms continuará recibiendo soporte durante 12 meses.

Más información

[Xamarin.Forms] App Themes

Temas

Los dispositivos móviles incluyen la opción de usar un tema claro u oscuro a nivel de sistema operativo. Las aplicaciones pueden detectar y responder al cambio del tema del sistema.

NOTA: El tema del sistema puede cambiar por diversos motivos dependiendo de la la configuración del dispositivo. Además de la elección explícita por parte del usuario, puede cambiar en base a factores ambientales como un nivel bajo de luz.

En Xamarin.Forms 4.6 se añade AppTheme. Ahora las aplicaciones de Xamarin. Forms pueden responder a los cambios de tema usado por el sistema y en este artículo vamos a ver como hacerlo.

Definir y utilizar recursos por tema

Utilizando AppThemeColor podemos definir un color para los temas del sistema Light y Dark.

<AppThemeColor x:Key="ThemeColor" Light="DarkRed" Dark="LightPink" />

Podemos usar el color como hemos estado haciendo hasta ahora:

<Label 
     Text="AppTheme Color"
     TextColor="{DynamicResource ThemeColor}"/>

De forma automática se aplicará el color rojo oscuro usando el tema claro, y rosa claro al usar el oscuro. También podemos definir un AppThemeColor usando estilos utilizando la extensión de marcado OnAppTheme:

<Style x:Key="OSThemeStyle" TargetType="Label" >
     <Setter Property="TextColor" Value="{OnAppTheme Black, Light=DarkRed, Dark=LightPink}" />
</Style>

Aplicamos el estilo:

<Label 
     Text="Using Style"
     Style="{DynamicResource OSThemeStyle}"/>

Detectar el tema actual usado por el sistema

Podemos saber el tema usado por el sistema utilizando la propiedad Application.RequestedTheme:

OSAppTheme currentTheme = Application.Current.RequestedTheme;

Obtenemos un valor de tipo enumeración OSAppTheme. La enumeración puede tener uno de los siguientes valores:

  • Unspecified, que indica que el dispositivo está utilizando un tema no especificado.
  • Light, que indica que el dispositivo está usando el tema claro.
  • Dark, que indica que el dispositivo está usando el tema oscuro.

Reaccionar al cambio de tema

El tema usado por el puede cambiar y desde nuestra aplicación, podemos detectar el cambio. Para detectar el cambio de tema podemos usar el evento Application.RequestedThemeChanged:

Application.Current.RequestedThemeChanged += (s, a) =>
{
     AppTheme requestedTheme = a.RequestedTheme;
};

Puedes encontrar un ejemplo de AppTheme en GitHub:

Ver GitHub

¿Qué te parece esta nueva API?. Recuerda, puedes dejar un comentario con cualquier pregunta o duda!.

Más información

[Xamarin.Forms] Primer vistazo al RadioButton

Introducción

En Xamarin.Forms 4.6 llega el esperado RadioButton. En este artículo vamos a conocer las características fundamentales del nuevo control.

RadioButton

RadioButton es un tipo de botón que permite seleccionar una opción de un conjunto. Cada opción está representada por un RadioButton y solo se puede seleccionar una opción del grupo.

RadioButton llega en fase experimental lo que significa que para poder utilizarlo se necesita establecer el flag RadioButton_Experimental para usar el control.

Veamos un ejemplo básico:

<RadioButton
     IsChecked="True"
     Text="Red"
     TextColor="Red"/>

Las propiedades básicas de RadioButton son:

  • IsChecked: Define si RadioButton está seleccionado o no. El valor por defecto es false.
  • GroupName: Define el nombre del grupo que indica qué RadioButtons se excluyen mutuamente. El valor por defecto es null.

Ambas propiedades son BindableProperties, lo que significa que soportan enlaces de datos.

Veamos un ejemplo más complejo:

<StackLayout>
     <RadioButton
          x:Name="RedRadioButton"
          GroupName="Colors"
          IsChecked="True"
          Text="Red"
          TextColor="Red"
          CheckedChanged="OnRedRadioButtonCheckedChanged"
          Clicked="OnRedRadioButtonClicked"/>
     <RadioButton 
          x:Name="BlueRadioButton"
          GroupName="Colors"
          Text="Blue"
          TextColor="Blue" 
          CheckedChanged="OnBlueRadioButtonCheckedChanged"
          Clicked="OnBlueRadioButtonClicked"/>
     <RadioButton 
          x:Name="GreenRadioButton"
          GroupName="Colors"
          Text="Green"
          TextColor="Green" 
          CheckedChanged="OnGreenRadioButtonCheckedChanged"
          Clicked="OnGreenRadioButtonClicked"/>
</StackLayout>

NOTA: Los RadioButton sin la propiedad GroupName establecida pero definidos dentro del mismo contenedor estarán agrupados de forma implícita.

Los tres RadioButtons están agrupados dentro del mismo grupo (al seleccionar uno de ellos, vamos a deseleccionar el resto). Además, en el control tenemos definidos varios eventos. El evento CheckedChanged se desencadena cuando cambia la propiedad IsChecked. CheckedChangedEventArgs  tiene una única propiedad Value que indica el valor de IsChecked.

void OnRedRadioButtonCheckedChanged(object sender, CheckedChangedEventArgs e)
{

}

Por otro lado, contamos con el evento Clicked que se desencadena cada vez que se pulsa un RadioButton.

Además el control RadioButton define otras propiedades como (no son todas las propiedades disponibles, solo las principales):

  • Text: El texto a mostrar.
  • TextColor: Color del texto.
  • Command: Define un comando que se ejecuta al seleccionar el RadioButton.
  • CommandParameter: Parámetro asociado al comando.

Puedes encontrar el ejemplo utilizado en el artículo en GitHub:Ver GitHub

¿Qué te parece el control RadioButton?. Si tienes alguna duda o pregunta recuerda que puedes añadir comentarios al artículo.

Más información

[Quedada Virtual] CartujaDotNet

Quedada

En CartujaDotNet, grupo de usuarios .NET de Sevilla solemos realizar quedadas informales de forma periódica. Se trata de sencillamente quedar para charlar abiertamente sobre próximos eventos, tecnologías Microsoft, Xamarin, herramientas utilizadas, intercambiar impresiones, etc. En estos momentos difíciles donde todos debemos ser responsables y el distanciamiento social es sumamente importante no podemos hacer este tipo de actividades…

Quedada virtual

Por supuesto que podemos hacer este tipo de actividades de forma virtual!. Mañana Sábado 09 de Mayo, tendremos nuestra quedada virtual de 12:00h a 14:00h.

¿Te animas?

Tanto si eres desarrollador .NET de Sevilla como si no, estas invitado!.

Más información

  • ¿Quieres unirte a la quedada?. Tan solo tienes que usar este enlace.

[Xamarin.Forms] EmptyView llega a BindableLayouts

Introducción

En Xamarin.Forms tenemos un tipo especial de View llamada Layout. Un Layout es un contenedor para otros elementos permitiendo ayudar a posicionar y gestionar el tamaño de los elementos que contiene. En Xamarin.Forms contamos con una gran variedad de Layouts:

Layouts

Los más utilizados son el StackLayout y el Grid, y suele ser habitual hacer una composición de varios así como utilizarlos para crear controles, etc. Por ejemplo, en ocasiones se utiliza la combinación de ScrollView y StackLayout junto con ContentViews para crear un pequeño listado de elementos horizontal.

Bindable Layouts

Con la llegada de Xamarin.Forms 3.5 nos llegó Bindable Layout. En toda clase derivada de Layout<T>, contamos con las siguientes propiedades:

  • ItemsSource: De tipo IEnumerable, soporta el enlace de una colección de datos.
  • ItemTemplate: De tipo DataTemplate, permitirá definir la apariencia visual de cada elemento.
  • ItemTemplateSelector: De tipo ItemTemplateSelector, permite poder elegir entre diferentes templates para cada elemento en base a ciertas condiciones.

Las propiedades resultan familiares conociendo otros controles en Xamarin.Forms como el ListView o CollectionView.

Utilizando Bindable Layout

Vamos a hacer uso de Bindable Layout con un FlexLayout para crear una galería de imágenes y así evitar definir N imágenes dentro del Layout.

<FlexLayout
     BindableLayout.ItemsSource="{Binding Profile.Gallery}"
     BindableLayout.ItemTemplateSelector="{StaticResource GalleryItemTemplateSelector}"
     Style="{StaticResource GalleryStyle}" />

Al contar con la necesidad de tener imágenes de diferente tamaño en la galería, vamos a utilizar la propiedad ItemTemplateSelector para utilizar diferentes plantillas:

<styles:GalleryItemTemplateSelector x:Key="GalleryItemTemplateSelector">
     <styles:GalleryItemTemplateSelector.MediumGalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}"
                    Aspect="AspectFill"
                    StyleClass="photo, medium"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.MediumGalleryItemTemplate>
     <styles:GalleryItemTemplateSelector.BigGalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}"
                    Aspect="AspectFill"
                    StyleClass="photo, big"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.BigGalleryItemTemplate>
     <styles:GalleryItemTemplateSelector.GalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}" 
                    Aspect="AspectFill"
                    StyleClass="photo"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.GalleryItemTemplate>
</styles:GalleryItemTemplateSelector>

Veamos el resultado completo:

El resultado

EmptyView

Lo visto hasta aquí de BindableLayout resuelve una gran variedad de situaciones de forma sencilla pero…¿qué ocurre si no hay datos?. Aquí entran en juego los Converters, enlace a propiedades de tipo bool para ocultar y mostrar elementos visuales, etc.

Esto era necesario… hasta ahora. Con Xamarin.Forms 4.6-pre4 nos llega el soporte a EmptyView en BindableLayouts. BindableLayouts (al igual que previamente lo hicieron CollectionView o CarouselView) define las siguientes propiedades que se pueden usar para proporcionar contenido visual cuando no hay datos para mostrar:

  • EmptyView, de tipo object, cadena, enlace o vista que se mostrarán cuando la propiedad ItemsSource sea nula o esta vacía.
  • EmptyViewTemplate, de tipo DataTemplate, plantilla que se va a utilizar para dar formato al EmptyView.

Siguiendo nuestro ejemplo anterior, vamos a definir EmptyView:

<BindableLayout.EmptyView>
     <Grid>
          <Label
               Text="No images"
               HorizontalOptions="Center"/>
     </Grid>
</BindableLayout.EmptyView>

De modo que, cuando no fotos en la galería:

EmptyView en BindableLayouts

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

¿Qué te parece esta nueva funcionalidad incluida en Layouts Bindable?. Recuerda, puedes dejar cualquier duda o comentario en la entrada!.

Más información

[Xamarin.Forms] Primer vistazo a Expander

Expander

Como en las últimas versiones de Xamarin.Forms, llega nueva versión y viene con algun nuevo control que permita expandir las posibilidades. En la versón 4.6-pre4 recibimos varias novedades de peso entre las que se incluyen el control Expander.

Expander proporciona un contenedor expandible para alojar cualquier contenido de modo que, se puede mostrar u ocultar este contenido interactuando con el encabezado.

Contamos con dos vistas diferenciadas:

  • Header: Cabecera que permitirá controlar el estado del control (si se encuentra expandido, colapsado, etc).
  • Content: El contenido que se puede expandir.

Veamos el uso básico del control:

<Expander>
     <Expander.Header>
          <Label Text="Header" />
     </Expander.Header>
     <ContentView x:Name="content"/>
</Expander>

De esta forma inicializamos y tenemos en el árbol visual tanto a la cabecera como al contenido. Sin embargo, también podemos hacer lazy loading del contenido usando ContentTemplate:

<Expander>
     <Expander.Header>
          <Label Text="Header" />
     </Expander.Header>
     <Expander.ContentTemplate>
          <DataTemplate>
               <ContentView x:Name="content"/>
          </DataTemplate>
     </Expander.ContentTemplate>
</Expander>

Podemos saber en todo momento el estado gracias a la propiedad IsExpanded (booleano que indica si el Expander esta expandido o no) y State. Los posibles valores de State son:

  • Expanding
  • Expanded
  • Collapsing
  • Collapsed

Una de las características fundamentales del control es el comportamiento al expandir o contraer, por ello, tenemos control en el tiempo y función Easing a utilizar en ambos casos:

  • ExpandAnimationLength: Duración de la animación al expandir.
  • CollapseAnimationLength: Duración de la animación al contraer.
  • ExpandAnimationEasing: Easing utilizado al expandir.
  • CollapseAnimationEasing: Easing utilizado al contraer.

NOTA: También por supuesto contamos con comando (Command y CommandParameter) y eventos (Tapped) para controlar cuando cambiamos el estado del Expander.

UI Challenge usando Expander

Una forma divertida de probar nueva funcionalidad, es hacer un UI Challenge utilizándola. En esta ocasión, vamos a crear una lista de la compra personalizada usando el nuevo control Expander basado en este diseño de Hila Peleg.

El diseño

Los retos del ejemplos

Los retos para conseguir esta lista de la compra son los siguientes:

  • Tenemos elementos organizados por categorías a los que accedemos expandiendo la categoría. Claramente, aquí el nuevo control Expander nos va a ayudar a conseguir la clave de la UI.
  • El indicator de cada categoría (círculo que rodea al icono de cada categoría) se expande al expandir los detalles. Para conseguir esos bordes redondeados podríamos usar un Frame sin necesidad de ningun plugin, o bien, podemos conseguir fácilmente utilizando PancakeView. Pero…¿como expandir?. Si recuerdas, el control Expander cuenta con una propiedad que nos indica si esta expandido o no. Vamos a utilizar esa propiedad para modificar CornerRadius y otras propiedades usando DataTriggers!.

Expandir elementos del listado de la compra

Comenzamos creando el listado de elementos. Dado que tendremos un listado relativamente pequeño de elementos (que se podrán expandir), vamos a utilizar Bindable Layout:

<ScrollView>
     <StackLayout
          BindableLayout.ItemsSource="{Binding Path=Items}">
          <BindableLayout.ItemTemplate>
               <DataTemplate>
                    <Expander>
                    ...
                    </Expander>
               </DataTemplate>
          </BindableLayout.ItemTemplate>
     </StackLayout>
</ScrollView>

Hemos definido un StackLayout que se encargará de apilar cada Expander. Fíjate que como padre hemos definido un ScrollView que nos permita tener scroll en caso necesario.

Así por cada categoría dentro del listado de la compra utilizaremos un Expander:

<Expander 
     ExpandAnimationEasing="{x:Static Easing.Linear}"
     CollapseAnimationEasing="{x:Static Easing.Linear}"
     IsExpanded="{Binding IsDetailVisible, Mode=TwoWay}">

Definimos Las propiedades básicas relacionadas con el comportamiento de expandir y contraer (animaciones) así como enlazar la propiedad clave para saber el estado del mismo, IsExpanded.

Pasamos a definir la cabecera:

<Expander.Header>
     <Grid 
     Style="{StaticResource ExpandeLayoutStyle}">
     <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
     </Grid.ColumnDefinitions>
     <Grid.RowDefinitions>
          <RowDefinition Height="*" />
          <RowDefinition Height="Auto" />
     </Grid.RowDefinitions>
          <pancakeview:PancakeView
               Grid.Column="0"
               BackgroundColor="{Binding Color}"
               Style="{StaticResource CollpasedColorSytle}">
               <Image
                    Aspect="AspectFit"
                    Source="{Binding Icon}"/>
          </pancakeview:PancakeView>
          <Label 
               Grid.Column="1"
               Text="{Binding Name}"
               Style="{StaticResource ExpanderTitleTextStyle}"/>
     </Grid>
</Expander.Header>

Definimos la imagen de la categoría junto al texto de la misma. Ahora pasamos al contenido.

<StackLayout 
     BindableLayout.ItemsSource="{Binding Items}">

Usaremos de nuevo BindableLayouts para definir cada listado correspondiente a una categoría.

El indicador de cada categoría

¿Te has fijado en la animación del borde que rodea al icono de una categoría al expandir y contraer?. Para conseguir el resultado vamos a combinar:

  • La propia animación de expandir y contraer de Expander.
  • Cambiaremos la forma del borde usando DataTriggers.
<pancakeview:PancakeView.Triggers>
     <DataTrigger
          TargetType="pancakeview:PancakeView" Binding="{Binding IsDetailVisible}" Value="True">
          <Setter Property="Style" Value="{StaticResource ExpandedColorSytle}" />
     </DataTrigger>
     <DataTrigger
          TargetType="pancakeview:PancakeView" Binding="{Binding IsDetailVisible}" Value="False">
          <Setter Property="Style" Value="{StaticResource CollpasedColorSytle}" />
     </DataTrigger>
</pancakeview:PancakeView.Triggers>

De forma sencilla, aplicamos diferentes estilos basándonos en el estado del Expander. Al estar expandido, la cabecerá mostrará el icono rodeado por un borde que se unirá con otro borde que tendremos en los detalles (Contenido) del expander.

¿El resultado?.

ShoppingList

Puedes encontrar el código en GitHub:

Ver GitHub¿Qué te parece el control Expander?, ¿echas en falta algo?. Personalmente creo que Expander cubre unas necesidades concretas pero que se ven en cierta medidas en Apps. Me agrada ver como se incrementan el conjunto de posibilidades ofrecidas directamente por Xamarin.Forms sin necesidad de plugins.

Recuerda, cualquier tipo de feedback es bienvenido en los comentarios de la entrada.

Más información

[Xamarin UI Challenge] Flights UI

El reto

Volvemos a por un reto de interfaz de usuario con Xamarin.Forms. En esta ocasión, crear un efecto Pull To Refresh personalizado.

La UI a replicar

Los retos del ejemplo

Los retos para conseguir un Pull to Refresh personalizado son los siguientes:

  • Pull to Refresh: Contamos con el control RefreshView diseñado justamente para hacer pull to refresh. Sin embargo, el contenido al hacer refresco no es personalizable. Así que, para conseguir el resultado deseado vamos a utilizar un SwipeView. El contenido del SwipeView será el listado de vuelos mientras que usaremos un SwipeItemView (soporta una View como contenido) para añadir la animación personalizada.
  • La animación de refresco: Para la animación de refresco vamos a usar Lottie.
  • El listado de vuelos: El listado de vuelos se realizará usando CollectionView.

Listado de vuelos

El listado es de todos los elementos de la interfaz, la parte más sencilla.

<CollectionView
     ItemsSource="{Binding Booking}">
</CollectionView>

Mostramos un listado de vuelos resultados de una búsqueda. En la cabecera mostramos información básica como el número total de resultados. Podemos conseguir añadir la información encima de cada resultado usando la cabecera (Header) del CollectionView.

<CollectionView.Header>
     <Grid
          Padding="0, 12, 0, 0">
          <Label 
               Text="2 Search Results"
               Style="{StaticResource HeaderStyle}"/>
     </Grid>
</CollectionView.Header>

El resultado:

La cabecera del listado de vuelos

Cada resultado de trayecto puede tener múltiples vuelos. Vamos a usar Bindable Layout dentro de cada elemento del CollectionView.

<StackLayout
     BindableLayout.ItemsSource="{Binding Flights}"> 
</StackLayout>

De esta forma adaptaremos cada celda de forma sencilla a mostrar el total de paradas y trayectos de un resultado:

Detalles de cada vuelos

Sencillo, ¿verdad?.

Pull to Refresh personalizado

La clave del ejemplo, el efecto Pull to Refresh personalizado lo conseguimos usando el control SwipeView. SwipeView permite cuatro direcciones para hacer swipe: arriba, abajo, izquierda y derecha. En nuestro caso, queremos hacer pull desde la parte superior y por ello vamos a utilizar TopItems. Podemos usar:

  • SwipeItem: Acción contextual sencilla. Es fácil de configurar (color de fondo, texto e icono), pudiendo ejecutar un comando o evento.
  • SwipeItemView: Permite como contenido una View, por lo que las opciones de personalización son más altas.

¿Sabes que vamos a utilizar en este caso?. Probablemente has acertado, vamos a usar un SwipeItemView donde vamos a añadir nuestra animación personalizada.

<SwipeView
     x:Name="SwipeView"
     SwipeEnded="OnSwipeEnded">
     <!-- PULL TO REFRESH CONTENT -->
     <SwipeView.TopItems>
          <SwipeItemView>
               <Grid
                    HeightRequest="60">
                    <!-- CUSTOM ANIMATION -->
               </Grid>
          </SwipeItemView>
     </SwipeView.TopItems>
     <!-- CONTENT -->
     <Grid>
          <CollectionView
               ItemsSource="{Binding Booking}">
          </CollectionView>
     </Grid>
</SwipeView>

Pasamos a ver como definir la animación personalizada.

Animación usando Lottie

La librería Lottie fue creada por Airbnb inicialmente para iOS, Android y React Native. Sin embargo, gracias a la contrinución de Martijn van Dijk y otros miembros de la comuidad, tenemos soporte en Xamarin y Xamarin.Forms.

Para comenzar a trabajar con Lottie en Xamarin.Forms utilizaremos el paquete NuGet Aribnb.Xamarin.Forms.Lottie:

Install-Package Com.Airbnb.Xamarin.Forms.Lottie

Para completar el proceso de preparación de Lottie en nuestro proyecto Xamarin.Forms, necesitamos añadir la siguiente línea tras la inicialización de Xamarin.Forms en cada proyecto nativo:

AnimationViewRenderer.Init();

Para poder mostrar una animación de Lottie, debemos añadir el archivo JSON a cada proyecto nativo:

  • Android: En la carpeta Assets. El archivo debe tener como acción de compilación AndroidAsset.
  • iOS: En la carpeta Resources. El archivo debe tener como acción de compilación BundleResource.
  • UWP: En la carpeta Assets (o en la raíz), el archivo debe tener como acción de compilación Content.

Para mostrar la animación necesitamos un elemento visual que nos permita visualizar la animación además realizar una gestión de la misma. Este elemento visual es AnimationView.

<SwipeView.TopItems>
     <SwipeItemView>
          <Grid
               BackgroundColor="{StaticResource AccentColor}"
               HeightRequest="60">
               <lottie:AnimationView
                    Animation="pulltorefresh.json"
                    Loop="False" 
                    AutoPlay="True"
                    VerticalOptions="FillAndExpand"
                    HorizontalOptions="FillAndExpand"/>
          </Grid>
     </SwipeItemView>
</SwipeView.TopItems>

En nuestro ejemplo usamos esta gran animación creada por Lenny Miranda jr.

Animación de refresco

Obtener recursos

Un gran fuente de animaciones es LottieFiles. Puedes encontrar una gran variedad de animaciones listas para utilizar con opciones de búsqueda, etc.

LottieFiles

NOTA: Si usas animaciones de LottieFiles, recuerda otorgar crédito a sus creadores.

Con la definición del CollectionView, SwipeView y animación con Lottie, tenemos todo lo necesario. Veamos el resultado final!.

El resultado

Puedes encontrar el código en GitHub:

Ver GitHub

Más información