Talk With Angel Garcia!

Talk With Angel Garcia!

a blog about software and beers

CabañasRD, from native to cross-platform with Xamarin Forms [1/4].

Hi folks, this time we are going to migrate a famous Dominican app to Xamarin Forms, CabañasRD (+50,000 downloads in Play Store).

Right now the app is only available for Android, we will rebuild and redefine the design with Xamarin Forms to have both platforms (iOS and Android).

The plan is to divide all the process in 4 parts:

  1. Taking our Adobe XD design to XAML: Design and structure (this post).
  2. Implementing the command for opening native map, app icons, splash screens, info tab and also the search functionality over the map.
  3. Moving the data to the cloud: Let’s make our backend with Azure Functions and CosmosDB and consume it from the app.
  4. Implementing continuous integration and continuous deployment to the stores with App Center.

From Adobe XD to XAML: Design and structure.

The current design of the app looks like this:

So, we are going to change it to this (made with Adobe XD):

For the app structure we are going to follow my recipe for a Clean Architecture using this template which already has Prism as MVVM framework, if you want to know more about making and consuming your own templates see this post.

Let’s begin installing the template using the .NET Core CLI, to do this we are going to download or clone this repository, open a terminal in the root folder and run the following command:

dotnet new -i PrismCleanApp

Cool! now we are going to create our CabanasRD app based on the installed template, you can leave the previous folder and run this command:

dotnet new cleanxam -n CabanasRD -o CabanasRD

Go to the created project and open the solution file to start coding our Xamarin app.

As the app is already published in the Play Store, we have to use the same package name for Android and why not, we will use the same for the bundle identifier (for iOS).

  • Open the Android manifest in App/CabanasRD.Android/Properties/AndroidManifest.xml and change the package name to com.cabanasrd.
  • Open the Info.plist in App/CabanasRD.iOS/Info.plist and change the bundle identifier to com.cabanasrd.

We need to check one more thing before starting, the template doesn’t have the versions of Xamarin.Forms and Xamarin.Essentials that we are going to use:

Using the “Manage NuGet Packages” options in the solution, update in all the App projects the following:

  • Xamarin.Forms ⇒ 4.7.0.1080
  • Xamarin.Essentials ⇒ 1.5.3.2

Now we are ready to create the first element we need, the TabbedPage, which will be our main view.

Create a folder named “Main” in App/CabanasRD/UI, then inside the new folder create two folders for our Views and ViewModels, and then add a new Forms TabbedPage XAML named “MainTabbedPage.xaml” in the View folder and also we need its ViewModel called “MainTabbedPageViewModel.cs”, so your UI folder should look like this:

Proceed to register this view in the Prism container in App/CabanasRD/App.xaml.cs and use it as the main page in the navigation:

If you look inside the App/CabanasRD/UI folder, the template already had included a folder named “Orders” as boilerplate code, delete this, we don’t need it anymore, also remove it from Core/CabanasRD.Domain, Core/CabanasRD.UseCases and Core/CabanasRD.Data.

Go back to the App/CabanasRD/UI/Main/Views/MainTabbedPage.xaml and add the following for create the tabs (also add some styles):

The result is this:

One important thing here, the property android:TabbedPage.ToolbarPlacement=”Bottom” in the TabbedPage is a Platform-Specific feature for placing the tabs at bottom in Android (iOS by default has the bottom placement).

For the icons, we are going to use a FontImageSource, follow the docs about embedding custom Fonts, first create a folder for placing the fonts (.ttf) in App/CabanasRD called “Resources” and inside it create another one called “Fonts”, download the Font Awesome files from here, copy the .ttf files to the Fonts folder (with build action “EmbeddedResource”), and include these fonts in our app:

//In the Assembly.cs

[assembly: ExportFont("fa-regular-400.ttf", Alias = "Font-Awesome-Regular")]
[assembly: ExportFont("fa-solid-900.ttf", Alias = "Font-Awesome-Solid")]
[assembly: ExportFont("fa-brands-400.ttf", Alias = "Font-Awesome-Brands")]

Now we can use the unicode provided by Font Awesome for the desired icons:

  • For the first tab ⇒ 
  • For the second tab ⇒ 

With this clear, let’s add the icons to our MainTabbedPage.xaml:

Now the tabs are ready:

The next step will be set up the map, if you go back to the design you notice that the map is black and white (has a style) and we have orange custom pins, the Xamarin.Forms.Map control doesn’t have this feature and we don’t want to waste time doing a custom renderer just for this, so, we are going to use Xamarin.Forms.GoogleMaps package (3.2.1) which extends the functionality of the original Map from Xamarin.

Please, before continuing, go and check the necessary stuffs we need for use the GoogleMaps control in the “Usage” section in the project repo.

Once completed the map initializations, it’s time to use it in the corresponding view, create a new folder structure for the Map in App/CabanasRD/UI like this:

Open the “MotelsMapPage.xaml” and put the following:

At the moment we don’t have the the Locations collection in the ViewModel (we are going to add it later).

Now, add this as the content page for the first tab, so our MainTabbedPage.xaml will look like this:

Register the new view in the container and …. Yessir! we have the map view ready:

Next step is the style of the map, go to this site, select and download the “Silver Theme” JSON, add it to App/CabanasRD root folder as “GoogleMapStyles.json”, and then add the following to the code behind of our MotelsMapPage.xaml (thanks Rendy for this post):

This is looking cool now:

Before adding the Pins collection we need the custom pin image, the map control we are using has a Bindable Property for this.

Add a “marker.png” in the Assets folder for the Android project, and also add the marker image to the Resources folder for the iOS project.

Time to make our app business logic, we are going to create the following structure in the Core/CabanasRD.Domain layer:

For the Core/CabanasRD.Data layer:

For the Core/CabanasRD.UseCases layer:

Now we have to implement the data source, in this first post will be in memory list, let’s move to App/CabanasRD/Framework/DataSources and create a “InMemoryMotelsSource.cs” with the following content:

Register all the necessary dependencies for the use case in the App.xaml.cs:

For the map pins position and image we are going to use a DTO (or model in the UI), place a file called “MotelLocation.cs” in App/CabanasRD/UI/Map/Models:

Now let’s place the UI logic for the map in the MotelsMapPageViewModel.cs:

Let’s see how it looks in the map:

Now we need to implement the “InfoWindowClicked” event in the map, to do this we have to add the event handler in the MotelsMapPage.xaml (and its code behind) which will just act as a bridge for calling the real implementation of this event in the MotelsMapPageViewModel, 😭 we don’t have any Bindable Property to do this directly in the view model.

As you can see, we have now the logic to navigate to the motel details page, let’s create this view (this is the funny part), we need to create a MotelDetailsPage.xaml (under Views), MotelDetailsPageViewModel.cs (under ViewModels) in App/CabanasRD/UI/Map, and then register the new view in the App.xaml.cs.

Before going to the XAML’s part, let’s put the following code in our MotelDetailsPageViewModel:

Cool, now that we have the blank view connected with its view model, let’s start defining the main container as a Grid with 5 rows with the following definitions:

For the gradients and the rounded layouts, we are going to use the awesome PancakeView🥞 version 1.4.2, follow the setup before continuing.

For some elements style, like the button in the header, we are going to use Xamarin.Forms.Visual.Material, follow the setup before continuing.

Cool, now let’s use a PancakeView as container for the first row (header) inside the Grid we defined before:

Add the following namespace to the ContentPage:

xmlns:yummy="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"

So far, this is the result for the MotelDetailsPage:

We need to remove the Navigation Page Separator in iOS, I tried to use this Platform Specific but it didn’t work, so let’s override this property globally in the AppDelegate.cs (App/CabanasRD.iOS), put this line before the LoadApplication call in the FinishedLaunching method:

UINavigationBar.Appearance.ShadowImage = new UIImage();

🤓 let’s see:

For making the bottom shape for this row, we are going to use a SVG with the desired form (extracted from the Adobe XD design) and place it vertically at the end.

Follow this blog post for setting up Xamarin.FFImageLoading.Svg.Forms, this library allows us to use SVG images in Xamarin Forms.

Create a folder in App/CabanasRD/Resources named “Images” and place there the SVG file (header_shape.svg) with build action EmbeddedResource, then add the image in the row 0, the MotelDeatilsPage.xaml would be like this:

The result 😎 is this beautiful shape effect:

Now, for putting the header elements we are going to use some nested grids, so, first let’s define a grid like this:

We need two new fonts here:

  • Roboto-Light
  • Roboto-Bold

Define the styles for the “Title” (row 0), “Subtitle” (row 1), “CreditCardAllowedIcon” (row 2, column 0), “HeaderIndication” (row 2, column 0) and “SeeRouteButton” (row 2, column 1):

Cool, let’s make the grid, then put the “Title” and “Subtitle” labels inside it with the following code:

Looking good 🙂:

To achieve the transparent chip effect (row 2 column 0) we will use another grid just for absolute positioning, this is because we don’t want the transparency in the label, just in the “background” :

And, for the button we are going to add a negative margin in the right to make the effect of “only left right rounded”, export the icon needed and put it in the row 2 column 1 with the “SeeRoute” style.

So, our XAML will be like this:

😉 we have our header ready!

Time for the pics (row 1 and 2 of our main grid), the “Pics Layout” will be a CarouselView with an IndicatorView, see the docs for initialization setup.

For the rounded corners of the images we need to clip the image to a PancakeView and use this as the DataTemplate of our CarouselView, the ItemsSource it’s already defined in the ViewModel as an ObservableCollection.

Our MotelDetailsPage.xaml will look like this:

So far we got this:

Cool, let’s make the row 3 of our main grid, the “Services Layout”, before going to our view, add the following styles:

We want to do something like this:

Go back to the MotelDetailsPage.xaml and add the “Services Layout”:

Run the app and go to the motel details, scroll down, 😉 you see this now:

For the last row, the “Phones Layout”, we are going to do almost the same, but the gradients colors and the grid inside the BindableLayout will be different.

Finally, the XAML is going to look like this:

One more thing, let’s avoid the Dark mode for now, go to the iOS project and add the following value to the info.plist:

🕺🏽Yessir, we are done!

This is the final result:

See you in the next post of this serie! 🚀

See the code on Github

DROP A COMMENT

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.