Saltar al contenido
Ricardo RetamalSoftware y cosas que vale la pena construir
/
Conversemos
← Ver todos los escritos
Software4 min read

Animated Login in .Net MAUI.

Today, I wanted to do something different. I like the concept of using animations in mobile apps to keep them interactive and attractive…

Publicado originalmente en Medium ↗

Spanish version: Login animado en .Net MAUI. Hoy quería hacer algo distinto. Me… | by rretamal | Mar, 2024 | Medium

Today, I wanted to do something different. I like the concept of using animations in mobile apps to keep them interactive and attractive for users, so I said, “I’ll make an animated login. Why not, after all?”

So, I opened my Visual Studio and created a new super project called MauiLoginAnimation, yes, I know, super original.

The project will look as follows:

Next, we need a login screen. For this, right-click on the project -> Add -> New Item and select Content Page. Once you’ve created the file, you’ll see a XAML with the text: “Welcome to .NET MAUI!”

Here I’ll pause for a moment. First, how will we animate the login? Wanting to keep it fun, I wanted to place an animation over the login controls in this style:

Perfect, now we have the idea, how do we animate? Well, my dear friend, I went to LottieFiles. I’m not an expert animator or drawer, so why reinvent the wheel?

I opted for a happy face option and tried to find the same animation style with different states. So, we’ll have one for: a normal state (smiling), a happy state when the user enters the username and password, an annoyed state when the user doesn’t enter anything, and two more states just for fun: one when you touch it and another when you swipe on the image.

LottieFiles gives you a JSON file for each animation, which you should save in Resources -> Raw, and they should look like this:

Next, we need to add a NuGet package to use those animations, so you should install the following:

SkiaSharp.Extended.UI.Maui

And if you don’t have it yet, you need to install the CommunityToolkit to display some messages:

CommunityToolkit.Maui

Next, modify the Program of the solution to use the libraries. So it should look like this:

using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
using SkiaSharp.Views.Maui.Controls.Hosting;


namespace MauiLoginAnimation
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .UseMauiCommunityToolkit()
                .UseSkiaSharp()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });


#if DEBUG
    		builder.Logging.AddDebug();
#endif


            return builder.Build();
        }
    }
}

GitHub Gist ↗

Once that’s done, let’s modify our login Page to look like our mockup, which will finally look like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiLoginAnimation.LoginPage"
             xmlns:skia="clr-namespace:SkiaSharp.Extended.UI.Controls;assembly=SkiaSharp.Extended.UI"
             Title="LoginPage"
             NavigationPage.HasNavigationBar="False"
             BackgroundColor="{AppThemeBinding Light={StaticResource BackgroundColorLight}, Dark={StaticResource BackgroundColorDark}}">
    <Grid Margin="40">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="2*"/>
        </Grid.RowDefinitions>


        <Image Grid.Row="0" Source="icon.png" Aspect="AspectFit" HeightRequest="100" HorizontalOptions="Center" VerticalOptions="Center" />
        <skia:SKLottieView
                        Grid.Row="0"
                        x:Name="faceView"
                        Source="happyface.json"
                        RepeatCount="-1"
                        HeightRequest="150" 
                        HorizontalOptions="Center" 
                        VerticalOptions="Center"                        
                        IsAnimationEnabled="True">
            <skia:SKLottieView.GestureRecognizers>
                <TapGestureRecognizer Tapped="OnLottieViewTapped"/>
                <SwipeGestureRecognizer Direction="Right" Swiped="OnSwipe"/>
                <SwipeGestureRecognizer Direction="Left" Swiped="OnSwipe"/>
            </skia:SKLottieView.GestureRecognizers>
        </skia:SKLottieView>
        
        <VerticalStackLayout Grid.Row="1" Spacing="10">
            <Label Text="Login into our App!" FontSize="20"
                   TextColor="{AppThemeBinding Light={StaticResource TextColorLight}, Dark={StaticResource TextColorDark}}"
                   FontAttributes="Bold" Margin="0,0,0,20"
                   HorizontalOptions="Center"/>


            <Label Text="Email Address"
                   TextColor="{AppThemeBinding Light={StaticResource TextColorLight}, Dark={StaticResource TextColorDark}}"/>


            <Entry x:Name="txtEmail" Placeholder="email@domain.com" Keyboard="Email"
                   TextColor="{AppThemeBinding Light={StaticResource TextColorLight}, Dark={StaticResource TextColorDark}}"
                   PlaceholderColor="{AppThemeBinding Light={StaticResource SecondaryTextColorLight}, Dark={StaticResource SecondaryTextColorDark}}"/>


            <Label Text="Password"
                   TextColor="{AppThemeBinding Light={StaticResource TextColorLight}, Dark={StaticResource TextColorDark}}"/>


            <Entry x:Name="txtPassword" Placeholder="password" IsPassword="True"
                   TextColor="{AppThemeBinding Light={StaticResource TextColorLight}, Dark={StaticResource TextColorDark}}"
                   PlaceholderColor="{AppThemeBinding Light={StaticResource SecondaryTextColorLight}, Dark={StaticResource SecondaryTextColorDark}}"/>


            <Label Text="Forgot password?" HorizontalOptions="End"
                   TextColor="{AppThemeBinding Light={StaticResource SecondaryTextColorLight}, Dark={StaticResource SecondaryTextColorDark}}"
                   FontSize="Small"/>


            <Button x:Name="btnLogin" Text="Log In"
                    BackgroundColor="{AppThemeBinding Light={StaticResource AccentColorLight}, Dark={StaticResource AccentColorDark}}"
                    TextColor="{AppThemeBinding Light={StaticResource BackgroundColorLight}, Dark={StaticResource BackgroundColorDark}}"
                    Margin="0,10,0,0"/>


            <Label HorizontalOptions="CenterAndExpand" Margin="0,10,0,0">
                <Label.FormattedText>
                    <FormattedString>
                        <Span Text="New User? "
                              TextColor="{AppThemeBinding Light={StaticResource SecondaryTextColorLight}, Dark={StaticResource SecondaryTextColorDark}}"/>
                        <Span Text="Create Account"
                              TextColor="{AppThemeBinding Light={StaticResource AccentColorLight}, Dark={StaticResource AccentColorDark}}"/>
                    </FormattedString>
                </Label.FormattedText>
            </Label>


        </VerticalStackLayout>
    </Grid>
