Xamarincommunitytoolkit: [Spec] Badge support for Shell

Created on 7 May 2019  路  62Comments  路  Source: xamarin/XamarinCommunityToolkit

Summary

There are requests for badge support for classic tabs and there are also some libraries out there which kinda support that. With shell it's not possible to use this libraries without touching / replacing too much of the shell internals so it would be great if shell supports this scenario out of the box. I couldn't find anything related in shell spec, that's why I'm proposing it here.

API Changes

Badges should be supported on all shell navigation patterns, which are

  • Flyout menu items
  • Bottom tabs
  • Top tabs

I propose the following properties for first iteration of badge support:

Primary properties

BaseShellItem (bindable properties)

  • BadgeText: string to set the number (or something else) of notification-like items
  • BadgeMoreText: string to set the number (or something else) of the aggregated more tab
  • BadgeTextColor: Foreground color of badge text
  • BadgeUnselectedTextColor: Foreground color of badge text if underlying navigation item is in unselected state
  • BadgeEffectiveTextColor (readonly): If BadgeUnselectedTextColor is unset, BadgeTextColor will be returned. Otherwise it will return BadgeTextColor if the underlying navigation item is in selected state or BadgeUnselectedTextColor if in unselected state.
  • BadgeColor: background color of badge
  • BadgeUnselectedColor: background color of badge if navigation item is in unselected state
  • BadgeEffectiveColor (readonly): If BadgeUnselectedColor is unset, BadgeColor will be returned. Otherwise it will return BadgeColor if the underlying navigation item is in selected state or BadgeUnselectedColor if in unselected state.

Future implementations / considerations

  • Add font image support (might also work for "little dot mode")
  • CSS Styling
  • Inheritance of styling related properties
  • Additional More properties (TextColor, UnselectedTextColor, Color, UnselectedColor, ...)

Bottom tabs use the concept of the More tab if there are more than 5 items. All items with index >= 5 will be "hidden" by this more tab and handled by a platform specific context menu.

One use case would be the transformation of the underlying items hidden by this more tab. For example Item 5 (Messages) has "1" new item and Item 6 (Notifications) has "2" new items, so you could show "3" in the more tab, but the user would not how how this is split between the aggregated items.

We could think of modifying the text (e.g. for the example above "Messages (1)" and "Notifications (6)"). I'm proposing this because adding a badge to the underlying platform specific context menu could be too much work / impossible for a specific platform. Modifying the title value should definitely work though and we would see at least something.

Internal implementation

Android

Material components will support badges out of the box, which when I understood it correctly can be used when AndroidX is supported by Xamarin. In the meantime we have to supply a custom implementation like this for badges.

Flyout

Flyout menu is created by the ShellFlyoutTemplatedContentRenderer in combination with ShellFlyoutRecyclerAdapter. The data template is based on forms controls, so we just might use a Frame here for the default template. For custom templates the user has to provide the necessary controls and bind color, text, etc..

Bottom tabs

ShellItemRenderer uses BottomNavigationView, which can be used to retrieve the underlying BottomNavigationItemView items and inject the badge view.

The more tab uses a BottomSheetDialog.

Top tabs

ShellSectionRenderer uses TabLayout, which can be used to retrieve the underlaying TabView items and inject the badge view.

iOS

Flyout

Flyout menu is created by the ShellTableViewController in combination with ShellTableViewSource. The data template is based on forms controls, so we just might use a Frame here for the default template. For custom templates the user has to provide the necessary controls and bind color, text, etc..

Bottom tabs

ShellItemRenderer uses UITabBarController which can use the UITabBarItem to specify native Badge properties.

The more tab uses the default implementation of the MoreNavigationController of UITabBarController.

Top tabs

ShellSectionRootRenderer uses a UICollectionView, so you would have to modify ShellSectionHeaderCell and add a custom badge view (something like this).

Screenshots

TODO: Add them here

Feedback welcome.

Most helpful comment

Hello. +1 assigning to the priority of this feature. It's a "must-have" thing for Xamarin.Forms development with the help of Shell.

All 62 comments

Sounds great to me! @davidortinau @pauldipietro Anything to add?

I have started implementation for bottom tabs support (see fork), because this will cover most needs and is the easiest to implement.

How should I proceed? Should I go for a PR? Do you want to refine specification or implementation details?

