ContributingPlugin Development

CONTRIBUTING

Plugin Development

Learn how to create custom plugins to extend Invenicum’s functionality using the STAC framework.

Invenicum’s plugin system allows you to extend the application with custom UI components and integrations. Plugins are built using the STAC framework and interact with Invenicum’s core features through a comprehensive SDK.


Plugin Architecture Overview

STAC UI Definition

Declarative UI in JSON

Plugin Manifest

Metadata & configuration

SDK Actions

Native functionality calls

UI Slots

Predefined render locations

How Plugins Work

1Plugin UI is defined using STAC (Simple Template for App Components)
2Plugins are loaded dynamically from GitHub or local storage
3The InvenicumSdkParser handles SDK function calls from plugins
4Plugins render in designated UI slots throughout the app

Getting Started

Step 01

Set Up Your Development Environment

You’ll need:

A GitHub account (for publishing)A text editor or IDEBasic knowledge of JSON and FlutterA local Invenicum instance for testing
Step 02

Understand the STAC Framework

STAC allows you to build UIs declaratively using JSON. Here’s a minimal example:

{
"type": "container",
"child": {
  "type": "text",
  "data": "Hello from my plugin!"
}
}
Step 03

Create Your Plugin Structure

A plugin is a single JSON file with this top-level structure:

my-plugin.json
{
"id": "my-custom-plugin",
"name": "My Custom Plugin",
"version": "1.0.0",
"author": "your-github-username",
"description": "A brief description of what this plugin does",
"slot": "dashboard_top",
"ui": {
  // STAC UI definition here
}
}

Plugin Manifest

Every plugin requires these fields in its manifest:

Manifest Fields
id
String
Required

Unique identifier for your plugin

name
String
Required

Display name

version
String
Required

Semantic version (e.g., "1.0.0")

author
String
Required

Your GitHub username

description
String
Required

Brief description (max 200 chars)

slot
String
Required

Where the plugin renders (see UI Slots)

ui
Object
Required

STAC UI definition

authorAvatar
String
Optional

URL to author avatar image

isPublic
Boolean
Optional

Whether plugin is publicly available (default: false)

Example Manifest

dashboard-stats.json
{
"id": "dashboard-stats",
"name": "Enhanced Dashboard Stats",
"version": "1.2.0",
"author": "yourusername",
"description": "Display advanced statistics on your dashboard with charts and insights",
"slot": "dashboard_top",
"authorAvatar": "https://github.com/yourusername.png",
"isPublic": true,
"ui": {
  "type": "container",
  "child": {
    "type": "text",
    "data": "Stats go here"
  }
}
}

UI Slots

Plugins can render in various locations throughout the Invenicum app:

dashboard_top

Top of dashboard

Summary widgets, important alerts

dashboard_bottom

Bottom of dashboard

Charts, recent activity

asset_detail_top

Top of asset detail page

Custom asset actions

asset_detail_bottom

Bottom of asset detail page

Additional asset information

sidebar

App sidebar

Quick actions, navigation

Invenicum SDK Functions

The SDK allows plugins to interact with Invenicum’s core functionality via InvenicumSdkParser (sdk_plugin_parser.dart:21).

Available SDK Methods

METHODgetUserName()sdk_plugin_parser.dart:42

Description

Get the current user’s name and display it as a toast notification.

Implementation

case 'getUserName':
ToastService.info(
  "Usuario actual: ${userName ?? 'Invitado'}");
break;

STAC Action

{
"type": "action",
"action": {
  "type": "invenicum_sdk",
  "method": "getUserName",
  "params": {}
}
}
METHODshowAlert(message)sdk_plugin_parser.dart:47

Description

Display a success toast message to the user.

messageStringThe message to display

Implementation

case 'showAlert':
ToastService.success(
  model.params['message'] ?? 'Acción ejecutada');
break;

STAC Action

