Skip to content

A modern MAUI frame component supporting blur, acrylic, dark mode. Implemented with RealtimeBlurView on Android (custom blurview) and UIVisualEffectView on iOS.

License

Notifications You must be signed in to change notification settings

roubachof/Sharpnado.MaterialFrame

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sharpnado.MaterialFrame

Presentation

Supported platforms

Nuget

✔️ Android
✔️ iOS
✔️ MacCatalyst
✔️ WinUI

Initialization

  • In MauiProgram.cs:
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .UseSharpnadoMaterialFrame(loggerEnable: false)
        ...
}

Version 3.0 - What's New

Version 3.0 brings a complete overhaul of the MaterialFrame architecture with major improvements:

material_frame_v3.mp4

Handler Migration

All platforms have been migrated from the old Renderer pattern to modern MAUI Handlers:

  • Android: ContentViewHandler
  • iOS: ContentViewHandler
  • MacCatalyst: ContentViewHandler (full support added!)
  • Windows: ViewHandler<MaterialFrame, Grid>

Benefits: Better performance, cleaner code, better maintainability, and future-proof architecture.

Android Blur Revolution

Breaking Change: Replaced RenderScript with StackBlur algorithm

Why?

RenderScript was deprecated by Google and completely broken on Android 15+ devices with 16KB page size (crashes on Pixel 8 and newer devices).

The Solution: StackBlur

  • Pure C# implementation - No native dependencies, works on ALL Android versions including 15+
  • Async background processing - Blur runs on background thread with double buffering
  • Change detection - Skips blur when content hasn't changed (0% CPU when static)
  • Smooth performance - UI thread blocking reduced from ~22ms to ~3ms
  • 60 FPS scrolling - No more frame drops during animations

Performance Metrics

Metric Before (RenderScript) After (StackBlur)
Android 15+ compatibility đź’Ą Broken âś… Works
UI thread time ~22ms ~3ms
Static content CPU 100% 0%
Frame rate 30-45 FPS 60 FPS
Blur quality Excellent Excellent

New Features

  • MacCatalyst Support: Full blur support on Mac with shared iOS handler
  • Better Memory Management: Improved resource cleanup across all platforms
  • PropertyMapper: Declarative property handling for better performance

Migration Guide

No breaking changes in the API! Your existing XAML and code will work as-is. The improvements are all under the hood.

The only change: Android now uses StackBlur instead of RenderScript, which means:

  • âś… Works on Android 15+
  • âś… No more crashes on 16KB page size devices
  • âś… Better performance overall

Android Compatibility issues

