Replicate a bank application UI using .NET MAUI

Replicate a bank application UI using .NET MAUI

21 July 2022

.NET MAUI/Xamarin

Buy Me A Coffee

It's the middle of summer, and it's time to enter some competitions. Thanks to Matt Goldman who organized the MAUI UI July.

In this article, we will try to replicate the Ukrainian Monobank mobile application using .NET MAUI. Dribbble design.

Let's start by implementing that beautiful gradient background:

<ContentPage.Background>
	<LinearGradientBrush StartPoint="0,1" EndPoint="1,0">
		<GradientStop Color="#515ca6"
						Offset="0.1" />
		<GradientStop Color="#93767b"
						Offset="1.0" />
	</LinearGradientBrush>
</ContentPage.Background>

The main page of the application consists of only one control - CarouselView.

<CarouselView ItemsSource="{Binding Pages}"
			  CurrentItem="{Binding CurrentPage}"
			  ItemTemplate="{StaticResource PageSelector}"
			  IsBounceEnabled="True"
			  Loop="False"/>

The CarouselView contains 3 items-subpages: Rewards, Main, and Card. Because all subpages have different designs and elements we need to provide different DataTemplates for each item. DataTemplateSelector can help us return the correct DataTemplate depending on the CurrentItem.

public class CardsTemplateSelector : DataTemplateSelector
{
	public DataTemplate Rewards { get; set; }
	public DataTemplate Main { get; set; }
	public DataTemplate Card { get; set; }

	protected override DataTemplate OnSelectTemplate(object currentItem, BindableObject container)
	{
		return currentItem switch
		{
			"1" => Rewards,
			"3" => Card,
			_ => Main,
		};
	}
}
<ContentPage.Resources>
	<local:CardsTemplateSelector x:Key="PageSelector"
		                                     Rewards="{StaticResource RewardsTemplate}"
		                                     Main="{StaticResource MainTemplate}"
		                                     Card="{StaticResource CardTemplate}" />
</ContentPage.Resources>

Now we need to create DataTemplate for each page. Let's start with Rewards:

<DataTemplate x:Key="RewardsTemplate">
	<CollectionView ItemsSource="{Binding Source={x:Reference Name=CardsPage},Path=BindingContext.Rewards}"
					x:DataType="models:Reward"
					ItemSizingStrategy="MeasureFirstItem">
		<CollectionView.Header>
			<VerticalStackLayout Spacing="25">
				<Image Source="dotnet_bot.png"
							WidthRequest="200"
							HeightRequest="200"
							Margin="0,50,0,15"/>
				<Label Text="You opened 10 rewards" HorizontalOptions="Center"/>
				<IndicatorView ItemsSource="{Binding Source={x:Reference Name=CardsPage},Path=BindingContext.Pages}"
									Position="0"
									IsEnabled="False"
									HorizontalOptions="Center"
									Margin="0,15,0,30"/>
			</VerticalStackLayout>


		</CollectionView.Header>
		<CollectionView.ItemsLayout>
			<GridItemsLayout Orientation="Vertical"
									Span="3"
									VerticalItemSpacing="20"/>
		</CollectionView.ItemsLayout>
		<CollectionView.ItemTemplate>
			<DataTemplate>
				<VerticalStackLayout Spacing="10">
					<Border Style="{StaticResource RewardStyle}"
								Stroke="{Binding IsAchieved, Converter={StaticResource BoolToColorConverter}}">
						<Image Source="{Binding Image}"
									WidthRequest="50"
									HeightRequest="50"/>
					</Border>
					<Label Text="{Binding Text}"
							HorizontalOptions="Center"/>
				</VerticalStackLayout>
			</DataTemplate>
		</CollectionView.ItemTemplate>
	</CollectionView>
</DataTemplate>

