-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[Android] Implemented Material3 Support for Switch Control #33132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Android] Implemented Material3 Support for Switch Control #33132
Conversation
| ? isEnabled | ||
| : EnableAspireByDefault; | ||
|
|
||
| #if NET10_0_OR_GREATER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? I thought that main is always .NET 10 or higher right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe because Maui 9 officially expires on May 12, 2026, if necessary they can work on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements Material 3 support for the Switch control on Android in .NET MAUI. The implementation is controlled by a runtime feature flag (IsMaterial3Enabled) that defaults to false, allowing developers to opt-in via the UseMaterial3 MSBuild property. When enabled, Switch controls use the new Material 3 styling with the Material Design 3 components library.
Key Changes
- Added runtime feature flag
IsMaterial3Enabledwith MSBuild property binding for opt-in Material 3 support - Implemented
MaterialSwitchHandlerandMauiMaterialSwitchto provide Material 3 switch functionality on Android - Extended
SwitchExtensionswith Material 3-specific property update methods for theme-aware color management - Added comprehensive Material 3 color tokens and style definitions for Android resources
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/src/RuntimeFeature.cs | Adds IsMaterial3Enabled runtime feature flag with conditional compilation for .NET 10+ |
| src/Core/src/Platform/Android/SwitchExtensions.cs | Extends switch utilities with Material 3 switch support for IsOn, TrackColor, and ThumbColor properties |
| src/Core/src/Platform/Android/Resources/values/styles-material3.xml | Defines Material 3 base themes, splash themes, and action mode styles for Android |
| src/Core/src/Platform/Android/Resources/values/colors-material3.xml | Adds complete Material 3 color palette with light/dark theme tokens and state overlay colors |
| src/Core/src/Platform/Android/MauiMaterialContextThemeWrapper.cs | Updates theme wrapper to select Material 3 or Material 2 base theme based on feature flag |
| src/Core/src/Platform/Android/Material3Controls/MauiMaterialSwitch.cs | New internal wrapper class for Material 3 switch widget with proper theme context |
| src/Core/src/Handlers/Switch/MaterialSwitchHandler.Android.cs | New handler for Material 3 switch with property mapping and change listener implementation |
| src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs | Adds conditional handler registration logic to use MaterialSwitchHandler when Material 3 is enabled on Android |
| src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets | Configures MSBuild property UseMaterial3 to set the Material 3 runtime feature flag |
| else if (thumbColor is null && materialSwitch is MauiMaterialSwitch mauiSwitch) | ||
| { | ||
| materialSwitch.ThumbTintList = _defaultThumbTintList; | ||
|
|
||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition thumbColor is null is redundant and makes the logic confusing. The outer condition on line 56 already checks if thumbColor is not null, so the else-if on line 60 will only execute when thumbColor is null. The additional thumbColor is null check in the condition is therefore unnecessary.
Additionally, the materialSwitch is MauiMaterialSwitch type check appears unnecessary since this is an internal extension method specifically for Material Switch. If there's a specific reason this check is needed, it should be documented.
| else if (thumbColor is null && materialSwitch is MauiMaterialSwitch mauiSwitch) | ||
| { | ||
| materialSwitch.ThumbTintList = _defaultThumbTintList; | ||
|
|
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an extra blank line inside the if block that should be removed for consistency with the codebase style.
| static ColorStateList? _defaultTrackTintList; | ||
| static ColorStateList? _defaultThumbTintList; |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The static fields _defaultTrackTintList and _defaultThumbTintList are shared across all switch instances (both traditional and Material3). This could cause issues when both switch types are used in the same application. The first switch instance initialized will set these defaults, and those defaults will be incorrectly applied to the other switch type when colors are reset to null.
Consider maintaining separate default tint lists for each switch type, or storing defaults per-instance using a different mechanism such as attached properties or a dictionary keyed by switch instance.
| internal partial class MaterialSwitchHandler : ViewHandler<ISwitch, MauiMaterialSwitch> | ||
| { | ||
| public static PropertyMapper<ISwitch, MaterialSwitchHandler> Mapper = | ||
| new(ViewMapper) | ||
| { | ||
| [nameof(ISwitch.IsOn)] = MapIsOn, | ||
| [nameof(ISwitch.TrackColor)] = MapTrackColor, | ||
| [nameof(ISwitch.ThumbColor)] = MapThumbColor, | ||
| }; | ||
|
|
||
| public static CommandMapper<ISwitch, MaterialSwitchHandler> CommandMapper = | ||
| new(ViewCommandMapper); | ||
|
|
||
| MaterialSwitchCheckedChangeListener? _changeListener; | ||
|
|
||
| public MaterialSwitchHandler() : base(Mapper, CommandMapper) | ||
| { | ||
| } | ||
|
|
||
| protected override MauiMaterialSwitch CreatePlatformView() | ||
| { | ||
| return new MauiMaterialSwitch(Context); | ||
| } | ||
|
|
||
| protected override void ConnectHandler(MauiMaterialSwitch platformView) | ||
| { | ||
| _changeListener = new MaterialSwitchCheckedChangeListener(this); | ||
| platformView.SetOnCheckedChangeListener(_changeListener); | ||
|
|
||
| base.ConnectHandler(platformView); | ||
| } | ||
|
|
||
| protected override void DisconnectHandler(MauiMaterialSwitch platformView) | ||
| { | ||
| platformView.SetOnCheckedChangeListener(null); | ||
| _changeListener?.Dispose(); | ||
| _changeListener = null; | ||
|
|
||
| base.DisconnectHandler(platformView); | ||
| } | ||
|
|
||
| public override Size GetDesiredSize(double widthConstraint, double heightConstraint) | ||
| { | ||
| Size size = base.GetDesiredSize(widthConstraint, heightConstraint); | ||
|
|
||
| if (size.Width == 0) | ||
| { | ||
| int width = (int)widthConstraint; | ||
|
|
||
| if (widthConstraint <= 0) | ||
| width = (int)(Context?.GetThemeAttributeDp(global::Android.Resource.Attribute.SwitchMinWidth) ?? 0); | ||
|
|
||
| size = new Size(width, size.Height); | ||
| } | ||
|
|
||
| return size; | ||
| } | ||
|
|
||
| public static void MapIsOn(MaterialSwitchHandler handler, ISwitch view) | ||
| { | ||
| handler.PlatformView?.UpdateIsOn(view); | ||
| } | ||
|
|
||
| public static void MapTrackColor(MaterialSwitchHandler handler, ISwitch view) | ||
| { | ||
| handler.PlatformView?.UpdateTrackColor(view); | ||
| } | ||
|
|
||
| public static void MapThumbColor(MaterialSwitchHandler handler, ISwitch view) | ||
| { | ||
| handler.PlatformView?.UpdateThumbColor(view); | ||
| } | ||
|
|
||
| void OnCheckedChanged(bool isOn) | ||
| { | ||
| if (VirtualView is null || VirtualView.IsOn == isOn) | ||
| return; | ||
|
|
||
| VirtualView.IsOn = isOn; | ||
| } | ||
|
|
||
| sealed class MaterialSwitchCheckedChangeListener : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener | ||
| { | ||
| readonly WeakReference<MaterialSwitchHandler> _handler; | ||
|
|
||
| public MaterialSwitchCheckedChangeListener(MaterialSwitchHandler handler) | ||
| { | ||
| _handler = new WeakReference<MaterialSwitchHandler>(handler); | ||
| } | ||
|
|
||
| void CompoundButton.IOnCheckedChangeListener.OnCheckedChanged(CompoundButton? buttonView, bool isToggled) | ||
| { | ||
| if (_handler.TryGetTarget(out var handler)) | ||
| { | ||
| handler.OnCheckedChanged(isToggled); | ||
| } | ||
| } | ||
| } | ||
| } No newline at end of file |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new MaterialSwitchHandler lacks test coverage. Given that the repository has comprehensive tests for SwitchHandler (as seen in src/Core/tests/DeviceTests/Handlers/Switch/SwitchHandlerTests.cs), the new MaterialSwitchHandler should have equivalent test coverage to ensure Material3 switch behavior works correctly, especially for property mapping (IsOn, TrackColor, ThumbColor) and event handling.
| internal class MauiMaterialSwitch : MaterialSwitch | ||
| { | ||
| public MauiMaterialSwitch(Context context) | ||
| : base(MauiMaterialContextThemeWrapper.Create(context)) | ||
| { | ||
| } | ||
|
|
||
| protected MauiMaterialSwitch(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) | ||
| { | ||
| } | ||
|
|
||
| public MauiMaterialSwitch(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs) | ||
| { | ||
| } | ||
|
|
||
| public MauiMaterialSwitch(Context context, IAttributeSet? attrs, int defStyleAttr) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr) | ||
| { | ||
| } | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MauiMaterialSwitch class lacks XML documentation comments. Since this is a new internal class that wraps the Material 3 switch widget, it would benefit from documentation explaining its purpose and why it exists (i.e., to apply the correct Material 3 theme context to the Material Switch).
PR #33132 Code Review SummaryTitle: [Android] Implemented Material3 Support for Switch Control Overall Assessment✅ Well-structured implementation following MAUI patterns, but has critical bugs and unrelated changes that need addressing. Critical Issues
Medium Issues
Minor Issues
RecommendationsMust fix:
Should add:
|
Description of Change
This pull request adds support for Material 3 styling for the
Switchcontrol on Android in .NET MAUI. It introduces a new Material 3 switch handler, configures runtime feature toggling for Material 3, and adds the necessary Android resources and theming infrastructure for Material 3 support.Material 3 Switch support and theming:
MaterialSwitchHandlerfor Android, which uses the Material 3MauiMaterialSwitchwhen the Material 3 feature is enabled. This handler includes property mapping forIsOn,TrackColor, andThumbColor. (src/Core/src/Handlers/Switch/MaterialSwitchHandler.Android.cs)MauiMaterialSwitchclass, which wraps the Material 3 switch widget and applies the correct Material 3 theme context. (src/Core/src/Platform/Android/Material3Controls/MauiMaterialSwitch.cs)SwitchExtensionsto support updating properties (IsOn,TrackColor,ThumbColor) on both traditional and Material 3 switches. (src/Core/src/Platform/Android/SwitchExtensions.cs) [1] [2]Feature toggle and runtime configuration:
IsMaterial3Enabledruntime feature flag, with a default offalse, and logic to read it from app context or MSBuild properties. (src/Core/src/RuntimeFeature.cs,src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets) [1] [2] [3]MauiMaterialContextThemeWrapperto select the correct base theme depending on the Material 3 feature flag. (src/Core/src/Platform/Android/MauiMaterialContextThemeWrapper.cs)Resource and style additions:
src/Core/src/Platform/Android/Resources/values/colors-material3.xml,src/Core/src/Platform/Android/Resources/values/styles-material3.xml) [1] [2]Handler registration logic:
MaterialSwitchHandlerforSwitchcontrols on Android when Material 3 is enabled, otherwise falling back to the default handler. (src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs) [1] [2]These changes collectively enable opt-in Material 3 switch styling on Android, controlled by a feature flag, and lay the groundwork for broader Material 3 support in .NET MAUI.
Previous base style PR: #33074
Issues Fixed
Fixes #33131
Screenshots
Material Design Spec - Switch