Android

iOS

@Dresel can you create a draft PR against your fork and then we can check it out?

Feel free to test.

Little dot mode might be looking something like this (Android):

Badge support shouldn't be limited to only Tabs (top or bottom) - it should also be supported in the FlyoutMenu itself so the badges can be seen when the flyout opens and users don't need to navigate into each page to see the badges.

Badge support shouldn't be limited to only Tabs (top or bottom) - it should also be supported in the FlyoutMenu itself so the badges can be seen when the flyout opens and users don't need to navigate into each page to see the badges.

Yes, it's on my (extended) list, might look similar to

Since Flyout Items are rendered with forms controls and can use custom templates, it's (from an implementations perspective) different though. I will update the spec according to that soon.

Can we please have this for non Shell too? I am thinking that it should be first implemented in the non Shell (classic) and then surfaced in Shell.

If we start having having feature discrepancies between Shell and non-Shell that doesn鈥檛 sound good to me. I am referring to only features like this one, obviously not everything can or should be in non Shell, that鈥檚 the reason why Shell exists.

Can we please have this for non Shell too? I am thinking that it should be first implemented in the non Shell (classic) and then surfaced in Shell.

If we start having having feature discrepancies between Shell and non-Shell that doesn鈥檛 sound good to me. I am referring to only features like this one, obviously not everything can or should be in non Shell, that鈥檚 the reason why Shell exists.

What's wrong with packages like xamarin-forms-tab-badge? Problem with shell is that it's not possible to use this libraries without touching / replacing too much of the shell internals, that's why I was proposing this in the first place.

My suggestion is not related to that package, it might be a great package. I was only suggesting a way I think it's best to handle this, which is to give this new feature to non-Shell apps too. I just wanted to share my feedback, your proposal is great.

Spec updated.

This is great! Hopefully it's available in pre-release soon

Yep, for our app to be able to continue to use the new Shell, this is a must! Thanks, and waiting anxiously!

Yep, for our app to be able to continue to use the new Shell, this is a must! Thanks, and waiting anxiously!

Also for our app! This is highly requested from the business side

when will the badge for shell appear in xamarin forms?
Can you show an example of a renderer for Tab (ShellSection) with badge?

I'm currently waiting for review / feedback from the XF team, I cannot tell you when this will be ready.

Can you show an example of a renderer for Tab (ShellSection) with badge?

If you want you can build the XF repo and play with the Control Gallery sample or check the PR sources.

@Dresel do you have an ETA when badge support within toolbar and flyout menu will be available? We're released a Xamarin based app and user engagement is being impacted by the lack of badges. In-app badges are essential for all modern apps and I'm surprised that this feature is not getting prioritized on the Xamarin team. Thanks and looking forward to your feedback.

@Dresel do you have an ETA when badge support within toolbar and flyout menu will be available? We're released a Xamarin based app and user engagement is being impacted by the lack of badges. In-app badges are essential for all modern apps and I'm surprised that this feature is not getting prioritized on the Xamarin team. Thanks and looking forward to your feedback.

PR is ready for review - hopefully soon 馃憤

When is this going to be released?

When is this going to be released?

I'm currently waiting for review. I don't know how work items are prioritized, maybe @samhouts can tell you more.

I'm pretty sure you have a team of testers ready to check it out, myself included.

When is this going to be released?

I'm currently waiting for review. I don't know how work items are prioritized, maybe @samhouts can tell you more.

@samhouts will this be included anytime soon? Been waiting on this for awhile since switching to AppShell! It's pretty much expected in apps to have a red badge on a tab icon for notifications....

We're still reviewing the PR. We hope to have it ready soon! Thanks for your patience!

Hello @samhouts really this is an urgent in our software , I hope it will be applied so soon
Thank you

Hello. +1 assigning to the priority of this feature. It's a "must-have" thing for Xamarin.Forms development with the help of Shell.

What does "ready soon" mean? I don't know why this takes so long, it's open for more than a year now and a very basic feature. I hope it will be really ready soon ...

+1

vNext+1 means 4.8.0? have it done in 4.8.0, please.

Im really waiting for this feature! Thanks @Dresel

I'm also waiting for this feature.

June 2020, 13 months later, but Xamarin still doesn't supports badge on Shell tabs.... I know it's not your fault, but please @samhouts update us about this release, all my customers needs this feature. Thanks.

