Xamarin.Forms: it works

In July 2017, I received a call from a former colleague asking me wether I wanted to begin a new app journey with him. The client would be Paris Turf, an editor specialized in horse races results.
My lovely lovely lovely horse [1]
They wanted to diversify and launch a new product called Exalt Training aimed at horses trainers.
For this, they teamed up with Souffl, an award winning innovation french company.
If you want to know more about the product itself: https://souffl.com/projets/la-premiere-solution-de-tracking-sportif-dediee-aux-entraineurs-de-chevaux-de-course/
After failing to deliver an iOS app, they were 6 months behind schedule, and decided to switch from pure native to Xamarin native, to cover both android and iOS platforms.
Xamarin Forms is mature AND powerful
Yup I said it. If anyone of you is still wondering, here is your answer: it is robust, fast, it handles complex scenarios and even fantasmagoric ideas from your favorite designer.
But ENOUGH SAID!

We successfully implemented a complex design including:
- Gradients backgrounds
- Many animations
- Drawing gradient paths on Google Maps
- Data visualization with curves
- A carousel with a large number of views (20+)
- Horizontal list view with snap effect
- Grid view with drag and drop
- Custom Tabbar
- Custom navigation made of Tabbar with hidden menu revealing with a slide up gesture
For each of these prerequisites we had to find a technical answer. We will walk through all these answers as follow:
Technical answer | Prerequisites |
---|---|
SkiaSharp | Animation / Draw on map / Plots |
Custom renderers | Gradient / Horizontal list views |
Xamarin animation API | Animations |
Xamarin custom components | BottomBar / Tabbar |
Lottie | Animations |
WebSocket | Real time communication |
Finally we'll add a note on the awesome Visual Studio App Center.
Remark: for convenience, all the included gif and png are captured from the Android emulator, but be assured that the exact same resources are available on our iOS app.
UPDATE: here is the video of the iOS version.
SkiaSharp
SkiaSharp is a cross platform and low-level api. You draw in canvas with primitives like DrawText, DrawCircle, DrawPath. So it's very powerful but also requires some maths skills to make your own layouts.
All the following views have been made with SkiaSharp.
Drawing on top of Google Maps
This one was a bit challenging because you have to convert gps positions to pixel coordinates without calling the Google Maps SDK (to have good response time).
Fortunately I already solved this issue 10 years ago working on another project.
At this time we were rendering custom icons and paths with GDI+ on a Virtual Earth hosted in a web view. So I had already the mathematical Mercator projection magic :)
The remaining job was not difficult, since it's just drawing a path with shaders so we could show the horse effort with gradients.
This skiasharp bit was embedded in a custom ContentView
called SessionMap
.
The colors represent the effort of the horse, blue is light and red is maximum effort.
Drawing curves
Next to this map we wanted to show the horse effort (heart rate and speed), so we needed to draw curves over the horse training time.
We created a ContentView named SessionGraphView
for this.
Linking the two
Of course we wanted some interaction. The user can drag the timeline along updating the effort number AND the matching position on the map.
This was achieved by binding property CurrentTime
from the SessionGraphView
to the SessionMap
component.

Custom control
For another screen, we needed a date range selector indexed by month. Since such a control didn't exist in Xamarin Forms, we decided to implement it in SkiaSharp.
This is a simple control named MonthSlidesView
embedded in a ContentView
and exposes properties such as CurrentLowerDate
and CurrentUpperDate
.
Of course this control interacts with curves reflecting horse's progression over time.

Glowing number animation
This animation is 100% SkiaSharp, included in a SKCancasView
named GlowingNumberView
.
It's made thanks to an infinite loop, it basically follows the technique described here: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/basics/animation.
With two circles growing and alpha channel interpolation.

Zoom in on animation

Custom renderers
CarouselView
One of the screen feature a carousel of all the horses which are currently training. We call it the Live screen.
So we needed a carousel view. Unfortunately the Xamarin.Forms CarouselView is still in pre alpha (for 2 years now :) https://www.nuget.org/packages/Xamarin.Forms.CarouselView.
So we switched to an open source one: https://github.com/alexrainman/CarouselView.
I was disappointed with the performance and some resize issues, so we ended up forking it: https://github.com/roubachof/CarouselView. For example, on Android we switched from PageAdapter
to FragmentStatePagerAdapter
to avoid memory issues.

