# A simple way to implement Dark Mode in .NET MAUI

### Intro

I absolutely **love** dark app themes. As a person working a lot in front of computer screens and using mobile devices a lot, I have come to appreciate the ease on the eyes that **Dark (or *Night*) Modes** of software and mobile apps provide. In this blog post, I would like to show a simple way to implement this wonderful feature in order to provide a flawless user experience to your app's users without having to manipulate a `ResourceDictionary` at run-time.

As the convention goes these days, users should have the option to choose whether to use 
a Dark or Light Theme or to simply follow the system setting. **.NET MAUI** conveniently supports "Light" and "Dark" App Themes right out of the box. The sample Shell app even comes pre-configured with some styles that use the built-in markup extension `AppThemeBinding`. Let's explore how to take advantage of this. *The code for this sample implementation can be found in my [GitHub repository](https://github.com/ewerspej/maui-samples).*

### The UserAppTheme property and Styles

The `Application` class (*App.xaml.cs*) comes with a property of the `enum` type `AppTheme` called `UserAppTheme` which can be set to change the current theme:

```csharp
// dark theme
UserAppTheme = AppTheme.Dark;

// light theme
UserAppTheme = AppTheme.Light;

// follow system default
UserAppTheme = AppTheme.Unspecified;
```

Coupled with the `AppThemeBinding` markup extension inside some `Style` elements in a `ResourceDictionary` (e.g. defined in the *Styles.xaml* file) this would already to suffice to set the theme once and be done:

```xml
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
```

Our app would now just be either shown with a black or white page background depending on the hard-coded value for the `UserAppTheme` property. This is already pretty neat, but we want to let the user decide. So let's do that.

### Our own Theme class

In order to provide a meaningful user experience, we are going to implement our own `Theme` class which holds the `AppTheme` as well as a `DisplayName`. 

The advantages of this class will become apparent further down. *Hint: The DisplayName could be used in advanced scenarios to show a translated string depending on the selected language, but we won't cover localization of string resources in this article.*

```csharp
public sealed class Theme
{
    public static Theme Dark = new(AppTheme.Dark, "Night Mode");
    public static Theme Light = new(AppTheme.Light, "Day Mode");
    public static Theme System = new(AppTheme.Unspecified, "Follow System");

    public static List<Theme> AvailableThemes { get; } = new()
    {
        Dark,
        Light,
        System
    };

    public AppTheme AppTheme { get; }
    public string DisplayName { get; }

    private Theme(AppTheme theme, string displayName)
    {
        AppTheme = theme;
        DisplayName = displayName;
    }
}
```

Since the theme can usually be selected in a **Settings** section of an app, we will also implement a small `SettingsService` class.

### The SettingsService

In this example, we simply create a small class that implements the *INotifyPropertyChanged* interface and use it as a singleton for convenience. I am skipping advanced topics like Dependency Injection and MVVM Code Generation.

```csharp
public class SettingsService : INotifyPropertyChanged
{
    private static SettingsService _instance;
    public static SettingsService Instance => _instance ??= new SettingsService();

    private SettingsService()
    {
        // set the default (in advanced scenarios, this could be read from the preferences)
        Theme = Theme.System;
    }

    private Theme _theme;
    public Theme Theme
    {
        get => _theme;
        set
        {
           if(_theme == value) return;
            _theme = value;
           OnPropertyChanged();
        }
    }

    //...
}
```

We are going to use the `SettingsService` to hold the information about the current app theme, but it can also be extended to actually store the theme to the app's preferences. *To see how this can be done, check out the repository with the sample code further down.*

### A sample UI

Now that we have the Theme and SettingsService classes, we can create a simple UI that will update its styling based on the selected `AppTheme`. We can add the following XAML to our `MainPage` (or any other *Page* for that matter) and bind to the `SettingsService.Instance` singleton and the `DisplayName` of our `Theme` class:

