VitrumMAUI 1.0.13

dotnet add package VitrumMAUI --version 1.0.13
                    
NuGet\Install-Package VitrumMAUI -Version 1.0.13
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="VitrumMAUI" Version="1.0.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="VitrumMAUI" Version="1.0.13" />
                    
Directory.Packages.props
<PackageReference Include="VitrumMAUI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add VitrumMAUI --version 1.0.13
                    
#r "nuget: VitrumMAUI, 1.0.13"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package VitrumMAUI@1.0.13
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=VitrumMAUI&version=1.0.13
                    
Install as a Cake Addin
#tool nuget:?package=VitrumMAUI&version=1.0.13
                    
Install as a Cake Tool

VitrumMAUI

Visitor Count

Vitrum (Latin: glass) — GPU-accelerated frosted-glass backdrop blur for .NET MAUI. Zero-CPU acrylic blur, glass blur, and backdrop blur on Android, powered by the native RenderNode + RenderEffect pipeline.

Keywords: maui blur acrylic blur glass blur backdrop blur frosted glass acrylic view maui acrylic android blur blur effect maui glassmorphism maui


In the wild — DChat

DChat is an upcoming decentralized, privacy-first messaging app built on blockchain infrastructure — currently in development for Android & iOS.

VitrumMAUI powers every blurred surface in DChat: the action bar, the top panel, and the message input — all blurring a live scrolling message list at 60/120 fps with zero CPU involvement.

<table> <tr> <td><img src="assets/demo_part1.gif" width="260" alt="DChat blur demo 1"/></td> <td><img src="assets/demo_part2.gif" width="260" alt="DChat blur demo 2"/></td> <td><img src="assets/screenshot.jpg" width="260" alt="DChat screenshot"/></td> </tr> </table>


Why this exists

Every existing MAUI blur library (Sharpnado Acrylic, AcrylicView, etc.) works by capturing a Bitmap on the CPU, running a stack-blur pass in software, then uploading it back to the GPU on every frame. On a simple layout that's fine. Open a CollectionView loaded with images and the frame rate drops because the blur bitmap is regenerated on the UI thread every time the content scrolls.

The correct approach is to use the Android 12+ RenderNode + RenderEffect API: record the background content into a RenderNode, attach a RenderEffect.createBlurEffect directly to that node, and let the GPU driver handle all blurring in hardware with zero CPU involvement. The blurred texture is a GPU resource — it never touches the CPU. The result is smooth 60/120 fps blur even over a fast-scrolling list of images. VitrumMAUI brings that API to .NET MAUI C#.


Platform support

Platform Status
Android 12+ (API 31+) ✅ GPU RenderEffect + saturation chain, zero CPU
Android 9–11 (API 28–30) ⚠️ Tint-only fallback
Android < 9 ⚠️ Tint-only fallback
iOS 26+ / macOS 26+ ✅ Use Apple Liquid Glass — UIVisualEffectView / .glassEffect()
iOS < 26 / macOS < 26 ✅ Use UIVisualEffectView — built-in, no library needed
Windows 🚧 Not implemented

iOS and macOS are intentionally not implemented here. Apple introduced Liquid Glass in iOS 26 / macOS 26 as a first-party design primitive — the OS itself provides real-time frosted-glass rendering baked into standard controls with zero third-party code required. Shipping a custom blur on top of that would fight the platform and produce an inferior result. Use Liquid Glass on Apple platforms; use VitrumMAUI on Android.


How it works

BlurHostView (NativeBlurHostView extends ContentViewGroup)
│
│  DispatchDraw(canvas):
│    1. Pre-fill recording canvas with CaptureBackground color
│    2. Hide all registered BlurConsumerViews (INVISIBLE)
│    3. Record GetChildAt(0) into a RenderNode
│       with RenderEffect = blur(60dp) chained with colorFilter(saturation=2x)
│    4. Show BlurConsumerViews again
│    5. base.DispatchDraw(canvas) — normal draw pass
│
└─ BlurConsumerView (NativeBlurConsumerView extends ContentViewGroup)
     DispatchDraw(canvas):
       1. Draw blur RenderNode at 100% opacity (blurred background)
       2. Draw TintColor rect on top (semi-transparent scrim)
       3. base.DispatchDraw(canvas) — draws children on top