Horizontal/Grid-ListView
For this one, we wanted some specific behaviors:
- With a horizontal layout, a snap effect should be applied to the first visible item
- With a grid layout, we should be able to drag and drop items to reorder the collection
- Since the items layout were complex, we wanted some view preloading to smoothen the scrolling
The Xamarin Forms ListView was too simple for this use.
So we dug the web and found this blog post: (https://causerexception.com/2018/02/06/xamarin-forms-ultimate-horizontal-list-guide/).
Unfortunately the implementation was very naive and the performance really bad (each time a view was retrieved, a new one was created, so no recycling...).
We simply had to reimplement from scratch a Forms ListView which we called HorizontalListView
.
Implementation
On Android, we used a RecyclerView
, snapping was achieved with the help of a LinearSnapHelper
and drag and drop with a ItemTouchHelper
.
For performance, we preload X item views asynchronously on a background thread.
On iOS, a UICollectionView
was used. Drag and drop is fully supported and we use a simple trick to have the snap effect.
The implementation of this Xamarin Forms ListView will be uploaded to github soon.


Other renderers
- Gradient
- Material frame (cardView on android custom shadow on ios)
We tried to create most of the time custom Xamarin.Forms views.
Xamarin Forms animation
We used the animation api extensively to achieve several visual effects like:
Animations | Type | Gif |
---|---|---|
Folding/unfolding | Rotation + Height | ![]() |
Heartbeat animation | Scale | ![]() |
Appearing view | Translation + Fade | See TabBar. |
Custom Xamarin Forms containers
We also created some components in "pure" Xamarin.Forms, meaning without renderers.
TabBar (android style)
We splitted the component in two:
- A
TabHostView
which is responsible for drawing tabs and keeping their visual state - A
ViewSwitcher
which hides/shows view with a discrete animation
The two are linked together with a SelectedIndex
bindable property.

TaskLoaderView
We created a generic Xamarin.Forms component displaying a ActivityLoader
while loading and handling all the error state of the view:
- Empty state (no result)
- Error state (error message + retry button)
- Error while refreshing (snackbar with error message)
- Success state (displaying the view content)
This component is based on Stephen Cleary's NotifyTask
.
You can have a more detailed technical explanation of it here: https://github.com/roubachof/Xamarin-Forms-Practices.
BottomBar (ios style)
A custom navigation container was designed. At first it's really just a bottom tabbar, but if you swipe the tabbar up, a container taking all the screen reveals itself, showing a menu.
The menu contains the remaining available secondary actions, like user profile and settings.
This component is just a regular TabHostView
and ViewSwitcher
. But the TabHostView
is positionned at the bottom of the screen.
When the component is at Peek
state, only the bottom bar is shown.
Applying a slide gesture, we use a simple translation to move the whole view up and reveal the menu which was hidden so far.
Respectively, a slide down closes it.

Lottie
We use lottie for some components which were just too diffult to implement:
![]() |
![]() |
Web socket and communication
We used the .Net ClientWebSocket
for handling live communication with the server.
For REST services, we used:
- Refit
- Polly (retry policy for our socket client)
- InsanePowerPack (http cache for refit)
- Fusillade (lesser priority for sync services and image loading)
- Custom in-memory cache (cache invalidation is hard ;)
Visual Studio App Center
We use app center for continuous integration. Each push on master triggers a build on Android and iOS.
We use a prebuild script to create 2 different applications based on the branch the build is running on. If we build an app on the qa branch, the app will be wired to our back office. But if it's built against the qa-mock, then all the data are mocked.
We also use the distribution feature to beta test our app with various users.
What we like the most about app center crash reports, is the ability to include some text with each report. Since we are logging relevant info in our app, we can attach our logs to each report to add specific context to the crash.
Xamarin Forms is mature AND powerful
Ok, so now we said it but we also shown it.
In the following weeks, I will publish posts on various aspects of the project. The HorizontalListView
, TabHostView
, TaskLoaderView
, will be available on GitHub.
Restez à l'écoute !