Files
App/packages/miniapp-example/INTEGRATION.md

14 KiB

Mini-App Example - Integration Overview

This document explains how the miniapp-example package integrates with the main Island application and the plugin registry system.

Architecture Overview

Island Main App
│
├── PluginRegistry (lib/modular/registry.dart)
│   ├── loadMiniApp()       # Downloads and caches .evc files
│   ├── enablePlugin()      # Activates mini-app
│   └── launchMiniApp()     # Runs mini-app in full-screen
│
├── PaymentAPI (lib/modular/api/payment.dart)
│   └── Singleton with internal Dio client
│
└── Eval Bridge
    ├── PaymentAPI instance registered
    └── Available to mini-apps at runtime

Integration Flow

1. Mini-App Development

Developer creates mini-app in packages/miniapp-example
         ↓
Tests with `flutter test` (17 tests passing)
         ↓
Runs with `flutter run` (debug mode)
         ↓
Compiles with `dart run flutter_eval:compile -i lib/main.dart -o payment_demo.evc`
         ↓
Uploads payment_demo.evc to server

2. Server Setup

Server provides mini-app metadata:

{
  "id": "payment-demo",
  "name": "Payment Demo",
  "version": "1.0.0",
  "description": "Example payment mini-app",
  "download_url": "https://your-server.com/mini-apps/payment_demo.evc",
  "checksum": "sha256:abc123...",
  "size": 3456,
  "min_host_version": "1.0.0"
}

3. Main App Discovery

// In main app startup
final registry = ref.read(pluginRegistryProvider.notifier);

// Register PaymentAPI with eval bridge
registry.registerBridge('PaymentAPI', PaymentAPI.instance);

// Sync with server to discover mini-apps
await registry.syncMiniApps();

4. User Downloads Mini-App

// User selects mini-app from list
final miniApp = await registry.loadMiniApp(
  'https://your-server.com/mini-apps/payment_demo.evc',
);

// PluginRegistry handles:
// 1. Download from server
// 2. Save to {appDocuments}/mini_apps/{id}/payment_demo.evc
// 3. Validate checksum
// 4. Cache locally
// 5. Save to SharedPreferences (enabled apps list)

5. Enable Mini-App

// User enables mini-app
await registry.enablePlugin('payment-demo');

// PluginRegistry:
// 1. Adds to enabled apps in SharedPreferences
// 2. Prepares for launch

6. Launch Mini-App

// User launches mini-app
await registry.launchMiniApp(context, 'payment-demo');

// PluginRegistry:
// 1. Loads .evc bytecode
// 2. Creates Runtime with flutter_eval
// 3. Provides PaymentAPI through eval bridge
// 4. Runs mini-app main() function
// 5. Displays full-screen

7. Mini-App Uses PaymentAPI

// Inside mini-app (payment_demo.evc)

// Access PaymentAPI through eval bridge
final paymentApi = PaymentAPI.instance;

// Create order
final order = await paymentApi.createOrder(
  CreateOrderRequest(
    amount: 19.99,
    currency: 'USD',
    description: 'Premium subscription',
  ),
);

// Process payment
final result = await paymentApi.processPaymentWithOverlay(
  orderId: order.orderId,
);

// Show result
if (result.success) {
  print('Payment successful!');
} else {
  print('Payment failed: ${result.errorMessage}');
}

Key Integration Points

1. PaymentAPI Registration

Location: lib/modular/registry.dart

class PluginRegistry {
  final Map<String, dynamic> _bridge = {};
  
  void registerBridge(String name, dynamic api) {
    _bridge[name] = api;
  }
  
  // Called when loading mini-app
  Runtime _createRuntime() {
    return Runtime(
      bridge: _bridge, // Pass PaymentAPI to mini-app
      // ... other config
    );
  }
}

Usage: In main app startup

void main() async {
  final registry = ref.read(pluginRegistryProvider.notifier);
  
  // Register PaymentAPI
  registry.registerBridge('PaymentAPI', PaymentAPI.instance);
  
  runApp(MyApp());
}

2. Mini-App Loading

Location: lib/modular/registry.dart - loadMiniApp() method

Future<MiniApp> loadMiniApp(String url) async {
  // 1. Download .evc file
  final bytes = await _downloadFile(url);
  
  // 2. Save to cache
  final path = await _saveToCache(url, bytes);
  
  // 3. Create MiniApp object
  final miniApp = MiniApp(
    id: _extractId(url),
    bytecodePath: path,
    // ... metadata
  );
  
  return miniApp;
}

3. Mini-App Launch

Location: lib/modular/registry.dart - launchMiniApp() method

Future<void> launchMiniApp(BuildContext context, String id) async {
  // 1. Get mini-app
  final miniApp = _getMiniApp(id);
  
  // 2. Load bytecode
  final bytecode = await File(miniApp.bytecodePath).readAsBytes();
  
  // 3. Create runtime with PaymentAPI bridge
  final runtime = _createRuntime();
  
  // 4. Execute mini-app
  final program = runtime.loadProgram(bytecode);
  program.execute();
  
  // 5. Navigate to mini-app screen
  Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => MiniAppScreen(program)),
  );
}

Data Flow

Mini-App Request Flow

Mini-App (payment_demo.evc)
         ↓
PaymentAPI instance (from eval bridge)
         ↓
PaymentAPI class (lib/modular/api/payment.dart)
         ↓
Dio client (internal)
         ↓
Server API (your payment server)
         ↓
Response back to mini-app

State Management

SharedPreferences (Persistent)
├── enabled_mini_apps: ['payment-demo', ...]
└── last_sync: '2025-01-18T00:00:00Z'

File System (Cached .evc files)
└── {appDocuments}/mini_apps/
    ├── payment-demo/
    │   └── payment_demo.evc
    └── other-miniapp/
        └── app.evc

Runtime (Memory)
├── Loaded mini-apps
└── Bridge APIs (PaymentAPI, etc.)

File Locations

Main App Files

lib/
├── modular/
│   ├── interface.dart          # Plugin interfaces
│   ├── registry.dart           # PluginRegistry class
│   ├── api/
│   │   └── payment.dart        # PaymentAPI implementation
│   └── README.md              # Plugin system docs
└── pods/
    └── plugin_registry.dart    # Riverpod providers

Mini-App Example Files

packages/miniapp-example/
├── lib/
│   ├── main.dart              # Mini-app code
│   └── payment_api.dart       # API reference
├── test/
│   └── main_test.dart         # Widget tests
├── README.md                  # API usage docs
├── BUILD_GUIDE.md             # Build instructions
├── SUMMARY.md                 # This document
├── build.sh                   # Build script
└── pubspec.yaml              # Package config

Generated Files

build/                         # Build artifacts (ignored)
*.evc                          # Compiled bytecode (optional)
*.freezed.dart                # Generated code
*.g.dart                      # Generated code

Configuration

Main App Setup

  1. Add to pubspec.yaml:
dependencies:
  flutter_eval: ^0.8.2
  1. Initialize PluginRegistry:
final registry = ref.read(pluginRegistryProvider.notifier);
await registry.initialize();
registry.registerBridge('PaymentAPI', PaymentAPI.instance);
  1. Set up server:
final serverInfo = MiniAppServerInfo(
  baseUrl: 'https://your-server.com/api',
  miniAppsPath: '/mini-apps',
);
registry.setServerInfo(serverInfo);

Mini-App Setup

  1. Create package:
mkdir packages/my-miniapp
cd packages/my-miniapp
flutter create --org com.example my_miniapp
  1. Add flutter_eval:
dependencies:
  flutter_eval: ^0.8.2
  1. Write mini-app:
import 'package:flutter/material.dart';

class MyMiniApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(child: Text('Hello from Mini-App!')),
      ),
    );
  }
}

void main() => runApp(MyMiniApp());
  1. Test and build:
flutter test
dart run flutter_eval:compile -i lib/main.dart -o my_miniapp.evc

Testing Strategy

Unit Tests

Test business logic in isolation:

test('PaymentAPI should create order', () async {
  final api = PaymentAPI.instance;
  
  final order = await api.createOrder(
    CreateOrderRequest(
      amount: 19.99,
      currency: 'USD',
      description: 'Test',
    ),
  );
  
  expect(order.orderId, isNotEmpty);
});

Widget Tests

Test UI components:

cd packages/miniapp-example
flutter test

Integration Tests

Test full flow:

testWidgets('mini-app should process payment', (tester) async {
  // Setup registry
  final registry = PluginRegistry();
  registry.registerBridge('PaymentAPI', PaymentAPI.instance);
  
  // Load mini-app
  final miniApp = await registry.loadMiniApp('test.evc');
  
  // Launch
  await registry.launchMiniApp(tester.element(miniApp), miniApp.id);
  
  // Test payment flow
  await tester.tap(find.text('Pay with Overlay'));
  await tester.pumpAndSettle();
  
  expect(find.text('Payment successful!'), findsOneWidget);
});

Troubleshooting

Mini-App Won't Load

Checklist:

  • .evc file exists at expected path
  • File is not corrupted (verify checksum)
  • PaymentAPI is registered with bridge
  • flutter_eval is properly initialized
  • Mini-app main() function exists

Debug:

// Add logging in registry.dart
print('Loading mini-app from: $path');
print('Bytecode size: ${bytecode.length}');
print('Bridge APIs: ${_bridge.keys}');

PaymentAPI Not Available