```xml
  <ContentPage.Resources>
    <ResourceDictionary>
      <Style TargetType="Label" x:Key="LabelStyle">
        <Setter Property="Text" Value="{AppThemeBinding Light=Light, Dark=Dark}" />
      </Style>
    </ResourceDictionary>
  </ContentPage.Resources>

  <Grid>
    <VerticalStackLayout
      Spacing="25"
      Padding="30,0"
      Margin="0,50,0,0"
      VerticalOptions="Start">

      <Label 
        HorizontalOptions="Center"
        Style="{DynamicResource LabelStyle}"
        FontSize="Title"/>

      <HorizontalStackLayout
        VerticalOptions="Center"
        HorizontalOptions="Center">
        <Label
          VerticalOptions="Center"
          Text="Select Theme: " />

        <!-- Note: We are binding to the SettingsService singleton and the DisplayName of the Theme -->
        <Picker
          VerticalOptions="Center"
          ItemsSource="{Binding Source={x:Static models:Theme.AvailableThemes}}"
          ItemDisplayBinding="{Binding DisplayName}"
          SelectedItem="{Binding Path=Theme, Source={x:Static services:SettingsService.Instance}}"/>
      </HorizontalStackLayout>

    </VerticalStackLayout>
  </Grid>
```

We also need to define a few more styles in our main `ResourceDictionary` which lives in the **Styles.xaml** file of our project, but they can also be defined anywhere else:

```xml
  <Style TargetType="ContentPage" ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
  </Style>

  <Style TargetType="Label">
    <Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray500}, Dark={StaticResource White}}" />
  </Style>

  <Style TargetType="Picker">
    <Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray500}, Dark={StaticResource White}}" />
  </Style>
```

This should roughly look like this:

![00_SampleUI.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1666341678295/8RhG2-s_g.PNG align="left")

At the moment, nothing will actually happen when we tap on the Picker and select any of the Themes, because we still need to wire up the `SettingService` with the App's `UserAppTheme` property. Let's do that next.

### Bringing it all together

The final step is to subscribe to the `PropertyChanged` event of our `SettingsService` in order to be able to react to changes in the settings. We will do this from within the **App.xaml.cs**, because that's where the `UserAppTheme` property lives:

```
public App()
{
    InitializeComponent();
    MainPage = new AppShell();

    // let's set the initial theme already during the app start
    SetTheme();

    // subscribe to changes in the settings
    SettingsService.Instance.PropertyChanged += OnSettingsPropertyChanged;
}

private void OnSettingsPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(SettingsService.Theme))
    {
        SetTheme();
    }
}

private void SetTheme()
{
    UserAppTheme = SettingsService.Instance?.Theme != null
                 ? SettingsService.Instance.Theme.AppTheme
                 : AppTheme.Unspecified;
}
```

Essentially, what we are doing here is to respond to changes in the settings directly. This is possible, because our `SettingsService` implements the `INotifyPropertyChanged` interface.

### Running the app

Finally, we can run the app and select the App Theme during run-time, which is pretty awesome:

![01_SelectTheme.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1666342866947/cvPqFJfJ0.PNG align="left")

![02_DarkMode.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1666342875779/7uj-TdUlN.PNG align="left")

### Conclusion and further writing

**.NET MAUI** comes with a lot of functionality out of the box and ready to use. I hope this blog post gives you an idea of how to provide an easy way to add a Dark Mode to your app and let your users select their preferred App Theme.

In future posts, I will show more advanced topics like wiring up the Picker control with localized strings, creating custom components and sharing them via nuget.org, as well as using MVVM Code Generation to your advantage to reduce. Stay tuned!

If you enjoyed this blog post, you may want to follow me on [LinkedIn](https://www.linkedin.com/in/jewerspeters), subscribe to this [blog](https://hashnode.com/@ewerspej) and star the [GitHub repository](https://github.com/ewerspej/maui-samples) for this post.

**Note:** *At the time of writing, I have used Visual Studio 2022 17.4 Preview 2.1 and .NET 7 RC1.*
