MaterialColorUtilities.Maui 0.3.0

dotnet add package MaterialColorUtilities.Maui --version 0.3.0
NuGet\Install-Package MaterialColorUtilities.Maui -Version 0.3.0
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="MaterialColorUtilities.Maui" Version="0.3.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MaterialColorUtilities.Maui --version 0.3.0
#r "nuget: MaterialColorUtilities.Maui, 0.3.0"
#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.
// Install MaterialColorUtilities.Maui as a Cake Addin
#addin nuget:?package=MaterialColorUtilities.Maui&version=0.3.0

// Install MaterialColorUtilities.Maui as a Cake Tool
#tool nuget:?package=MaterialColorUtilities.Maui&version=0.3.0

MaterialColorUtilities.Maui

A library for adding Material You colors to your .NET MAUI app

Features

  • Dynamic theming on every platform (except iOS)
  • Light/dark theme support
  • Automatically storing and reapplying seed color/dark mode/style preferences

All of these can be turned on/off at any time.

Getting started

  1. Add a reference to the package. For instructions visit the NuGet page.
  2. Call the UseMaterialColors extension method in MauiProgram.cs. This will add all the services required for theming to the service container.
+using MaterialColorUtilities.Maui;
namespace YourApp;
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        MauiAppBuilder builder = MauiApp
            .CreateBuilder()
+           .UseMaterialColors()
            .UseMauiApp<App>();
        return builder.Build();
    }
}

You can specify the fallback seed as an argument to the extension method or use a lambda for more options:

.UseMaterialColors(options =>
{
    options.FallbackSeed = 0xB000B5;
    options.UseDynamicColor = false;
})
  1. Initialize MaterialColorService with a ResourceDictionary:

<details> <summary> If you use XAML click here </summary>

Copy these lines:

<Color x:Key="Primary" />
<Color x:Key="PrimaryContainer" />
<Color x:Key="Secondary" />
<Color x:Key="SecondaryContainer" />
<Color x:Key="Tertiary" />
<Color x:Key="TertiaryContainer" />
<Color x:Key="Surface" />
<Color x:Key="SurfaceVariant" />
<Color x:Key="Background" />
<Color x:Key="Error" />
<Color x:Key="ErrorContainer" />
<Color x:Key="OnPrimary" />
<Color x:Key="OnPrimaryContainer" />
<Color x:Key="OnSecondary" />
<Color x:Key="OnSecondaryContainer" />
<Color x:Key="OnTertiary" />
<Color x:Key="OnTertiaryContainer" />
<Color x:Key="OnSurface" />
<Color x:Key="OnSurfaceVariant" />
<Color x:Key="OnError" />
<Color x:Key="OnErrorContainer" />
<Color x:Key="OnBackground" />
<Color x:Key="Outline" />
<Color x:Key="Shadow" />
<Color x:Key="InverseSurface" />
<Color x:Key="InverseOnSurface" />
<Color x:Key="InversePrimary" />
<Color x:Key="Surface1" />
<Color x:Key="Surface2" />
<Color x:Key="Surface3" />
<Color x:Key="Surface4" />
<Color x:Key="Surface5" />
<SolidColorBrush x:Key="PrimaryBrush" />
<SolidColorBrush x:Key="PrimaryContainerBrush" />
<SolidColorBrush x:Key="SecondaryBrush" />
<SolidColorBrush x:Key="SecondaryContainerBrush" />
<SolidColorBrush x:Key="TertiaryBrush" />
<SolidColorBrush x:Key="TertiaryContainerBrush" />
<SolidColorBrush x:Key="SurfaceBrush" />
<SolidColorBrush x:Key="SurfaceVariantBrush" />
<SolidColorBrush x:Key="BackgroundBrush" />
<SolidColorBrush x:Key="ErrorBrush" />
<SolidColorBrush x:Key="ErrorContainerBrush" />
<SolidColorBrush x:Key="OnPrimaryBrush" />
<SolidColorBrush x:Key="OnPrimaryContainerBrush" />
<SolidColorBrush x:Key="OnSecondaryBrush" />
<SolidColorBrush x:Key="OnSecondaryContainerBrush" />
<SolidColorBrush x:Key="OnTertiaryBrush" />
<SolidColorBrush x:Key="OnTertiaryContainerBrush" />
<SolidColorBrush x:Key="OnSurfaceBrush" />
<SolidColorBrush x:Key="OnSurfaceVariantBrush" />
<SolidColorBrush x:Key="OnErrorBrush" />
<SolidColorBrush x:Key="OnErrorContainerBrush" />
<SolidColorBrush x:Key="OnBackgroundBrush" />
<SolidColorBrush x:Key="OutlineBrush" />
<SolidColorBrush x:Key="ShadowBrush" />
<SolidColorBrush x:Key="InverseSurfaceBrush" />
<SolidColorBrush x:Key="InverseOnSurfaceBrush" />
<SolidColorBrush x:Key="InversePrimaryBrush" />
<SolidColorBrush x:Key="Surface1Brush" />
<SolidColorBrush x:Key="Surface2Brush" />
<SolidColorBrush x:Key="Surface3Brush" />
<SolidColorBrush x:Key="Surface4Brush" />
<SolidColorBrush x:Key="Surface5Brush" />

