216 lines
6.0 KiB
Markdown
216 lines
6.0 KiB
Markdown
# Plugin Registry and Loader System
|
|
|
|
## Overview
|
|
|
|
This module provides a plugin system for the Island app with two types of plugins:
|
|
- **Raw Plugins**: Extend app abilities (services, hooks, utilities)
|
|
- **Mini-Apps**: Full-screen applications loaded from network with caching
|
|
|
|
## File Structure
|
|
|
|
```
|
|
lib/
|
|
modular/
|
|
interface.dart # Plugin interfaces and metadata models
|
|
registry.dart # PluginRegistry class for managing plugins
|
|
pods/
|
|
plugin_registry.dart # Riverpod providers for plugin management
|
|
```
|
|
|
|
## Core Components
|
|
|
|
### 1. Plugin Interface (`lib/modular/interface.dart`)
|
|
|
|
Defines the base types for plugins:
|
|
|
|
- `Plugin`: Base interface for all plugins
|
|
- `RawPlugin`: For plugins that extend app functionality
|
|
- `MiniApp`: For full-screen apps with entry widgets
|
|
- `PluginMetadata`: Common metadata structure
|
|
- `MiniAppMetadata`: Metadata for mini-apps including download URL, cache path
|
|
- `MiniAppServerInfo`: Server response format for mini-app listings
|
|
- `PluginLoadResult`: Enum for load operation results
|
|
|
|
### 2. Plugin Registry (`lib/modular/registry.dart`)
|
|
|
|
`PluginRegistry` class manages plugin lifecycle:
|
|
|
|
**Raw Plugins:**
|
|
- `registerRawPlugin(RawPlugin plugin)`
|
|
- `unregisterRawPlugin(String id)`
|
|
- `getRawPlugin(String id)`
|
|
|
|
**Mini-Apps:**
|
|
- `loadMiniApp(MiniAppMetadata metadata, {ProgressCallback? onProgress})`: Loads .evc bytecode
|
|
- `unloadMiniApp(String id)`: Unloads and cleans up
|
|
- `getMiniApp(String id)`: Get loaded mini-app
|
|
- `getMiniAppCacheDirectory()`: Get cache directory path
|
|
- `clearMiniAppCache()`: Clear all cached mini-apps
|
|
- `dispose()`: Cleanup all resources
|
|
|
|
### 3. Riverpod Providers (`lib/pods/plugin_registry.dart`)
|
|
|
|
**Providers:**
|
|
- `pluginRegistryProvider`: Main registry provider with `keepAlive: true`
|
|
- `miniAppsProvider`: List of loaded mini-apps
|
|
- `rawPluginsProvider`: Map of raw plugins
|
|
|
|
**Methods (via `ref.read(pluginRegistryProvider.notifier)`:**
|
|
- `syncMiniAppsFromServer(apiEndpoint)`: Sync with server, returns `MiniAppSyncResult`
|
|
- `downloadMiniApp(id, downloadUrl, {ProgressCallback? onProgress})`: Download and cache
|
|
- `updateMiniApp(id, {ProgressCallback? onProgress})`: Update to latest version
|
|
- `enableMiniApp(id, enabled)`: Enable/disable mini-app
|
|
- `deleteMiniApp(id, {deleteCache})`: Remove mini-app
|
|
- `clearMiniAppCache()`: Clear cache
|
|
- `getMiniApp(id)`: Get specific mini-app
|
|
- `getLastSyncTime()`: Get last sync timestamp
|
|
|
|
## Storage
|
|
|
|
### SharedPreferences Keys:
|
|
- `kMiniAppsRegistryKey`: JSON array of MiniAppMetadata
|
|
- `kMiniAppsLastSyncKey`: ISO8601 timestamp of last sync
|
|
|
|
### Cache Structure:
|
|
```
|
|
{applicationDocuments}/mini_apps/
|
|
├── {app_id}.evc # Compiled bytecode
|
|
└── {app_id}_metadata.json # Metadata backup (optional)
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Registering a Raw Plugin
|
|
|
|
```dart
|
|
class MyRawPlugin extends RawPlugin {
|
|
@override
|
|
PluginMetadata get metadata => PluginMetadata(
|
|
id: 'my_plugin',
|
|
name: 'My Plugin',
|
|
version: '1.0.0',
|
|
description: 'Extends app with new features',
|
|
);
|
|
}
|
|
|
|
final container = ProviderContainer();
|
|
container.read(pluginRegistryProvider.notifier).registerRawPlugin(MyRawPlugin());
|
|
```
|
|
|
|
### Syncing Mini-Apps from Server
|
|
|
|
```dart
|
|
final syncResult = await ref.read(pluginRegistryProvider.notifier).syncMiniAppsFromServer(
|
|
'https://api.example.com/mini-apps',
|
|
);
|
|
|
|
if (syncResult.success) {
|
|
print('Added: ${syncResult.added}');
|
|
print('Updated: ${syncResult.updated}');
|
|
}
|
|
```
|
|
|
|
### Downloading and Loading a Mini-App
|
|
|
|
```dart
|
|
await ref.read(pluginRegistryProvider.notifier).downloadMiniApp(
|
|
'com.example.miniapp',
|
|
'https://cdn.example.com/mini-apps/v1/app.evc',
|
|
onProgress: (progress, message) {
|
|
print('$progress: $message');
|
|
},
|
|
);
|
|
```
|
|
|
|
### Using a Loaded Mini-App Entry
|
|
|
|
```dart
|
|
class MiniAppScreen extends ConsumerWidget {
|
|
final String appId;
|
|
|
|
const MiniAppScreen({required this.appId, super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final miniApp = await ref.read(pluginRegistryProvider.notifier).getMiniApp(appId);
|
|
|
|
if (miniApp == null) {
|
|
return const Center(child: Text('Mini-app not loaded'));
|
|
}
|
|
|
|
return Scaffold(
|
|
body: miniApp.buildEntry(),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Server API Response Format
|
|
|
|
```json
|
|
{
|
|
"mini_apps": [
|
|
{
|
|
"id": "com.example.miniapp",
|
|
"name": "Example Mini-App",
|
|
"version": "1.2.0",
|
|
"description": "An example mini-application",
|
|
"author": "Example Corp",
|
|
"iconUrl": "https://cdn.example.com/icons/miniapp.png",
|
|
"downloadUrl": "https://cdn.example.com/mini-apps/v1/app.evc",
|
|
"updatedAt": "2026-01-18T00:00:00Z",
|
|
"sizeBytes": 524288
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Mini-App Development
|
|
|
|
A mini-app should export a `buildEntry()` function:
|
|
|
|
```dart
|
|
// mini_app/main.dart
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
Widget buildEntry() {
|
|
return MaterialApp(
|
|
title: 'My Mini-App',
|
|
home: Scaffold(
|
|
appBar: AppBar(title: const Text('My Mini-App')),
|
|
body: const Center(
|
|
child: Text('Hello from Mini-App!'),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
```
|
|
|
|
Compile to .evc using flutter_eval toolchain before uploading to server.
|
|
|
|
## FlutterEval Integration
|
|
|
|
Currently, a stub `Runtime` class is provided in `lib/modular/registry.dart` for compilation. To enable full flutter_eval functionality:
|
|
|
|
1. Resolve dependency conflicts with analyzer package
|
|
2. Replace stub `Runtime` class with actual flutter_eval import
|
|
3. Test with actual .evc bytecode files
|
|
|
|
## Notes
|
|
|
|
- Registry uses `keepAlive: true` to persist across app lifecycle
|
|
- All operations are async and return appropriate results
|
|
- Progress callbacks provide real-time feedback for download/load operations
|
|
- Error handling includes talker logging for debugging
|
|
- SharedPreferences persistence survives app restarts
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] Full flutter_eval integration
|
|
- [ ] Mini-app permissions and security model
|
|
- [ ] Version comparison and auto-update
|
|
- [ ] Dependency resolution for mini-apps
|
|
- [ ] Mini-app marketplace UI
|
|
- [ ] Hot-swapping without app restart
|