Press "Enter" to skip to content

Adding Badge to ToolbarItem in Xamarin Forms

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:

https://github.com/CrossGeeks/ToolbarItemBadgeSample/blob/master/ToolbarItemBadgeSample/ToolbarItemBadgeSample.Android/BadgeDrawable.cs

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://github.com/Diaspora-for-Android/diaspora-android/blob/master/app/src/main/java/com/github/dfa/diaspora_android/ui/BadgeDrawable.java

https://gist.github.com/freedom27/c709923b163e26405f62b799437243f4

Happy badge!

23 Comments

  1. Rod Doe Rod Doe

    Nice!

  2. sumit sumit

    Very Thanks For this great toolbar
    But i am stuck on another toolbar how to add badge on another(Multiple) toolbar ..

  3. Johan Johan

    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

  4. Ramiro Ramiro

    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

    • ChrisA ChrisA

      Hi what was the patch you made, i’m having the same issue….

    • Jonh Bravo Jonh Bravo

      Hi! What did you do to solve that problem? I´m getting the same error in iOS

    • Amr Kamal Amr Kamal

      how to solve ios problem ? i’m facing the same problem also updating cart in android

    • Amr Kamal Amr Kamal

      How to solve this problem ? i’m facing the same problem

  5. Jorge ERP Jorge ERP

    Thank you very much It Worked for me, I had to do some changes and a new project but it worked hahaha

  6. Anit Anit

    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.

  7. Dan Abdul Dan Abdul

    Is there a way to make this work for masterdetailpage with a toolbaritem on the right?

  8. Washington Washington

    I have the same problem

  9. Washington Morais Washington Morais

    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.

  10. Louise Melo Louise Melo

    Excelent!!!

  11. Mikilll Mikilll

    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.

    • Wasim Wasim

      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.

  12. click here click here

    greatae

  13. Freerk Freerk

    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.

  14. Rdiaz Rdiaz

    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,

  15. xamassassin xamassassin

    not working anymore . pls update

  16. Prime Prime

    Badge Text not coming only circle is coming

  17. York York

    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…

  18. joshua joshua

    Not Working on MasterDetail Page

Comments are closed.