when can it be used?

FYI as a workaround, I've managed to get the badges working on iOS and Android on tabs / shells via renderers. Maybe you can use some of this code for yourself..

Android

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.Droid.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer(Context context) : base(context)
      { }

      protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
      {
         return new CustomShellBottomAppearance(this);
      }
   }
}

Then the CustomShellBottomAppearance.cs:

namespace Xamarin.Droid.Renderers
{
   internal class CustomShellBottomAppearance : IShellBottomNavViewAppearanceTracker
   {
      public CustomShellBottomAppearance(CustomShellRenderer shellRenderer)
      { }

      public void Dispose()
      { }

      public void ResetAppearance(BottomNavigationView bottomView)
      { }

      public async void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
      {
         bottomView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilitySelected;
         var _bottomNavigationMenuView = (BottomNavigationMenuView)bottomView.GetChildAt(0);

         var badge = await MainViewModel.CheckNewEventsAsync();
         CreatePageBadge(2, badge > 0, badge, _bottomNavigationMenuView);
      }

      private void CreatePageBadge(int index, bool ShowBadge, int BadgeCount, BottomNavigationMenuView _bottomNavigationMenuView)
      {
         var itemView = (BottomNavigationItemView)_bottomNavigationMenuView.GetChildAt(index);
         if (ShowBadge && BadgeCount > 0)
         {
            var mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            if (mtxtnotificationsbadge == null)
            {
               var vBadge = LayoutInflater.From(CrossCurrentActivity.Current.Activity).Inflate(Resource.Layout.notification_badge, _bottomNavigationMenuView, false);
               itemView.AddView(vBadge);
               mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            }
            mtxtnotificationsbadge.Text = BadgeCount.ToString();
            if (mtxtnotificationsbadge.Text.Length == 1)
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_circle_shape);
            else
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_rectangle_shape);
         }
         else
         {
            var badgeLayout = itemView.FindViewById<FrameLayout>(Resource.Id.badge);
            if (badgeLayout != null)
               itemView.RemoveView(badgeLayout);
         }
      }
   }
}

then under resource/layout (notification_badge.axml):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/badge"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/txtbadge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:gravity="center"
        android:padding="3dp"
        android:textColor="@color/white"
        android:textSize="11sp" />
</FrameLayout>

custom_rectangle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

custom_circle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

iOS

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.iOS.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer() : base()
      { }

      protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
      {
         return new CustomShellBottomAppearance();
      }
   }
}

And then the CustomShellBottomAppearance.cs:

namespace Xamarin.iOS.Renderers
{
   public class CustomShellBottomAppearance : IShellTabBarAppearanceTracker
   {
      public void Dispose()
      { }

      public void ResetAppearance(UITabBarController controller)
      { }

      public async void SetAppearance(UITabBarController controller, ShellAppearance appearance)
      {
         if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any())
         {
            var badge = await MainViewModel.CheckNewEventsAsync();
            if (badge > 0)
               controller.TabBar.Items[2].BadgeValue = badge.ToString();
         }
      }
      public void UpdateLayout(UITabBarController controller)
      { }
   }
}

result on iOS:
image

Thanks for the work around @galadril!
Do you have the code for Resource.Drawable.custom_circle_shape and Resource.Drawable.custom_rectangle_shape?

Thanks for the work around @galadril!
Do you have the code for Resource.Drawable.custom_circle_shape and Resource.Drawable.custom_rectangle_shape?

Ive added those drawable xml files to the comment as well.. sry

@galadril awesome!! many thanks!!
I imagine MainViewModel.CheckNewEventsAsync ()checks for new notifications?

@galadril awesome!! many thanks!!
I imagine MainViewModel.CheckNewEventsAsync ()checks for new notifications?

yeah thats the async method that gets the new event count, to show the badge

Any Updates or ETA on this?

In iOS you could set up a Subscription and call it from anywhere in your code. Building on @galadril example. In the renderer...

public async void SetAppearance(UITabBarController controller, ShellAppearance appearance)
{
if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any())
{

            MessagingCenter.Subscribe<Shell, string>(
            this,
            "BadgeTab1",
            (shell, arg) =>
            {

                Device.BeginInvokeOnMainThread(() => controller.TabBar.Items[0].BadgeValue = arg);

            });


            MessagingCenter.Subscribe<Shell, string>(
            this,
            "BadgeTab2",
            (shell, arg) =>
            {

                Device.BeginInvokeOnMainThread(() => controller.TabBar.Items[1].BadgeValue = arg);

            });

        }
    }

