You did actually read that title correctly – we have a FloatingActionButton
to use in Xamarin.Forms that works in both Android and iOS!
I’ve put the source code up for this here: https://github.com/SuavePirate/Xamarin.Forms.Controls.FloatingActionButton
It’s rudimentary and has room for some more fun properties, but it is fully functional! If you would like to contribute to the repository, see the TODO:
list at the bottom of the README and start forking and making pull requests!
To breakdown the steps to create your own Floating Action Button in Xamarin.Forms, you’ll need:
- A custom Xamarin.Forms `Element`
- An Android Custom renderer to use the native `Android.Compat.Design.Widgets.FloatingActionButton`
- An iOS Custom renderer to create a button that looks like a FAB.
So let’s go in that order.
In Xamarin.Forms PCL
FloatingActionButton.xaml.cs
public partial class FloatingActionButton : Button
{
public static BindableProperty ButtonColorProperty = BindableProperty.Create(nameof(ButtonColor), typeof(Color), typeof(FloatingActionButton), Color.Accent);
public Color ButtonColor
{
get
{
return (Color)GetValue(ButtonColorProperty);
}
set
{
SetValue(ButtonColorProperty, value);
}
}
public FloatingActionButton()
{
InitializeComponent();
}
}
We added a new BindableProperty
for the ButtonColor
. This is done because setting the BackgroundColor
will mess up the Android renderer and apply the background behind the FAB. We want to inherit from Button
so that we can utilize some of the already useful properties that come with it – namely the Image
property that consumes a FileImageSource
. We can use this to set the icon for our FAB.
In Android
FloatingActionButtonRenderer.cs
using FAB = Android.Support.Design.Widget.FloatingActionButton;
[assembly: ExportRenderer(typeof(SuaveControls.Views.FloatingActionButton), typeof(FloatingActionButtonRenderer))]
namespace SuaveControls.FloatingActionButton.Droid.Renderers
{
public class FloatingActionButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ViewRenderer<SuaveControls.Views.FloatingActionButton, FAB>
{
protected override void OnElementChanged(ElementChangedEventArgs<SuaveControls.Views.FloatingActionButton> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
var fab = new FAB(Context);
// set the bg
fab.BackgroundTintList = ColorStateList.ValueOf(Element.ButtonColor.ToAndroid());
// set the icon
var elementImage = Element.Image;
var imageFile = elementImage?.File;
if (imageFile != null)
{
fab.SetImageDrawable(Context.Resources.GetDrawable(imageFile));
}
fab.Click += Fab_Click;
SetNativeControl(fab);
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
Control.BringToFront();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var fab = (FAB)Control;
if (e.PropertyName == nameof(Element.ButtonColor))
{
fab.BackgroundTintList = ColorStateList.ValueOf(Element.ButtonColor.ToAndroid());
}
if (e.PropertyName == nameof(Element.Image))
{
var elementImage = Element.Image;
var imageFile = elementImage?.File;
if (imageFile != null)
{
fab.SetImageDrawable(Context.Resources.GetDrawable(imageFile));
}
}
base.OnElementPropertyChanged(sender, e);
}
private void Fab_Click(object sender, EventArgs e)
{
// proxy the click to the element
((IButtonController)Element).SendClicked();
}
}
}
A few important things to point out:
- We add the additional using statement `using FAB = Android.Support.Design.Widget.FloatingActionButton;` to help us distinguish between our Xamarin.Forms element and the built in Android control.
- We are NOT using a `ButtonRenderer` as our base class, but instead using a basic `ViewRenderer`. This is because the underlying control will not be a native Android `Button`, but the native Android `FloatingActionButton`.
- Because we replace the `ButtonRenderer`, we need to make sure we still propagate click events up to the Xamarin.Forms element.
Now let’s look at iOS, which can utilize more of the built in pieces from Xamarin.Forms since it supports the BorderRadius
property on Buttons
.
In iOS
FloatingActionButtonRenderer.cs
[assembly: ExportRenderer(typeof(SuaveControls.Views.FloatingActionButton), typeof(FloatingActionButtonRenderer))]
namespace SuaveControls.FloatingActionButton.iOS.Renderers
{
[Preserve]
public class FloatingActionButtonRenderer : ButtonRenderer
{
public static void InitRenderer()
{
}
public FloatingActionButtonRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
// remove text from button and set the width/height/radius
Element.WidthRequest = 50;
Element.HeightRequest = 50;
Element.BorderRadius = 25;
Element.BorderWidth = 0;
Element.Text = null;
// set background
Control.BackgroundColor = ((SuaveControls.Views.FloatingActionButton)Element).ButtonColor.ToUIColor();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
// add shadow
Layer.ShadowRadius = 2.0f;
Layer.ShadowColor = UIColor.Black.CGColor;
Layer.ShadowOffset = new CGSize(1, 1);
Layer.ShadowOpacity = 0.80f;
Layer.ShadowPath = UIBezierPath.FromOval(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "ButtonColor")
{
Control.BackgroundColor = ((SuaveControls.Views.FloatingActionButton)Element).ButtonColor.ToUIColor();
}
}
}
}
We set an explicit WidthRequest
, HeightRequest
, and BorderRadius
to get ourselves a circle. I’m not a big fan of doing it here, since it’s better suited as a calculation, but for now it works.
Lastly in our Draw
override, we set up the drop shadow behind out button, and make sure that our ShadowPath
is actually built from an oval so that it rounds off with the Button
.
Also note that we take the ButtonColor
property and apply it as the BackgroundColor
of the UIButton
to override the color from Xamarin.Forms. Don’t forget to set Text
to null so that we can’t add text to the button and mess it up.
As a side note, iOS might try to link our your custom renderer if you are using it in an iOS Class Library. In order to avoid this, make sure to call a static InitRenderer
method in your AppDelegate.cs
as it will prevent it from being linked out.
Using the FloatingActionButton
Now that we have our renderers registered for our new Element
, we can use it in our XAML or C# of our PCL or Shared Project:
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:SuaveControls.FabExample" xmlns:controls="clr-namespace:SuaveControls.Views;assembly=SuaveControls.FloatingActionButton" x:Class="SuaveControls.FabExample.MainPage">
<StackLayout Margin="32">
<Label Text="This is a Floating Action Button!" VerticalOptions="Center" HorizontalOptions="Center"/>
<controls:FloatingActionButton x:Name="FAB" HorizontalOptions="CenterAndExpand" WidthRequest="50" HeightRequest="50" VerticalOptions="CenterAndExpand" Image="ic_add_white.png" ButtonColor="#03A9F4" Clicked="Button_Clicked"/>
</StackLayout>
</ContentPage>
and our code behind:
MainPage.xaml.cs
namespace SuaveControls.FabExample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void Button_Clicked(object sender, EventArgs e)
{
await DisplayAlert("FAB Clicked!", "Congrats on creating your FAB!", "Thanks!");
}
}
}
Then we get these results in our Android and iOS apps:
Android

iOS

If you want to just pull down the control I built on GitHub, the steps are straight forward:
- Clone the repository
- Reference the PCL in your PCL/Shared Lib
- Reference the PCL and native projects in your respective native project
- Pull the namespace into your XAML (or C#)
- Start using it!
The repository also contains an example app that references the source libraries.
If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!
Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.
Like this:
Like Loading...