Xamarin.forms: [Spec] OS Themes (Dark Mode support)

Created on 2 Mar 2020  Â·  19Comments  Â·  Source: xamarin/Xamarin.Forms

OS Themes (Dark Mode)

In order to be able to use the different themes that are now supported in iOS, Android and UWP, we should surface these APIs to Xamarin.Forms for our end-users.

This spec describes all the changes needed to leverage the different functionalities that are available in the underlaying platforms today. This way, people will have the ability to implement OS Theme mode support in their apps easier.

API

Different App Themes

We determine the supported user interface styles through an enum, provided by Essentials. Repeated here for clarity.

public enum AppTheme
{
    Unspecified,
    Light,
    Dark
}

Detect & Change User Interface Style

DeviceInfo

In the Device.DeviceInfo we add a property to be able to see what appearance is currently active

| API | Description |
| ------------- | ------------- |
| AppTheme CurrentAppTheme | What is the actual theme currently for this device |

Already available through Essentials

We wrap the Essentials call for this in an API of our own. This will make the API for this more discoverable.

VisualElement

AppThemeChanged Event

To be able to respond to changes we expose this event on all visual elements.

| API | Description |
| ------------- | ------------- |
| void AppThemeChanged(AppThemeChangedEventArgs args) | Event that is fired whenever the app theme changed |

public class AppThemeChangedEventArgs : EventArgs
{
    public AppTheme RequestedTheme { get; }
}
~Fix a VisualElement on a Certain AppTheme~ Not possible on all major platforms

We want to be able to set a fixed app theme per VisualElement. If this property is not set it will just use the current setting from the OS which will be reflected by AppTheme.Unspecified.

| API | Description |
| ------------- | ------------- |
| AppTheme VisualElement.AppTheme | Property to set the specific page to a specific theme. Default value: AppTheme.Unspecified |

Images & Colors

To be able to differentiate images and colors between themes we will introduce the OnAppTheme which will act alike OnIdiom but now for the different app themes. We will also make this available as a markup extension for XAML.

Example 1: Different Colors per AppTheme

<Label.TextColor>
     <OnAppTheme x:TypeArguments="Color" Default="Black" Light="Red" Dark="Green" />
</Label.TextColor>

Example 2: Different Colors per AppTheme Markup Extension

<Label ... TextColor="{OnAppTheme Black, Light=Red, Dark=Green}" />

Note that the Default can be omitted for this extension.

Example 3: Different Images per AppTheme

<Image.Source>
     <OnAppTheme x:TypeArguments="ImageSource" Default="Logo" Light="LogoOnBlack" Dark="LogoOnWhite" />
</Image.Source>

Example 4: Working with ResourceDictionaries

<Application ...>
    <Application.Resources>
        <Style x:Key="LabelPageHeadingStyle" TargetType="Label">
            <Setter Property="TextColor" Value="{OnAppTheme Black, Light=Red, Dark=Green}" />
        </Style>
    </Application.Resources>
</Application>

The above examples would automatically also work for images as they also support ImageSource as well as all the other property types. This way they would also be easy to incorporate into existing styles people may have in place.

AppThemeColor

We might consider implement a new type: AppThemeColor, which makes the declaration of the different colors in different themes less verbose. The idea behind AppThemeColor is to have a container that produces a color depending on the current OS theme setting on the control. So, in your resources you can define:

<Application ...>
    <Application.Resources>
        <AppThemeColor x:Key="BackgroundColor" Light="Black" Dark="White" Default="Azure" />
    </Application.Resources>
</Application>

And then you can simply reference this color by its key BackgroundColor and depending on the appearance mode it will give you black, white or azure.

<!-- or to make it update use a DynamicResource -->
<ContentPage ... BackgroundColor="{StaticResource BackgroundColor}" />

Usage in CSS

For CSS you can also use the new OnAppTheme object to differentiate between Light, Dark or Default. You can see a sample below.