Then you can call from inside your tab.

MessagingCenter.Send(Shell.Current, "BadgeTab2", "1");

I have another solution.

Advantage

  • No custom render needed.
  • ALL Customized, by using Grid(or others you prefer) for tabbar layout.

    • Badge with/without number

    • Colorfull image as ICON, or colorfull image for selected tab

    • More

  • Keep the same animation as iOS when open no tabbar page in Android

    • original Shell will hide tabbar before switch, not good

Disadvantage

  • Ideal for only use Shell in home page, other pages prefer to hide tabbar.

    • tabbar will come and go with each page

  • Case a little lower performance but hard to detect
  • Maintain the status between MyTab instances

1. Hide tab bar in AppShell page

<Shell Shell.TabBarIsVisible="False">
  Your layout
</Shell>

It will keep hiding all time without manually hide in other page.

2. Create your own tab bar view by creating a custom Grid view

<Grid x:Class="MyLib.Views.MyTab" BackgroundColor="#2196F3" ColumnSpacing="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    # Tabbar layout
    <StackLayout Grid.Column="0" Padding="0,5,0,0" Orientation="Vertical">
        <Image Source="course.png" WidthRequest="30" HeightRequest="30"></Image>
        <Label Text="MyCourse" TextColor="White" HorizontalOptions="Center"></Label>
    </StackLayout>

    # Handle tapped, show badge, show selected mask
    <AbsoluteLayout Grid.Column="0" x:Name="lay0">
        <AbsoluteLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSelected" CommandParameter="0"></TapGestureRecognizer>
        </AbsoluteLayout.GestureRecognizers>

        <Grid AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,1,1,1" IsVisible="true" BackgroundColor="White" Opacity="0.2"></Grid>
        <Frame AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds=".9,0.1,12,12" CornerRadius="6" BackgroundColor="#cf1322" Padding="0"></Frame>
    </AbsoluteLayout>

     Other Tabs Here
</Grid>

TIPS: MyLib.Views.MyTab needs to inherit from Grid.

3. Add switch tab logic in MyTab.xaml.cs

``` c#
private void OnSelected(object sender, EventArgs e)
{
if (e is TappedEventArgs tap)
{
var newIndex = Convert.ToInt32(tap.Parameter);
var shell = Shell.Current;
shell.CurrentItem.CurrentItem = shell.CurrentItem.Items[newIndex];
}
}

int selectedIndex = -1;
public int SelectedIndex
{
get { return selectedIndex; }
set
{
if (selectedIndex != value)
{
selectedIndex = value;

        switch (value)
        {
            case 0: this.lay0.Children[0].IsVisible = false; break;
            case 1: this.lay1.Children[0].IsVisible = false; break;
            case 2: this.lay2.Children[0].IsVisible = false; break;
            case 3: this.lay3.Children[0].IsVisible = false; break;
        }
    }
}

}

#### 4. Add MyTab to all tab pages which shown in AppShell page
``` xml
<Grid Margin="0" RowSpacing="0">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="60" />
    </Grid.RowDefinitions>

    <local:MyTab Grid.Row="1" SelectedIndex="0"></local:MyTab>

     Your original page layout here
</Grid>

Result on Android
screenshot

@galadril
Sorry, I have a question.
How to reset BadgeCount value on android?

@galadril
Thank you for your work on providing a workaround example for shell tabbar badges.
Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

@galadril
How to reset BadgeCount value on android?

You can use MessagingCenter for this?

MessagingCenter.Send(this, App.MessageNameRemoveBadge);

Then in the SetAppearance :
MessagingCenter.Subscribe<MainPage>(this, App.MessageNameRemoveBadge, async app => await ClearBadgeAsync());

@galadril
Thank you for your work on providing a workaround example for shell tabbar badges.
Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

FYI as a workaround, I've managed to get the badges working on iOS and Android on tabs / shells via renderers. Maybe you can use some of this code for yourself..

Android

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.Droid.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer(Context context) : base(context)
      { }

      protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
      {
         return new CustomShellBottomAppearance(this);
      }
   }
}

