MVVM and MAUI - Checkout page for Ecommerce APP

MVVM and MAUI - Checkout page for Ecommerce APP
MVVM and MAUI - Checkout page for Ecommerce APP


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

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

School UI Design using xamarin form