Respond to system theme changes in Xamarin.Forms applications
Devices typically include light and dark themes, which each refer to a broad set of appearance preferences that can be set at the operating system level. Applications should respect these system themes, and respond immediately when the system theme changes.
The system theme may change for a variety of reasons, depending on the device configuration. This includes the system theme being explicitly changed by the user, it changing due to the time of day, and it changing due to environmental factors such as low light.
Xamarin.Forms applications can respond to system theme changes by consuming resources with the AppThemeBinding
markup extension, and the SetAppThemeColor
and SetOnAppTheme<T>
extension methods.
The following requirements must be met for Xamarin.Forms to respond to a system theme change:
- Xamarin.Forms 4.6.0.967 or greater.
- iOS 13 or greater.
- Android 10 (API 29) or greater.
- UWP build 14393 or greater.
- macOS 10.14 or greater.
The following screenshots show themed pages, for light and dark system themes on iOS and Android:
Define and consume theme resources
Resources for light and dark themes can be consumed with the AppThemeBinding
markup extension, and the SetAppThemeColor
and SetOnAppTheme<T>
extension methods. With these approaches, resources are automatically applied based on the value of the current system theme. In addition, objects that consume these resources are automatically updated if the system theme changes while an app is running.
AppThemeBinding markup extension
The AppThemeBinding
markup extension enables you to consume a resource, such as an image or color, based on the current system theme:
<ContentPage ...>
<StackLayout Margin="20">
<Label Text="This text is green in light mode, and red in dark mode."
TextColor="{AppThemeBinding Light=Green, Dark=Red}" />
<Image Source="{AppThemeBinding Light=lightlogo.png, Dark=darklogo.png}" />
</StackLayout>
</ContentPage>
In this example, the text color of the first Label
is set to green when the device is using its light theme, and is set to red when the device is using its dark theme. Similarly, the Image
displays a different image file based upon the current system theme.
In addition, resources defined in a ResourceDictionary
can be consumed with the StaticResource
markup extension:
<ContentPage ...>
<ContentPage.Resources>
<!-- Light colors -->
<Color x:Key="LightPrimaryColor">WhiteSmoke</Color>
<Color x:Key="LightSecondaryColor">Black</Color>
<!-- Dark colors -->
<Color x:Key="DarkPrimaryColor">Teal</Color>
<Color x:Key="DarkSecondaryColor">White</Color>
<Style x:Key="ButtonStyle"
TargetType="Button">
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource LightPrimaryColor}, Dark={StaticResource DarkPrimaryColor}}" />
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource LightSecondaryColor}, Dark={StaticResource DarkSecondaryColor}}" />
</Style>
</ContentPage.Resources>
<Grid BackgroundColor="{AppThemeBinding Light={StaticResource LightPrimaryColor}, Dark={StaticResource DarkPrimaryColor}}">
<Button Text="MORE INFO"
Style="{StaticResource ButtonStyle}" />
</Grid>
</ContentPage>
In this example, the background color of the Grid
and the Button
style changes based on whether the device is using its light theme or dark theme.
For more information about the AppThemeBinding
markup extension, see AppThemeBinding markup extension.
Extension methods
Xamarin.Forms includes SetAppThemeColor
and SetOnAppTheme<T>
extension methods that enable VisualElement
objects to respond to system theme changes.
The SetAppThemeColor
method enables Color
objects to be specified that will be set on a target property based on the current system theme:
Label label = new Label();
label.SetAppThemeColor(Label.TextColorProperty, Color.Green, Color.Red);
In this example, the text color of the Label
is set to green when the device is using its light theme, and is set to red when the device is using its dark theme.
The SetOnAppTheme<T>
method enables objects of type T
to be specified that will be set on a target property based on the current system theme:
Image image = new Image();
image.SetOnAppTheme<FileImageSource>(Image.SourceProperty, "lightlogo.png", "darklogo.png");
In this example, the Image
displays lightlogo.png
when the device is using its light theme, and darklogo.png
when the device is using its dark theme.
Detect the current system theme
The current system theme can be detected by getting the value of the Application.RequestedTheme
property:
OSAppTheme currentTheme = Application.Current.RequestedTheme;
The RequestedTheme
property returns an OSAppTheme
enumeration member. The OSAppTheme
enumeration defines the following members:
Unspecified
, which indicates that the device is using an unspecified theme.Light
, which indicates that the device is using its light theme.Dark
, which indicates that the device is using its dark theme.
Set the current user theme
The theme used by the application can be set with the Application.UserAppTheme
property, which is of type OSAppTheme
, regardless of which system theme is currently operational:
Application.Current.UserAppTheme = OSAppTheme.Dark;
In this example, the application is set to use the theme defined for the system dark mode, regardless of which system theme is currently operational.
Note
Set the UserAppTheme
property to OSAppTheme.Unspecified
to default to the operational system theme.
React to theme changes
The system theme on a device may change for a variety of reasons, depending on how the device is configured. Xamarin.Forms apps can be notified when the system theme changes by handling the Application.RequestedThemeChanged
event:
Application.Current.RequestedThemeChanged += (s, a) =>
{
// Respond to the theme change
};
The AppThemeChangedEventArgs
object, which accompanies the RequestedThemeChanged
event, has a single property named RequestedTheme
, of type OSAppTheme
. This property can be examined to detect the requested system theme.
Important
To respond to theme changes on Android you must include the ConfigChanges.UiMode
flag in the Activity
attribute of your MainActivity
class.