githubxiaoa / Xamarin.Forms.StateSquid

Spiritual successor to EasyLoading, turn any layout element into an individual state-aware element.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Moved to the Xamarin Community Toolkit

This project has been archived, as it has been integrated into the Xamarin Community Toolkit. From there it can be maintained better by an evergrowing community of awesome contributors. The NuGet package itself is also deprecated from this point onwards. Please migrate over to the Xamarin Community Toolkit version when available.


Xamarin.Forms.StateSquid

A collection of attached properties that let you specify state views for any of your existing layouts.

Build Status

Why StateSquid?

Displaying items when your app is in a specific state is a common pattern throughout any mobile app. People create loading views to overlay on an entire screen or maybe just a subsection of your screen needs an individual loader. When there's no data to display we create empty state views or when something goes wrong we need to build an error state view. By implementing the StateLayout's attached properties you can turn any layout element like a Grid or StackLayout into an individual state-aware element! StateSquid will take care of when to display which view.

Getting started

The project is up on NuGet at the following URL:

https://www.nuget.org/packages/Xamarin.Forms.StateSquid

Install this package into your shared project. There is no need to install it in your platform specific projects. After that you're good to go! Simply add the namespace declaration and the new StateLayout related attached properties should be available to you!

Adding a simple loading state

Each layout that you make state-aware using the StateLayout attached properties contains a collection of StateView objects. These can be used as templates for the different states supported by StateLayout. Whenever the CurrentState is set to a value that matches the State property of one of the StateViews its contents will be displayed instead of the main content.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
   xmlns:state="clr-namespace:Xamarin.Forms.StateSquid;assembly=Xamarin.Forms.StateSquid" 
   x:Class="SampleApp.MainPage">

   <Grid state:StateLayout.CurrentState="{Binding CurrentState}">
      <state:StateLayout.StateViews>
         <state:StateView StateKey="Loading">
            <Grid BackgroundColor="White">
               <StackLayout VerticalOptions="Center" HorizontalOptions="Center">
                  <ActivityIndicator Color="#1abc9c" IsRunning="{Binding CurrentState, Converter={StaticResource StateToBooleanConverter}, ConverterParameter={x:Static state:State.Loading}}" />
                  <Label Text="Loading..." HorizontalOptions="Center" />
               </StackLayout>
            </Grid>
         </state:StateView>
      </state:StateLayout.StateViews>      
  
     ...
     
  </Grid>
  
</ContentPage>

The State property supports one of the following values:

  • Loading
  • Saving
  • Success
  • Error
  • Empty
  • Custom
  • None (this will show the default view).

Using custom states

Besides the built-in state StateSquid also supports a Custom state. By setting State="Custom" and CustomStateKey="[yourvalue]" you can create custom states beyond the built-in ones. You can use the CurrentCustomStateKey on your root StateLayout element to databind a variable that indicates when to show one of your custom states.

 <StackLayout Padding="10" state:StateLayout.CurrentState="{Binding CurrentState}" state:StateLayout.CurrentCustomStateKey="{Binding CustomState}" BackgroundColor="#f0f1f2">
   <state:StateLayout.StateViews>
      <state:StateView StateKey="Custom" CustomStateKey="ThisIsCustomHi">
         <Label Text="Hi, I'm a custom state!" VerticalOptions="Center" VerticalTextAlignment="Center" HorizontalOptions="Center" HorizontalTextAlignment="Center"  />
      </state:StateView>
      <state:StateView StateKey="Custom" CustomStateKey="ThisIsCustomToo">
         <Label Text="Hi, I'm a custom state too!" VerticalOptions="Center" VerticalTextAlignment="Center" HorizontalOptions="Center" HorizontalTextAlignment="Center"  />
      </state:StateView>
   </state:StateLayout.StateViews>
   <Label Text="This is the normal state." VerticalOptions="Center" VerticalTextAlignment="Center" HorizontalOptions="Center" HorizontalTextAlignment="Center" />
</StackLayout>

Skeleton loading

Skeleton screens in different shapes and sizes are found everywhere across the web and apps — anywhere us humans are forced to wait. They are usually perceived as being shorter in duration when compared against a blank screen and a spinner.

