Quick Tour using Xamarin Community Toolkit

Introduction

A Quick Tour in app allows us to understand what’s an application about and how the app works before using it. In this post, we are going to learn how to create a Quick Tour for the Xamarin Forms Application using the Xamarin Community Toolkit. This post is a part of Xamarin Community Toolkit - Tutorial Series. Please visit the post if you are new to Xamarin Community Toolkit.

The Xamarin Community Toolkit is a collection of reusable elements for mobile development with Xamarin.Forms, including animations, behaviors, converters, effects, and helpers. It simplifies and demonstrates common developer tasks when building iOS, Android, macOS, WPF and Universal Windows Platform (UWP) apps using Xamarin.Forms.

Xamarin Community Toolkit Popup

The Popup control allows developers to create and display popups within their applications. The Popup can be extended to return data to the invocation code. This Popup can be used just like a ContentView or ContentPage is used in the Xamarin Forms Application.

Steps

  • Step 1: Creating new Xamarin.Forms Projects.
  • Step 2: Setting up the Xamarin Community Toolkit in Xamarin.Forms .Net Standard Project.
  • Step 3: Implementation of Interactive Quick Tour using Xamarin Community Toolikit.

Step 1: Creating new Xamarin.Forms Projects

Create New Project by selecting "New Project" à "Xamarin Cross Platform App" and Click "OK".

Note: Xamarin.Forms version should be greater than 5.0.

Then select Android and iOS Platforms as shown below with Code Sharing Strategy as PCL or .Net Standard and Click OK.

Step 2: Setting up the Xamarin Community Toolkit in Xamarin.Forms .Net Standard Project.

In this step, we will see how to setup the plugin.

  • Open the Nuget Manager in the Visual Studio Solution by right click the solution and select “Manage Nuget Packages”.
  • Then select “Xamarin Community Toolkit” and check all the projects in the solution, install the plugin

Step 3: Implementation of Interactive Quick Tour using Xamarin Community Toolkit

In this step, we will see how to implement the Quick Tour for the Xamarin Forms App using Popup offered in Xamarin Community Toolkit. This control allows you to have customised popup according to the application requirement.

