[Android] Implemented material3 support for Editor#33478
[Android] Implemented material3 support for Editor#33478jfversluis merged 6 commits intodotnet:inflight/currentfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request implements Material Design 3 support for the Editor control on Android by introducing a new MaterialEditorHandler and MauiMaterialEditText control. The implementation enables Material 3 styling for editors when the Material 3 runtime feature is enabled, providing a more modern and consistent UI experience.
Changes:
- Added
MauiMaterialEditTextclass that extendsTextInputEditTextto support Material 3 styling with selection change events - Implemented
MaterialEditorHandlerwith full property and command mapping support for Material 3 editors - Updated handler registration logic to conditionally use
MaterialEditorHandlerwhen Material 3 is enabled - Extended property mapping to support the new Material 3 handler for text-related properties
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs |
New Material 3 EditText control with selection change event support |
src/Core/src/Handlers/Editor/MaterialEditorHandler.Android.cs |
New handler implementing Material 3 styling and behavior for Editor control |
src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs |
Conditional handler registration based on Material 3 feature flag |
src/Controls/src/Core/Editor/Editor.Mapper.cs |
Property mapping registration for Material 3 handler |
src/Controls/src/Core/Editor/Editor.Android.cs |
Material 3-specific MapText implementation with data flow handling |
src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs
Outdated
Show resolved
Hide resolved
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Added TODO comment and refactored class and file names ·
|
| Issue | Reviewer Says | Status |
|---|---|---|
MauiMaterialEditText constructor inconsistency |
Constructors with attrs params don't call MauiMaterialContextThemeWrapper.Create(context) unlike primary inconsistent with MauiMaterialButton REAL ISSUE |
pattern |
| Duplicate handler registration code | AppHostBuilderExtensions.cs if/else could be simplified with STYLE |
ternary |
| Missing Focus/IsFocused mappings | Suggested these ARE present in the diff ALREADY DONE | |
| Missing tests | No test coverage for Material3-specific REAL ISSUE | behavior |
Key Technical Observations
- Naming inconsistency: PR description calls it
MaterialEditorHandlerbut actual class isEditorHandler2(marked// TODO: Material3 - make it public in .net 11) - Pattern: Mirrors the Material3 Button handler approach (compare
MauiMaterialButton.cs) - MauiMaterialEditText ctor bug: The
MauiMaterialButtonwraps context in ALL constructors withMauiMaterialContextThemeWrapper.Create(context).MauiMaterialEditTextonly wraps the primary(Context context)the(Context, IAttributeSet)and(Context, IAttributeSet, int)ctors pass raw context, meaning Material3 theme won't apply when inflated from XML or via those ctors.ctor - Missing newline at end of file: Both new files lack trailing newline
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #33478 | New EditorHandler2 + MauiMaterialEditText, conditional PENDING (Gate) | 5 files | Original no tests included | PR registration |
🚦 Gate — Test Verification
📝 Review Session — Added TODO comment and refactored class and file names · f895a14
** FAILEDResult:**
Platform: android
Mode: N/ No tests existA
Finding
No test files exist for this PR or issue #33476. The PR does not include any:
- UI test pages in
TestCases.HostApp/Issues/ - NUnit test implementations in
TestCases.Shared.Tests/Tests/Issues/ - Device test extensions in
Core/tests/DeviceTests/
This is a new feature (Material3 Editor handler) and the PR ships without any automated test coverage.
Action Required
Tests must be added before Gate can pass. The write-tests-agent skill can help create them.
Suggested test coverage:
- Verify
EditorHandler2is registered when Material3 is enabled on Android - Verify
EditorHandleris registered when Material3 is disabled - Verify basic Editor property mapping works correctly with the Material3 handler (Text, Placeholder, TextColor, etc.)
🔧 Fix — Analysis & Comparison
📝 Review Session — Added TODO comment and refactored class and file names · f895a14
Skipped: FAILED (no tests Phase 3 requires Gate to pass first.exist) Gate
No try-fix attempts were made.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #33478 | New EditorHandler2 + MauiMaterialEditText, conditional NOT VERIFIED (no tests) | 5 files | Gate could not run | registration |
Exhausted: N/A (skipped)
Selected Fix: N/ Gate did not passA
📋 Report — Final Recommendation
📝 Review Session — Added TODO comment and refactored class and file names · f895a14
Final Recommendation: REQUEST CHANGES##
Summary
PR #33478 implements Material Design 3 support for the Android Editor control by introducing EditorHandler2 (a new internal handler) and MauiMaterialEditText (extending TextInputEditText). The implementation uses conditional handler registration based on RuntimeFeature.IsMaterial3Enabled. The overall approach is correct and follows existing Material3 patterns in the codebase.
However, there are blocking issues that must be addressed before merge:
- Gate No tests were included with this PRFailed
- Bug in
MauiMaterialEditTextMaterial3 theme not applied consistentlyconstructors
Root Cause (of Issue #33476)
The Editor control on Android used EditorHandler/MauiEditText which has no Material3 styling. When UseMaterial3 is enabled, a dedicated handler backed by TextInputEditText (Material3 styled) is needed.
The PR includes zero test coverage. No UI tests, no device tests for the new EditorHandler2.###
Code Review Findings
File: src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs####
The primary constructor wraps the context with MauiMaterialContextThemeWrapper.Create(context), but the additional constructors do NOT:
// Primary correctctor
public MauiMaterialEditText(Context context) : base(MauiMaterialContextThemeWrapper.Create(context))
Missing theme wrapper//
public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(context, attrs)
public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)This is inconsistent with the established pattern in MauiMaterialButton:
// MauiMaterialButton pattern (correct)
public MauiMaterialButton(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
public MauiMaterialButton(Context context, IAttributeSet? attrs, int defStyleAttr) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr)Fix: Apply MauiMaterialContextThemeWrapper.Create(context) in all constructors.
No automated tests validate that EditorHandler2 is properly registered and functional when Material3 is enabled. This is required for Gate to pass.####
File: src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs####
The if/else block can be simplified:
// Current: verbose if/else
if (RuntimeFeature.IsMaterial3Enabled)
handlersCollection.AddHandler<Editor, EditorHandler2>();
else
handlersCollection.AddHandler<Editor, EditorHandler>();
// Suggested: single line
handlersCollection.AddHandler(typeof(Editor),
RuntimeFeature.IsMaterial3Enabled ? typeof(EditorHandler2) : typeof(EditorHandler));PR description references MaterialEditorHandler but actual code uses EditorHandler2. The TODO comments say "make it public in .net 11" which clarifies the intent. PR description should be updated to match the actual class names.####
Both EditorHandler2.Android.cs and MauiMaterialEditText.cs are missing a trailing newline (\ No newline at end of file).####
PR Title & Description Review
Title: [Android] Implemented material3 support for Editor
- "Implemented" should be imperative: "Add Material3 support for Editor"
- Recommended:
[Android] Editor: Add Material3 handler support-
Description:
Missing NOTE block (required by contributing guidelines)-
References actual class isEditorHandler2MaterialEditorHandler -
- Has "Description of Change" and "Issues Fixed" sections
- Includes before/after screenshots
Recommended additions:
- Add NOTE block at the top
- Update class name references to
EditorHandler2 - Clarify that the handler is currently internal (to be made public in .NET 11)
Required Changes (must-fix before merge)
- Fix
MauiMaterialEditTextconstructors to consistently callMauiMaterialContextThemeWrapper.Create(context)in ALL constructors - Add tests (device tests validating Material3 handler registration and basic Editor properties)
Suggested Changes (nice-to-have)
- Simplify handler registration to a single ternary line
- Add NOTE block to PR description
- Fix
MaterialEditorHandlerreferences toEditorHandler2in PR description - Add trailing newlines to new files
📋 Expand PR Finalization Review
Title: ✅ Good
Current: [Android] Implemented material3 support for Editor
Description: ⚠️ Needs Update
- "Implemented" is past tense; MAUI convention uses imperative/present tense
- "material3" should be capitalized as "Material 3"
- Missing component "support" is vague; the actual change is a new handler + platform controlclarity
Missing Elements:
**
Missing NOTE block (required per MAUI contribution guidelines)-
Does not mention that EditorHandler2 and MauiMaterialEditText are internal with TODO to make public in .NET 11-
Does not mention the DataFlowDirection pattern used to track text-update origin-
Recommendation: Update description to fix class names and add the NOTE block. A full recommended description is provided in recommended-description.md.
Phase 2: Code Review
See code-review.md for detailed findings.
Summary:
-
-
- Core property mapping, text DataFlowDirection tracking, focus/keyboard handling, and lifecycle management look correct
-
Overall Verdict
The PR implements the intended feature correctly but has:
- Significant description wrong handler class name throughout (
MaterialEditorHandlervs actualEditorHandler2)inaccuracy - Fragile event
SelectionChangedsubscribed inSetVirtualViewinstead ofConnectHandler/DisconnectHandlersubscription - Missing NOTE block in description
These should be addressed before merge.
✨ Suggested PR Description
[!NOTE]
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
This PR introduces Material Design 3 support for the Editor control on Android.
New classes (internal, TODO: make public in .NET 11):
EditorHandler2(src/Core/src/Handlers/Editor/EditorHandler2.Android. A new handler class for Android that wrapsMauiMaterialEditText(aTextInputEditText-based view) instead of the standardAppCompatEditText. Implements full property/command mapper parity withEditorHandler, includingDataFlowDirectiontracking to distinguish platform-initiated vs MAUI-initiated text updates.cs)MauiMaterialEditText(src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText. ExtendsTextInputEditText(from the Material library) to expose aSelectionChangedevent via override ofOnSelectionChanged, enabling cursor/selection position tracking by the handler.cs)
Handler registration (AppHostBuilderExtensions.cs):
- On Android, when
RuntimeFeature.IsMaterial3Enabledistrue,EditorHandler2is registered forEditor; otherwise the standardEditorHandleris used. - On all other platforms, the standard
EditorHandleris used (unchanged).
Mapper setup (Editor.Mapper.cs, Editor.Android.cs):
- When Material 3 is enabled on Android, additional mappings are registered on
EditorHandler2.MapperandEditorHandler2.CommandMapper:
platform-overloadedMapText(handlesDataFlowDirection)
InputView.MapIsFocused
InputView.MapFocus
Issues Fixed
Fixes #33476
Output
| Material2 | Material3 |
|---|---|
![]() |
![]() |
Code Review: ✅ Passed
Code PR #33478Review
Files reviewed:
src/Core/src/Handlers/Editor/EditorHandler2.Android.cs(new, 221 lines)src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs(new, 36 lines)src/Controls/src/Core/Editor/Editor.Android.cs(modified)src/Controls/src/Core/Editor/Editor.Mapper.cs(modified)src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs(modified)
SelectionChanged subscription should be in ConnectHandler/DisconnectHandler, not SetVirtualView
File: src/Core/src/Handlers/Editor/EditorHandler2.Android.cs
Problem:
public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);
if (!_set)
{
PlatformView.SelectionChanged += OnSelectionChanged;
}
_set = true;
}The SelectionChanged event is subscribed in SetVirtualView with a _set guard, but SetVirtualView can be called multiple times across the view's lifecycle. The standard MAUI handler pattern is to subscribe in ConnectHandler and unsubscribe in which already unsubscribesSelectionChangedusing the same_setflag. Using a secondary flag inSetVirtualViewis fragile and diverges fromTextChangedandFocusChangewhich correctly useConnectHandler/DisconnectHandler.DisconnectHandler
Recommendation: Move SelectionChanged subscription to ConnectHandler and remove the _set field and the SetVirtualView override:
protected override void ConnectHandler(MauiMaterialEditText platformView)
{
platformView.TextChanged += OnTextChanged;
platformView.FocusChange += OnFocusChange;
platformView.SelectionChanged += OnSelectionChanged; // move here
}
protected override void DisconnectHandler(MauiMaterialEditText platformView)
{
platformView.TextChanged -= OnTextChanged;
platformView.FocusChange -= OnFocusChange;
platformView.SelectionChanged -= OnSelectionChanged; // move here
_set = false; // _set can then be removed entirely
}1. MauiMaterialEditText secondary constructors don't wrap context with theme
File: src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs
The primary constructor correctly wraps the context:
public MauiMaterialEditText(Context context) : base(MauiMaterialContextThemeWrapper.Create(context))But the secondary constructors do not:
public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(context, attrs)
public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)This matches the pattern in MauiMaterialButton and other Material3 controls. While these constructors are less likely to be called in practice (the handler always uses the primary constructor), consistency is important for correctness.
Recommendation:
public MauiMaterialEditText(Context context, IAttributeSet? attrs)
: base(MauiMaterialContextThemeWrapper.Create(context), attrs) { }
public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr)
: base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr) { }2. Missing newline at end of file
Files:
src/Core/src/Handlers/Editor/EditorHandler2.Android. ends with}\ No newline at end of filecssrc/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText. ends with}\ No newline at end of filecs
Both new files are missing a trailing newline. Add \n at the end of each file.
3. Handler registration could be simplified
File: src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs
Current:
#if ANDROID
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Editor, EditorHandler2>();
}
else
{
handlersCollection.AddHandler<Editor, EditorHandler>();
}
#else
handlersCollection.AddHandler<Editor, EditorHandler>();
#endifThis duplicates the non-Material3 registration. Consider:
#if ANDROID
handlersCollection.AddHandler(typeof(Editor),
RuntimeFeature.IsMaterial3Enabled ? typeof(EditorHandler2) : typeof(EditorHandler));
#else
handlersCollection.AddHandler<Editor, EditorHandler>();
#endif4. No tests added
No unit or device tests were added for EditorHandler2. Given that this is a new handler class with distinct behavior (Material 3 styling, different platform view), basic tests verifying handler registration when IsMaterial3Enabled is true/false would improve confidence.
Looks Good
DataFlowDirectionCorrectly setsDataFlowDirection = DataFlowDirection.FromPlatformbefore callingVirtualView.UpdateText(e)and resets it after, preventing feedback loops. This matches the pattern used inEditorHandler.pattern- Property mapper
EditorHandler2.Mappercovers all the same properties asEditorHandler(background, character spacing, font, read-only, text prediction, spell check, max length, placeholder, placeholder color, text, text color, alignment, keyboard, cursor position, selection length).completeness IsFocused+FocusCorrectly added inEditor.Mapper.cswhen Material 3 is enabled, addressing the review comments.mappingsPlatformArrangeCorrectly callsthis.PrepareForTextViewArrange(frame)beforebase.PlatformArrange(frame), consistent withEditorHandler.override
Correctly callsVirtualView?.Completed()when focus is lost, matching the standard editor behavior.Completed()**- Class Both
EditorHandler2andMauiMaterialEditTextare correctlyinternalwith// TODO: Material3 - make it public in .net 11comments, following the established Material3 staging pattern.visibility
f895a14 to
2299a6f
Compare
|
Added UI tests and addressed all valid concerns. |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
### Description of Change - This PR introduces Material Design 3 support for the Editor control on Android. ### New classes (internal, TODO: make public in .NET 11): - EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText. - Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange. - MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme. ### Handler registration (AppHostBuilderExtensions.cs): - On Android, when RuntimeFeature.IsMaterial3Enabled is true, EditorHandler2 is registered for Editor; otherwise the standard EditorHandler is used. - On all other platforms, the standard EditorHandler is used (unchanged). ### Mapper setup (Editor.Mapper.cs, Editor.Android.cs): - When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper: - ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection) - AppendToMapping for IsFocused → InputView.MapIsFocused - PrependToMapping for Focus → InputView.MapFocus ### Issues Fixed Fixes #33476 ### Output | Material2 | Material3 | |----------|----------| | <img src="https://github.com/user-attachments/assets/7d97f447-4228-4df8-b63d-8a8b96823734"> | <img src="https://github.com/user-attachments/assets/1798d8bb-2f5a-40bd-97c8-cf9396d34d59"> | --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
### Description of Change - This PR introduces Material Design 3 support for the Editor control on Android. ### New classes (internal, TODO: make public in .NET 11): - EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText. - Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange. - MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme. ### Handler registration (AppHostBuilderExtensions.cs): - On Android, when RuntimeFeature.IsMaterial3Enabled is true, EditorHandler2 is registered for Editor; otherwise the standard EditorHandler is used. - On all other platforms, the standard EditorHandler is used (unchanged). ### Mapper setup (Editor.Mapper.cs, Editor.Android.cs): - When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper: - ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection) - AppendToMapping for IsFocused → InputView.MapIsFocused - PrependToMapping for Focus → InputView.MapFocus ### Issues Fixed Fixes #33476 ### Output | Material2 | Material3 | |----------|----------| | <img src="https://github.com/user-attachments/assets/7d97f447-4228-4df8-b63d-8a8b96823734"> | <img src="https://github.com/user-attachments/assets/1798d8bb-2f5a-40bd-97c8-cf9396d34d59"> | --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
### Description of Change - This PR introduces Material Design 3 support for the Editor control on Android. ### New classes (internal, TODO: make public in .NET 11): - EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText. - Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange. - MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme. ### Handler registration (AppHostBuilderExtensions.cs): - On Android, when RuntimeFeature.IsMaterial3Enabled is true, EditorHandler2 is registered for Editor; otherwise the standard EditorHandler is used. - On all other platforms, the standard EditorHandler is used (unchanged). ### Mapper setup (Editor.Mapper.cs, Editor.Android.cs): - When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper: - ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection) - AppendToMapping for IsFocused → InputView.MapIsFocused - PrependToMapping for Focus → InputView.MapFocus ### Issues Fixed Fixes #33476 ### Output | Material2 | Material3 | |----------|----------| | <img src="https://github.com/user-attachments/assets/7d97f447-4228-4df8-b63d-8a8b96823734"> | <img src="https://github.com/user-attachments/assets/1798d8bb-2f5a-40bd-97c8-cf9396d34d59"> | --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
### Description of Change - This PR introduces Material Design 3 support for the Editor control on Android. ### New classes (internal, TODO: make public in .NET 11): - EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText. - Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange. - MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme. ### Handler registration (AppHostBuilderExtensions.cs): - On Android, when RuntimeFeature.IsMaterial3Enabled is true, EditorHandler2 is registered for Editor; otherwise the standard EditorHandler is used. - On all other platforms, the standard EditorHandler is used (unchanged). ### Mapper setup (Editor.Mapper.cs, Editor.Android.cs): - When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper: - ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection) - AppendToMapping for IsFocused → InputView.MapIsFocused - PrependToMapping for Focus → InputView.MapFocus ### Issues Fixed Fixes dotnet#33476 ### Output | Material2 | Material3 | |----------|----------| | <img src="https://github.com/user-attachments/assets/7d97f447-4228-4df8-b63d-8a8b96823734"> | <img src="https://github.com/user-attachments/assets/1798d8bb-2f5a-40bd-97c8-cf9396d34d59"> | --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
### Description of Change - This PR introduces Material Design 3 support for the Editor control on Android. ### New classes (internal, TODO: make public in .NET 11): - EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText. - Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange. - MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme. ### Handler registration (AppHostBuilderExtensions.cs): - On Android, when RuntimeFeature.IsMaterial3Enabled is true, EditorHandler2 is registered for Editor; otherwise the standard EditorHandler is used. - On all other platforms, the standard EditorHandler is used (unchanged). ### Mapper setup (Editor.Mapper.cs, Editor.Android.cs): - When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper: - ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection) - AppendToMapping for IsFocused → InputView.MapIsFocused - PrependToMapping for Focus → InputView.MapFocus ### Issues Fixed Fixes #33476 ### Output | Material2 | Material3 | |----------|----------| | <img src="https://github.com/user-attachments/assets/7d97f447-4228-4df8-b63d-8a8b96823734"> | <img src="https://github.com/user-attachments/assets/1798d8bb-2f5a-40bd-97c8-cf9396d34d59"> | --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
## What's Coming .NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 46 commits with various improvements, bug fixes, and enhancements. ## Button - [Android] Implemented material3 support for Button by @Dhivya-SF4094 in #33173 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for Button](#33172) </details> ## CollectionView - [Android] Fix RemainingItemsThresholdReachedCommand not firing when CollectionView has Header and Footer both defined by @SuthiYuvaraj in #29618 <details> <summary>🔧 Fixes</summary> - [Android : RemainingItemsThresholdReachedCommand not firing when CollectionVew has Header and Footer both defined](#29588) </details> - [iOS/MacCatalyst] Fix CollectionView ScrollTo for horizontal layouts by @Shalini-Ashokan in #33853 <details> <summary>🔧 Fixes</summary> - [[iOS/MacCatalyst] CollectionView ScrollTo does not work with horizontal Layout](#33852) </details> - [iOS & Mac] Fixed IndicatorView Size doesnt update dynamically by @SubhikshaSf4851 in #31129 <details> <summary>🔧 Fixes</summary> - [[iOS, Catalyst] IndicatorView.IndicatorSize does not update dynamically at runtime](#31064) </details> - [Android] Fix for CollectionView Scrolled event is triggered on the initial app load. by @BagavathiPerumal in #33558 <details> <summary>🔧 Fixes</summary> - [[Android] CollectionView Scrolled event is triggered on the initial app load.](#33333) </details> - [iOS, Android] Fix for CollectionView IsEnabled=false allows touch interactions by @praveenkumarkarunanithi in #31403 <details> <summary>🔧 Fixes</summary> - [More issues with CollectionView IsEnabled, InputTransparent, Opacity via Styles and code behind](#19771) </details> - [iOS] Fix VerticalOffset Update When Modifying CollectionView.ItemsSource While Scrolled by @devanathan-vaithiyanathan in #34153 <details> <summary>🔧 Fixes</summary> - [[iOS]VerticalOffset Not Reset to Zero After Clearing ItemSource in CollectionView](#26798) </details> ## DateTimePicker - [Android] Fix DatePicker MinimumDate/MaximumDate not updating dynamically by @HarishwaranVijayakumar in #33687 <details> <summary>🔧 Fixes</summary> - [[regression/8.0.3] [Android] DatePicker control minimum date issue](#19256) - [[Android] DatePicker does not update MinimumDate / MaximumDate in the Popup when set in the viewmodel after first opening](#33583) </details> ## Drawing - Android drawable perf by @albyrock87 in #31567 ## Editor - [Android] Implemented material3 support for Editor by @SyedAbdulAzeemSF4852 in #33478 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for Editor](#33476) </details> ## Entry - [iOS, Mac] Fix for CursorPosition not updating when typing into Entry control by @SyedAbdulAzeemSF4852 in #30505 <details> <summary>🔧 Fixes</summary> - [Entry control CursorPosition does not update on TextChanged event [iOS Maui 8.0.7] ](#20911) - [CursorPosition not calculated correctly on behaviors events for iOS devices](#32483) </details> ## Flyoutpage - [Android, Windows] Fix for FlyoutPage toolbar button not updating on orientation change by @praveenkumarkarunanithi in #31962 <details> <summary>🔧 Fixes</summary> - [Flyout page in Android does not show flyout button (burger) consistently](#24468) </details> - Fix for First Item in CollectionView Overlaps in FlyoutPage.Flyout on iOS by @praveenkumarkarunanithi in #29265 <details> <summary>🔧 Fixes</summary> - [[iOS] CollectionView not rendering first item correctly in FlyoutPage.Flyout](#29170) </details> ## Image - [Android] Fix excessive memory usage for stream and resource-based image loading by @Shalini-Ashokan in #33590 <details> <summary>🔧 Fixes</summary> - [[Android] Unexpected high Bitmap.ByteCount when loading image via ImageSource.FromResource() or ImageSource.FromStream() in .NET MAUI](#33239) </details> - [Android] Fix for Resize method returns an image that has already been disposed by @SyedAbdulAzeemSF4852 in #29964 <details> <summary>🔧 Fixes</summary> - [In GraphicsView, the Resize method returns an image that has already been disposed](#29961) - [IIMage.Resize bugged behaviour](#31103) </details> ## Label - Fixed Label Span font property inheritance when applied via Style by @SubhikshaSf4851 in #34110 <details> <summary>🔧 Fixes</summary> - [`Span` does not inherit text styling from `Label` if that styling is applied using `Style` ](#21326) </details> - [Android] Implemented material3 support for Label by @SyedAbdulAzeemSF4852 in #33599 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for Label](#33598) </details> ## Map - [Android] Fix Circle Stroke color is incorrectly updated as Fill color. by @NirmalKumarYuvaraj in #33643 <details> <summary>🔧 Fixes</summary> - [[Android] Circle Stroke color is incorrectly updated as Fill color.](#33642) </details> ## Mediapicker - [iOS] Fix: invoke MediaPicker completion handler after DismissViewController by @yuriikyry4enko in #34250 <details> <summary>🔧 Fixes</summary> - [[iOS] Media Picker UIImagePickerController closing issue](#21996) </details> ## Navigation - Fix ContentPage memory leak on Android when using NavigationPage modally (fixes #33918) by @brunck in #34117 <details> <summary>🔧 Fixes</summary> - [[Android] Modal TabbedPage whose tabs are NavigationPage(ContentPage) is retained after PopModalAsync()](#33918) </details> ## Picker - [Android] Implement material3 support for TimePicker by @HarishwaranVijayakumar in #33646 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for TimePicker](#33645) </details> - [Android] Implemented Material3 support for Picker by @SyedAbdulAzeemSF4852 in #33668 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for Picker](#33665) </details> ## RadioButton - [Android] Implemented material3 support for RadioButton by @SyedAbdulAzeemSF4852 in #33468 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for RadioButton](#33467) </details> ## Setup - Clarify MA003 error message by @jeremy-visionaid in #34067 <details> <summary>🔧 Fixes</summary> - [MA003 false positive with 9.0.21](#26599) </details> ## Shell - [Android] Fix TabBar FlowDirection not updating dynamically by @SubhikshaSf4851 in #33091 <details> <summary>🔧 Fixes</summary> - [[Android, iOS] FlowDirection RTL is not updated dynamically on Shell TabBar](#32993) </details> - [Android] Fix page not disposed on Shell replace navigation by @Vignesh-SF3580 in #33426 <details> <summary>🔧 Fixes</summary> - [[Android] [Shell] replace navigation leaks current page](#25134) </details> - [Android] Fixed Shell flyout does not disable scrolling when FlyoutVerticalScrollMode is set to Disabled by @NanthiniMahalingam in #32734 <details> <summary>🔧 Fixes</summary> - [[Android] Shell.FlyoutVerticalScrollMode="Disabled" does not disable scrolling](#32477) </details> ## Single Project - Fix: Throw a clear error when an SVG lacks dimensions instead of a NullReferenceException by @Shalini-Ashokan in #33194 <details> <summary>🔧 Fixes</summary> - [MAUI Fails To Convert Valid SVG Files Into PNG Files (Object reference not set to an instance of an object)](#32460) </details> ## SwipeView - [iOS] Fix SwipeView stays open on iOS after updating content by @devanathan-vaithiyanathan in #31248 <details> <summary>🔧 Fixes</summary> - [[iOS] - Swipeview with collectionview issue](#19541) </details> ## TabbedPage - [Windows] Fixed IsEnabled Property not works on Tabs by @NirmalKumarYuvaraj in #26728 <details> <summary>🔧 Fixes</summary> - [ShellContent IsEnabledProperty does not work](#5161) - [[Windows] Shell Tab IsEnabled Not Working](#32996) </details> - [Android] Fix NavigationBar overlapping StatusBar when NavigationBar visibility changes by @Vignesh-SF3580 in #33359 <details> <summary>🔧 Fixes</summary> - [[Android] NavigationBar overlaps with StatusBar when mixing HasNavigationBar=true/false in TabbedPage on Android 15 (API 35)](#33340) </details> ## Templates - Fix for unable to open task using keyboard navigation on windows platform by @SuthiYuvaraj in #33647 <details> <summary>🔧 Fixes</summary> - [Unable to open task using keyboard: A11y_.NET maui_User can get all the insights of Dashboard_Keyboard](#30787) </details> ## TitleView - Fix for NavigationPage.TitleView does not expand with host window in iPadOS 26+ by @SuthiYuvaraj in #33088 ## Toolbar - [iOS] Fix toolbar items ignoring BarTextColor on iOS/MacCatalyst 26+ by @Shalini-Ashokan in #34036 <details> <summary>🔧 Fixes</summary> - [[iOS 26] ToolbarItem color with custom BarTextColor not working](#33970) </details> - [Android] Fix for ToolbarItem retaining the icon from the previous page on Android when using NavigationPage. by @BagavathiPerumal in #32311 <details> <summary>🔧 Fixes</summary> - [Toolbaritem keeps the icon of the previous page on Android, using NavigationPage (not shell)](#31727) </details> ## WebView - [Android] Fix WebView in a grid expands beyond it's cell by @devanathan-vaithiyanathan in #32145 <details> <summary>🔧 Fixes</summary> - [Android - WebView in a grid expands beyond it's cell](#32030) </details> ## Xaml - ContentPresenter: Propagate binding context to children with explicit TemplateBinding by @HarishwaranVijayakumar in #30880 <details> <summary>🔧 Fixes</summary> - [Binding context in ContentPresenter](#23797) </details> <details> <summary>🔧 Infrastructure (1)</summary> - [Revert] ContentPresenter: Propagate binding context to children with explicit TemplateBinding by @Ahamed-Ali in #34332 </details> <details> <summary>🧪 Testing (6)</summary> - [Testing] Feature Matrix UITest Cases for Shell Flyout Page by @NafeelaNazhir in #32525 - [Testing] Feature Matrix UITest Cases for Brushes by @LogishaSelvarajSF4525 in #31833 - [Testing] Feature Matrix UITest Cases for BindableLayout by @LogishaSelvarajSF4525 in #33108 - [Android] Add UI tests for Material 3 CheckBox by @HarishwaranVijayakumar in #34126 <details> <summary>🔧 Fixes</summary> - [[Android] Add UI tests for Material 3 CheckBox](#34125) </details> - [Testing] Feature Matrix UITest Cases for Shell Tabbed Page by @NafeelaNazhir in #33159 - [Testing] Fixed Test case failure in PR 34294 - [03/2/2026] Candidate - 1 by @TamilarasanSF4853 in #34334 </details> <details> <summary>📦 Other (2)</summary> - Bumps Syncfusion.Maui.Toolkit dependency to version 1.0.9 by @PaulAndersonS in #34178 - Fix crash when closing Windows based app when using TitleBar by @MFinkBK in #34032 <details> <summary>🔧 Fixes</summary> - [Unhandled exception "Value does not fall within the expected range" when closing Windows app](#32194) </details> </details> **Full Changelog**: main...inflight/candidate