{
"type": "action",
"action": {
  "type": "invenicum_sdk",
  "method": "showAlert",
  "params": {
    "message": "Plugin action completed!"
  }
}
}
METHODnavigate(path)sdk_plugin_parser.dart:51

Description

Navigate to a specific route in the app.

pathStringRoute path to navigate to

Implementation

case 'navigate':
final path = model.params['path'];
if (path != null) {
  debugPrint("Navegando a: $path");
  // Navigation implementation
}
break;

STAC Action

{
"type": "action",
"action": {
  "type": "invenicum_sdk",
  "method": "navigate",
  "params": {
    "path": "/assets/create"
  }
}
}
METHODlogout()sdk_plugin_parser.dart:59

Description

Log out the current user from the app.

Implementation

case 'logout':
context.read<AuthProvider>().logout();
ToastService.info("Sesión cerrada");
break;

STAC Action

{
"type": "action",
"action": {
  "type": "invenicum_sdk",
  "method": "logout",
  "params": {}
}
}

Using SDK in a Button — Complete Example

{
"type": "elevatedButton",
"data": "Click Me",
"onPressed": {
  "type": "invenicum_sdk",
  "method": "showAlert",
  "params": {
    "message": "Hello from my plugin!"
  }
}
}

Building Your First Plugin

Step 01

Create the Plugin File

Create a new file called quick-actions.json:

quick-actions.json
{
"id": "quick-actions",
"name": "Quick Actions",
"version": "1.0.0",
"author": "yourusername",
"description": "Quick action buttons for common tasks",
"slot": "dashboard_top",
"ui": {}
}
Step 02

Design the UI

Add the STAC UI definition to the “ui” field:

"ui": {
"type": "card",
"child": {
  "type": "column",
  "children": [
    {
      "type": "text",
      "data": "Quick Actions",
      "style": { "fontSize": 18, "fontWeight": "bold" }
    },
    {
      "type": "row",
      "mainAxisAlignment": "spaceEvenly",
      "children": [
        {
          "type": "elevatedButton",
          "data": "Add Asset",
          "onPressed": {
            "type": "invenicum_sdk",
            "method": "navigate",
            "params": { "path": "/assets/create" }
          }
        },
        {
          "type": "elevatedButton",
          "data": "View Loans",
          "onPressed": {
            "type": "invenicum_sdk",
            "method": "navigate",
            "params": { "path": "/loans" }
          }
        }
      ]
    }
  ]
}
}
Step 03

Test Locally

1.Start your Invenicum instance
2.Go to Settings > Plugins > Developer Mode
3.Upload your quick-actions.json file
4.The plugin will appear in the dashboard
5.Make adjustments as needed and reload
Step 04

Publish to GitHub

1.Create a new GitHub repository for your plugin
2.Upload your quick-actions.json file
3.Create a release with a version tag (e.g. v1.0.0)
4.Copy the raw URL to your plugin file
5.Submit to the Invenicum marketplace

Advanced Plugin Examples

Interactive Counter Plugin
{
"id": "counter-widget",
"name": "Counter Widget",
"version": "1.0.0",
"slot": "dashboard_top",
"ui": {
  "type": "card",
  "child": {
    "type": "column",
    "mainAxisAlignment": "center",
    "children": [
      {
        "type": "text",
        "data": "Counter: 0",
        "id": "counterText"
      },
      {
        "type": "row",
        "mainAxisAlignment": "center",
        "children": [
          {
            "type": "iconButton",
            "icon": "remove",
            "onPressed": {
              "type": "invenicum_sdk",
              "method": "showAlert",
              "params": { "message": "Decrement clicked" }
            }
          },
          {
            "type": "iconButton",
            "icon": "add",
            "onPressed": {
              "type": "invenicum_sdk",
              "method": "showAlert",
              "params": { "message": "Increment clicked" }
            }
          }
        ]
      }
    ]
  }
}
}
User Greeting Plugin
{
"id": "user-greeting",
"name": "User Greeting",
"version": "1.0.0",
"slot": "dashboard_top",
"ui": {
  "type": "card",
  "child": {
    "type": "column",
    "children": [
      {
        "type": "text",
        "data": "Welcome back!",
        "style": {
          "fontSize": 24,
          "fontWeight": "bold"
        }
      },
      {
        "type": "elevatedButton",
        "data": "Show My Username",
        "onPressed": {
          "type": "invenicum_sdk",
          "method": "getUserName",
          "params": {}
        }
      }
    ]
  }
}
}

Plugin Service API

The PluginService (plugin_service.dart:22) provides methods for plugin management:

Install a Plugin
Future<void> installPlugin(
Map<String, dynamic> pluginData) async {
await _dio.post('/plugins/install', data: pluginData);
}
Toggle Active State
Future<void> toggleUserPlugin(
String pluginId, bool isActive) async {
await _dio.put('/plugins/user/toggle',
  data: {'pluginId': pluginId, 'isActive': isActive});
}
Get Installed Plugins
Future<List<Map<String, dynamic>>> getMyPlugins() async {
final response = await _dio.get('/plugins/installed');
return List<Map<String, dynamic>>.from(response.data);
}
Get Community Plugins
Future<List<Map<String, dynamic>>> getCommunityPlugins() async {
final response = await _dio.get('/plugins/community');
return List<Map<String, dynamic>>.from(response.data);
}

Plugin Model Structure

store_plugin_model.dart:1 — StorePlugin
class StorePlugin {
final String id;
final String name;
final String author;
final String version;
final String description;
final String slot;
final Map<String, dynamic>? ui;
final String? authorAvatar;
final int downloadCount;
final bool hasUpdate;
final String latestVersion;
final bool isMine;
final bool isActive;
final bool isPublic;
}

Testing Your Plugin

Local Testing

1.

Developer Mode

Enable developer mode in plugin settings

2.

Upload

Upload your plugin JSON file

3.

Verify

Check that it appears in the correct slot

4.

Test Actions

Click buttons and verify SDK calls work

5.

Check Console

Look for debug messages in Flutter DevTools

Testing Checklist

Publishing to Marketplace

Step 01

Prepare for Release

Test thoroughlyWrite clear documentationAdd screenshots (optional)Ensure manifest is complete
Step 02

Create GitHub Repository

Create a new public repositoryAdd your plugin JSON fileInclude a README with usage instructionsAdd a LICENSE file
Step 03

Create a Release

Go to Releases in your GitHub repoClick "Create a new release"Tag version (e.g. v1.0.0) matching plugin versionWrite release notes and publish
Step 04

Submit to Marketplace

Copy the raw URL to your plugin JSONIn Invenicum, go to Plugins > Marketplace > SubmitPaste the URL and submitYour plugin will be reviewed and published

Best Practices

Keep It Simple

Plugins should do one thing well. Don't try to cram too much functionality into a single plugin.

Follow Naming Conventions

  • Use descriptive, unique plugin IDs
  • Follow semantic versioning (MAJOR.MINOR.PATCH)
  • Keep names under 50 characters

Optimize Performance

  • Keep UI hierarchies shallow
  • Avoid excessive nesting
  • Don't make plugins too large

Provide Good UX

  • Use clear, actionable button labels
  • Provide feedback for user actions
  • Handle errors gracefully

Document Your Plugin

  • Write a clear description
  • Include usage instructions in GitHub README
  • Add comments explaining complex logic

Troubleshooting

Plugin Doesn’t Load
  • Verify JSON is valid — use a JSON validator
  • Check that all required manifest fields are present
  • Ensure the “slot” value is valid
SDK Functions Don’t Work
  • Verify the “type” is set to “invenicum_sdk”
  • Check that the “method” name is correct (case-sensitive)
  • Ensure “params” is an object, not an array
UI Doesn’t Render Correctly
  • Check STAC documentation for proper widget structure
  • Verify property names match STAC specifications
  • Test with simpler UI first, then add complexity