</ContentPage>

GitHub Gist ↗

Then, in our code-behind, I added two functions, one to change the animation:

private void ChangeAnimationAndReturn(string newAnimationFile)
{
    // Save the file name of the original animation
    var originalAnimationFile = new SKFileLottieImageSource { File = "happyface.json" };


    // Change the animation of the AnimationView to the new JSON file.
    faceView.RepeatCount = 0;
    faceView.Source = new SKFileLottieImageSource { File = newAnimationFile };
    
    // Subscribe to the AnimationCompleted event to switch back to the original animation
    faceView.AnimationCompleted += (sender, args) =>
    {
        // Switch back to the original animation
        faceView.RepeatCount = -1;
        faceView.Source = originalAnimationFile;
        faceView.IsAnimationEnabled = true;
    };


    faceView.IsAnimationEnabled = true;
}

GitHub Gist ↗

You’ll see that I have a default animation. We specify the number of times it will repeat, given that we only want the animation to run once, we set it to zero. And when the animation completes, we’ll return to the original animation, which is the default state of laughing. People, we enable the view again for the animation to execute.

I also added a second function that serves to display messages. Each change in the animation state will generate a message. If the user enters a username and password, we’ll execute a message. If the user doesn’t enter any username or password, we’ll show another, and we’ll do that with the following function:

private void ShowMessage(string message)
{
    var snackbar = Snackbar.Make(message, visualOptions: new SnackbarOptions()
    {
        BackgroundColor = Color.FromArgb("#FFFCBA03"),
        TextColor = Color.FromArgb("#FF000000"),
        ActionButtonTextColor = Color.FromArgb("#FFFCBA03"),
        Font = Microsoft.Maui.Font.SystemFontOfSize(20, FontWeight.Bold),
        CornerRadius = new CornerRadius(12),


    }, anchor: faceView);
    snackbar.Show();
}

GitHub Gist ↗

In the function, we slightly modify the aspect by changing the background, the text color, and the button to give it a more cartoonish look and anchor it to the animation view.

Now we will execute the animations:

  • When the user clicks on the login:
private async void BtnLogin_Clicked(object? sender, EventArgs e)
{
    if(string.IsNullOrEmpty(txtEmail.Text) && string.IsNullOrEmpty(txtPassword.Text))
    {
        ShowMessage("I think you missed something");


        ChangeAnimationAndReturn("putpassword.json");


        return;
    }


    ShowMessage("Now we are talking!");
    ChangeAnimationAndReturn("cowboyface.json");
}

GitHub Gist ↗

  • When the user taps on the image:
private void OnLottieViewTapped(object sender, EventArgs e)
{
    ShowMessage("Ouch!");


    ChangeAnimationAndReturn("dizzyface.json");
}

GitHub Gist ↗

  • When the user swipes on the image:
 private void OnSwipe(object sender, SwipedEventArgs e)
 {
     ShowMessage("Hahahahaha");
     ChangeAnimationAndReturn("laughingface.json");
 }

GitHub Gist ↗

And this is how the app will finally look when you run it:

Here you can see a small recording of the app working:

If you liked the article, like it and share it.

rretamal/MauiLoginAnimation (github.com)

Happy Coding!