Description of Change
New classes (internal, TODO: make public in .NET 11):
EditorHandler2 (EditorHandler2.Android.cs): A new handler extending ViewHandler<IEditor, MauiMaterialEditText> that wraps MauiMaterialEditText (a TextInputEditText-based view) instead of the standard AppCompatEditText.
Implements full property/command mapper parity with EditorHandler, including DataFlowDirection tracking to distinguish platform-initiated vs MAUI-initiated text updates. Subscribes to TextChanged, FocusChange, and SelectionChanged events; calls VirtualView.Completed() on focus loss; syncs CursorPosition/SelectionLength on selection changes. Overrides PlatformArrange with PrepareForTextViewArrange.
MauiMaterialEditText (MauiMaterialEditText.cs): Extends TextInputEditText (from the Material library) to expose a SelectionChanged event via override of OnSelectionChanged, enabling cursor/selection position tracking by the handler. Wraps the context with MauiMaterialContextThemeWrapper to apply the Material 3 theme.
Handler registration (AppHostBuilderExtensions.cs):
Mapper setup (Editor.Mapper.cs, Editor.Android.cs):
When Material 3 is enabled on Android, additional mappings are registered on EditorHandler2.Mapper and EditorHandler2.CommandMapper:
ReplaceMapping for Text and TextTransform → platform-overloaded MapText (handles DataFlowDirection)
AppendToMapping for IsFocused → InputView.MapIsFocused
PrependToMapping for Focus → InputView.MapFocus
Issues Fixed
Fixes #33476
Output