<StyleSheet>
    <StyleSheetExtension.Style>
        <OnAppTheme x:TypeArguments="x:String">
            <OnAppTheme.Light>
                <![CDATA[
                    #cssStyledLabel {
                        color: purple;
                    }
                    ]]>
            </OnAppTheme.Light>
            <OnAppTheme.Dark>
                <![CDATA[
                    #cssStyledLabel {
                        color: orange;
                    }
                    ]]>
            </OnAppTheme.Dark>
        </OnAppTheme>
    </StyleSheetExtension.Style>
</StyleSheet>

To keep your XAML readable, you probably want to use the StyleSheetExtension.Source property and link to a different stylesheet depending on the current AppTheme.

<StyleSheet>
    <StyleSheetExtension.Source>
        <OnAppTheme x:TypeArguments="x:Uri" Light="/Light.css" Dark="/Dark.css" />
    </StyleSheetExtension.Source>
</StyleSheet>

(Additional) Tasks

I've tried to identify the tasks needed to implement this spec. Each, or a combination of some, could be one PR. This way we can track progress and break up the changes into smaller ones that are easier to trace.

Xamarin.Forms

  • [x] Provide a way to detect current mode (PR #9926)
  • [x] Provide an event on ~VisualElement~ Application to detect change in appearance mode (PR #9958)
  • [x] Provide an overridable method on VisualElement to be able to respond to changes in appearance mode more easily (PR #9958)
  • ~Provide property on VisualElement to set a fixed appearance mode~
  • [x] Provide markup extension to specify image/color depending on mode (also see https://github.com/xamarin/Xamarin.Forms/issues/7304#issuecomment-525896293) (PR #9958)
  • [ ] All issues with the darkmode tag
  • [x] Expose the platform specific (iOS semantic, android and UWP system) colors (GetNamedColor like GetNamedSize?) (PR #10008)
  • [x] Decide on Essentials dependency and/or naming sync

In addition to the work in the Xamarin.Forms layer, we also need to take care of some platform-specific things that might not be directly related to the proper working of all of the above, but is necessary for end-users to be able to utilize everything related to OS Themes.

iOS Specific

  • [x] Remove/replace all hard-coded colors from Platform.iOS
    (also see #7683 and #8495) (PR #9840)

Android Specific

  • [x] Inherit from new day/night aware theme (PR #9869)
  • [x] Identify and remove/replace any hard-coded colors from Platform.Android (no problems identified right now)
  • [x] Adjust VS Templates (PR make on templates repo)
  • [ ] Look into why customizing dialogs etc. causes weird light/dark behavior (see https://github.com/xamarin/Xamarin.Forms/issues/9033)

UWP Specific

  • [x] Remove/replace all hard-coded colors from Platform.UAP (PR #10150)
  • [x] Adjust VS Templates (PR make on templates repo)

Backward Compatibility

Minimum API levels?

  • iOS: iOS 13 and up
  • Android: Android 10 (API 29) and up
  • UWP: 14393

Breaking changes?
All changes should be backwards compatible. We will tamper with hard-coded colors that snuck in the codebase at some point in time, so in terms of that we might be looking at some breaking changes in styling.

Unsupported platforms?
Platforms underneath will most likely not be supported in the first iteration. At least for now, also depending on the platform support certain features may or may not be implemented.

MacOS, GTK, Tizen (@rookiejava ?)

Difficulty : medium

From what I can tell at this point the changes shouldn't be too hard. We need to keep a close eye on backwards compatibility and keep in mind any breaking (styling) changes.

darkmode iOS 13 high impact Android UWP proposal-accepted enhancement âž•

Most helpful comment

Aaah ok! I see :) This is more like a bug, the other more like a feature request. I'll see to get this fixed with implementing this

All 19 comments

Any plan to support dark mode on Entry control ?

Take a look at botom line

Dark mode
image

Light Mode
image

@transis2 definitely! That will probably fall under the

"Inherit from new day/night aware theme" task :)

We have decided to take on the dependency on Essentials. We are seeing more and more areas where we would have potential overlap. In order not to duplicate functionality, we simply decided to add the dependency to Essentials and make use of their awesome stuff.

Updated the spec to reflect that. Since they don't have any high contrast theme right now, I removed that. I will see what that is and see if we can get that added ASAP.

I did add a sort of duplicate API call to determine which AppTheme is currently active to the DeviceInfo. I feel this will make the API more discoverable as people might expect it to be part of Forms and not need to reach into Essentials. The API will just pass through the Essentials result though.

It would be nice to add a ClearButtonColor property to Entry, or make it the same color as the text.

Fantastic idea! But I don't think it's very relevant in this context :) But you're welcome to see if there is already an issue for it or open one.

If XF (4.6?) gets a dependency to Essentials, you should also mark all duplicated API in XF as obsolete and tell users to use the API from Essentials instead and bugfixes to the duplicated API should on be implemented in Essentials.

I said that because I have the following problem:
Sem título

Notice it's hard to see the clear button in dark mode. Didn't find an open issue for that.

Aaah ok! I see :) This is more like a bug, the other more like a feature request. I'll see to get this fixed with implementing this

@leo-adm I think if you let your theme inherit from the new DayNight theme you should be able to get that clear button in the right color, even with the Forms versions of today. But in any case, wait until all of this is available and see then if it still happens.

@MagicAndre1981 see #9927

@MagicAndre1981 see #9927

yes, I already subscribed to the issue

@jfversluis just read your comments about XamEssentials in this issue, and it seems the only bits that XF would need from it are types, not really implementations. I'm wondering, then, if the best approach would be to split Xamarin.Essentials into 2: Xamarin.Essentials and Xamarin.Essentials.Interfaces; then this way XF could depend on the latter, without bringing all the issues that could arise by depending on the whole package (e.g. different platforms supported, etc).

@knocte I’m sorry that I didn’t make myself clear. That is not the case. We want to use actual implementations of Essentials as well. I need to update to bits that will make more clear what are plans are. I will try to do that today and write a bit more about it to get us all on the same page.

I think everyone will be happy, we just need to get aligned :) thank you for being so involved though!

Good idea, but does OnAppTheme a Bindable element? Like we could also use DynamicResources and StaticResource?

How do we use different color for different platforms and mode same time?

I really hope we could let it work with: [Enhancement] Request OnPlatform value support Bindable element! Then we could use any color for any platform and mode.

A number of things happened since the last update.

I have chosen not to implement the "fixed" theme on a VisualElement simply because it is not supported on all major platforms. That way it will not be consistent at this time and would not make sense.

I did implement the "theme changed" event, but a couple of layers higher and on the Application level. Again, because this is not consistently available on all major platforms. On Android this is still dependant on a number of factors, but that is a risk I'm willing to take.

The OnAppTheme and AppThemeColor now have all bindable properties to make life a bit easier. That should answer part of your question @huangjinshe. The other part of the answer is; you should be able to combine the OnPlatform and OnAppTheme concepts :)

@knocte I still owe you the Essentials solution, it's coming!

As of Xamarin.Forms 4.5.0.657, Entry is working well with dark mode of iOS, however, Editor is not good: while the placehoder is in light gray text on black ground, however when you type something, the text is in black color apparently.

@zijianhuang could you open a new issue for this please? If possible add some screenshots then I will look at it ASAP. Thanks! :)

Hello @jfversluis,

I saw that "your" dark theme support has been removed from xamarin roadmap until MAUI.

https://github.com/xamarin/Xamarin.Forms/wiki/Feature-Roadmap

It's still in development ?

@transis2 far from it. I would say that it’s mostly done :)

Closing this for now. Majority of the work is done, all that remains is a number of issues which can be tracked by their own number

Was this page helpful?
0 / 5 - 0 ratings