and update App.xaml like this:

<?xml version="1.0" encoding="UTF-8" ?>
<Application
    x:Class="Playground.Maui.App"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+   xmlns:mcu="clr-namespace:MaterialColorUtilities.Maui;assembly=MaterialColorUtilities.Maui">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
+               <ResourceDictionary>
+                   
+               </ResourceDictionary>
+               <mcu:MaterialColorResourceDictionary />

                
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>

            
            <Style TargetType="Button">
                <Setter Property="BackgroundColor" Value="{DynamicResource Primary}" />
                <Setter Property="TextColor" Value="{DynamicResource OnPrimary}" />
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

</details>

<details> <summary>If you don't use XAML click here</summary>

Edit App.xaml.cs like this:

+using MaterialColorUtilities.Maui;

public class App : Application
{
    public App()
    {
+       IMaterialColorService.Current.Initialize(this.Resources);
        
        MainPage = new AppShell();
    }
}

</details>

Usage

By default the colors are added as global resources. You can access them

  • in XAML using DynamicResource:
<Button BackgroundColor="{DynamicResource Primary}" />
  • in C# using Element.SetDynamicResource():
Button button = new();
button.SetDynamicResource(Button.BackgroundProperty, "Primary");

The MaterialColorService can be resolved from the MAUI dependency injection container. It contains the current core palette and schemes. It also has properties for updating the seed color and style.

using MaterialColorUtilites.Maui;

public class MyService
{
    public MyService(MaterialColorService materialColorService)
    {
        Scheme<Color> scheme = materialColorService.SchemeMaui;
        materialColorService.Seed = 0x123456;
        materialColorService.Style = Style.Spritz;
    }
}

Customization

Extending MaterialColorService allows you to use your custom colors or set different outputs for theming.

When you do this, register the new service at startup:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        MauiAppBuilder builder = MauiApp
            .CreateBuilder()
+           .UseMaterialColors<MyMaterialColorService>()
            .UseMauiApp<App>();
        return builder.Build();
    }
}

Custom colors

If you want to use custom colors, follow the instructions here on how to create you own palettes/schemes. Then specify them as generic type arguments:

public class MyMaterialColorService : MaterialColorService<MyCorePalette, MyScheme<uint>, MyScheme<Color>, MyLightSchemeMapper, MyDarkSchemeMapper>
{
    public MyMaterialColorService(IOptions<MaterialColorOptions> options, IDynamicColorService dynamicColorService, IPreferences preferences)
        : base(options, dynamicColorService, preferences)
    {
    }
}

Custom theming

By overriding the Apply method you can define things that should happen when colors change. For example invoking an event, setting native colors or if you are using .NET MAUI with Blazor, updating CSS.

public class MyMaterialColorService : MaterialColorService
{
    private readonly WeakEventManager _weakEventManager = new();
    
    public MyMaterialColorService(IOptions<MaterialColorOptions> options, IDynamicColorService dynamicColorService, IPreferences preferences) : base(options, dynamicColorService, preferences)
    {
    }
    
    public event EventHandler SeedChanged
    {
        add => _weakEventManager.AddEventHandler(value);
        remove => _weakEventManager.RemoveEventHandler(value);
    }

    protected override async void Apply()
    {
        base.Apply();
        _weakEventManager.HandleEvent(null!, null!, nameof(SeedChanged));

#if ANDROID
        Activity activity = await Platform.WaitForActivityAsync();

        // Update status/navigation bar background color
        Android.Graphics.Color androidColor = SchemeMaui.Surface2.ToPlatform();
        activity.Window!.SetNavigationBarColor(androidColor);
        activity.Window!.SetStatusBarColor(androidColor);

        // Update status/navigation bar text/icon color
        _ = new WindowInsetsControllerCompat(activity.Window, activity.Window.DecorView)
        {
            AppearanceLightStatusBars = !IsDark,
            AppearanceLightNavigationBars = !IsDark
        };
#endif
    }
}

Implementation details

Light/dark mode handling works everywhere, but the dynamic theming source is different on all of the platforms:

  • Android ≥12.0 (API 31): use system colors
  • Android ≥8.1 (API 27): use WallpaperColors.Primary as seed color
  • Android ≥7.0 (API 24): if the StorageRead permission has been granted, extract a color from the wallpaper, otherwise use fallback seed
  • Android ≥5.0 (API 21): use fallback seed
  • iOS: no accent color, use fallback seed
  • Mac: use accent color
  • Windows: use accent color
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-android31.0 is compatible.  net6.0-ios was computed.  net6.0-ios16.1 is compatible.  net6.0-maccatalyst was computed.  net6.0-maccatalyst16.1 is compatible.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net6.0-windows10.0.19041 is compatible.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on MaterialColorUtilities.Maui:

Package Downloads
RichMvvm.MAUI

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.3.0 1,861 4/11/2023
0.2.0 569 11/4/2022
0.1.2 453 8/31/2022
0.1.1 504 7/14/2022
0.1.0 398 7/13/2022