MVVM and MAUI - Checkout page for Ecommerce APP

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.

For fontawesome refer this link
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
    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
<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. 

Comments

  1. Friend where does this come from xmlns:fontawesome="clr-namespace:Ecommerce.UI.Template.Models", there is no code :( or that you have to install

    ReplyDelete
  2. That is fontawesome. you have to setup fontawesome. follow step: https://xamarinuidesigns.blogspot.com/2022/02/create-font-awesome-icon-in-xamarin-form.html

    ReplyDelete

Post a Comment

Popular posts from this blog

Push Notifications in .NET MAUI: A Comprehensive Guide

Explore the UI libraries available for .NET MAUI at no cost.

Push Notification using Firebase in xamarin form (Android and IOS)