MVVM and MAUI - Checkout page for Ecommerce APP
MVVM and MAUI - Checkout page for Ecommerce APP
Please, support my blog by clicking on our sponsors ad!
Watch this video to understand code.
First We will create all template
CheckoutStepTemplate.xaml
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Ecommerce.UI.Template.Pages.CheckOut.Template.CheckoutStepTemplate"
xmlns:fontawesome="clr-namespace:Ecommerce.UI.Template.Models">
<Grid x:Name="grid" ColumnDefinitions="Auto,*,Auto,*,Auto" HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<StackLayout Grid.Column="0" Spacing="5" >
<Label x:Name="lblCartCircle" Text="{x:Static fontawesome:Solid.Circle}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
<Label x:Name="lblCartIcon" FontSize="20" Text="{x:Static fontawesome:Solid.Cart_Plus}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
</StackLayout>
<Line x:Name="lineCart" X1="15" Y1="0" X2="120" Y2="0" Stroke="{x:DynamicResource Primary}"
Margin="0,5,0,0" VerticalOptions="Center"
StrokeDashArray="5,5" Grid.Column="1" HorizontalOptions="FillAndExpand"></Line>
<StackLayout Grid.Column="2" Spacing="5">
<Label x:Name="lblAddressCircle" Text="{x:Static fontawesome:Solid.Circle}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
<Label x:Name="lblAddressIcon" FontSize="20" Text="{x:Static fontawesome:Solid.Home}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
</StackLayout>
<Line x:Name="lineAddress" X1="15" Y1="0" X2="120" Y2="0" Margin="0,5,0,0"
Stroke="{x:DynamicResource Primary}" StrokeDashArray="3,5"
Grid.Column="3" HorizontalOptions="FillAndExpand"></Line>
<StackLayout Grid.Column="4" HeightRequest="50" Spacing="5">
<Label x:Name="lblPaymentCircle" Text="{x:Static fontawesome:Solid.Circle}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
<Label x:Name="lblPaymentIcon" FontSize="20" Text="{x:Static fontawesome:Solid.Money_Bill}"
HorizontalOptions="Center" TextColor="{x:DynamicResource Primary}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
</StackLayout>
</Grid>
</ContentView>
CheckoutStepTemplate.xaml.cs
public partial class CheckoutStepTemplate : ContentView
{
public enum CheckoutStepEnum
{
Cart,
Address,
Payment
}
public CheckoutStepTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty CheckoutStepProperty =
BindableProperty.Create(nameof(CheckoutStepEnum), typeof(CheckoutStepEnum),
typeof(CheckoutStepTemplate), CheckoutStepEnum.Cart);
public CheckoutStepEnum CheckoutStep
{
get => (CheckoutStepEnum)GetValue(CheckoutStepProperty);
set => SetValue(CheckoutStepProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(CheckoutStepEnum))
{
if (CheckoutStep == CheckoutStepEnum.Address)
{
lblCartCircle.Text = Models.Solid.Check_Circle;
lineCart.Stroke = lblCartIcon.TextColor =
lblCartCircle.TextColor = Color.FromArgb("#2400CB");
}
else
{
lblCartCircle.Text = Models.Solid.Check_Circle;
lineCart.Stroke = lblCartIcon.TextColor = lblCartCircle.TextColor =
Color.FromArgb("#2400CB");
lblAddressCircle.Text = Models.Solid.Check_Circle;
lineAddress.Stroke = lblAddressIcon.TextColor = lblAddressCircle.TextColor =
Color.FromArgb("#2400CB");
}
}
}
}
CartListTemplate.xaml
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Ecommerce.UI.Template.Pages.CheckOut.Template.CartListTemplate"
xmlns:model="clr-namespace:Ecommerce.UI.Template.Models">
<StackLayout Padding="10">
<Grid x:DataType="model:ItemViewModel" RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="100,*" ColumnSpacing="10">
<Frame CornerRadius="20" Padding="0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="3">
<Image Source="{Binding Img}" WidthRequest="100" Aspect="Fill" HeightRequest="100"></Image>
</Frame>
<Label Text="{Binding Title}" FontSize="18" FontAttributes="Bold" Grid.Row="0" Grid.Column="1"></Label>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" Grid.Row="1" Grid.Column="1" Spacing="10">
<Label Text="{Binding Price,StringFormat='${0}'}" TextDecorations="Strikethrough"></Label>
<Label Text="{Binding Discountprice,StringFormat='${0}'}"></Label>
<Label Text="Color:" FontSize="15" FontAttributes="Bold" Margin="20,0,0,0"></Label>
<Label Text="{x:Static model:Solid.Square_Full}" FontFamily="{x:DynamicResource FontAwesomeFamily}"
Margin="0,2,0,0" TextColor="{Binding ItemColor}" ></Label>
</StackLayout>
<StackLayout Orientation="Horizontal" Spacing="10" Grid.Row="2" Grid.Column="1">
<Label Text="{x:Static model:Solid.Trash}" FontFamily="{x:DynamicResource FontAwesomeFamily}" FontSize="18" TextColor="#D43572"></Label>
<Label Text="Remove" FontSize="15" TextColor="#D43572" FontAttributes="Bold" >
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding RemoveItemCommand}" CommandParameter="{Binding .}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</Grid>
</StackLayout>
</ContentView>
AddressTemplate.xaml
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Ecommerce.UI.Template.Pages.CheckOut.Template.AddressTemplate"
xmlns:model="clr-namespace:Ecommerce.UI.Template.Models">
<StackLayout Padding="20,10">
<Frame BackgroundColor="#E9F1FA" CornerRadius="10" BorderColor="{Binding BorderColor}" HasShadow="True">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto" ColumnSpacing="5">
<Label Grid.Row="0" Grid.Column="0" TextColor="{x:DynamicResource Primary}"
Text="{Binding FontAwesomeIcon}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" FontSize="18"></Label>
<Label Grid.Row="0" Text="{Binding Title}" HorizontalOptions="FillAndExpand" FontSize="15"
FontAttributes="Bold"
VerticalOptions="StartAndExpand" TextColor="{x:DynamicResource Primary}" Grid.Column="1"/>
<Label Grid.Row="0" Grid.Column="2" TextColor="{x:DynamicResource Primary}" Margin="0,2,0,0"
Text="{x:Static model:Solid.Edit}"
FontFamily="{x:DynamicResource FontAwesomeFamily}"></Label>
<Label Grid.Column="3" TextColor="{x:DynamicResource Primary}" Text="Edit"></Label>
<Label Text="{Binding Area}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4"
TextColor="{x:DynamicResource Primary}"></Label>
<Label Grid.Row="2" Grid.Column="0" TextColor="{x:DynamicResource Primary}" Margin="0,0,0,3"
VerticalOptions="End" Text="{x:Static model:Solid.Phone_Square}"
FontFamily="{x:DynamicResource FontAwesomeFamily}" ></Label>
<Label Text="{Binding PhoneNumber}" Margin="0,10,0,0" Grid.Row="2" Grid.Column="1"
TextColor="{x:DynamicResource Primary}"></Label>
<Label Text="{Binding Address}" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4"
TextColor="{x:DynamicResource Primary}"></Label>
</Grid>
</Frame>
</StackLayout>
</ContentView>
Now create all classes
CartModel.cs
CartModel.cs
public class CartModel
{
public ObservableCollection<ItemViewModel> ItemList { get; set; } = new ObservableCollection<ItemViewModel>();
public decimal TotalPrice { get; set; }
public decimal TotalDiscountPrice { get; set; }
public decimal DeliveryCharge { get; set; }
public decimal Total { get; set; }
}
public class CartViewModel : BaseViewModel
{
public Command LoadItemCommands { get; set; }
public Command ClickCommand { get; set; }
private CartModel cartModel;
CartModel vm;
public CartModel CartModel
{
get
{
return cartModel;
}
set
{
SetProperty(ref cartModel, value);
}
}
public CartViewModel()
{
ClickCommand = new Command(async () => await ExecuteClickCommand());
LoadItemCommands = new Command(async () => await ExecuteLoadItemCommand());
}
public async Task ExecuteClickCommand()
{
await Shell.Current.GoToAsync("Address");
}
public async Task ExecuteLoadItemCommand()
{
IsBusy = true;
try
{
int i = 0;
vm = new CartModel();
foreach (var product in new ProductModel().GetProducts())
{
if (i < 3)
{
vm.ItemList.Add(new ItemViewModel
{
Title = product.Title,
Img = product.Img,
Discountprice = product.DiscountPrice,
Price = product.Price,
ItemColor = Color.FromHex("#89E6DB"),
RemoveItemCommand = new Command<ItemViewModel>(a => RemoveItem(a))
});
i++;
}
}
vm.TotalPrice = vm.ItemList.Sum(a => a.Price);
vm.TotalDiscountPrice = vm.ItemList.Sum(a => a.Discountprice);
vm.DeliveryCharge = 30;
vm.Total = vm.TotalPrice - vm.TotalDiscountPrice - vm.DeliveryCharge;
CartModel = vm;
}
catch (Exception ex)
{
await App.Current.MainPage.DisplayAlert("Ecommerce", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
private async void RemoveItem(ItemViewModel item)
{
var result = await App.Current.MainPage.DisplayAlert("Ecommerce", "Record will delete permenantly, " +
"Do you wish to continue?", "Yes", "No");
if (result)
{
vm.ItemList.Remove(item);
CartModel = vm;
}
}
public void OnAppearing()
{
IsBusy = true;
}
}
ItemViewModel.cs
public class ItemViewModel
{
public string Title { get; set; }
public ImageSource Img { get; set; }
public decimal Price { get; set; }
public decimal Discountprice { get; set; }
public Color ItemColor { get; set; }
public Command RemoveItemCommand { get; set; }
}
AddressModel.cs
public class AddressModel
{
public int ID { get; set; }
public string Title { get; set; }
public Color BorderColor { get; set; }
public bool IsSelected { get; set; }
public string FontAwesomeIcon { get; set; }
public string Area { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
}
public class AddressViewModel : BaseViewModel
{
public Command LoadItemCommands { get; set; }
private List<AddressModel> addressModelList;
public List<AddressModel> AddressModelList
{
get
{
return addressModelList;
}
set
{
SetProperty(ref addressModelList, value);
}
}
public AddressViewModel()
{
LoadItemCommands = new Command(async () => await ExecuteLoadItemCommand());
}
public async Task ExecuteLoadItemCommand()
{
IsBusy = true;
try
{
List<AddressModel> list = new List<AddressModel>();
list.Add(new AddressModel()
{
ID = 1,
Area = "Rander",
Address = "154, Jalaram Nagar behind Oxford Soceity,India",
FontAwesomeIcon = Solid.Home,
PhoneNumber = "457987877",
Title = "Home"
});
list.Add(new AddressModel()
{
ID = 2,
Area = "Amabala",
Address = "201, Habitat Milestone near Oxford Soceity,India",
FontAwesomeIcon = Solid.Store,
PhoneNumber = "454878978",
Title = "Office"
});
AddressModelList = list;
}
catch (Exception ex)
{
await App.Current.MainPage.DisplayAlert("Ecommerce", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
}
Now we will create Checkout Pages
Cart.xaml
Cart.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Ecommerce.UI.Template.Pages.CheckOut.Cart"
Title="Cart"
xmlns:fontawesome="clr-namespace:Ecommerce.UI.Template.Models"
xmlns:template="clr-namespace:Ecommerce.UI.Template.Pages.CheckOut.Template"
xmlns:controls="clr-namespace:Ecommerce.UI.Template.CustomControls"
xmlns:model="clr-namespace:Ecommerce.UI.Template.Models"
Shell.NavBarIsVisible="False">
<RefreshView x:Name="refreshView" x:DataType="model:CartViewModel" Command="{Binding LoadItemCommands}"
IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto,Auto" Padding="20">
<Label Text="Check your Cart" Grid.Row="0" FontAttributes="Bold" FontSize="25" Margin="0,0,0,20"></Label>
<template:CheckoutStepTemplate CheckoutStep="Cart" Grid.Row="1" ></template:CheckoutStepTemplate>
<Grid BackgroundColor="#F0F8FF" ColumnSpacing="30" HeightRequest="50" Margin="-20,10" Grid.Row="2"
Padding="10" ColumnDefinitions="Auto,Auto,*">
<Label Text="Your Cart" Grid.Column="0" VerticalOptions="Center" FontAttributes="Bold"></Label>
<Frame CornerRadius="10" Grid.Column="1" BorderColor="#0066B2" BackgroundColor="#0066B2"
Padding="5" WidthRequest="80">
<Label Text=" 3 Items" TextColor="White" HorizontalOptions="Center" ></Label>
</Frame>
<Label Text="Add more" Grid.Column="2" VerticalOptions="Center" HorizontalOptions="EndAndExpand"
FontAttributes="Bold"></Label>
</Grid>
<ListView x:Name="listView" ItemsSource="{Binding CartModel.ItemList}" Grid.Row="3" Margin="-20,0"
HasUnevenRows="True" SeparatorVisibility="Default">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<template:CartListTemplate></template:CartListTemplate>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="4" RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="*,Auto"
Margin="0,10" RowSpacing="10">
<Label Text="Total Price" Grid.Row="0" Grid.Column="0"></Label>
<Label Text="{Binding CartModel.TotalPrice}" Grid.Row="0" Grid.Column="1"
HorizontalTextAlignment="End"></Label>
<Label Text="Total Discount" Grid.Row="1" Grid.Column="0"></Label>
<Label Text="{Binding CartModel.TotalDiscountPrice}" Grid.Row="1" Grid.Column="1"
HorizontalTextAlignment="End"></Label>
<Label Text="Delivery Charge" Grid.Row="2" Grid.Column="0"></Label>
<Label Text="{Binding CartModel.DeliveryCharge}" Grid.Row="2" Grid.Column="1"
HorizontalTextAlignment="End"></Label>
<Label Text="Total" Grid.Row="3" Grid.Column="0" FontAttributes="Bold" FontSize="20" />
<Label Text="{Binding CartModel.Total}" Grid.Row="3" Grid.Column="1" FontSize="20"
FontAttributes="Bold"
HorizontalTextAlignment="End"></Label>
</Grid>
<Button Text="Continue" BackgroundColor="{x:DynamicResource Primary}"
Grid.Row="5" Command="{Binding ClickCommand}"></Button>
</Grid>
</RefreshView>
</ContentPage>
Cart.xaml.cs
public partial class Cart : ContentPage
{
CartViewModel _viewModel;
public Cart()
{
InitializeComponent();
BindingContext = _viewModel = new CartViewModel();
}
private void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
{
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
Address.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Ecommerce.UI.Template.Pages.CheckOut.Address"
xmlns:control="clr-namespace:Ecommerce.UI.Template.CustomControls"
xmlns:template="clr-namespace:Ecommerce.UI.Template.Pages.CheckOut.Template"
xmlns:model="clr-namespace:Ecommerce.UI.Template.Models"
Title="Address"
Shell.NavBarIsVisible="False">
<RefreshView x:Name="refreshView" x:DataType="model:AddressViewModel" Command="{Binding LoadItemCommands}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto" Padding="20">
<control:BackNavigation Grid.Row="0" Title="Address" Margin="0,0,0,20"/>
<template:CheckoutStepTemplate CheckoutStep="Address" Grid.Row="1" />
<Grid BackgroundColor="#F0F8FF" ColumnSpacing="30" HeightRequest="50"
Margin="-20,10" Grid.Row="2" Padding="10" ColumnDefinitions="Auto,Auto,*">
<Label Text="Select your address" Grid.Column="0" VerticalOptions="Center"
FontAttributes="Bold"></Label>
<Frame CornerRadius="10" Grid.Column="1" HasShadow="True" BorderColor="White"
BackgroundColor="White" Padding="5" WidthRequest="130">
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Label Text="{x:Static model:Solid.Plus_Circle}"
FontFamily="{x:DynamicResource FontAwesomeFamily}"
HorizontalOptions="Center" Margin="0,2,0,0"
TextColor="{x:DynamicResource Primary}"></Label>
<Label Text=" Add Address" TextColor="{x:DynamicResource Primary}"
HorizontalOptions="Center" ></Label>
</StackLayout>
</Frame>
</Grid>
<ListView x:Name="listView" ItemsSource="{Binding AddressModelList}"
Grid.Row="3" Margin="-20,0" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<template:AddressTemplate></template:AddressTemplate>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Continue" BackgroundColor="{x:DynamicResource Primary}" Grid.Row="4"></Button>
</Grid>
</RefreshView>
</ContentPage>
Address.xaml.cs
public partial class Address : ContentPage
{
AddressViewModel _viewModel;
public Address()
{
InitializeComponent();
BindingContext = _viewModel = new AddressViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
Hope this is helpful to you. watch the video to understand this code.
Friend where does this come from xmlns:fontawesome="clr-namespace:Ecommerce.UI.Template.Models", there is no code :( or that you have to install
ReplyDeleteThat is fontawesome. you have to setup fontawesome. follow step: https://xamarinuidesigns.blogspot.com/2022/02/create-font-awesome-icon-in-xamarin-form.html
ReplyDelete