6

I want to change the status bar color on some views.

I found this solution https://github.com/yuv4ik/XFDynamicStatusBarAppearance

but it's working only with NavigationPage.

I don't need from my app Navigation Page ...

    public App()
    {
        InitializeComponent();

        MainPage = new MainPage();
    }

Here is my try ...

    var statusBarStyleManager = DependencyService.Get<IStatusBarStyleManager>();

    MainCarousel.PositionChanged += (sender, e) =>
    {
        switch (e.CurrentPosition)
        {
            case 1:
                Device.BeginInvokeOnMainThread(() =>
                {
                    Xamarin.Forms.Application.Current.MainPage.SetValue(Xamarin.Forms.NavigationPage.BarBackgroundColorProperty, Color.DarkCyan);

                    //((Xamarin.Forms.NavigationPage)Xamarin.Forms.Application.Current.MainPage).BarBackgroundColor = Color.DarkCyan;
                    statusBarStyleManager.SetDarkTheme();
                });
                break;
            case 0:
            case 2:
                Device.BeginInvokeOnMainThread(() =>
                {
                    Xamarin.Forms.Application.Current.MainPage.SetValue(Xamarin.Forms.NavigationPage.BarBackgroundColorProperty, Color.LightGreen);
                    statusBarStyleManager.SetLightTheme();
                });
                break;
            default:
                break;
        }
    };

How can I change the status bar color ?

Cfun
  • 8,442
  • 4
  • 30
  • 62
G.Mich
  • 1,607
  • 2
  • 24
  • 42
  • 1
    I do not think you can do this with pure Xamarin.Forms. As @Cherry Bu - MSFT said, you will have to use the DependencyService, and implement it separately in Android and iOS. An iOS solution can be found here: https://forums.xamarin.com/discussion/89840/change-status-bar-color-on-ios . If you have problems implementing this, let us know :) – deczaloth Oct 18 '19 at 09:44
  • Thanks @Julipan , this link is very helpful !! – G.Mich Oct 18 '19 at 17:56

4 Answers4

14

Here is my working solution on both platforms ...

public interface IStatusBarStyleManager
{
    void SetColoredStatusBar(string hexColor);
    void SetWhiteStatusBar();
}

Setup the Status bar color with this line

DependencyService.Get<IStatusBarStyleManager>().SetColoredStatusBar("#2196F3");

or you can keep it white with black font color

DependencyService.Get<IStatusBarStyleManager>().SetWhiteStatusBar();

Android

[assembly: Xamarin.Forms.Dependency(typeof(StatusBarStyleManager))]
namespace ShaXam.Droid.DependencyServices
{
    public class StatusBarStyleManager : IStatusBarStyleManager
    {
        public void SetColoredStatusBar(string hexColor)
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    var currentWindow = GetCurrentWindow();
                    currentWindow.DecorView.SystemUiVisibility = 0;
                    currentWindow.SetStatusBarColor(Android.Graphics.Color.ParseColor(hexColor);
                });
            }
        }

        public void SetWhiteStatusBar()
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    var currentWindow = GetCurrentWindow();
                    currentWindow.DecorView.SystemUiVisibility = (StatusBarVisibility)SystemUiFlags.LightStatusBar;
                    currentWindow.SetStatusBarColor(Android.Graphics.Color.White);
                });
            }
        }

        Window GetCurrentWindow()
        {
            var window = CrossCurrentActivity.Current.Activity.Window;

            // clear FLAG_TRANSLUCENT_STATUS flag:
            window.ClearFlags(WindowManagerFlags.TranslucentStatus);

            // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
            window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);

            return window;
        }
    }
}

iOS

[assembly: Dependency(typeof(StatusBarStyleManager))]
namespace ShaXam.iOS.DependencyServices
{
    public class StatusBarStyleManager : IStatusBarStyleManager
    {
        public void SetColoredStatusBar(string hexColor)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
                if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
                {
                    statusBar.BackgroundColor = Color.FromHex(hexColor).ToUIColor();
                }
                UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, false);
                GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
            });
        }

        public void SetWhiteStatusBar()
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
                if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
                {
                    statusBar.BackgroundColor = UIColor.White;
                }
                UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.Default, false);
                GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
            });
        }

        UIViewController GetCurrentViewController()
        {
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            while (vc.PresentedViewController != null)
                vc = vc.PresentedViewController;
            return vc;
        }
    }
}

POST UPDATED TO SUPPORT THE IOS 13

[assembly: Dependency(typeof(StatusBarStyleManager))]
namespace ShaXam.iOS.DependencyServices
{
    public class StatusBarStyleManager : IStatusBarStyleManager
    {
        public void SetColoredStatusBar(string hexColor)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
                {
                    UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
                    statusBar.BackgroundColor = Color.FromHex(hexColor).ToUIColor();
                    UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
                }
                else
                {
                    UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
                    if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
                    {
                        statusBar.BackgroundColor = Color.FromHex(hexColor).ToUIColor();
                    }
                }
                UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, false);
                GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
            });
        }

        public void SetWhiteStatusBar()
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
                {
                    UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
                    statusBar.BackgroundColor = UIColor.White;
                    UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
                }
                else
                {
                    UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
                    if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
                    {
                        statusBar.BackgroundColor = UIColor.White;
                    }
                }
                UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.DarkContent, false);
                GetCurrentViewController().SetNeedsStatusBarAppearanceUpdate();
            });
        }

        UIViewController GetCurrentViewController()
        {
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            while (vc.PresentedViewController != null)
                vc = vc.PresentedViewController;
            return vc;
        }
    }
}

The full working sample is here https://github.com/georgemichailou/ShaXam

G.Mich
  • 1,607
  • 2
  • 24
  • 42
  • SystemUiVisibility is obsolete in Android 11. Use this for API 30+: WindowInsetsControllerAppearance lightStatusBars = isLight ? WindowInsetsControllerAppearance.LightStatusBars : 0; currentWindow.InsetsController?.SetSystemBarsAppearance((int)lightStatusBars, (int)lightStatusBars); – maxc137 Oct 05 '20 at 12:34
  • Hi @G.Mich your solution works well for Android, but this doesn't longer work for iOS, for devices that manage the `DarkMode`. When `DarkMode`is activated, the background of the `StatusBar` is well white, but the text/informations of the `StatusBar`are also white: so they are hidden... – Gold.strike Dec 04 '20 at 13:31
  • Hi @Gold.strike, check my updated code! Thanks !! – G.Mich Dec 04 '20 at 13:52
  • Thanks for your feedback @G.Mich, but I still have the same behavior. I've also tested you ShaXam sample and I encounter the same issue: when the DarkMode is activated, the text of the StatusBar is not visible. – Gold.strike Dec 04 '20 at 14:15
  • 1
    @Gold.strike thanks, the dark mode make some conflicts here... i try to fix this. – G.Mich Dec 06 '20 at 17:06
  • 1
    @Gold.strike i fixed it. https://github.com/georgemichailou/ShaXam/commit/b93e1f733e72984338ce14a8b0cbbc418d1c0ec4 – G.Mich Dec 06 '20 at 17:28
  • You're right, it works fine now! Thank you! – Gold.strike Dec 07 '20 at 18:21
  • `GetCurrentViewController` also shouldn't use `KeyWindow` on iOS 13 – maxc137 Apr 15 '21 at 20:43
  • HI there, this doesn't seem to work for a flyout detail page. Would you know how to amend? – ledragon Sep 28 '21 at 10:44
5

Answer from @Cherry Bu - MSFT is great, but a bit outdated. SystemUiVisibility is obsolete in Android 11, so here is updated StatusBarStyleManager compatible with new API:

using Android.Graphics;
using Android.OS;
using Android.Views;
using Plugin.CurrentActivity;
using Xamarin.Essentials;

[assembly: Xamarin.Forms.Dependency(typeof(StatusBarStyleManager))]
namespace YourApp.Droid.Dependences
{
    public class StatusBarStyleManager : IStatusBarStyleManager
    {
        public void SetColoredStatusBar(string hexColor)
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.M)
            {
                return;
            }

            MainThread.BeginInvokeOnMainThread(() =>
            {
                var currentWindow = GetCurrentWindow();
                SetStatusBarIsLight(currentWindow, false);
                currentWindow.SetStatusBarColor(Color.ParseColor(hexColor));
                currentWindow.SetNavigationBarColor(Color.ParseColor(hexColor));
            });
        }

        public void SetWhiteStatusBar()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.M)
            {
                return;
            }

            MainThread.BeginInvokeOnMainThread(() =>
            {
                var currentWindow = GetCurrentWindow();
                SetStatusBarIsLight(currentWindow, true);
                currentWindow.SetStatusBarColor(Color.White);
                currentWindow.SetNavigationBarColor(Color.White);
            });
        }

        private static void SetStatusBarIsLight(Window currentWindow, bool isLight)
        {
            if ((int)Build.VERSION.SdkInt < 30)
            {
#pragma warning disable CS0618 // Type or member is obsolete. Using new API for Sdk 30+
                currentWindow.DecorView.SystemUiVisibility = isLight ? (StatusBarVisibility)(SystemUiFlags.LightStatusBar) : 0;
#pragma warning restore CS0618 // Type or member is obsolete
            }
            else
            {
                var lightStatusBars = isLight ? WindowInsetsControllerAppearance.LightStatusBars : 0;
                currentWindow.InsetsController?.SetSystemBarsAppearance((int)lightStatusBars, (int)lightStatusBars);
            }
        }

        private Window GetCurrentWindow()
        {
            Window window = Platform.CurrentActivity.Window;
            window.ClearFlags(WindowManagerFlags.TranslucentStatus);
            window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
            return window;
        }
    }
}

Side note: if you also want to change color of status bar, here are lines that you need to change:

  • currentWindow.DecorView.SystemUiVisibility = isLight ? (StatusBarVisibility)(SystemUiFlags.LightStatusBar | SystemUiFlags.LightNavigationBar) : 0;
  • var lightStatusBars = isLight ? WindowInsetsControllerAppearance.LightStatusBars | WindowInsetsControllerAppearance.LightNavigationBars : 0;

Update - Summer 2021:

This code (more or less) is now part of Xamarin.CommunityToolkit: https://github.com/xamarin/XamarinCommunityToolkit/pull/812. I'd recommend using it from there, and in case of any problems open an issue, so it's fixed for everybody.

Currently XCT targets Android 10, so there's no Android 11 related code, but when this logic will be migrated to CommunityToolkit.Maui, Android 11 logic will be added as well (both XCT and CT.MAUI work just fine on any Android version, just wanted to point out code differences)

maxc137
  • 2,291
  • 3
  • 20
  • 32
  • Best answer in 2021 since it avoids using deprecated property SystemUiVisibility – GuybrushThreepwood Feb 26 '21 at 10:46
  • HI there, this doesn't seem to work for a flyout detail page. Would you know how to amend? – ledragon Sep 28 '21 at 10:44
  • 2
    @ledragon Hi, updated the answer to indicate that this code is now part of XCT. Could you open an issue there, please? – maxc137 Sep 28 '21 at 11:08
  • In XCT, when I change the status bar of a new pushed page, it is not changed back to the original color on the original page. So for example: - On page 1, XTC:StatusBarEffect.Color="Red" - Then I push on the navigation stach to page 2 - On page 2, XTC:StatusBarEffect.Color="Blue" - Then I pop back to page 1. Collar of statusbar is not changed back to red. How can I solve that? Can you also change the color of the navigation bar in code? How? Thanks!!!! – Peter de Leeuw van Weenen Mar 21 '22 at 19:03
  • @PeterdeLeeuwvanWeenen Yes, you can do this in code during OnAppearing event. Something like [this](https://github.com/xamarin/XamarinCommunityToolkit/blob/6a8c500ce087363cff0dd75514dcdf15de6f407d/samples/XCT.Sample/ViewModels/Effects/StatusBarEffectViewModel.cs#L79) – maxc137 Mar 22 '22 at 10:42
  • @maxc137 thanks, I tested it, however the PropertyChanged event is only doing something when the property value of the BindingContext is really changed. So I first set the StatusBarColor to white, then to its original color it had before the push to the second page. I do this from the OnAppearing event. Please understand I do not test myself if the property is changed in my ModelView binded context, I always call the event like: PropertyChanged(this, new PropertyChangedEventArgs("StatusBarColor")). – Peter de Leeuw van Weenen Mar 28 '22 at 19:48
  • @maxc137 I don't like to change the color to white first and then change it back to the original color because otherwise the status bar won't go back to the original color of the first page. Can't this be any other way? – Peter de Leeuw van Weenen Mar 28 '22 at 19:49
  • @PeterdeLeeuwvanWeenen That's strange. Could you open an issue (or discussion) in XCT's github, so we could continue there? – maxc137 Mar 29 '22 at 10:15
3

You can try add this code in Android platform, OnCreate method:

 protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(savedInstanceState);

        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

        LoadApplication(new App());
       Window.SetStatusBarColor(Android.Graphics.Color.Argb(255, 0, 0, 0)); //here

    }

Please note SetStatusBarColor is only supported in API level 21 and up. Therefore, we should check for this before calling SetStatusBarColor.

if (Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)

  {
   Window.SetStatusBarColor(...);
  }

Update:

Create interface in PCL:

public  interface IStatusBarColor
{
    void changestatuscolor(string color);
}

Now, Mainactivity implement this interface.

[assembly: Dependency(typeof(demo2.Droid.MainActivity))]
 namespace demo2.Droid
 {
[Activity(Label = "demo2", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity,IStatusBarColor
{

    public static Context context;
    protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(savedInstanceState);

        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

        LoadApplication(new App());

    }



    public void changestatuscolor(string color)
    {
        if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
        {
            var c = MainActivity.context as FormsAppCompatActivity;
            c?.RunOnUiThread(() => c.Window.SetStatusBarColor(Android.Graphics.Color.ParseColor(color)));
        }

    }
    protected override void OnResume()
    {
        context = this;
        base.OnResume();
    }

}

}

In Mainpage, to change status bar color in Button click event.

 private void Changecolor_Clicked(object sender, EventArgs e)
    {
        DependencyService.Get<IStatusBarColor>().changestatuscolor(Color.Red.ToHex());
    }

enter image description here

Cherry Bu - MSFT
  • 10,160
  • 1
  • 10
  • 16
  • This is a very different for that what i am talking about. I use Xamarin.Forms and i want dynamicaly change the status color on every view. Your answer is for Xamarin.Android and the status color setup is OnCreate method..... – G.Mich Oct 18 '19 at 07:36
  • @G.Mich, you can use dependencyservice to do this, please see my update. – Cherry Bu - MSFT Oct 18 '19 at 07:59
  • @G.Mich, If my reply solved your issue, please remember to mark my reply as answer, thanks. – Cherry Bu - MSFT Oct 21 '19 at 01:13
  • I'am sorry but this answer is only for android platform. Thanks for your help anyway !!! – G.Mich Oct 21 '19 at 18:31
1

As said by @maxc137, this feature is now available in the Xamarin Community Toolkit. You just have to add the Xamarin.CommunityToolkit package to your projects and set the status bar color like this:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="Example.Views.MyPage"
         xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
         xct:StatusBarEffect.Color="Red"
         Title="My Page">

If you want to set the same color across all pages, then set a global implicit style:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
         x:Class="Example.App">
<Application.Resources>
    <ResourceDictionary>            
        <Style TargetType="ContentPage" ApplyToDerivedTypes="True">
            <Setter Property="xct:StatusBarEffect.Color" Value="Red"/>
        </Style>
    </ResourceDictionary>        
</Application.Resources>
Marcio J
  • 673
  • 7
  • 17
  • In XCT, when I change the status bar of a new pushed page, it is not changed back to the original color on the original page. So for example: - On page 1, XTC:StatusBarEffect.Color="Red" - Then I push on the navigation stach to page 2 - On page 2, XTC:StatusBarEffect.Color="Blue" - Then I pop back to page 1. Collar of statusbar is not changed back to red. How can I solve that? Can you also change the color of the navigation bar in code? How? Thanks!!!! – Peter de Leeuw van Weenen Mar 21 '22 at 19:08
  • Hi @PeterdeLeeuwvanWeenen, I stopped using XCT because after upgrading some packages, it was crashing my iOS app with the exception "System.NullReferenceException: RootViewController at Xamarin.CommunityToolkit.iOS.Effects.PlatformStatusBarEffect.UpdateStatusBarAppearance". About your case, I think would be better to set only one color for status bar across all your app pages. – Marcio J Mar 22 '22 at 13:36