Then the CustomShellBottomAppearance.cs:

namespace Xamarin.Droid.Renderers
{
   internal class CustomShellBottomAppearance : IShellBottomNavViewAppearanceTracker
   {
      public CustomShellBottomAppearance(CustomShellRenderer shellRenderer)
      { }

      public void Dispose()
      { }

      public void ResetAppearance(BottomNavigationView bottomView)
      { }

      public async void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
      {
         bottomView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilitySelected;
         var _bottomNavigationMenuView = (BottomNavigationMenuView)bottomView.GetChildAt(0);

         var badge = await MainViewModel.CheckNewEventsAsync();
         CreatePageBadge(2, badge > 0, badge, _bottomNavigationMenuView);
      }

      private void CreatePageBadge(int index, bool ShowBadge, int BadgeCount, BottomNavigationMenuView _bottomNavigationMenuView)
      {
         var itemView = (BottomNavigationItemView)_bottomNavigationMenuView.GetChildAt(index);
         if (ShowBadge && BadgeCount > 0)
         {
            var mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            if (mtxtnotificationsbadge == null)
            {
               var vBadge = LayoutInflater.From(CrossCurrentActivity.Current.Activity).Inflate(Resource.Layout.notification_badge, _bottomNavigationMenuView, false);
               itemView.AddView(vBadge);
               mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            }
            mtxtnotificationsbadge.Text = BadgeCount.ToString();
            if (mtxtnotificationsbadge.Text.Length == 1)
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_circle_shape);
            else
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_rectangle_shape);
         }
         else
         {
            var badgeLayout = itemView.FindViewById<FrameLayout>(Resource.Id.badge);
            if (badgeLayout != null)
               itemView.RemoveView(badgeLayout);
         }
      }
   }
}

then under resource/layout (notification_badge.axml):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/badge"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/txtbadge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:gravity="center"
        android:padding="3dp"
        android:textColor="@color/white"
        android:textSize="11sp" />
</FrameLayout>

custom_rectangle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

custom_circle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

iOS

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.iOS.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer() : base()
      { }

      protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
      {
         return new CustomShellBottomAppearance();
      }
   }
}

And then the CustomShellBottomAppearance.cs:

namespace Xamarin.iOS.Renderers
{
   public class CustomShellBottomAppearance : IShellTabBarAppearanceTracker
   {
      public void Dispose()
      { }

      public void ResetAppearance(UITabBarController controller)
      { }

      public async void SetAppearance(UITabBarController controller, ShellAppearance appearance)
      {
         if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any())
         {
            var badge = await MainViewModel.CheckNewEventsAsync();
            if (badge > 0)
               controller.TabBar.Items[2].BadgeValue = badge.ToString();
         }
      }
      public void UpdateLayout(UITabBarController controller)
      { }
   }
}

result on iOS:
image

Brother how can i set the badge in my class library main project? I have copied all this code..All my project backend code is in main library project..Now i want to set value of badge and show it on tab..How can i do this in mainpage.xaml file?

@galadril
Thank you for your work on providing a workaround example for shell tabbar badges.
Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

Very true @galadril there is no XAML referenced in your iOS platform's CustomShellBottomAppearance.cs's SetAppearance method but where we assign the badge value:聽 "controller.TabBar.Items[2].BadgeValue = badge.ToString();". We are setting the value to a badge which I assume is either created on the client XAML side or in code for the iOS implementation of the Shell's tabbar. I am curious to see how you added the badges for iOS to the tabbar. Thank you in advance for your help.

At this moment I have a public static method that returns an int with the badge value:
var badge = await MainViewModel.CheckNewEventsAsync();

But you can easily use MessagingCenter for this, to subscribe for a new badge value..
Its up to you.

I have noticed that SetAppearance is called twice.

I for example, use a static variable and then in the notifications tab, I do my logic.

    protected override async void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);

        if (Current?.CurrentItem == null) return;

        var s = args.Target.Location.ToString();

        if (s.Contains("notificationsTab"))
        {
           //do something
        }
    }

@galadril
Thank you for your work on providing a workaround example for shell tabbar badges.
Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