Warning, because of LayerDrawable the Acrylic glow effect (the white glow on the top of the MaterialFrame is only available on API 23+ (since Marshmallow).

Presentation

The MAUI MaterialFrame delivers out of the box modern popular theming:

  • Light
  • Dark
  • Acrylic
  • AcrylicBlur

You can switch from one theme to another thanks to the MaterialTheme property.

Switch

MaterialTheme

Light

In light theme, you can set the LightThemeBackgroundColor and control the Elevation.

Dark

In dark theme, you can only control the Elevation, more elevation equals more light on the black frame (see below).

Acrylic

In Acrylic theme, you can still set the LightThemeBackgroundColor, also a Color of F1F1F1 is advised to have a good Acrylic effect.

AcrylicBlur

In AcrylicBlur theme, LightThemeBackgroundColor and Elevation properties are discarded.

You can set the BlurStyle property for both Android and iOS.

REMARK: On Android, the blur uses a pure C# StackBlur implementation that's optimized with async processing and change detection. It's performant but still a CPU operation, so use it wisely.

Light

ExtraLight

Dark

WinUI specific properties

WinUI is the home of the Acrylic effect \o/

WinUIBlurOverlayColor

WinUI only.

Changes the overlay color over the blur (should be a transparent color, obviously). If not set, the different blur style styles take over.

Android specific properties

Because the Android version is a custom blur implementation, you have access to some fine tuning properties.

AndroidBlurOverlayColor

Android only.

Changes the overlay color over the blur (should be a transparent color, obviously). If not set, the different blur style styles take over.

AndroidBlurRadius

Android only.

Changes the blur radius on Android. If set, it takes precedence over MaterialBlurStyle. If not set, the different blur style styles take over.

AndroidBlurRootElement (Performance)

Android only: the root element must be an ancestor of the MaterialFrame.

Blur computation is very costly on Android since it needs to process all the view hierarchy from the root element to be blurred (most of the time the element displaying the underlying image) to the blur frame. The shorter the path, the better the performance. If no root element is set, the activity decor view is used.

Android handler configuration

You can configure the Android handler with static properties:

BlurUpdateIntervalMs

Minimum interval in milliseconds between blur updates. This throttles blur processing to prevent infinite loops and reduce CPU usage.

#if ANDROID
// In MauiProgram.cs or App.xaml.cs
AndroidMaterialFrameHandler.BlurUpdateIntervalMs = 16;  // 60fps - smoother
AndroidMaterialFrameHandler.BlurUpdateIntervalMs = 32;  // 30fps - default (balanced)
AndroidMaterialFrameHandler.BlurUpdateIntervalMs = 50;  // 20fps - battery saver
#endif

Default: 32ms (~30fps) - Good balance between smoothness and battery life

Use cases:

  • 16ms (60fps): Smooth animations, gaming scenarios
  • 32ms (30fps): General UI, recommended default
  • 50ms+ (20fps): Static content, battery-critical apps
BlurProcessingDelayMilliseconds

Delay before starting blur processing after view attachment. Sometimes the computation of the background can take some time (svg images for example). Setting a bigger delay ensures that the background is rendered first and can fix some glitches.

The Android implementation includes:

  • StackBlur: Pure C# blur algorithm (no native dependencies)
  • Async processing: Blur runs on background thread with double buffering
  • Change detection: Skips blur when content hasn't changed (0% CPU when static)
  • Time throttling: Configurable update interval prevents infinite loops
  • Performance: UI thread blocking reduced from ~22ms to ~3ms

LightThemeBackgroundColor

The background color in Light and Acrylic themes. In Dark theme, this value is ignored because the background color depends on the Elevation. In AcrylicBlur, the value is discarded cause iOS doesn't allow you to control the overlay color. Note that setting the BackgroundColor property has no effect with the MaterialFrame.

AcrylicGlowColor

You can change the "glow" of a MaterialFrame with an acrylic theme (the thin top white glow). Default is white. This property is ignored is the theme is not set to Acrylic.

Elevation

This property semantic changes according to the theme currently set:

Light Theme

Cast a shadow according to Google's Material elevation specs.

Dark Theme

Change the frame's background color according to Google's dark mode specs:

Acrylic Theme

Property is ignored and a custom shadow is applied.

AcrylicBlur Theme

Property is ignored, no shadow is cast.

CornerRadius

Sets the corner radius of the frame (default: 5).

Changing theme for every frames

You either use DynamicResource as explained in my previous post.

Or use the static method called ChangeGlobalTheme(Theme newTheme). Setting a new theme on this method will change the MaterialTheme of every MaterialFrame of your app.

Examples of styles

Acrylic style

<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:sh="clr-namespace:Sharpnado.MaterialFrame;assembly=Sharpnado.MaterialFrame">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Colors.xaml" />
    </ResourceDictionaries.MergedDictionaries>

    <Style TargetType="sh:MaterialFrame">
        <Setter Property="MaterialTheme" Value="Acrylic" />
        <Setter Property="Margin" Value="5, 5, 5, 10" />
        <Setter Property="Padding" Value="20,15" />
        <Setter Property="CornerRadius" Value="10" />
        <Setter Property="LightThemeBackgroundColor" Value="{StaticResource AcrylicFrameBackgroundColor}" />
    </Style>

</ResourceDictionary>

Colors.xaml file:

<?xml version="1.0" encoding="UTF-8" ?>

<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

    <Color x:Key="AcrylicSurface">#E6E6E6</Color>

    <Color x:Key="AcrylicFrameBackgroundColor">#F1F1F1</Color>

    <Color x:Key="AccentColor">#00E000</Color>

    <Color x:Key="PrimaryColor">Black</Color>
    <Color x:Key="SecondaryColor">#60000000</Color>
    <Color x:Key="TernaryColor">#30000000</Color>

    <Color x:Key="TextPrimaryColor">Black</Color>
    <Color x:Key="TextSecondaryColor">#60000000</Color>
    <Color x:Key="TextTernaryColor">#40000000</Color>

</ResourceDictionary>

Dynamic styles

MaterialFrame xaml:

    <sh:MaterialFrame
        Margin="0,16"
        Padding="16,10"
        Elevation="4"
        LightThemeBackgroundColor="{DynamicResource DynamicLightThemeColor}"
        CornerRadius="{DynamicResource DynamicCornerRadius}"
        MaterialTheme="{DynamicResource DynamicMaterialTheme}" />

Styles:

    <Color x:Key="DarkSurface">#121212</Color>
    <Color x:Key="LightSurface">#02FF0266</Color>
    <Color x:Key="AcrylicSurface">#E4E4E4</Color>

    <Color x:Key="OnSurfaceColor">#FFFFFF</Color>
    <Color x:Key="AcrylicFrameBackgroundColor">#F1F1F1</Color>

Theme switching code:

    public static void SetDarkMode()
    {
        // MaterialFrame.ChangeGlobalTheme(MaterialFrame.Theme.Dark);
        SetDynamicResource(DynamicMaterialTheme, MaterialFrame.Theme.Dark);

        SetDynamicResource(DynamicBackgroundColor, "DarkSurface");
        SetDynamicResource(DynamicCornerRadius, 5);
    }

    public static void SetLightMode(bool isAcrylic)
    {
        // MaterialFrame.ChangeGlobalTheme(isAcrylic ? MaterialFrame.Theme.Acrylic : MaterialFrame.Theme.Light);
        SetDynamicResource(DynamicMaterialTheme, isAcrylic ? MaterialFrame.Theme.Acrylic : MaterialFrame.Theme.Light);

        SetDynamicResource(DynamicBackgroundColor, isAcrylic ? "AcrylicSurface" : "LightSurface");
        SetDynamicResource(DynamicLightThemeColor, isAcrylic ? "AcrylicFrameBackgroundColor" : "OnSurfaceColor");
        SetDynamicResource(DynamicCornerRadius, isAcrylic ? 10 : 5);
    }

Performance

Android Blur Performance

The Android blur implementation has been completely rewritten for v3.0:

  • StackBlur algorithm: Pure C# implementation, no RenderScript dependency
  • Async processing: Runs on background thread with double buffering
  • Change detection: Only processes blur when content changes
  • Results: UI thread blocking reduced from ~22ms to ~3ms, smooth 60 FPS scrolling

Acrylic Mode

The Acrylic glow effect uses a single view with lightweight rendering:

  • LayerDrawable on Android
  • CALayer on iOS/MacCatalyst

Only the background changes, no view stacking required.

License

Android Blur Implementation

The StackBlur algorithm is based on Mario Klingemann's implementation.

Copyright Mario Klingemann (http://incubator.quasimondo.com)

Licensed under the MIT License.

Legacy Code Attribution

Previous versions used RealtimeBlurView by Tu Yimin (http://github.com/mmin18). Licensed under the Apache License, Version 2.0.

About

A modern MAUI frame component supporting blur, acrylic, dark mode. Implemented with RealtimeBlurView on Android (custom blurview) and UIVisualEffectView on iOS.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages