# Create custom components

All of the existing components can be found in `PlutoFramework/Components` folder. Feel free to take a look at them and get inspired.

I recommend creating a new folder to keep the components sorted. (Or use an existing folder if relevant)

To keep consistency, please utilise MVVM architecture patterns.&#x20;

<figure><img src="https://1720293510-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7cPs2j0GvSzYIyVs3t35%2Fuploads%2FG4GDjQiApGKO6DBeSt6x%2Fimage.png?alt=media&#x26;token=a367344b-03a5-4f8d-80a4-51d8a80306c9" alt=""><figcaption><p>View + ViewModel</p></figcaption></figure>

```csharp
// Constructor
public TemplateView()
{
    InitializeComponent();
    
    // Add the ViewModel to BindingContext
    BindingContext = new TemplateViewModel();
}
```

In `View.xaml`, programme the visual UI using xaml syntax:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PlutoFramework.Components._000ComponentTemplate.TemplateView">
    <!-- Your code here -->
</ContentView>
```

We have created these unified interfaces to handle parallel data loading. Feel free to implement them in `View.xaml.cs` file.

```csharp
interface ILocalLoadableView
{
    /// <summary>
    /// This function is called first in the queue.
    /// Used for loading locally stored data.
    /// </summary>
    void Load();
}

interface ILocalLoadableAsyncView
{
    /// <summary>
    /// This function is called first in the queue.
    /// Used for loading locally stored data asynchronically.
    /// </summary>
    Task LoadAsync(CancellationToken token);
}

interface ISetEmptyView
{
    /// <summary>
    /// This is a fallback function that is called if the view has not loaded any data,
    /// to display a default empty state instead.
    /// </summary>
    void SetEmpty();
}

public interface ISubstrateClientLoadableView
{
    /// <summary>
    /// Loads information from the SubstrateClient.
    /// </summary>
    void Load(PlutoFrameworkSubstrateClient client);
}

public interface IMainSubstrateClientLoadableView
{
    /// <summary>
    /// Loads information from the main SubstrateClient.
    /// </summary>
    void MainLoad(PlutoFrameworkSubstrateClient client);
}

public interface ISubstrateClientLoadableAsyncView
{
    /// <summary>
    /// Loads information asynchronically from the SubstrateClient.
    /// Usually queries on-chain state.
    /// </summary>
    Task LoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token);
}

public interface IMainSubstrateClientLoadableAsyncView
{
    /// <summary>
    /// Loads information asynchronically from the main SubstrateClient.
    /// Usually queries on-chain state.
    /// </summary>
    Task MainLoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token);
}
```

Implement them like this:

```csharp
using PlutoFramework.Model;

namespace PlutoFramework.Components._000ComponentTemplate;

public partial class TemplateView : ContentView, ISubstrateClientLoadableAsyncView, ISetEmptyView
{
    public TemplateView()
    {
    	InitializeComponent();

        BindingContext = new TemplateViewModel();
    }

    public async Task LoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token)
    {
        var viewModel = (TemplateViewModel)BindingContext;
        // Implement loading logic here
    }

    public void SetEmpty()
    {
        var viewModel = (TemplateViewModel)BindingContext;
        // Implement empty state logic here
    }
}
```

{% hint style="info" %}
If you are curious, these interfaces are called in the `PlutoFramework/Model/SubstrateClientModel.cs` at the right time.
{% endhint %}

{% hint style="success" %}
All of the steps after this point are not mandatory, but it is appreciated if you completed them anyways ^^
{% endhint %}

## Register the new component to CustomLayoutModel

To support the new component in PlutoLayouts, add it to the `PlutoFramework/Model/CustomLayoutModel.cs`.

Firstly, register the unique component identifier

```csharp
public enum ComponentId
{
    U,
    dApp,
    // ...
    YoudComponentId
}
```

{% hint style="warning" %}
Keep the ComponentId as short as possible. Each ComponentId used is saved in the Preferences storage which is not very fast and each extra character will slow down your startup loading times.
{% endhint %}

Register this component id to the default layout.

{% code overflow="wrap" %}
```csharp
public const string DEFAULT_PLUTO_LAYOUT = "plutolayout: [U, dApp, ..., YourComponentId];[Polkadot, Kusama, ..., YourChain]";
```
{% endcode %}

{% hint style="success" %}
`DEFAULT_PLUTO_LAYOUT` defines the layout that will be shown to the user at the startup. Alongside the layout, it also defines to which blockchains to connect to.

Be sure to include your own blockchain.
{% endhint %}

Now provide the View to your ComponentId in the `GetView` method.

```csharp
public static IView GetView(ComponentId componentId)
{
    switch (componentId)
    {
        case ComponentId.U:
            return new UpdateView();
        case ComponentId.dApp:
            return new DAppConnectionView();
            
        // ...
        
        case ComponentId.YourComponentId:
            return new TemplateView();
    }

    throw new Exception("Could not parse the PlutoLayout");
}
```

Lastly, provide the ComponentInfo in the `GetComponentInfo` method.

```csharp
public static ComponentInfo GetComponentInfo(ComponentId componentId)
{
    switch (componentId)
    {
        case ComponentId.U:
            return new ComponentInfo
            {
                Name = "Update notification",
                ComponentId = ComponentId.U
            };
        case ComponentId.dApp:
            return new ComponentInfo
            {
                Name = "dApp connection",
                ComponentId = ComponentId.dApp,
            };
        
        // ...
        
        case ComponentId.YourComponentId:
            return new ComponentInfo
            {
                Name = "<Name of your component>",
                ComponentId = ComponentId.YourComponentId,
            };
    }

    throw new Exception("Could not parse the ComponentId");
}
```

Now, your component should be done and compatible with the rest of PlutoFramework.
