This is a project I made following the asfend's course. But some of the features didn't work well on Android, so I made a few changes to make it work as I expect. Below are my study notes.
01.Intruduction
Course Links: Build Real World App with .NET MAUI | Udemy Description: Learn to make Real Estate App with .NET MAUI From Beginning to End Instructor: Asfend
02.Config Application Backend
How to make an ASP.Net API is not the point of this course, so for us, it's enough to know how to publish it.
I didn't find the file named realestateapp01.PublishSettings
in the given assets. However, after trying many times, it finally works by checking the option named delete other files on the target
.
03.Test Rest Api Endpotints
It works very well, but there is one area for improvement: the tedious process that changing the domain name in the request url.
04.Getting Started With .NET MAUI Project
create a .Net MAUI project and add assets the instructor have published. File directory: Properties:
- launchSettings
Dependencies:
- net7.0-android
- net7.0-ios
- net7.0-maccatalyst
- net7.0-windows10.0.19041.0
Platforms:
- Android
- iOS
- MacCatalyst
- Tizen
- WIndows
Resources:
- AppIcon
- Fonts
- Images
- Raw
- Splash
- Styles
App.xaml
- App.xaml.cs
MainPage.xaml
- MainPage.xaml.cs
MauiProgram.cs
05.Create Model Classes
Steps to create a Model Class:
- Search
Newtonsoft.Json
in NuGet Software Package Manager and install it. - Import
Newtonsoft.Json
in the model class file. - Copy and paste the response's body into the text box on the left side of the following website. Convert JSON to C# Classes Online - Json2CSharp Toolkit
- Check the
Use Pascal Case
option and theAdd JsonProperty Attributes
option. - Click the
Convert
button and paste the converted result into the inside of the target model class as fields.
This section is boring, just doing the same thing over and over again. So I highly advise you to paste the rest of models after creating two or three models.
06.Create Service Layer
Add a new folder called Services
in the root directory. Then, add a new cs
file under this directory, named it ApiService.cs
. All the API interfaces we are going to use will be written here. Most of them are asynchronous functions, because waiting for a response from a server always takes some time. There are some examples:
public static async Task<bool> RegsiterUser(string name, string email, string password, string phone)
{
var register = new Register()
{
Name = name,
Email = email,
Password = password,
Phone = phone
};
var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(register);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(AppSettings.ApiUrl + "api/users/register", content);
if (!response.IsSuccessStatusCode) return false;
return true;
}
public static async Task<List<Category>> GetCategories()
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Preferences.Get("aceesstoken",string.Empty));
var response = await httpClient.GetStringAsync(AppSettings.ApiUrl + "api/categories");
return JsonConvert.DeserializeObject<List<Category>>(response);
}
Steps to create an API request function:
- Create an instance of the
HttpClient
class. We usually name ithttpClient
. - If it is an authorization-required request, you may need to set the Authorization field in the header of it.
- send a request through some functions of the
HttpClient
class, such asGetStringAsync
,PostAsync
,PutAsync
andDeleteAsync
. Don't forget to add theawait
keyword before calling these functions. - If this function you defined contains the formating JSON data part, you need to use the
DeserializeObject
orSerializeObject
function to deserialize or serialize it, and then you can use it like an instance of a related model class.
07.Register Page
A MAUI page consists of a XAML file and a CS file. XAML is a markup language, similar to HTML.
MAUI is designed based on MVVM
mode, XAML is the View
layer and CS is the ViewModel
layer. The Model
layer we already defined in section 5.
There I list a few tags and their attributes we are going to use in this section:
<ContentPage Title=""></ContentPage>
<VerticalStackLayout HorizentalOptions="" VerticalOptions="" ></VerticalStackLayout>
<HorizentalStackLayout Margin="" Spacing="">
<HorizontalStackLayout.GestureRecognizers>
<TapGestureRecognizer x:Name="TapLogin" Tapped="TapLogin_Tapped"/>
</HorizontalStackLayout.GestureRecognizers>
</HorizentalStackLayout>
<Label Text="" TextColor="" FontSize=""/>
<Image source="" HeightRequest="" WidthRequest=""/>
<Entry x:Name="" Placeholder="" IsPassword=""/>
<Button x:Name="" Text="" FontSize="" Clicked=""/>
Corresponding viewModel code:
async void BtnLogin_Clicked(object sender, EventArgs e)
{
var response = await ApiService.Login(EntEmail.Text, EntPassword.Text);
async void BtnRegister_Clicked(object sender, EventArgs e)
{
var response = await ApiService.RegsiterUser(EntFullName.Text, EntEmail.Text, EntPassword.Text, EntPhone.Text);
if (response)
{
await DisplayAlert("", "Your account has been created", "Alright");
await Navigation.PushModalAsync(new LoginPage());
}
else
{
await DisplayAlert("", "Oops something went wrong", "Cancel");
}
}
}
private async void TapJoinNow_Tapped(object sender, TappedEventArgs e)
{
await Navigation.PushModalAsync(new RegisterPage());
}
If we name an attribute in XAML, we can operate it in the ViewModel code.
In the above code, we realize the page-jumping function in two different ways. One is assigning
Application.Current.MainPage
, and the other is calling the Navigation.PushModelAsync
function.
However, there are some differences between them. If we assign Application.Current.MainPage
, the user can't come back to the previous page. If we call the Navigation.PushModelAsync
function, the user can come back to the previous page by swiping the screen left.
DisplayAlert
will display a pop-up dialog, the first argument is the dialog's title, the second argument is the dialog's content, and the third argument is the text of the dialog's button.
08.Login page
Much the same as in the previous section.
09.Tabbed Page and Pages Flow
As before, I list a few tags and their attributes that haven't been mentioned before but are used in this section.
<TabbedPage xmlns:local="clr-namespace:RealEstateApp.Pages"
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
android:TabbedPage.ToolbarPlacement="Bottom"
SelectedTabColor="#1d94ab"
UnselectedTabColor="#78909c">
<local:Page1 Title="page1" IconImageSource=""/>
<local:Page2 Title="page2" IconImageSource=""/>
</TabbedPage>
I don't know why the app released will stop running in both my android phone and android simulator when we jump from HomePage
to TabbedPage
if I use the NavigationPage
tag in TabbedPage
tag as he said. So I deleted the NavigationPage
tag and added a navigation bar on the propertyListPage
to realize the page-jumping function.
10.Home Page
As before, I list a few tags and their attributes that haven't been mentioned before but are used in this section.
<Grid RowSpacing="">
<Grid.RowDefinitions>
<RowDefinition Height=""/>
</Grid.RowDefinitions>
<Border Grid.Row="0">
<Border.StrokeShape>
<RoundRectangle ConorRadius=""/>
</Border.StrokeShape>
<Border.GestureRecognizers>
<TapGestureRecognize/>
</Border.GestureRecognizers>
</Border>
</Grid>
<CollectionView SelectionMode=""
SelectionChanged="">
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="" Orientation=""/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTamplate>
</DataTamplate>
</CollectionView.ItemTemplate>
<\CollectionView>
<Label Text="{Binding text}"/>
Corresponding viewModel code:
private async void GetCategories()
{
var categories = await ApiService.GetCategories();
CvCategories.ItemsSource = categories;
}
If we want to show information about a list of a model we defined on our webpage, a wise choice is to use the CollectionView
tag. In the collectionView, we can describe a template and bind the list data, then data will be assigned automatically to the binding attributes.
11.Properties List Page
As before, I list a few tags and their attributes that haven't been mentioned before but are used in this section.
<ImageButton x:Name="ImgBackBtn"
Source="back_icon"
Clicked="ImgBackBtn_Clicked"/>
private void ImgBackBtn_Clicked(object sender, EventArgs e)
{
Navigation.PopModalAsync();
}
Navigation.PopModalAsync
function is calling to turn back to previous page.
12.Property Detail Page
Much the same as in the previous section.
13.Search Page
Much the same as in the previous section.
<SearchBar />
14.Bookmark Page
Much the same as in the previous section.
15.Settings Page
Much the same as in the previous section.