Sharpnado 1.5 goes Acrylic! MaterialFrame with dynamic themes

Sharpnado 1.5 goes Acrylic! MaterialFrame with dynamic themes
Lib Version
Sharpnado.Presentation.Forms
Sharpnado.Presentation.HorizontalListView

Github: https://github.com/roubachof/Sharpnado.Presentation.Forms

Now it was kind of a undocumented feature, but since the first version of Sharpnado's lib, there is a little thing called MaterialFrame, which was at the beginning just a Frame with Elevation support.

In version 1.5, it receives:

  • Acrylic mode
  • Dynamic theming support
  • Performance improvements

Acrylic mode

It started with a tweet from @vastglad:

tweet

I found this Acrylic design really nice, and noticed right away that the effect that needed to be applied to a classic Xamarin.Forms frame wasn't so hard to implement:

Analysis

And since I already implemented dynamic modes in my MaterialFrame, I could just add a new Acrylic mode to the already existing Dark and Light modes.

I also decided to create a new repo to experiment with Acrylic effects, you can find it here:

https://github.com/roubachof/Sharpnado.Acrylic

To test my Acrylic implementation, I just recreated the exact same Layout that the one presented by @vastglad:

And here is the result:

Android iOS

MaterialFrame

Existing properties

MaterialTheme

It has 3 possible values:

  • Light
  • Dark
  • Acrylic

In light theme, you can set the LightThemeBackgroundColor and control the Elevation.

In dark theme, you can only control the Elevation, more elevation equals more light on the black frame (see below).

In Acrylic theme, you can still set the LightThemeBackgroundColor, also a Color of F1F1F1 is advised to have a good Acrylic effect.

LightThemeBackgroundColor

The background color in Light and Acrylic themes. In Dark theme, this value is ignore because the background color depends on the Elevation.
Note that setting the BackgroundColor property has no effect with the MaterialFrame.

Elevation

This property semantic changes according to the theme currently set:

Light Theme

Cast a shadow according to Google's Material elevation specs.

Dark Theme

Change the frame's background color according to Google's dark mode specs:

Acrylic Theme

Property is ignored and a custom shadow is applied.

CornerRadius

Same as the Xamarin.Forms Frame here.

Changing theme for every frames

You either use DynamicResource as explained in my previous post.

Or use the static method called ChangeGlobalTheme(Theme newTheme). Setting a new theme on this method will change the MaterialTheme of every MaterialFrame of your app.

Examples of styles

Acrylic style

From Sharpnado.Acrylic github repo, MaterialFrame.xaml file:

<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:rv="clr-namespace:Sharpnado.Presentation.Forms.RenderedViews;assembly=Sharpnado.Presentation.Forms">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Colors.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="rv:MaterialFrame">
        <Setter Property="MaterialTheme" Value="Acrylic" />
        <Setter Property="Margin" Value="5, 5, 5, 10" />
        <Setter Property="Padding" Value="20,15" />
        <Setter Property="CornerRadius" Value="10" />
        <Setter Property="LightThemeBackgroundColor" Value="{StaticResource AcrylicFrameBackgroundColor}" />
    </Style>

</ResourceDictionary>

Color.xaml file:

<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>

<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

    <Color x:Key="AcrylicSurface">#E6E6E6</Color>

    <Color x:Key="AcrylicFrameBackgroundColor">#F1F1F1</Color>

    <Color x:Key="AccentColor">#00E000</Color>

    <Color x:Key="PrimaryColor">Black</Color>
    <Color x:Key="SecondaryColor">#60000000</Color>
    <Color x:Key="TernaryColor">#30000000</Color>

    <Color x:Key="TextPrimaryColor">Black</Color>
    <Color x:Key="TextSecondaryColor">#60000000</Color>
    <Color x:Key="TextTernaryColor">#40000000</Color>

</ResourceDictionary>

Dynamic styles

From the Silly App! github repository.

MaterialFrame xaml:

    <renderedViews:MaterialFrame
        Margin="0,16"
        Padding="16,10"
        Elevation="4"
        LightThemeBackgroundColor="{DynamicResource DynamicLightThemeColor}"
        CornerRadius="{DynamicResource DynamicCornerRadius}">

Styles:

    <Color x:Key="DarkSurface">#121212</Color>
    <Color x:Key="LightSurface">#02FF0266</Color>
    <Color x:Key="AcrylicSurface">#E4E4E4</Color>

    <Color x:Key="OnSurfaceColor">#FFFFFF</Color>
    <Color x:Key="AcrylicFrameBackgroundColor">#F1F1F1</Color>

Theme switching code:

    public static void SetDarkMode()
    {
        MaterialFrame.ChangeGlobalTheme(MaterialFrame.Theme.Dark);

        SetDynamicResource(DynamicBackgroundColor, "DarkSurface");
        SetDynamicResource(DynamicCornerRadius, 5);
    }

    public static void SetLightMode(bool isAcrylic)
    {
        MaterialFrame.ChangeGlobalTheme(isAcrylic ? MaterialFrame.Theme.Acrylic : MaterialFrame.Theme.Light);

        SetDynamicResource(DynamicBackgroundColor, isAcrylic ? "AcrylicSurface" : "LightSurface");
        SetDynamicResource(DynamicLightThemeColor, isAcrylic ? "AcrylicFrameBackgroundColor" : "OnSurfaceColor");
        SetDynamicResource(DynamicCornerRadius, isAcrylic ? 10 : 5);
    }

Dynamic themes

Performance

To achieve the nice white glow effect, the first idea was to use two Xamarin.Forms Frame stacked. The first one white, and the second one on top painted with the LightThemeBackgroundColor.
This was quite hacky and not really stable (for example assigning BindableProperty inside of the object embedding those properties break the property changed events in the renderers).

I then plan to use 2 Frame on Android and 2 UIViews on iOS on the respective renderers.
But I didn't like the idea of stacking 2 views, that didn't seem a good tradeoff since now I was moving to the renderers world...

I finally find a way to have a unique view in each of the renderer:

  1. LayerDrawable on Android
  2. CALayer on iOS

Doing that only one view is used and only the background changes thanks to these lightweight objects.