Very true @galadril there is no XAML referenced in your iOS platform's CustomShellBottomAppearance.cs's SetAppearance method but where we assign the badge value:聽 "controller.TabBar.Items[2].BadgeValue = badge.ToString();". We are setting the value to a badge which I assume is either created on the client XAML side or in code for the iOS implementation of the Shell's tabbar. I am curious to see how you added the badges for iOS to the tabbar. Thank you in advance for your help.

Hi @tawrey , I have the same question. Did you find how the XAML is?

At this moment I have a public static method that returns an int with the badge value:
var badge = await MainViewModel.CheckNewEventsAsync();

But you can easily use MessagingCenter for this, to subscribe for a new badge value..
Its up to you.

@galadril Do you have a repository in witch i can see your complete solution?

I would like to see how do you create your shell xaml.

So this is still not in the new version that came out.. 4.8.0..?

So this is still not in the new version that came out.. 4.8.0..?

at the moment there's no version planned, they removed it from .NET MAUI too....

@samhouts why this simple functionality isn't merged yet? We have 3 workarounds here but still without official support form Xamarin. Fourth workaround could be to stick bottmbar contentview (with badges) to pages and hide shell's bottombar and still using magic of shell's navigation.
Maybe at least allow to write own bottombar contentview (or at least bottombar's buttons as contentview) in place of shell's bottombar (without badges).

@galadril
Thank you for your work on providing a workaround example for shell tabbar badges.
Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

Very true @galadril there is no XAML referenced in your iOS platform's CustomShellBottomAppearance.cs's SetAppearance method but where we assign the badge value:聽 "controller.TabBar.Items[2].BadgeValue = badge.ToString();". We are setting the value to a badge which I assume is either created on the client XAML side or in code for the iOS implementation of the Shell's tabbar. I am curious to see how you added the badges for iOS to the tabbar. Thank you in advance for your help.

Hi @tawrey , I have the same question. Did you find how the XAML is?

No, I did not come up with a XAML to handle the iOS workaround version.
Can anyone else provide a working example please?

Work is still ongoing on the associated PR. Thanks for your patience!

Any updates when can we expect this feature on Xamarin Forms Version ?

I've quickly setup a NuGet package / Plugin library to ease the implementation for a badge on a bottom bar for you guys:
https://github.com/galadril/Xam.Shell.Badge

( @angelru @jmelendez025 @tawrey @kamranmasud @tawreyhap @thuyetpv @dan5602 )

You can use the nuget package, or look at the code on how to do it yourself

FIXED, Looks like this is the reason it doesn't display https://github.com/xamarin/Xamarin.Forms/pull/10063. When default color is not specified, it goes to white. You have to basically replicate code in SetAppearance and ResetAppearance when overriding the renderer on Android.

Also, you can also use platform specific Dependency injection to control the badge label. Alternative for using messaging.

@galadril iOS seems to be working without issues, thanks!

Although, I am having an issue with Android. Looks like the Shell.TabBarTitleColor is not getting set when overriding the Renderer. The selected tab color is not set. I don't think any of the shell tab color setting are applied on Android. Any idea?

Do we have to persist IShellAppearanceElement from SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)?

Droid_Tabbar1
Droid_Tabbar2

+1 when...

I've quickly setup a NuGet package / Plugin library to ease the implementation for a badge on a bottom bar for you guys:
https://github.com/galadril/Xam.Shell.Badge

( @angelru @jmelendez025 @tawrey @kamranmasud @tawreyhap @thuyetpv @dan5602 )

You can use the nuget package, or look at the code on how to do it yourself

Not working for me. Nothing happens

I've quickly setup a NuGet package / Plugin library to ease the implementation for a badge on a bottom bar for you guys:
https://github.com/galadril/Xam.Shell.Badge
( @angelru @jmelendez025 @tawrey @kamranmasud @tawreyhap @thuyetpv @dan5602 )
You can use the nuget package, or look at the code on how to do it yourself

Not working for me. Nothing happens

Hello @alekssam, library has been updated, please, try again with new version. Also, please read repository readme.md info, as there are some breaking changes.

Although, I am having an issue with Android. Looks like the Shell.TabBarTitleColor is not getting set when overriding the Renderer. The selected tab color is not set. I don't think any of the shell tab color setting are applied on Android. Any idea?

@Deepfreezed > There is a new version, please check that one. (v1.0.8)

Was this page helpful?
0 / 5 - 0 ratings