Checklist:

  • PaymentAPI is registered before mini-app loads
  • Bridge name matches ('PaymentAPI')
  • Singleton instance is not null
  • Dio client is configured

Debug:

// Check bridge
print('Bridge has PaymentAPI: ${_bridge.containsKey('PaymentAPI')}');

// Check instance
print('PaymentAPI.instance: ${PaymentAPI.instance}');

Network Errors

Checklist:

  • Server URL is accessible
  • API endpoints are correct
  • Authentication token exists
  • Network permissions granted

Debug:

// Add Dio interceptors for logging
dio.interceptors.add(LogInterceptor(
  request: true,
  response: true,
  error: true,
));

Best Practices

For Mini-App Developers

  1. Keep it Small: Mini-apps should be focused and lightweight
  2. Test Thoroughly: Write comprehensive tests for all user flows
  3. Handle Errors: Always wrap API calls in try-catch
  4. Show Feedback: Provide loading states and user feedback
  5. Use Official APIs: Only use PaymentAPI and other provided APIs
  6. Version Control: Include version information in metadata
  7. Security: Never store sensitive data or API keys

For Main App Developers

  1. Register APIs Early: Register all bridge APIs before loading mini-apps
  2. Handle Failures: Gracefully handle mini-app load failures
  3. Cache Wisely: Implement proper caching for .evc files
  4. Validate Metadata: Verify server metadata before loading
  5. Secure Bridge: Only expose necessary APIs to mini-apps
  6. Monitor Usage: Track mini-app usage and performance
  7. Update System: Keep flutter_eval and dependencies updated

Performance Considerations

Load Time Optimization

  1. Compress .evc files: Use gzip compression for download
  2. Lazy Loading: Only load bytecode when needed
  3. Prefetch: Download popular mini-apps in background
  4. Cache Validation: Use ETags for cache validation

Memory Optimization

  1. Unload When Done: Release mini-app resources when closed
  2. Limit Concurrent Apps: Only load one mini-app at a time
  3. Clean Up Runtime: Dispose runtime after mini-app exits
  4. Monitor Memory: Track memory usage during operation

Network Optimization

  1. Batch Requests: Combine multiple requests when possible
  2. Retry Logic: Implement exponential backoff for retries
  3. Offline Support: Cache responses for offline use
  4. Compression: Enable compression for API responses

Security Considerations

Mini-App Sandbox

Mini-apps run in a sandboxed environment:

  • Limited file system access
  • No direct network access (through bridge APIs only)
  • No access to other mini-apps
  • Limited device permissions

PaymentAPI Security

  • Internal Dio client (no external dependencies)
  • Token management via SharedPreferences
  • HTTPS-only connections
  • Automatic error logging
  • No sensitive data in logs

Best Practices

  1. Validate Inputs: Always validate user inputs
  2. Encrypt Data: Encrypt sensitive data at rest
  3. Use HTTPS: Always use HTTPS for API calls
  4. Minimize Permissions: Request minimum necessary permissions
  5. Regular Updates: Keep dependencies updated
  6. Audit Logs: Review audit logs regularly

Future Enhancements

Planned Features

  1. Hot Reload: Support live reloading during development
  2. Plugin System: Allow mini-apps to use plugins
  3. Version Management: Automatic version checking and updates
  4. Analytics: Built-in analytics for mini-app usage
  5. Marketplace: Mini-app marketplace UI
  6. Permissions System: Fine-grained permissions for mini-apps

Potential Improvements

  1. Smaller Bytecode: Reduce .evc file size
  2. Faster Compilation: Improve compilation speed
  3. Better Debugging: Enhanced debugging tools
  4. Offline Support: Better offline functionality
  5. Background Tasks: Support for background operations

Resources

Documentation

  • Plugin System: lib/modular/README.md
  • Payment API: lib/modular/api/README.md
  • Build Guide: packages/miniapp-example/BUILD_GUIDE.md
  • API Reference: packages/miniapp-example/lib/payment_api.dart

Code Examples

  • Mini-App Example: packages/miniapp-example/lib/main.dart
  • Widget Tests: packages/miniapp-example/test/main_test.dart
  • Plugin Registry: lib/modular/registry.dart
  • Payment API: lib/modular/api/payment.dart

Tools

Support

For issues or questions:

  1. Check documentation files
  2. Review code examples
  3. Check test cases
  4. Review PaymentAPI interface
  5. Consult build guide

Conclusion

The mini-app example demonstrates a complete integration between:

  • Main app with PluginRegistry
  • PaymentAPI with internal Dio client
  • Mini-app compiled to .evc bytecode
  • Eval bridge providing API access

This architecture enables:

  • Dynamic loading of mini-apps
  • Secure API access through bridge
  • Full-screen mini-app experience
  • Version management and updates
  • Caching and offline support

For more details, see the documentation files listed above.