To mitigate focus on the loading process, versus the actual content that is loading, Luke Wroblewski introduced a new design pattern — the skeleton screen. In his own words, they are "essentially a blank version of a page into which information is gradually loaded." These visual placeholders were shown by Wroblewski to be light grey boxes that appeared instantly in areas where content had not yet completed loading.

StateSquid supports adding a skeleton loader like appearance using a SkeletonView. This view is automatically animated to show a slight blinking state animation. You can see it in action in the image below. Simply add it to your loading template and size it accordingly. You can even use CornerRadius to make rounded variations of it.

Repeating a template multiple times

When loading multiple items of the same type it could be benificial if we can simply repeat a piece of XAML without having to copy paste it multiple times. This is where RepeatCount comes into play. By defining a RepeatTemplate we can repeat the same bit of XAML but only define it once. A sample of this process can be seen here.

<StackLayout state:StateLayout.CurrentState="{Binding CurrentState}" Grid.Row="0">
     <state:StateLayout.StateViews>
        <state:StateView StateKey="Loading" RepeatCount="3">
            <state:StateView.RepeatTemplate>
                <DataTemplate>
                    <Grid Padding="20" HeightRequest="120">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="24" />
                            <RowDefinition Height="20" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="14" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <state:SkeletonView CornerRadius="6" Grid.Row="0" Grid.ColumnSpan="2" HeightRequest="20" BackgroundColor="#cccccc" WidthRequest="120" />
                        <state:SkeletonView CornerRadius="6" Grid.Row="1" Grid.ColumnSpan="3" HeightRequest="20" BackgroundColor="#cccccc" WidthRequest="200" />
                        <BoxView Grid.Row="2" Grid.ColumnSpan="3" HeightRequest="1" BackgroundColor="#cccccc" Margin="0,8" />
                        <Label Grid.Row="3" Grid.Column="0" HorizontalOptions="Center" Text="TOTAL ARTICLES" FontSize="10" TextColor="Gray" />
                        <Label Grid.Row="3" Grid.Column="1" HorizontalOptions="Center" Text="PRICE" FontSize="10" TextColor="Gray" />
                        <Label Grid.Row="3" Grid.Column="2" HorizontalOptions="Center" Text="BOXES" FontSize="10" TextColor="Gray" />
                        <state:SkeletonView CornerRadius="6" Grid.Row="4" Grid.Column="0" HeightRequest="20" BackgroundColor="#cccccc" HorizontalOptions="Fill" />
                        <state:SkeletonView CornerRadius="6" Grid.Row="4" Grid.Column="1" HeightRequest="20" BackgroundColor="#cccccc" HorizontalOptions="Fill" />
                        <state:SkeletonView CornerRadius="6" Grid.Row="4" Grid.Column="2" HeightRequest="20" BackgroundColor="#cccccc" HorizontalOptions="Fill" />
                    </Grid>
                </DataTemplate>
            </state:StateView.RepeatTemplate>
        </state:StateView>
    </state:StateLayout.StateViews>
<StackLayout>

This code results in the following UI, where the template is repeated three times:

Property reference

Your imagination is the only limit to what you can do. You can use this control to recreate your layout and make a skeleton loader our of it. Or you can just use a simple Label to indicate that your application is in a specific state. The following parts are what make this thing tick:

Property Available on What it does Extra info
CurrentState StateLayout Defines the current state of the layout and which template to show. Loading, Saving, Success, Error, Empty, None, Custom
CurrentCustomStateKey StateLayout Pair this with State="Custom" on a StateView to add custom states.
StateViews StateLayout A list of StateView objects that contains a template per State.
CustomStateKey StateView Used to identify a StateView when using State="Custom"
RepeatCount StateView Repeats the specific StateView by a given amount. Ideal to use to show a list of multiple items for e.g. a skeleton loader.
RepeatTemplate StateView Defines the DataTemplate that gets repeated when using RepeatCount.
State StateView Used to identify a StateView to be shown for a specific State.

About

Spiritual successor to EasyLoading, turn any layout element into an individual state-aware element.

License:MIT License


Languages

Language:C# 100.0%