Avalonia platform support - why it's simple
Tags: dotnet, csharp, mobile, webassembly
Published: 2022-06-03 10:25:27

I recently came across a developer discussing Avalonia on Discord who was conflating the wide range of platforms Avalonia supports with a significant increase in development effort. I thought it would be interesting to briefly explore how Avalonia works and explain why bringing Avalonia to new platforms isn't nearly as involved as some might imagine.

## Background

**How does it compare to MAUI?**
Before we go too far, it's essential to understand that Avalonia works very differently from frameworks like MAUI (or Xamarin.Forms), which uses a platform's native UI toolkit, providing a common API across all the supported platforms. MAUI is less a UI toolkit; more an abstraction layer over existing toolkits. The task of rendering the UI and handling user interactions isn't something the MAUI development team needs to worry about, as the device's UI toolkit is responsible for this. Instead, the primary problem for the MAUI development team has been creating an abstraction that conceals the wildly different UI toolkits, each with its quirks. Think of MAUI as a wrapper that provides native controls in an easy to use and lowest common denominator API.

## Avalonia - Making Pixel-Perfect Apps

Having established how MAUI works, let's examine how Avalonia's architecture and approach differ. It's worth knowing that Avalonia is in good company in its design and isn't altogether unique as it shares similarities with how Google's Flutter UI toolkit works. 

The most significant similarity is that Avalonia also utilises its own renderer, backed either by [Skia](https://skia.org/) or [Direct2D](https://docs.microsoft.com/en-us/windows/win32/direct2d/direct2d-portal). The renderer is responsible for drawing the entire application, allowing Avalonia to avoid depending on a device's UI toolkit. 

To illustrate why this is significant, we should consider embedded and low-powered devices. Avalonia has become particularly popular among manufacturers producing touch-based embedded industrial devices. 

We've found that they'll often use Linux, opting not to install a desktop environment, which would waste the limited storage, memory and CPU available on their low-powered devices. They can do this because Avalonia supports both accelerated rendering via [Direct Rendering Manager](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) and a software [framebuffer](https://en.wikipedia.org/wiki/Linux_framebuffer). Given it’s  using the same kernel API’s used internally by [Xorg](https://wiki.archlinux.org/title/xorg) and [Wayland](https://en.wikipedia.org/wiki/Wayland_(display_server_protocol)) compositors, it essentially replaces Xorg/Wayland, gaining exclusive access to the graphics hardware. 

Beyond industrial device manufacturing, this approach brings countless other benefits, such as ensuring that an app looks and behaves identically across all the supported platforms. The ability to customise any part of the UI for any particular platform is maintained and is as simple as altering XAML styles. With Avalonia, creating great-looking, cross-platform apps without needing to use platform-specific UI APIs for the various devices you're supporting is easy. It's the essence of "write once, run everywhere".

## Architecture

The architecture diagram below gives a good overview of how Avalonia works. Note that the majority of functionality in Avalonia resides in a netstandard2.0 project! 

![Overview of the Avalonia Architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x5q7x1d628kcb1d4g09g.png)

Rendering an application's user interface is just one piece of the puzzle. Avalonia also needs to handle elements such as wiring up the clipboard, windowing, cursors, hardware acceleration and input events. These pieces of the puzzle are required to facilitate Avalonia on a new platform.

## Platform Support

Regardless of the intricacies required to create a UI toolkit, between 10 and 15 percent of the Avalonia code relates to facilitating the platforms. Most of the code is agnostic to any platform, meaning it's relatively easy for us to bring Avalonia support to new platforms when we want. 

At the fundamental level, supporting a new platform with Avalonia requires two things; the ability to draw pixels and receive input events. 

## Avalonia VNC

To demonstrate this point, let's look at the [Avalonia VNC platform support](https://github.com/AvaloniaUI/Avalonia/tree/master/src/Avalonia.Headless.Vnc). While not typically considered a platform in its own right, in the context of Avalonia, VNC meets the prerequisites of being able to draw pixels and receive input events. It can thus be considered a platform! 

What's especially surprising about the Avalonia VNC platform support is its implementation stands at less than 200 lines of code! It demonstrates just how little code is required to enable a new platform! 

Why does Avalonia support VNC in the first place? Well, it's valuable for a few reasons, with my favourite being it enabled very early testing of web support. Before WASM with Blazor were viable options for enabling Avalonia in the browser, it was possible to run an embedded VNC server serving Avalonia content, delivering rudimentary web support. 

## Conclusion
 
While Avalonia has been historically focused on the desktop for almost a decade now, the recently released preview of iOS, Android and Web support represents a new milestone for the project. Supporting these platforms was never a technical problem or a matter of developer resources. 

The developers contributing to Avalonia have helped create a cross-platform UI framework that industry leaders such as GitHub, Unity and JetBrains have come to trust. Given the project's remarkable success and massive growth in contributors, it presents an excellent opportunity to look to the future and consider how we can improve mobile and web development. 

## Learn More

- [Homepage](https://avaloniaui.net/)
- [Docs](https://docs.avaloniaui.net/)
- [GitHub](https://github.com/avaloniaui)