<Style TargetType="Border" x:Key="RewardStyle">
	<Setter Property="Stroke" Value="LightGray"/>
	<Setter Property="Background" Value="Gray"/>
	<Setter Property="StrokeThickness" Value="4"/>
	<Setter Property="WidthRequest" Value="50"/>
	<Setter Property="HeightRequest" Value="50"/>
	<Setter Property="StrokeShape">
		<Setter.Value>
			<Ellipse/>
		</Setter.Value>
	</Setter>
</Style>

<Style TargetType="IndicatorView">
	<Setter Property="IndicatorColor" Value="#ada8c0"/>
</Style>

The page is pretty simple. Please pay attention to CollectionView.ItemSizingStrategy. In our cases, all items have the same size, so we do not need to measure each element in the collection. It improves the performance of the app.

Now we can move on and create MainDataTemplate. This page displays the card balance and transactions list ordered by and grouped by transaction DateTime.

<Style TargetType="Border" x:Key="MainActionsBorderStyle">
	<Setter Property="Stroke" Value="Black"/>
	<Setter Property="Background" Value="Black"/>
	<Setter Property="WidthRequest" Value="75"/>
	<Setter Property="HeightRequest" Value="75"/>
	<Setter Property="StrokeShape">
		<Setter.Value>
			<Ellipse/>
		</Setter.Value>
	</Setter>
</Style>

<Style TargetType="Label" x:Key="MainActionsLabelStyle">
	<Setter Property="FontSize" Value="30"/>
	<Setter Property="Padding" Value="15"/>
	<Setter Property="FontFamily" Value="FASolid"/>
	<Setter Property="HorizontalOptions" Value="Center"/>
	<Setter Property="VerticalOptions" Value="Center"/>
</Style>