I have divided the quick tour implementation into 3 steps, explained one by one.

  • Each step will have a common footer that shows a Skip button, an actual Step indicator, and a Next button. I have created a BasePage that will contain a ContentView with the common part containing the above mentioned views along with the ContentView. This view is useful to display the different views on each steps.
    <?xml version="1.0" encoding="utf-8" ?>
    <toolkit:Popup
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="XamarinCommunityToolkit.BaseQuickTourPage"
        xmlns:toolkit="http://xamarin.com/schemas/2020/toolkit"
        BackgroundColor="#D6072F40"
        Color="Transparent">
        
        <toolkit:Popup.Content>
            <StackLayout Padding="0,0,0,15">
                <ContentView VerticalOptions="FillAndExpand"
                             x:Name="body"/>
                <StackLayout Orientation="Horizontal"
                             VerticalOptions="End"
                             Padding="20"
                             Grid.Row="1">
                    <Button Text="Skip"
                            x:Name="skipbtn"
                                    TextColor="White"
                                    BackgroundColor="Transparent"
                                    VerticalOptions="End"
                                    Command="{Binding SkipCommand}"
                            Clicked="SkipButtonClicked"/>
    
                    <Label x:Name="stepslbl"
                                     HorizontalOptions="CenterAndExpand"
                           VerticalOptions="Center"
                                     TextColor="White">
                        <Label.FormattedText>
                            <FormattedString>
                                <Span Text="{Binding ActualStep}" x:Name="actual"/>
                                <Span Text="/" />
                                <Span Text="{Binding TotalSteps}" x:Name="total"/>
                            </FormattedString>
                        </Label.FormattedText>
                    </Label>
    
                    <Button Text="{TemplateBinding NextButtonText}"
                            x:Name="nextbtn"
                                      BackgroundColor="White"
                                      FontAttributes="Bold"
                                      TextColor="Black"
                                      Command="{TemplateBinding NextCommand}"
                                      CommandParameter="{TemplateBinding NextPage}"
                            Clicked="NextButtonClicked"/>
                </StackLayout>
            </StackLayout>
        </toolkit:Popup.Content>
    </toolkit:Popup>
  • In the code behind, we will add the Next and Skip functions. Also will initialize them with the navigation logic, so that every time the next is executed it will remove the previous page, and navigate to the next page.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using Xamarin.CommunityToolkit.Extensions;
    using Xamarin.CommunityToolkit.UI.Views;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace XamarinCommunityToolkit
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class BaseQuickTourPage : Popup
        {
            private bool isNavigating = false;
            public BaseQuickTourPage()
            {
                InitializeComponent();
                var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
                Size size = new Size();
                size.Width = mainDisplayInfo.Width / mainDisplayInfo.Density;
                size.Height = mainDisplayInfo.Height / mainDisplayInfo.Density;
                Size = size;
            }
    
            public static readonly BindableProperty NextButtonTextProperty = BindableProperty.Create(nameof(NextButtonText), typeof(string), typeof(BaseQuickTourPage), propertyChanged: OnNextButtonTextChanged);
            public static readonly BindableProperty ActualStepProperty = BindableProperty.Create(nameof(ActualStep), typeof(int), typeof(BaseQuickTourPage), propertyChanged: OnActualStepChanged);
            public static readonly BindableProperty TotalStepsProperty = BindableProperty.Create(nameof(TotalSteps), typeof(int), typeof(BaseQuickTourPage), propertyChanged: OnTotalStepsChanged);
            public static readonly BindableProperty NextPageProperty = BindableProperty.Create(nameof(NextPage), typeof(BaseQuickTourPage), typeof(BaseQuickTourPage));
            public static readonly BindableProperty BodyContentProperty = BindableProperty.Create(nameof(BodyContent), typeof(ContentView), typeof(BaseQuickTourPage), propertyChanged: OnBodyContentChanged);
    
            private static void OnBodyContentChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ((BaseQuickTourPage)bindable).body.Content = (ContentView)newValue;
            }
    
            private static void OnTotalStepsChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ((BaseQuickTourPage)bindable).total.Text = ((int)newValue).ToString();
            }
    
            private static void OnActualStepChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ((BaseQuickTourPage)bindable).actual.Text = ((int)newValue).ToString();
            }
    
            private static void OnNextButtonTextChanged(BindableObject bindable, object oldValue, object newValue)
            {
                ((BaseQuickTourPage)bindable).nextbtn.Text = (string)newValue;
            }
    
            public ContentView BodyContent
            {
                get => (ContentView)GetValue(BodyContentProperty);
                set => SetValue(BodyContentProperty, value);
            }
    
            public int ActualStep
            {
                get => (int)GetValue(ActualStepProperty);
                set => SetValue(ActualStepProperty, value);
            }
    
            public BaseQuickTourPage NextPage
            {
                get => (BaseQuickTourPage)GetValue(NextPageProperty);
                set => SetValue(NextPageProperty, value);
            }
    
            public int TotalSteps
            {
                get => (int)GetValue(TotalStepsProperty);
                set => SetValue(TotalStepsProperty, value);
            }
    
            public string NextButtonText
            {
                get => (string)GetValue(NextButtonTextProperty);
                set => SetValue(NextButtonTextProperty, value);
            }
    
            private async Task Next(BaseQuickTourPage page)
            {
                if (isNavigating)
                {
                    return;
                }
    
                isNavigating = true;
    
                Dismiss(null);
                if (page != null)
                {
                    await Application.Current.MainPage.Navigation.ShowPopupAsync(page);
                }
    
                isNavigating = false;
            }
    
            private async void NextButtonClicked(object sender, EventArgs e)
            {
                if (TotalSteps - ActualStep == 1)
                {
                    nextbtn.Text = "DONE";
                }
                if (TotalSteps == ActualStep)
                {
                    Dismiss(null);
                }
                else
                {
                    await Next(NextPage);
                }
            }
    
            private void SkipButtonClicked(object sender, EventArgs e)
            {
                Dismiss(null);
            }
        }
    }

    Each step will inherit from the BaseQuickTourPage and will wrap the view in the ContentView control. All the steps in the Quick Tour will be followed like the first step.

    <?xml version="1.0" encoding="utf-8" ?>
    <local:BaseQuickTourPage
        xmlns:local="clr-namespace:XamarinCommunityToolkit"
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="XamarinCommunityToolkit.QuickTourStep1PopUp" 
        NextButtonText="Next">
    
        <local:BaseQuickTourPage.BodyContent>
            <ContentView>
                <Frame VerticalOptions="Center"
                       HorizontalOptions="Center"
                       Margin="40,0">
                    <Label Text="This tutorial is build with ♥ to learn the new things in Xamarin Community Toolkit"
                           FontAttributes="Bold"
                           FontSize="Subtitle"
                           Padding="15,0"/>
                </Frame>
            </ContentView>
        </local:BaseQuickTourPage.BodyContent>
    </local:BaseQuickTourPage>
  • In order to handle which step/page goes next, I created a QuickTourBuilder using the builder pattern, which will handle the logic to indicate which step is going to go next.
    using System.Threading.Tasks;
    
    namespace XamarinCommunityToolkit
    {
        public interface IQuickTourLauncher
        {
            Task LaunchAsync();
        }
    }
    using System.Collections.Generic;
    using System.Linq;
    
    namespace XamarinCommunityToolkit
    {
        public class QuickTourBuilder<T> where T : BaseQuickTourPage, IQuickTourLauncher, new()
        {
            public static QuickTourBuilder<T> Initialize() => new QuickTourBuilder<T>(new T());
    
            public QuickTourBuilder<T> Next(BaseQuickTourPage page)
            {
                _pages.Last().NextPage = page;
    
                AddPage(page);
    
                return this;
            }
    
            public IQuickTourLauncher Build()
             => _pages.Select(page =>
                             {
                                 page.TotalSteps = _pages.Count;
                                 return page;
                             })
                      .ToList()
                      .OfType<IQuickTourLauncher>()
                      .First();
    
            private QuickTourBuilder(T initialPage) => AddPage(initialPage);
    
            private void AddPage(BaseQuickTourPage page)
            {
                if (_pages == null)
                    _pages = new List<BaseQuickTourPage>();
    
                _pages.Add(page);
    
                page.ActualStep = _pages.Count;
            }
    
            private IList<BaseQuickTourPage> _pages;
        }
    }
  • On the first page of the Quick Tour, implement the IQuickTourLauncher interface, and use the LaunchAsync method to navigate to it.
    using System.Threading.Tasks;
    using Xamarin.CommunityToolkit.Extensions;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
    namespace XamarinCommunityToolkit
    {
        public partial class QuickTourStep1PopUp : BaseQuickTourPage, IQuickTourLauncher
        {
            public QuickTourStep1PopUp() : base()
            {
                InitializeComponent();
            }
    
            public Task LaunchAsync() => Xamarin.Forms.Application.Current.MainPage.Navigation.ShowPopupAsync(this);
        }
    }
  • In the sample page code-behind, we need to create the quick tour by using the QuickTourBuilder and specifying the sequence of pages that will be part of it like below.
    var quickTourLauncher = QuickTourBuilder<QuickTourStep1PopUp>
    						  .Initialize()
    						  .Next(new QuickTourStep2PopUp())
    						  .Next(new QuickTourStep3PopUp())
    						  .Build();
    
    ShowQuickTourCommand = new Command(async () => await quickTourLauncher.LaunchAsync());

Result

Download Code

You can download the code from GitHub. If you have any doubts, feel free to post a comment. If you liked this article, and it is useful to you, do like, share the article & star the repository on GitHub.