The saturation chain (colorMatrix.setSaturation(2f)) gives the "glass" look — colours behind the blur pop slightly for a premium frosted effect.


Quick start

1. Register

// MauiProgram.cs
builder.UseVitrum();

2. Use in XAML

xmlns:vitrum="clr-namespace:Vitrum;assembly=VitrumMAUI"

<Grid>
    <vitrum:BlurHostView CaptureBackground="#FF141420">
        
        <CollectionView ItemsSource="{Binding Items}" ... />
    </vitrum:BlurHostView>

    
    <vitrum:BlurConsumerView TintColor="#B21F2936"
                             VerticalOptions="Start"
                             HeightRequest="56">
        <Label Text="Title" TextColor="White" ... />
    </vitrum:BlurConsumerView>

    
    <vitrum:BlurConsumerView TintColor="#B21F2936"
                             VerticalOptions="End"
                             HeightRequest="72">
        <Entry Placeholder="Message..." ... />
    </vitrum:BlurConsumerView>
</Grid>

⚠️ Crash rules — read before using

These are hard constraints. Violating them produces a SIGSEGV in libhwui.so — an infinite RenderNode.prepareTreeImpl recursion that kills the render thread. No exception is thrown; the process just dies.

Rule 1 — BlurConsumerView must NOT be inside the BlurHostView's content subtree

The blur is captured from BlurHostView.GetChildAt(0). If a BlurConsumerView exists anywhere in that subtree (even deeply nested inside a child view), its cached RenderNode from the previous frame will contain a reference to _blurNode. When the capture re-records _blurNode, it embeds that stale RenderNode — which contains _blurNode — creating an infinite cycle.


<vitrum:BlurHostView>
    <Grid>
        <CollectionView ... />
        <vitrum:BlurConsumerView ... />   
    </Grid>
</vitrum:BlurHostView>


<Grid>
    <vitrum:BlurHostView>
        <CollectionView ... />
    </vitrum:BlurHostView>
    <vitrum:BlurConsumerView ... />
</Grid>

Why it crashes even when consumers are marked invisible: Before capturing, BlurEngine sets all registered consumers to INVISIBLE. For consumers that are direct children of the content Grid this is sufficient. But when a consumer is nested inside another view (e.g. a custom bar component), that parent view holds a stale cached RenderNode from the previous frame that already contains the consumer's RenderNode → _blurNode. Toggling INVISIBLE on the consumer does not force the parent to re-record its RenderNode synchronously on the render thread. The stale reference gets embedded into the new _blurNode recording. Cycle. Crash.

Rule 2 — BlurHostView's direct child must not be a BlurConsumerView

GetChildAt(0) must return the background content. If the first child is itself a BlurConsumerView, the capture creates an immediate cycle.

Rule 3 — Do not nest BlurHostViews

One BlurHostView per visual tree branch. Two hosts in the same tree will fight over the same render pass.

Rule 4 — BlurConsumerViews inside sibling views are safe

A consumer can live arbitrarily deep inside a sibling of BlurHostView. FindHostHandler walks up the MAUI element tree and checks each level's siblings, so nesting depth inside a sibling is irrelevant.


<Grid>
    <vitrum:BlurHostView>
        <CollectionView ... />
    </vitrum:BlurHostView>

    <local:MyCustomBar ... />   
</Grid>

Properties

BlurHostView

Property Type Default Notes
BlurRadius float 60 Blur sigma in dp. Increase for heavier blur.
CaptureBackground Color Transparent Pre-fills the recording canvas before content. Set to your page background color for a denser frosted base.

BlurConsumerView

Property Type Default Notes
TintColor Color #661C1C25 ARGB scrim drawn on top of the blur. Alpha controls opacity.

TintColor reference

Value Effect
#B21F2936 Dark blue-gray, 70% opacity — recommended for dark theme
#D81F2936 Same, 85% opacity — denser dark theme
#B2FFFFFF White frost, 70% opacity — light theme

Draw order: blur at 100% first, then tint scrim on top. With #B21F2936 (alpha=0xB2): 30% of the blurred content shows through the 70% scrim.


License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0-android36.0 is compatible.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst26.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.13 118 4/24/2026
1.0.9 99 4/24/2026
1.0.8 98 4/23/2026
1.0.7 106 4/23/2026

Fix frozen blur on nested BlurHostViews: PostInvalidateOnAnimation drives continuous real-screen capture loop