<DataTemplate x:Key="MainTemplate">
	<ScrollView>
		<VerticalStackLayout Spacing="25">
			<Label Text="More" HorizontalOptions="Center" Margin="0,20,0,0"/>
			<Label Margin="30,70,30,0">
				<Label.FormattedText>
					<FormattedString>
						<Span Text="1 234" FontSize="50"/>
						<Span Text=".00" FontSize="30"/>
						<Span Text=" $" FontSize="30"/>
					</FormattedString>
				</Label.FormattedText>
			</Label>
			<Grid Margin="30,0,0,0" ColumnDefinitions="110,70" RowDefinitions="*,*">
				<Label Grid.Row="0" Grid.Column="0" Text="Personal money:" />
				<Label Grid.Row="0" Grid.Column="1" HorizontalOptions="End" Text="1234.00$" />
				<Label Grid.Row="1" Grid.Column="0" Text="Credit limit:" />
				<Label Grid.Row="1" Grid.Column="1" HorizontalOptions="End" Text="0.00$"/>
			</Grid>
			<IndicatorView ItemsSource="{Binding Source={x:Reference Name=CardsPage},Path=BindingContext.Pages}"
							Position="1"
							IsEnabled="False"
							HorizontalOptions="Center"
							Margin="0,70,0,20"/>
			<toolkit:UniformItemsLayout>

				<VerticalStackLayout>
					<Border Style="{StaticResource MainActionsBorderStyle}">
						<Label Text="{x:Static fonts:FontAwesomeIcons.Download}"
								Style="{StaticResource MainActionsLabelStyle}"/>
					</Border>
					<Label Text="Transfer money" HorizontalOptions="Center"/>
				</VerticalStackLayout>

				<VerticalStackLayout>
					<Border Style="{StaticResource MainActionsBorderStyle}">
						<Label Text="{x:Static fonts:FontAwesomeIcons.PaperPlane}"
								Style="{StaticResource MainActionsLabelStyle}"/>
					</Border>
					<Label Text="Transfer money" HorizontalOptions="Center"/>
				</VerticalStackLayout>

				<VerticalStackLayout>
					<Border Style="{StaticResource MainActionsBorderStyle}">
						<Label Text="{x:Static fonts:FontAwesomeIcons.SquarePlus}"
								Style="{StaticResource MainActionsLabelStyle}"/>
					</Border>
					<Label Text="Other payments" HorizontalOptions="Center"/>
				</VerticalStackLayout>
			</toolkit:UniformItemsLayout>

			<Grid RowDefinitions="20,*" Margin="0,20,0,0">
				<Border
					Stroke="#1e1e1e"
					Background="#1e1e1e"
					HeightRequest="40"
					VerticalOptions="Start">
					<Border.StrokeShape>
						<RoundRectangle CornerRadius="70,70,0,0"/>
					</Border.StrokeShape>
				</Border>
				<CollectionView ItemsSource="{Binding Source={x:Reference Name=CardsPage},Path=BindingContext.Transactions}"
							Background="#1e1e1e"
							Grid.Row="2"
							IsGrouped="true">
					<CollectionView.Header>
						<Grid Margin="30,0,30,10"
							ColumnDefinitions="*,*,*">
							<Border
								HorizontalOptions="Start"
								Background="#2c2c2c"
								WidthRequest="40"
								HeightRequest="40">
								<Border.StrokeShape>
									<Ellipse/>
								</Border.StrokeShape>
								<Label Text="{x:Static fonts:FontAwesomeIcons.ChartSimple}"
										FontFamily="FASolid"
										Padding="7"
										HorizontalOptions="Center"
										VerticalOptions="Center"/>
							</Border>
							<Label Grid.Column="1" HorizontalOptions="Center" VerticalOptions="Center"  Text="Today"/>
							<Border
								Grid.Column="2"
								HorizontalOptions="End"
								Background="#2c2c2c"
								WidthRequest="40"
								HeightRequest="40">
								<Border.StrokeShape>
									<Ellipse/>
								</Border.StrokeShape>
								<Label Text="{x:Static fonts:FontAwesomeIcons.MagnifyingGlass}"
										FontFamily="FASolid"
										Padding="7"
										HorizontalOptions="Center"
										VerticalOptions="Center"/>
							</Border>
						</Grid>
					</CollectionView.Header>

					<CollectionView.GroupHeaderTemplate>
						<DataTemplate>
							<Grid>
								<Label Text="{Binding Date, StringFormat='{0:dd MMMM yyyy}'}"
										HorizontalOptions="Center"/>
							</Grid>
						</DataTemplate>
					</CollectionView.GroupHeaderTemplate>

					<CollectionView.ItemTemplate>
						<DataTemplate>
							<Grid ColumnDefinitions="50,*,0.2*" RowDefinitions="*,*" Margin="30,10">
								<Label Grid.Row="0" Grid.Column="0" Grid.RowSpan="2"
									Text="{x:Static fonts:FontAwesomeIcons.PaperPlane}"
									FontFamily="FASolid"
									VerticalOptions="Center"
									FontSize="30"/>
								<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>
								<Label Grid.Row="1" Grid.Column="1" Text="{Binding Description}"/>
								<Label Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2"
									Text="{Binding Sum}"
									HorizontalOptions="End"
									VerticalOptions="Center"/>
							</Grid>
						</DataTemplate>
					</CollectionView.ItemTemplate>
				</CollectionView>
			</Grid>
		</VerticalStackLayout>
	</ScrollView>

</DataTemplate>

The last page is a Card page:

