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

Plugin Architecture Overview

Plugins in Invenicum consist of:
  • STAC UI Definition: Declarative UI components defined in JSON
  • Plugin Manifest: Metadata about your plugin (name, version, author, etc.)
  • SDK Actions: JavaScript-like calls to Invenicum’s native functionality
  • UI Slots: Predefined locations where plugins can render

How Plugins Work

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

Getting Started

1

Set Up Your Development Environment

You’ll need:
  • A GitHub account (for publishing)
  • A text editor or IDE
  • Basic knowledge of JSON and Flutter concepts
  • A local Invenicum instance for testing
2

Understand the STAC Framework

STAC allows you to build UIs declaratively using JSON. Here’s a simple example:
{
  "type": "container",
  "child": {
    "type": "text",
    "data": "Hello from my plugin!"
  }
}
Learn more about STAC at pub.dev/packages/stac.
3

Create Your Plugin Structure

A plugin consists of a single JSON file with this structure:
{
  "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:
FieldTypeRequiredDescription
idStringYesUnique identifier for your plugin
nameStringYesDisplay name
versionStringYesSemantic version (e.g., “1.0.0”)
authorStringYesYour GitHub username
descriptionStringYesBrief description (max 200 chars)
slotStringYesWhere the plugin renders (see UI Slots)
uiObjectYesSTAC UI definition
authorAvatarStringNoURL to author avatar image
isPublicBooleanNoWhether plugin is publicly available (default: false)

Example Manifest

{
  "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:
Slot NameLocationUse Case
dashboard_topTop of dashboardSummary widgets, important alerts
dashboard_bottomBottom of dashboardCharts, recent activity
asset_detail_topTop of asset detail pageCustom asset actions
asset_detail_bottomBottom of asset detail pageAdditional asset information
sidebarApp sidebarQuick actions, navigation
Specify the slot in your manifest:
{
  "slot": "dashboard_top",
  // ... rest of manifest
}

Invenicum SDK Functions

The SDK allows plugins to interact with Invenicum’s core functionality. SDK calls are made through the InvenicumSdkParser (see lib/core/utils/sdk_plugin_parser.dart:21).

Available SDK Methods

getUserName()

Get the current user’s name. STAC Action Example:
{
  "type": "action",
  "action": {
    "type": "invenicum_sdk",
    "method": "getUserName",
    "params": {}
  }
}
Implementation (from sdk_plugin_parser.dart:42):
case 'getUserName':
  ToastService.info("Usuario actual: ${userName ?? 'Invitado'}");
  break;

showAlert(message)

Display a success toast message to the user. Parameters:
  • message (String): The message to display
STAC Action Example:
{
  "type": "action",
  "action": {
    "type": "invenicum_sdk",
    "method": "showAlert",
    "params": {
      "message": "Plugin action completed successfully!"
    }
  }
}
Implementation (from sdk_plugin_parser.dart:47):
case 'showAlert':
  ToastService.success(model.params['message'] ?? 'Acción ejecutada');
  break;
Navigate to a specific route in the app. Parameters:
  • path (String): The route path to navigate to
STAC Action Example:
{
  "type": "action",
  "action": {
    "type": "invenicum_sdk",
    "method": "navigate",
    "params": {
      "path": "/assets/create"
    }
  }
}
Implementation (from sdk_plugin_parser.dart:51):
case 'navigate':
  final path = model.params['path'];
  if (path != null) {
    debugPrint("Navegando a: $path");
    // Navigation implementation
  }
  break;

logout()

Log out the current user. STAC Action Example:
{
  "type": "action",
  "action": {
    "type": "invenicum_sdk",
    "method": "logout",
    "params": {}
  }
}
Implementation (from sdk_plugin_parser.dart:59):
case 'logout':
  context.read<AuthProvider>().logout();
  ToastService.info("Sesión cerrada");
  break;

Using SDK Functions in Your Plugin

Here’s a complete example of a button that calls an SDK function:
{
  "type": "elevatedButton",
  "data": "Click Me",
  "onPressed": {
    "type": "invenicum_sdk",
    "method": "showAlert",
    "params": {
      "message": "Hello from my plugin!"
    }
  }
}

Building Your First Plugin

Let’s create a simple “Quick Actions” plugin for the dashboard.
1

Create the Plugin File

Create a new file called 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": {}
}
2

Design the UI

Add STAC UI definition:
"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"
              }
            }
          }
        ]
      }
    ]
  }
}
3

Test Locally

To test your plugin 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
Make adjustments as needed and reload.
4

Publish to GitHub

Once tested:
  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

A plugin with state management:
{
  "id": "counter-widget",
  "name": "Counter Widget",
  "version": "1.0.0",
  "author": "yourusername",
  "description": "A simple counter with increment/decrement buttons",
  "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

A plugin that displays user information:
{
  "id": "user-greeting",
  "name": "User Greeting",
  "version": "1.0.0",
  "author": "yourusername",
  "description": "Personalized greeting for the logged-in user",
  "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 (from lib/data/services/plugin_service.dart:22) provides methods for plugin management:

Key Methods

Install a Plugin

Future<void> installPlugin(Map<String, dynamic> pluginData) async {
  await _dio.post('/plugins/install', data: pluginData);
}

Toggle Plugin 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

Plugins are represented by the StorePlugin model (from lib/data/models/store_plugin_model.dart:1):
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

  • Plugin loads without errors
  • UI renders correctly in the target slot
  • All SDK function calls work as expected
  • Plugin doesn’t crash the app
  • Manifest data is accurate
  • Version number follows semantic versioning
  • Plugin works on multiple screen sizes

Publishing to Marketplace

1

Prepare for Release

  • Test thoroughly
  • Write clear documentation
  • Add screenshots (optional)
  • Ensure manifest is complete
2

Create GitHub Repository

  1. Create a new public repository
  2. Add your plugin JSON file
  3. Include a README with usage instructions
  4. Add a LICENSE file
3

Create a Release

  1. Go to Releases in your GitHub repo
  2. Click “Create a new release”
  3. Tag version (e.g., v1.0.0) matching your plugin version
  4. Write release notes
  5. Publish release
4

Submit to Marketplace

  1. Copy the raw URL to your plugin JSON
  2. In Invenicum, go to Plugins > Marketplace > Submit Plugin
  3. Paste the URL and submit
  4. Your plugin will be reviewed and published

Best Practices

Plugins should do one thing well. Don’t try to cram too much functionality into a single plugin.
  • Use descriptive, unique plugin IDs
  • Follow semantic versioning (MAJOR.MINOR.PATCH)
  • Keep names under 50 characters
  • Keep UI hierarchies shallow
  • Avoid excessive nesting
  • Don’t make plugins too large
  • Use clear, actionable button labels
  • Provide feedback for user actions
  • Handle errors gracefully
  • Write a clear description
  • Include usage instructions in your 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

Next Steps

Template Creation

Learn how to create custom asset templates

Contributing Guide

Contribute to Invenicum core development

Resources

Build docs developers (and LLMs) love