Skip to content

Instantly share code, notes, and snippets.

@kevinmutlow
Last active January 31, 2024 08:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save kevinmutlow/aee36441f6e9f594cdb246d7dd0253cd to your computer and use it in GitHub Desktop.
Save kevinmutlow/aee36441f6e9f594cdb246d7dd0253cd to your computer and use it in GitHub Desktop.
Xamarin.Forms Login page sample
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.Core.Pages.Account.LoginPage"
Title="{ Binding PageTitle }">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
</OnPlatform>
</ContentPage.Padding>
<Grid
RowSpacing="0"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
<RowDefinition Height="6*" />
</Grid.RowDefinitions>
<BoxView
Grid.Row="0"
BackgroundColor="#00CCD6" />
<Image
Grid.Row="0"
Source="brand_logo.png"
VerticalOptions="Center"
HorizontalOptions="Center"
HeightRequest="100"
WidthRequest="100" />
<Label
Grid.Row="1"
Text="LOGIN"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="Center" />
<StackLayout
Grid.Row="2"
VerticalOptions="Fill"
HorizontalOptions="Center"
Spacing="15">
<StackLayout HorizontalOptions="Center" Spacing="5">
<Label
Text="Email:" />
<Entry
Text="{ Binding Email }"
Keyboard="Email"
WidthRequest="300" />
</StackLayout>
<StackLayout HorizontalOptions="Center" Spacing="5">
<Label
Text="Password:" />
<Entry
Text="{ Binding Password }"
WidthRequest="300"
IsPassword="True" />
</StackLayout>
</StackLayout>
<StackLayout
Grid.Row="2"
VerticalOptions="End"
HorizontalOptions="Start"
Margin="10,0,0,15"
Spacing="15">
<Label Text="Forgotten Password?" >
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding ForgotPasswordCommand }" />
</Label.GestureRecognizers>
</Label>
<Label Text="Need an Account?" >
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding NewAccountCommand }" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
<Button
Grid.Row="2"
Text="LOGIN"
HorizontalOptions="End"
VerticalOptions="End"
Margin="15,10"
Command="{ Binding LoginCommand }" />
<!-- MODAL BACKGROUND LAYER -->
<BoxView
Grid.Row="0"
Grid.RowSpan="3"
BackgroundColor="Black"
Opacity="0.8"
IsVisible="{ Binding IsBusy }" />
<ActivityIndicator
Grid.Row="0"
Grid.RowSpan="3"
VerticalOptions="Center"
HorizontalOptions="Center"
Color="White"
IsRunning="{ Binding IsBusy }"
IsVisible="{ Binding IsBusy }" />
<!-- CANCEL BUTTON (appears after X seconds after login) -->
<StackLayout
Grid.Row="0"
Grid.RowSpan="3"
VerticalOptions="Center"
TranslationY="100"
Margin="50,0"
IsVisible="{ Binding IsShowCancel }">
<Label
Text="This is taking a little longer than expected, please hang in there..."
FontSize="Small"
TextColor="White"
LineBreakMode="WordWrap"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"/>
<Button
Text="Cancel"
Command="{ Binding CancelLoginCommand }"
HorizontalOptions="Center" />
</StackLayout>
</Grid>
</ContentPage>
using App.Core.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace App.Core.Pages.Account
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public LoginPage ()
{
InitializeComponent ();
BindingContext = new LoginPageViewModel(Navigation);
}
}
}
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace App.Core.ViewModels
{
public class LoginPageViewModel : BasePageViewModel
{
private readonly INavigation Navigation;
public LoginPageViewModel(INavigation _navigation)
{
Navigation = _navigation;
PageTitle = "Login";
}
#region Properties
private string _email;
public string Email
{
get { return _email; }
set
{
if (SetPropertyValue(ref _email, value))
{
((Command)LoginCommand).ChangeCanExecute();
}
}
}
private string _password;
public string Password
{
get { return _password; }
set
{
if (SetPropertyValue(ref _password, value))
{
((Command)LoginCommand).ChangeCanExecute();
}
}
}
private bool _isShowCancel;
public bool IsShowCancel
{
get { return _isShowCancel; }
set { SetPropertyValue(ref _isShowCancel, value); }
}
#endregion
#region Commands
private ICommand _loginCommand;
public ICommand LoginCommand
{
get { return _loginCommand = _loginCommand ?? new Command(LoginAction, CanLoginAction); }
}
private ICommand _cancelLoginCommand;
public ICommand CancelLoginCommand
{
get { return _cancelLoginCommand = _cancelLoginCommand ?? new Command(CancelLoginAction); }
}
private ICommand _forgotPasswordCommand;
public ICommand ForgotPasswordCommand
{
get { return _forgotPasswordCommand = _forgotPasswordCommand ?? new Command(ForgotPasswordAction); }
}
private ICommand _newAccountCommand;
public ICommand NewAccountCommand
{
get { return _newAccountCommand = _newAccountCommand ?? new Command(NewAccountAction); }
}
#endregion
#region Methods
bool CanLoginAction()
{
//Perform your "Can Login?" logic here...
if (string.IsNullOrWhiteSpace(this.Email) || string.IsNullOrWhiteSpace(this.Password))
return false;
return true;
}
async void LoginAction()
{
IsBusy = true;
//TODO - perform your login action + navigate to the next page
//Simulate an API call to show busy/progress indicator
Task.Delay(20000).ContinueWith((t) => IsBusy = false);
//Show the Cancel button after X seconds
Task.Delay(5000).ContinueWith((t) => IsShowCancel = true);
}
void CancelLoginAction()
{
//TODO - perform cancellation logic
IsBusy = false;
IsShowCancel = false;
}
void ForgotPasswordAction()
{
//TODO - navigate to your forgotten password page
//Navigation.PushAsync(XXX);
}
void NewAccountAction()
{
//TODO - navigate to your registration page
//Navigation.PushAsync(XXX);
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment