Change the Statusbar color from Xamarin.Forms

Do you want to change the default statusbar color and do it from Xamarin.Forms? Lets explore that here...

Change the Statusbar color from Xamarin.Forms

I set out to change the top-bar-menu on a Android/iOS device and discovered that I didn't have a clue what it was called... I even tried 'toolbar' and some other ones before I found out it is called Statusbar.

To set the record straight here are the two topmost bars
TheStatusBar

And then the internet had like 1000 versions of how to do it but I didn't find any where I could set the color in one place. Being away from actively coding in Xamarin.Forms for few years had made me forget allot of the basics.

It's slowly coming back to me and when it does I really want to share with you my problems and solutions to them.

Two solutions

You can either just hard code the colors into each platform or you can inject them there and have full control in your Xamarin Forms project.

Lets take a look at both solutions.

Hard-coded for Android

Add the following line

    <item name="android:statusBarColor">#544054</item>

into the styles.xml file in your Android project so it looks like this

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">  
     <item name="android:statusBarColor">#544054</item>  
  </style>
</resources>

Hard-coded for iOS

I go this code from Wei Loon Wong at StackOverflow. This is the most current answer I could find. If I find something better I will update it.

Add the following to your Info.plist (more on that in the docs here)

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>

note that you can hide your statusbar also with this

<key>UIStatusBarHidden</key>
<true/>

But you could also show the statusbar every where except where you want to skip it by using this code

<ContentPage ...
   xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
   ios:Page.PrefersStatusBarHidden="True"
   ios:Page.PreferredStatusBarUpdateAnimation="Fade">
  ...
</ContentPage>

Add the following to your FinishedLaunching() in your AppDelegate.cs

 UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
 if (statusBar != null && statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
 {
    // change to your desired color 
    statusBar.BackgroundColor = Color.FromHex("#7f6550").ToUIColor(); 
 }

NB. I got an error running this on iOS but I don't know what one because I build through the Azure devops/Appcenter.

The error went away when I registered for a global exception in my AppDelegate.cs

 public override bool FinishedLaunching(UIApplication app, NSDictionary options)
 {
    GlobalExceptionHelper.RegisterForGlobalExceptionHandling();
 }    

with this code

 public static class GlobalExceptionHelper
 {
   public static void RegisterForGlobalExceptionHandling()
   {
     AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
     {
        var newExc = new System.Exception("CurrentDomainOnUnhandledException",
        args.ExceptionObject as System.Exception);
        Crashes.TrackError(newExc);
     };

     TaskScheduler.UnobservedTaskException += (sender, args) =>
     {
        var newExc = new  ApplicationException(
            "TaskSchedulerOnUnobservedTaskException",
            args.Exception);
        Crashes.TrackError(newExc);
     };
   }
 }

The exception isn't shown in my Diagnostics view in AppCenter, maybe because I have to upload symbols? But I don´t have an XCode...

I will try to generate another crash with

 Crashes.GenerateTestCrash();

to see if I get that exception showing up in AppCenter or if there was something else causing the error and I somehow fixed it... hummm...

UPDATE on error: It just took sometime for the error to show up and the eror was totally un-related to this code.

Setting the color from the XF project

What I had forgot about was using the DependencyService to access native functionality from my Forms project. I have done this many times but long time ago last time.

Setup Forms to android communication

Create this interface in your Forms project

 public interface IStatusBarPlatformSpecific
 {
    void SetStatusBarColor(Color color);
 }
 

Then create this class in your Android project

 [assembly: Dependency(typeof(MyDemo.Droid.CustomRenderers.Statusbar))]
 namespace MyDemo.Droid.CustomRenderers
 {
  public class Statusbar : IStatusBarPlatformSpecific
  {
    public Statusbar()
    {
    }

    public void SetStatusBarColor(Color color)
    {
        // your color manipulating code here!
        // scroll to get my statusbar color chaning code
     }
  }
 }

Now you can call this code in your Forms project like this

 var statusbar = DependencyService.Get<IStatusBarPlatformSpecific>();
 statusbar.SetStatusBarColor(Color.Green);
 

Add color changing code to android

I found all kinds of obsolete code (and wrong code) out there on the internet that was supposed to get the current Activity in the Android project so I could get to the Windows.SetStatusBarColor(color).

Code like this and more

  // doesn't work
 var currentAct = ((Activity)Android.App.Application.Context);
 
 // doesn't work
 var currentAct = ((Activity)Android.App.Application.Context.ApplicationContext);

 // obsolete
 var currentAct = ((Activity)Forms.Context);
 
 // etc. etc

But when I thought I had tried just about everything I found this CurrentActivityPlugin from James Montemagno where he uses some "super undocumented google api to get the current activity"

With that I can do it like this here.

 // The SetStatusBarcolor is new since API 21
 if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
 {
    var androidColor = color.AddLuminosity(-0.1).ToAndroid();
    //Use the plugin
    CrossCurrentActivity.Current.Activity.Window.SetStatusBarColor(androidColor);
 }
 else
 {
     // Here you will just have to set your color in styles.xml file as above.
 }
 

I would love if this was baked into Xamarin.Forms. Maybe it is but I didn't find it.

Add color changing code to iOS

Just create everything like you did for the android project and swap out the code in your SetStatusBarColor() with this code

 public void SetStatusBarColor(Color color)
 {
    UIView statusBar = UIApplication.SharedApplication.ValueForKey(
    new NSString("statusBar")) as UIView;
    if (statusBar != null && statusBar.RespondsToSelector(
    new ObjCRuntime.Selector("setBackgroundColor:")))
    {
        statusBar.BackgroundColor = color.ToUIColor();
    }
 }

Add the color globally to all pages?

Just for completeness sake I'll tell you how you set the color everywhere in your app.

Just make all your pages or ViewModels (if your are using MVVM) inherit from a base page/viewModel and in that constructor call the DependencyService code.

This would be your ViewModel

 public class LoginPageViewModel : ViewModelBase
 {
   public LoginPageViewModel() : base()
   {}
 }
 

And this your base class

 public class ViewModelBase
 {
    public ViewModelBase()
    {
        var statusbar = DependencyService.Get<IStatusBarPlatformSpecific>();
        statusbar.SetStatusBarColor(Color.FromHex("544054"));
    }
 }

So

I hope this will help somebody and not be another useless/obsolete/wrong information on this subject on the internet. Like always hit me up on Twitter if you want to discuss this with me.