<DataTemplate x:Key="CardTemplate">
	<ScrollView>
		<VerticalStackLayout>
			<Border Margin="30"
				Background="#1e1e1e">
				<Border.StrokeShape>
					<RoundRectangle CornerRadius="40"/>
				</Border.StrokeShape>

				<Grid ColumnDefinitions="*,*,*,*" RowDefinitions="*,100,*,*,*"
						Padding="30">
					<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Text=".NET MAUI Bank" FontAttributes="Bold" FontSize="18"/>
					<Label Grid.Row="0" Grid.Column="3" Text="UAH" FontAttributes="Bold" FontSize="18" HorizontalOptions="End"/>
					<Label Grid.Row="2" Grid.Column="0" HorizontalOptions="Center" Text="1234" FontAttributes="Bold" FontSize="30" />
					<Label Grid.Row="2" Grid.Column="1" HorizontalOptions="Center" Text="5678" FontAttributes="Bold" FontSize="30" />
					<Label Grid.Row="2" Grid.Column="2" HorizontalOptions="Center" Text="9098" FontAttributes="Bold" FontSize="30" />
					<Label Grid.Row="2" Grid.Column="3" HorizontalOptions="Center" Text="7654" FontAttributes="Bold" FontSize="30" />
					<Label Grid.Row="3" Grid.Column="2" Text="08/29" HorizontalOptions="Center" FontAttributes="Bold" FontSize="25" />
					<Label Grid.Row="4" Grid.Column="4" Text="{x:Static fonts:FontAwesomeIcons.CcMastercard}" HorizontalOptions="End"
						FontAttributes="Bold" FontSize="50"
						FontFamily="FABrands"/>
				</Grid>
			</Border>

			<IndicatorView ItemsSource="{Binding Source={x:Reference Name=CardsPage},Path=BindingContext.Pages}"
							Position="2"
							HorizontalOptions="Center"
							IsEnabled="False"/>

			<Grid ColumnDefinitions="90,*" RowDefinitions="*,*,*" Margin="30,20,30,70">
				<Border Background="#1e1e1e"
						HorizontalOptions="Start"
						HeightRequest="75"
						WidthRequest="75"
						Grid.RowSpan="3">
					<Border.StrokeShape>
						<Ellipse />
					</Border.StrokeShape>
					<Label Text="{x:Static fonts:FontAwesomeIcons.Calendar}" FontFamily="FASolid"
						FontSize="30"
						Padding="15"
						VerticalOptions="Center"
						HorizontalOptions="Center"/>
				</Border>
				<Label Grid.Column="1" Grid.Row="0"  Text="Internet limit per month"/>
				<ProgressBar Grid.Column="1" Grid.Row="1"  Progress="0.5" ProgressColor="Green"
								HeightRequest="30"/>
				<Label Grid.Column="1" Grid.Row="2"
						Text="Left 500$ of 1000$"
						VerticalOptions="End"/>
			</Grid>

			<Border
					Stroke="#1e1e1e"
					Background="#1e1e1e"
					VerticalOptions="Start">
				<Border.StrokeShape>
					<RoundRectangle CornerRadius="30,30,0,0"/>
				</Border.StrokeShape>
				<Grid ColumnDefinitions="50,*"
						RowDefinitions="*,*,*,*"
						Padding="30,20">
					<Label Grid.Row="0" Grid.Column="0" Text="Settings" Grid.ColumnSpan="2"/>
					<Label Grid.Row="1" Grid.Column="0" Grid.RowSpan="2"
							Margin="0,10,0,0"
								Text="{x:Static fonts:FontAwesomeIcons.PaperPlane}"
								FontFamily="FASolid"
								VerticalOptions="Center"/>
					<Label Grid.Row="1" Grid.Column="1" Text="Apple Pay" Margin="0,10,0,0"/>
					<Label Grid.Row="2" Grid.Column="1" Text="Setup skins"/>
				</Grid>
			</Border>
		</VerticalStackLayout>
	</ScrollView>
</DataTemplate>

As a result, you should receive such app:

Main page

The full code can be found on GitHub.

Happy coding!

Buy Me A Coffee

Related:

Adding Application Insights to .NET MAUI Application

This article provides an in-depth exploration into how you can integrate Microsoft's Application Insights into your .NET MAUI application. A comparative study between Microsoft AppCenter and Application Insights is also highlighted, demystifying the inherent advantage of Application Insights in application management and analytics.

How to show SnackBar and Toast in .NET MAUI

Demonstrate how to configure SnackBar and Toast using .NET MAUI Community Toolkit.

An unhandled error has occurred. Reload

🗙