Search code examples
c#xamlmvvmmauimaui-community-toolkit

how to dynamically add numbers over Tab icons. .net maui


I have a bottom TabBar with a Message Icon, that shows a tab(ShellContent) a page with Messages. Now how can I dynamically show numbers on top of the Tabbar icon, example usecase: Display number 2 over Message icon when user has 2 new messages. I just have basic Tab item as below, please suggest how can I dynamically display number on it. I also follow MVVM architecture.

<Tab Title="Inbox" x:Name="messages" 
        Icon="message.svg">
        <ShellContent ContentTemplate="{DataTemplate views:MessagesPage}" Route="messages"/>
    </Tab>

Solution

  • You can try to achieve this with custom platform specific shell handlers for Android and iOS.

    Please refer to the following code:

    On android:

    namespace ShellTabbarBadgeMAUI
    {
        public class TabbarBadgeRenderer : ShellRenderer
        {
            protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
            {
                //return base.CreateBottomNavViewAppearanceTracker(shellItem);
                return new BadgeShellBottomNavViewAppearanceTracker(this, shellItem);
            }
        }
    
        class BadgeShellBottomNavViewAppearanceTracker : ShellBottomNavViewAppearanceTracker
        {
            private BadgeDrawable? badgeDrawable;
            public BadgeShellBottomNavViewAppearanceTracker(IShellContext shellContext, ShellItem shellItem) : base(shellContext, shellItem)
            {
            }
            public override void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
            {
                base.SetAppearance(bottomView, appearance);
    
                if (badgeDrawable is null)
                {
                    const int cartTabbarItemIndex = 1;
    
                    badgeDrawable = bottomView.GetOrCreateBadge(cartTabbarItemIndex);
                    UpdateBadge(0);
                    BadgeCounterService.CountChanged += OnCountChanged;
                }
            }
            private void OnCountChanged(object? sender, int newCount)
            {
                UpdateBadge(newCount);
            }
            private void UpdateBadge(int count)
            {
                if(badgeDrawable is not null)
                {
                    if (count <= 0)
                    {
                        badgeDrawable.SetVisible(false);
                    }
                    else
                    {
                        badgeDrawable.Number = count;
                        badgeDrawable.BackgroundColor = Colors.Red.ToPlatform();
                        badgeDrawable.BadgeTextColor = Colors.White.ToPlatform();
                        badgeDrawable.SetVisible(true);
                    }
                }
            }
            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                BadgeCounterService.CountChanged -= OnCountChanged;
            }
        }
    }
    

    On iOS:

    namespace ShellTabbarBadgeMAUI
    {
        public class TabbarBadgeRenderer : ShellRenderer
        {
            protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
            {
                //return base.CreateTabBarAppearanceTracker();
                return new BadgeShellTabbarAppearanceTracker();
            }
        }
        class BadgeShellTabbarAppearanceTracker : ShellTabBarAppearanceTracker
        {
            private UITabBarItem? _cartTabbarItem;
            public override void UpdateLayout(UITabBarController controller)
            {
                base.UpdateLayout(controller);
    
                if (_cartTabbarItem is null)
                {
                    const int cartTabbarItemIndex = 1;
    
                    _cartTabbarItem = controller.TabBar.Items?[cartTabbarItemIndex];
                    if (_cartTabbarItem is not null)
                    {
                        UpdateBadge(0);
                        BadgeCounterService.CountChanged += OnCountChanged;
                    }
                }
            }
            private void OnCountChanged(object? sender, int newCount)
            {
                UpdateBadge(newCount);
            }
            private void UpdateBadge(int count)
            {
                if(_cartTabbarItem is not null)
                {
                    if(count <= 0)
                    {
                        _cartTabbarItem.BadgeValue = null;
                    }
                    else
                    {
                        _cartTabbarItem.BadgeValue = count.ToString();
                        _cartTabbarItem.BadgeColor = Colors.Red.ToPlatform();
                    }
                }
            }
            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                BadgeCounterService.CountChanged -= OnCountChanged;
            }
        }
    }
    

    In MauiProgram.cs

    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                })
                .ConfigureMauiHandlers(h=>
                {
    #if ANDROID || IOS
                    h.AddHandler<Shell, TabbarBadgeRenderer>();
    #endif
                });
    
    #if DEBUG
            builder.Logging.AddDebug();
    #endif
    
            return builder.Build();
        }
    }
    

    Refer : https://github.com/dotnet/maui/issues/8305#issuecomment-1742160046