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.
// Constructor
public TemplateView()
{
InitializeComponent();
// Add the ViewModel to BindingContext
BindingContext = new TemplateViewModel();
}
In View.xaml, programme the visual UI using xaml syntax:
<?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.
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:
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
}
}
If you are curious, these interfaces are called in the PlutoFramework/Model/SubstrateClientModel.cs at the right time.
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
public enum ComponentId
{
U,
dApp,
// ...
YoudComponentId
}
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.
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.
Now provide the View to your ComponentId in the GetView method.
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.
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.