Add BadgeText, BadgeColor, and BadgeTextColor support to ToolbarItem#34669
Add BadgeText, BadgeColor, and BadgeTextColor support to ToolbarItem#34669jfversluis merged 9 commits intonet11.0from
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34669Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34669" |
There was a problem hiding this comment.
Pull request overview
Adds cross-platform badge support to ToolbarItem via new BadgeText/BadgeColor bindable properties, with platform renderers and accompanying tests/samples.
Changes:
- Introduces
ToolbarItem.BadgeTextandToolbarItem.BadgeColorplus PublicAPI updates. - Implements badge rendering on Android (Material
BadgeDrawable), iOS/MacCatalyst (nativeUIBarButtonItembadge), and Windows (InfoBadgeoverlay). - Adds unit tests, UI tests (HostApp + Appium), and a sample gallery page demonstrating toolbar badges.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/Core/Toolbar/ToolbarItem.cs | Adds BadgeText/BadgeColor bindable properties and XML docs. |
| src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs | Renders badges with BadgeDrawable, includes lifecycle tracking and async attachment logic. |
| src/Controls/src/Core/Compatibility/iOS/Extensions/ToolbarItemExtensions.cs | Updates iOS primary toolbar item wrapper to apply native badge when supported. |
| src/Controls/src/Core/Toolbar/Toolbar.Windows.cs | Adds InfoBadge overlay and updates it on ToolbarItem property changes. |
| src/Controls/src/Core/PublicAPI/*/PublicAPI.Unshipped.txt | Adds unshipped PublicAPI entries for the new ToolbarItem badge members across TFMs. |
| src/Controls/tests/Core.UnitTests/ToolbarItemBadgeTests.cs | Unit tests for default values, binding, and property-changed behavior. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue8305_Toolbar.cs | HostApp page used by UITests to exercise badge scenarios. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8305_Toolbar.cs | Appium UI tests + screenshots for toolbar badge scenarios. |
| src/Controls/samples/Controls.Sample/ViewModels/CoreViewModel.cs | Registers the new sample page in the gallery navigation. |
| src/Controls/samples/Controls.Sample/Pages/Core/ToolbarBadgePage.cs | Sample page demonstrating numeric/text badges and color changes. |
| src/Core/maps/src/PublicAPI/net/PublicAPI.Unshipped.txt | Removes duplicate/unannotated entries for IMap.ShowInfoWindow/HideInfoWindow. |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8305_Toolbar.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8305_Toolbar.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8305_Toolbar.cs
Show resolved
Hide resolved
|
Hi @jfversluis Nice to have: |
|
Thanks for the feedback @AlleSchonWeg! Great to hear you've implemented toolbar badges in your app. BadgeTextColor — Implemented in 374ae80! New
IsVisible — Setting BadgeTextFont — This is tricky as platform support varies significantly. Android's |
|
Thanks for implementing my suggestion @jfversluis
I find setting As i implemented my badge i observed a crazy behaviour on android. I added the badge on a toolbar item on one page and on another page(s) the badge also appeared. But only the badge. The icon, color etc. was correct. I don't know if i had done something wrong. Perhaps some internal caching or whatever. My setup was a TabbedPage with 4 tabs. Every tab had a NavigationPage and on the first Root ContentPage i had the item with badge plus another item without badge. The other Root ContentPages had also toolbar items but without badges. Perhaps this info can help with your implementation. |
|
/azp run maui-pr-uitests |
|
Implemented dot badge support! You can now set
Platform-specific behavior:
This applies to both PRs (#34659 Shell tabs and #34669 ToolbarItems). |
|
/azp run maui-pr |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
1 similar comment
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
also this issue #23802 will be solved by this pr |
|
/azp run maui-pr |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
1 similar comment
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run maui-pr |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Code Review SummaryAdds Findings🔴 CriticalAndroid: WeakReference not unwrapped in race condition guard ( Inside the if (_menuItemToolbarItemMap.TryGetValue(menuItemId, out var currentToolbarItem) &&
!ReferenceEquals(currentToolbarItem, toolbarItem))
return;
The existing correct pattern in the same file ( if (!_menuItemToolbarItemMap.TryGetValue(menuItem.ItemId, out var weakRef)
|| !weakRef.TryGetTarget(out var currentToolbarItem)
|| !ReferenceEquals(currentToolbarItem, toolBarItem))
return;Suggested fix: Use 🟡 Warnings
🔵 Info
Overall AssessmentWell-structured feature with consistent API design, good test coverage, and thoughtful platform-specific implementations. The Android Review performed by Copilot CLI |
Code Review Summary (Updated)ToolbarItem badge support — Adds Findings
Overall Assessment
Review performed by Copilot CLI |
f95a346 to
3659048
Compare
Code Review — PR #34669Independent AssessmentWhat this changes: Adds Target branch: Reconciliation with PR NarrativeAgreement: ✅ Code matches the description. All claimed API semantics (null = hidden, empty = dot, non-empty = text/count) are correctly implemented across all three platforms. The Findings✅ Good — Clean API design
✅ Good — Android: Robust race condition handling
✅ Good — iOS: Correct version check and implementationif (!OperatingSystem.IsIOSVersionAtLeast(26) && !OperatingSystem.IsMacCatalystVersionAtLeast(26))
return;Matches the established codebase pattern for iOS 26+ APIs. Correct mapping: ✅ Good — Windows: Proper InfoBadge overlayGrid wrapping pattern for both icon-based and text-only toolbar items. Dynamic badge changes handled via ✅ Good — Comprehensive test coverage
💡 Observation — Static dictionaries for Android badge tracking
However, if 💡 Observation —
|
Extends badge support from Shell bottom tabs (PR #34659) to ToolbarItems. Uses native platform APIs: Android BadgeUtils/BadgeDrawable, iOS 26+ UIBarButtonItem.badge, and Windows InfoBadge overlay. Includes 17 unit tests, UI test page, and sample gallery page. Fixes #8305 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace deprecated Frame with Border control - Add System and Microsoft.Maui usings - Fix nullable Color? parameter in SetBadgeColor Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test file was missing NUnit.Framework, UITest.Appium, and UITest.Core usings, and used incorrect namespace causing build failures in Controls.TestCases.Mac.Tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…text-only items, Android cleanup - Add BadgeTextColor property to ToolbarItem for foreground text color customization - Android: BadgeDrawable.BadgeTextColor - iOS/MacCatalyst 26+: UIBarButtonItemBadge.ForegroundColor - Windows: InfoBadge.Foreground brush - Add retryTimeout to all VerifyScreenshot() calls for async Android badge rendering - Fix Windows text-only toolbar items: wrap in Grid+InfoBadge overlay - Add ReferenceEquals guard in Android toolbar.Post() callback for recycled menu items - Clean up all badge drawables in DisposeMenuItems - Add 7 unit tests for BadgeTextColor (24 total) - Update all 7 PublicAPI.Unshipped.txt files - Add Badge Text Color section to sample gallery page Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change BadgeText semantics: null = no badge, empty string = dot indicator, non-empty string = text/count badge. Previously both null and empty string hid the badge. Platform implementations: - Android: ClearNumber() on BadgeDrawable for dot mode - iOS: UIBarButtonItemBadge.Create(0) for dot indicator - Windows: Value = -1 for empty string (dot indicator) Added 3 unit tests for dot badge behavior. Updated XML docs to document null vs empty string semantics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove WaitForElement for ToolbarItem AutomationId (not accessible via Appium on WinUI) — wait for page StatusLabel instead - Add Windows reference screenshots for ToolbarItemBadgesClear and ToolbarItemBadgeColorChanges tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 3 iOS (ios-26) reference screenshots: ToolbarItemBadgesDisplay, ToolbarItemBadgesClear, ToolbarItemBadgeColorChanges - Add 3 Android reference screenshots for the same tests - Generated from CI build 1355044 snapshot artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix critical Android bug: ReferenceEquals(WeakReference<T>, T) always returned false, preventing badges from rendering via Post callback. Now uses TryGetTarget pattern consistent with UpdateMenuItemIcon. - Scope DisposeMenuItems badge cleanup to current toolbar's menu items instead of clearing all badges globally from the static dictionary. - Windows: only create Grid+InfoBadge wrapper when BadgeText is set, avoiding unnecessary UI overhead for toolbar items without badges. - Windows: handle dynamic badge addition on icon items that were initially created without a Grid wrapper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3659048 to
71649f7
Compare
🧪 PR Test EvaluationOverall Verdict: The PR adds solid unit test coverage for badge property logic and appropriate screenshot-based UI tests for visual rendering. A few conventions issues and missing robustness improvements in the Shell badge UI tests should be addressed before merge.
📊 Expand Full EvaluationPR Test Evaluation ReportPR: #34669 — ToolbarItem & Shell Badge Support Overall VerdictUnit tests for 1. Fix Coverage — ✅
2. Edge Cases & Gaps —
|
| Test | Type Used | Assessment |
|---|---|---|
ToolbarItemBadgeTests |
Unit (xUnit) | ✅ Correct — pure property logic |
ShellBadgeTests |
Unit (xUnit) | ✅ Correct — pure property logic |
Issue8305_Toolbar / Issue8305 |
UI + screenshot | ✅ Appropriate — badge rendering is visual and platform-specific; screenshots are the right tool |
The ToolbarItemBadgeIncrements test verifies a label text change after tapping a button, which is slightly heavier than needed (a unit test could verify the count mutation), but it also validates the end-to-end UI refresh path, so it is defensible.
4. Convention Compliance — ⚠️
Issue8305_Toolbar:
⚠️ File nameIssue8305_Toolbardoes not follow theIssueXXXXXconvention. Since this is the toolbar sub-feature of issue Add Shell Badge support #8305, it should either be namedIssue8305Toolbar(no underscore) or live in a dedicated issue number if one exists.
Issue8305 (Shell badge):
⚠️ ShellBadgeMultipleTabsCanHaveBadgescallsApp.Tap("SetTab3BadgeButton")but only hasApp.WaitForElement("SetBadgeButton")as its guard.SetTab3BadgeButtonis a separate element and should also have aWaitForElementbefore it is tapped.
ToolbarItemBadgeTests / ShellBadgeTests:
- ✅ Correct xUnit
[Fact]attributes, correct namespace, inheritsBaseTestFixture/ShellTestBase.
UtilExtensions (incidental):
⚠️ Script flaggedUtilExtensions.csas missing_IssuesUITestbase class and[Category]— this appears to be a utility class, not a test class, so the warnings are likely false positives.
5. Flakiness Risk — ⚠️ Medium
Issue8305_Toolbar.cs (ToolbarItem):
- ✅
ToolbarItemBadgesDisplay,ToolbarItemBadgesClear, andToolbarItemBadgeColorChangesall useVerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2))— correct for async Android badge rendering.
Issue8305.cs (Shell):
⚠️ ShellBadgeInitialBadgeIsVisiblecallsVerifyScreenshot()with noretryTimeout— if Shell tab badge rendering is async on Android (which it likely is, using the sameBadgeDrawablemechanism), this test may be flaky.⚠️ ShellBadgeCanBeSetAtRuntimeandShellBadgeCanBeClearedalso useVerifyScreenshot()withoutretryTimeoutafter tapping a button that triggers badge updates.
Recommendation: Add retryTimeout: TimeSpan.FromSeconds(2) to all three Shell VerifyScreenshot() calls to match the approach used in the Toolbar tests.
6. Duplicate Coverage — ✅ No duplicates
No existing tests for ToolbarItem badge properties or ShellSection badge were found. ShellBadgeTests.cs is a new file alongside existing ShellAppearanceTests.cs, and there is no overlap in what they test.
7. Platform Scope — ✅
Reference screenshots are present for iOS-26, Android, and Windows for all three ToolbarItemBadge* tests and all four ShellBadge* tests. MacCatalyst reuses iOS screenshots, which is standard in this repository.
The iOS badge implementation correctly guards against pre-iOS-26 versions (silently ignores on older OS). This platform-conditional behavior is not explicitly tested via an older-OS device test, but that is an accepted limitation for a new-API feature.
8. Assertion Quality — ⚠️
| Test | Assertion | Assessment |
|---|---|---|
ToolbarItemBadgeIncrements |
Assert.That(text, Is.EqualTo("Count badge: 4")) |
✅ Specific |
ToolbarItemBadgesClear |
Assert.That(text, Is.EqualTo("All badges cleared")) |
✅ Specific |
ToolbarItemBadgeColorChanges |
Assert.That(text, Is.EqualTo("Badge color: Red")) |
✅ Specific |
ToolbarItemBadgesDisplay |
VerifyScreenshot() only |
|
ShellBadgeInitialBadgeIsVisible |
VerifyScreenshot() only |
|
ShellBadgeMultipleTabsCanHaveBadges |
VerifyScreenshot() only |
The screenshot-only tests will catch visual regressions but won't detect cases where a badge renders with the wrong text value (e.g., "3" vs "5"). Adding a programmatic assertion on badge state (via Appium element text, if accessible) would strengthen these tests.
9. Fix-Test Alignment — ✅
ToolbarItemBadgeTests→ToolbarItem.cs(new properties) ✅Issue8305_ToolbarUI tests →ToolbarExtensions.cs(Android),ToolbarItemExtensions.cs(iOS),Toolbar.Windows.cs(Windows) ✅ShellBadgeTests/Issue8305UI tests → Shell badge properties ✅
The badge feature is clearly divided by concern, and each test file targets the correct code path.
Recommendations
- Add
WaitForElement("SetTab3BadgeButton")beforeApp.Tap("SetTab3BadgeButton")inIssue8305.ShellBadgeMultipleTabsCanHaveBadgesto prevent flakiness. - Add
retryTimeout: TimeSpan.FromSeconds(2)to the three ShellVerifyScreenshot()calls inIssue8305.csfor consistency with the Toolbar badge tests and to guard against async badge rendering on Android. - Add a test for
ToolbarItemOrder.Secondaryitems — verify that a secondary-orderToolbarItemwithBadgeTextset does not render a badge (it should be silently ignored per the documented behavior). - (Optional) Add a UI test scenario exercising
BadgeTextColoron-device so the property is visually verified end-to-end. - (Minor) Consider renaming
Issue8305_ToolbartoIssue8305Toolbar(no underscore) to align with theIssueXXXXXnaming convention pattern.
Warning
⚠️ Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
dc.services.visualstudio.com
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
network:
allowed:
- defaults
- "dc.services.visualstudio.com"See Network Configuration for more information.
Note
🔒 Integrity filtering filtered 1 item
Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.
- pr:Add BadgeText, BadgeColor, and BadgeTextColor support to ToolbarItem #34669 (
pull_request_read: Resource 'pr:Add BadgeText, BadgeColor, and BadgeTextColor support to ToolbarItem #34669' has lower integrity than agent requires. Agent would need to drop integrity tags [unapproved:all approved:all] to trust this resource.)
🧪 Test evaluation by Evaluate PR Tests
|
🚨 API change(s) detected @davidortinau FYI |
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
Adds
BadgeText,BadgeColor, andBadgeTextColorproperties toToolbarItem, enabling badge notifications on toolbar items across all platforms.Fixes #8305 (partial - ToolbarItem badges; Shell tab badges handled in #34659)
API
Badge Text Semantics
null""(empty string)"3""New"Platform Support
BadgeDrawablevia Material ComponentsClearNumber()for dot mode. UsesBadgeUtils.attachBadgeDrawable()with race condition guard.UIBarButtonItem.badgeAPICreate(0)for dot. Requires iOS 26+, no-op on earlier versions.InfoBadgeoverlay onAppBarButtonProperties
BadgeTextstring?null= hidden,""= dot, non-empty = text/count.BadgeColorColor?null= platform default.BadgeTextColorColor?null= platform default (typically white).Changes
ToolbarItem.cs— AddedBadgeText,BadgeColor,BadgeTextColorbindable properties with XML docsToolbarExtensions.cs(Android) — Badge rendering viaBadgeDrawablewithConcurrentDictionarytracking, stale-update guard, and cleanup inDisposeMenuItemsToolbarItemExtensions.cs(iOS) — iOS 26+ native badge API with platform version checkToolbar.Windows.cs—InfoBadgeoverlay onAppBarButtoncontent grid (both icon and text-only items)ToolbarItemBadgeTests.csToolbarBadgePage.cs