Sometimes we need to add a badge to a toolbar item to show a dynamic counter. An example of this might be when having a shopping cart on a e-commerce application (Showing how many items there are in the cart), Notifications (Showing how to many new notifications you have), etc.
The actual way that I had seen people doing this is just hiding the navigation bar and using a badge view (https://alexdunn.org/2017/03/15/xamarin-controls-badgeview/). Other way I have seen this is by using different images for each number.
In this article I will show you how to do it, using the ToolbarItem that already exists on the Navigation Bar.
Let’s code
For this sample I will use a dependency service with a SetBadge method to be able to add a badge to a ToolbarItem.
1-Create a new interface called IToolbarItemBadgeService and add the SetBadge method.
This method will have 5 parameters:
Page: Current page
Item: Actual toolbar item in which will add the badge
Value: Actual number of the badge (Is a string so you can set numbers and texts, but just tested with 2 digits numbers :P).
BackgroundColor: To set the badge background color
TextColor: To set the badge text color
public interface IToolbarItemBadgeService
{
void SetBadge(Page page,ToolbarItem item, string value,Color backgroundColor,Color textColor);
}
2-In your Android and iOS projects create a new class and implement the interface already created.
[assembly: Dependency(typeof(ToolbarItemBadgeService))]
namespace ToolbarItemBadgeSample.Droid.Services
{
public class ToolbarItemBadgeService : IToolbarItemBadgeService
{
public void SetBadge(Page page,ToolbarItem item, string value,Color backgroundColor,Color textColor)
{
//Implementation here
}
}
}
3-Let’s add the magic on Android
Create the badge
We will add a new class called BadgeDrawable which creates the badge drawable. Check the code here:
Adding the badge to the toolbar item
To do this we will use the plugin called Plugin.CurrentActivity by James Montemagno, to detect the actual Activity shown.
Then in our service SetBadge method we will look for the native MenuItem which should match our ToolbarItem index, then we apply the badge to the MenuItem by calling the SetBadgeText static method of BadgeDrawable class.
using Plugin.CurrentActivity;
using ToolbarItemBadgeSample.Droid.Services;
using ToolbarItemBadgeSample.Services;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: Dependency(typeof(ToolbarItemBadgeService))]
namespace ToolbarItemBadgeSample.Droid.Services
{
public class ToolbarItemBadgeService : IToolbarItemBadgeService
{
public void SetBadge(Page page,ToolbarItem item, string value,Color backgroundColor,Color textColor)
{
Device.BeginInvokeOnMainThread(() =>
{
var toolbar = CrossCurrentActivity.Current.Activity.FindViewById(Resource.Id.toolbar) as Android.Support.V7.Widget.Toolbar;
if (toolbar != null)
{
if (!string.IsNullOrEmpty(value))
{
var idx = page.ToolbarItems.IndexOf(item);
if (toolbar.Menu.Size() > idx)
{
var menuItem = toolbar.Menu.GetItem(idx);
BadgeDrawable.SetBadgeText(CrossCurrentActivity.Current.Activity, menuItem, value, backgroundColor.ToAndroid(), textColor.ToAndroid());
}
}
}
});
}
}
}
4-Let’s add the magic on iOS
On iOS we have to use some Objective C native apis and map them into C#. That’s why created the BarButtonItemExtension class which allows to update badges in Bar Button Items.
public static class BarButtonItemExtensions
{
…
public static void UpdateBadge(this UIBarButtonItem barButtonItem, string text, UIColor backgroundColor, UIColor textColor)
{
var bLayer = GetBadgeLayer(barButtonItem);
if (string.IsNullOrEmpty(text) || text == "0")
{
bLayer?.RemoveFromSuperLayer();
objc_setAssociatedObject(barButtonItem.Handle, BadgeKey.Handle, new CAShapeLayer().Handle, AssociationPolicy.ASSIGN);
return;
}
var textLayer = bLayer?.Sublayers?.First(p => p is CATextLayer) as CATextLayer;
if (textLayer != null)
{
textLayer.String = text;
}
else
{
barButtonItem.AddBadge(text, backgroundColor, textColor);
}
}
}
Full class source here: https://github.com/CrossGeeks/ToolbarItemBadgeSample/blob/master/ToolbarItemBadgeSample/ToolbarItemBadgeSample.iOS/Services/ToolbarItemBadgeService.cs
Adding the badge to the toolbar item using the UpdateBadge extension method we already created.
Same concept as in android we look for the native BarButtonItem that matches the ToolbarItem index and apply the badge to it.
using ToolbarItemBadgeSample.iOS.Services;
using ToolbarItemBadgeSample.Services;
using Xamarin.Forms;
using ToolbarItemBadgeSample.iOS.Utils;
using Xamarin.Forms.Platform.iOS;
[assembly: Dependency(typeof(ToolbarItemBadgeService))]
namespace ToolbarItemBadgeSample.iOS.Services
{
public class ToolbarItemBadgeService : IToolbarItemBadgeService
{
public void SetBadge(Page page, ToolbarItem item, string value,Color backgroundColor, Color textColor)
{
Device.BeginInvokeOnMainThread(() =>
{
var renderer = Platform.GetRenderer(page);
if (renderer == null)
{
renderer = Platform.CreateRenderer(page);
Platform.SetRenderer(page, renderer);
}
var vc = renderer.ViewController;
var rightButtomItems = vc?.ParentViewController?.NavigationItem?.RightBarButtonItems;
var idx = page.ToolbarItems.IndexOf(item);
if (rightButtomItems != null && rightButtomItems.Length > idx)
{
var barItem = rightButtomItems[idx];
if (barItem != null)
{
barItem.UpdateBadge(value, backgroundColor.ToUIColor(), textColor.ToUIColor());
}
}
});
}
}
}
You can find the full source code sample here:
https://github.com/CrossGeeks/ToolbarItemBadgeSample
References:
https://gist.github.com/freedom27/c709923b163e26405f62b799437243f4
Happy badge!
23 Comments
Nice!
Very Thanks For this great toolbar
But i am stuck on another toolbar how to add badge on another(Multiple) toolbar ..
Hi Rendy, Thanks for sharing. It works fine for me on iOS, but I’m facing problems on Android. In my case I have a tabbedpage with 3 views. All 3 have the same 2 toolbar items. On one item I need a badge. The badge is shown only on the first tab.
When navigating to another (non-tabbed) page, the badge isn’t displayed too.
Any idea?
Btw, in iOS the badge was displayed on the wrong toolbar item. I fixed that in the ToolbarItemBadgeService by reversing the item index:
var reverseItemIndex = rightButtomItems.GetUpperBound(0) – idx;
var barItem = rightButtomItems[reverseItemIndex];
Regards,
Johan
Thank you very much!
It works fine but if you are on a navigation page with zero items go forward and then go back it get crash on iOS. At least on my experience. I made a little change (patch ) on UpdateBage Function and it works fine.
And for Android I have to put a Task Delay on Xamarin forms project
await Task.Delay(500);
Because without it the number doesn’t update when you go back . At least on my MVVM project.
Regards
Hi what was the patch you made, i’m having the same issue….
Hi! What did you do to solve that problem? I´m getting the same error in iOS
how to solve ios problem ? i’m facing the same problem also updating cart in android
How to solve this problem ? i’m facing the same problem
Thank you very much It Worked for me, I had to do some changes and a new project but it worked hahaha
HI
I have toolbaritem in detail page of master page.
Detail page is a content page.
How can i access content page and its toolbar.
Is there a way to make this work for masterdetailpage with a toolbaritem on the right?
I have the same problem
Please, how can I make this to work with MasterDetail Page in Xamarin Forms?
This line is ALWAYS null:
var toolbar = CrossCurrentActivity.Current.Activity.FindViewById(Resource.Id.toolbar);
But inside the MainActivity works.
Excelent!!!
There are two big problems with this method of creating badge counter on the navigation bar.
1) When I change the orientation of the device, the badge counter disappears.
2) If I navigate to another page and the pop this page from navigation stack, the badge counter disappears.
I was testing that only on Android.
Anybody knows how to solve that issues.
On Android, I was also facing the same issue: “the badge value was disappearing for example while going back (or pressing the back button).
However, I’ve fixed the issue by applying the following workaround,
“`
Task.Run(async () =>
{
await Task.Delay(500);
//Call the method which should update the badge value here
});
“`
So, while debugging, I noticed all code and everything looked fine, and most likely the events were not being executed in the right order. However, adding little delay actually fixed it.
greatae
I have a problem on Android.
The toolbar has an item, but when it gets the toolbar with CrossCurrentActivity, that toolbar doesn’t have any item.
Hello,
How can I set the badge from a service like Firebase service when receiving a push message? I mean how do I pass the Page and ToolBarItems parameter from a non-page class?
Regards,
not working anymore . pls update
Badge Text not coming only circle is coming
Hello, I have a portable project with Android and iOS.
In iOS I don’t have a problem but in ToolbarItemBadgeService class on Android the menu come with zero size and of course not show the bagde. For this reason I’m included the menu on android project and in MainActivity I override the OnCreateOptionsMenu trying to solve that but not working neither.
Some suggestion to solve this issue?
Thx…
Not Working on MasterDetail Page
Comments are closed.