🗑️ Clean unused code
This commit is contained in:
@@ -12,7 +12,6 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:image_picker_android/image_picker_android.dart';
|
import 'package:image_picker_android/image_picker_android.dart';
|
||||||
import 'package:island/modular/miniapp_loader.dart';
|
|
||||||
import 'package:island/services/analytics_service.dart';
|
import 'package:island/services/analytics_service.dart';
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
import 'package:island/firebase_options.dart';
|
import 'package:island/firebase_options.dart';
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
# Payment API for Mini-Apps
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Payment API (`lib/modular/api/payment.dart`) provides a simple interface for mini-apps to process payments without needing access to Riverpod or Flutter widget trees.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Basic Setup
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
// Get singleton instance
|
|
||||||
final paymentApi = PaymentApi.instance;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating a Payment Order
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final order = await paymentApi.createOrder(
|
|
||||||
CreateOrderRequest(
|
|
||||||
amount: 1000, // $10.00 in cents
|
|
||||||
currency: 'USD',
|
|
||||||
remarks: 'Premium subscription',
|
|
||||||
payeeWalletId: 'wallet_123',
|
|
||||||
appIdentifier: 'my.miniapp',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use the order ID for payment
|
|
||||||
final orderId = order.id;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Processing Payment with Overlay
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processPaymentWithOverlay(
|
|
||||||
context: context,
|
|
||||||
createOrderRequest: CreateOrderRequest(
|
|
||||||
amount: 1000,
|
|
||||||
currency: 'USD',
|
|
||||||
remarks: 'Premium subscription',
|
|
||||||
),
|
|
||||||
enableBiometric: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
print('Payment successful: ${result.order}');
|
|
||||||
} else {
|
|
||||||
print('Payment failed: ${result.error}');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Processing Existing Payment
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processPaymentWithOverlay(
|
|
||||||
context: context,
|
|
||||||
request: PaymentRequest(
|
|
||||||
orderId: 'order_123',
|
|
||||||
amount: 1000,
|
|
||||||
currency: 'USD',
|
|
||||||
pinCode: '123456',
|
|
||||||
enableBiometric: true,
|
|
||||||
showOverlay: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Processing Payment Without Overlay (Direct)
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processDirectPayment(
|
|
||||||
PaymentRequest(
|
|
||||||
orderId: 'order_123',
|
|
||||||
amount: 1000,
|
|
||||||
currency: 'USD',
|
|
||||||
pinCode: '123456',
|
|
||||||
enableBiometric: false, // No biometric for direct
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
// Handle success
|
|
||||||
} else {
|
|
||||||
// Handle error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Methods
|
|
||||||
|
|
||||||
### `createOrder(CreateOrderRequest)`
|
|
||||||
|
|
||||||
Creates a new payment order on the server.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `amount` (required): Amount in smallest currency unit (cents for USD, etc.)
|
|
||||||
- `currency` (required): Currency code (e.g., 'USD', 'EUR')
|
|
||||||
- `remarks` (optional): Payment description
|
|
||||||
- `payeeWalletId` (optional): Target wallet ID
|
|
||||||
- `appIdentifier` (optional): Mini-app identifier
|
|
||||||
- `meta` (optional): Additional metadata
|
|
||||||
|
|
||||||
**Returns:** `SnWalletOrder?` or throws exception
|
|
||||||
|
|
||||||
### `processPayment({String orderId, String pinCode, bool enableBiometric})`
|
|
||||||
|
|
||||||
Processes a payment for an existing order. Must be called from within mini-app context.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `orderId` (required): Order ID to process
|
|
||||||
- `pinCode` (required): 6-digit PIN code
|
|
||||||
- `enableBiometric` (optional, default: true): Allow biometric authentication
|
|
||||||
|
|
||||||
**Returns:** `SnWalletOrder?` or throws exception
|
|
||||||
|
|
||||||
### `processPaymentWithOverlay({BuildContext, PaymentRequest?, CreateOrderRequest?, bool enableBiometric})`
|
|
||||||
|
|
||||||
Shows payment overlay UI and processes payment. Use this for user-facing payments.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `context` (required): BuildContext for showing overlay
|
|
||||||
- `request` (optional): Existing payment request with orderId
|
|
||||||
- `createOrderRequest` (optional): New order request (must provide one)
|
|
||||||
- `enableBiometric` (optional, default: true): Enable biometric authentication
|
|
||||||
|
|
||||||
**Returns:** `PaymentResult`
|
|
||||||
|
|
||||||
### `processDirectPayment(PaymentRequest)`
|
|
||||||
|
|
||||||
Processes payment without showing UI overlay. Use for automatic/background payments.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `request` (required): PaymentRequest with all details including pinCode
|
|
||||||
|
|
||||||
**Returns:** `PaymentResult`
|
|
||||||
|
|
||||||
## Data Types
|
|
||||||
|
|
||||||
### `PaymentRequest`
|
|
||||||
|
|
||||||
```dart
|
|
||||||
const factory PaymentRequest({
|
|
||||||
required String orderId,
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
String? pinCode,
|
|
||||||
@Default(true) bool showOverlay,
|
|
||||||
@Default(true) bool enableBiometric,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### `CreateOrderRequest`
|
|
||||||
|
|
||||||
```dart
|
|
||||||
const factory CreateOrderRequest({
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
String? appIdentifier,
|
|
||||||
@Default({}) Map<String, dynamic> meta,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### `PaymentResult`
|
|
||||||
|
|
||||||
```dart
|
|
||||||
const factory PaymentResult({
|
|
||||||
required bool success,
|
|
||||||
SnWalletOrder? order,
|
|
||||||
String? error,
|
|
||||||
String? errorCode,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
The API handles common error scenarios:
|
|
||||||
|
|
||||||
- **401/403**: Invalid PIN code
|
|
||||||
- **400**: Payment error with message
|
|
||||||
- **404**: Order not found
|
|
||||||
- **503**: Service unavailable/maintenance
|
|
||||||
- **Network errors**: Connection issues
|
|
||||||
|
|
||||||
## Internals
|
|
||||||
|
|
||||||
The API:
|
|
||||||
- Uses a singleton pattern (`PaymentAPI.instance`)
|
|
||||||
- Manages its own Dio instance with proper interceptors
|
|
||||||
- Reads server URL and token from SharedPreferences
|
|
||||||
- Handles authentication automatically
|
|
||||||
- Reuses existing `PaymentOverlay` widget for UI
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
class MiniAppPayment extends StatelessWidget {
|
|
||||||
const MiniAppPayment({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text('Payment Example')),
|
|
||||||
body: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => _processWithOverlay(context),
|
|
||||||
child: const Text('Pay $10.00 (with overlay)'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => _processDirect(context),
|
|
||||||
child: const Text('Pay $10.00 (direct)'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _processWithOverlay(BuildContext context) async {
|
|
||||||
final api = PaymentAPI.instance;
|
|
||||||
|
|
||||||
final result = await api.processPaymentWithOverlay(
|
|
||||||
context: context,
|
|
||||||
createOrderRequest: CreateOrderRequest(
|
|
||||||
amount: 1000, // $10.00
|
|
||||||
currency: 'USD',
|
|
||||||
remarks: 'Test payment from mini-app',
|
|
||||||
appIdentifier: 'com.example.miniapp',
|
|
||||||
),
|
|
||||||
enableBiometric: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!context.mounted) return;
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Payment successful!')),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text('Payment failed: ${result.error}'),
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _processDirect(BuildContext context) async {
|
|
||||||
final api = PaymentAPI.instance;
|
|
||||||
|
|
||||||
final result = await api.processDirectPayment(
|
|
||||||
PaymentRequest(
|
|
||||||
orderId: 'order_${DateTime.now().millisecondsSinceEpoch}',
|
|
||||||
amount: 1000,
|
|
||||||
currency: 'USD',
|
|
||||||
pinCode: '123456', // Should come from user input
|
|
||||||
enableBiometric: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!context.mounted) return;
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Payment successful!')),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text('Payment failed: ${result.error}'),
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- All methods are async and return Futures
|
|
||||||
- Errors are thrown as exceptions, catch and handle in your mini-app
|
|
||||||
- PIN codes must be 6 digits
|
|
||||||
- Amount is in smallest currency unit (cents for USD)
|
|
||||||
- Token is managed internally, no need to provide it
|
|
||||||
- Server URL is loaded from app preferences
|
|
||||||
|
|
||||||
## Integration with flutter_eval
|
|
||||||
|
|
||||||
### Current Status
|
|
||||||
|
|
||||||
**FlutterEval Plugin Initialization: ✅ Complete**
|
|
||||||
- `flutterEvalPlugin` is properly initialized in `lib/modular/miniapp_loader.dart`
|
|
||||||
- Initialized from `main()` during app startup
|
|
||||||
- Plugin is shared between `miniapp_loader.dart` and `lib/modular/registry.dart`
|
|
||||||
- Runtime adds plugin when loading miniapps: `runtime.addPlugin(flutterEvalPlugin)`
|
|
||||||
|
|
||||||
**PaymentBridgePlugin: ✅ Created but not yet registered**
|
|
||||||
- Located at `lib/modular/api/payment_bridge.dart`
|
|
||||||
- Provides simplified wrapper around PaymentApi
|
|
||||||
- Designed for easier integration with eval bridge
|
|
||||||
|
|
||||||
**Full Eval Bridge: ⚠️ Requires Additional Setup**
|
|
||||||
To expose PaymentApi to miniapps through eval, you need to:
|
|
||||||
|
|
||||||
1. **Create EvalPlugin Implementation**
|
|
||||||
```dart
|
|
||||||
// In a new file: lib/modular/api/payment_eval_plugin.dart
|
|
||||||
import 'package:dart_eval/dart_eval_bridge.dart';
|
|
||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
import 'package:island/modular/api/payment_bridge.dart';
|
|
||||||
|
|
||||||
class PaymentEvalPlugin implements EvalPlugin {
|
|
||||||
@override
|
|
||||||
String get identifier => 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
@override
|
|
||||||
void configureForCompile(BridgeDeclarationRegistry registry) {
|
|
||||||
// Define bridge classes for PaymentRequest, PaymentResult, CreateOrderRequest
|
|
||||||
// Requires using @Bind() annotations or manual bridge definition
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void configureForRuntime(Runtime runtime) {
|
|
||||||
// Register functions that miniapps can call
|
|
||||||
// This requires bridge wrapper classes to be generated or created manually
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Generate or Create Bridge Code**
|
|
||||||
- Option A: Use `dart_eval_annotation` package with `@Bind()` annotations
|
|
||||||
- Add annotations to PaymentApi classes
|
|
||||||
- Run `dart run build_runner build` to generate bridge code
|
|
||||||
- Option B: Manually create bridge wrapper classes
|
|
||||||
- Define `$PaymentRequest`, `$PaymentResult`, etc.
|
|
||||||
- Implement `$Instance` interface for each
|
|
||||||
- Register bridge functions in `configureForRuntime`
|
|
||||||
|
|
||||||
3. **Register Plugin in Registry**
|
|
||||||
```dart
|
|
||||||
// In lib/modular/registry.dart
|
|
||||||
import 'package:island/modular/api/payment_eval_plugin.dart';
|
|
||||||
|
|
||||||
Future<PluginLoadResult> loadMiniApp(...) async {
|
|
||||||
// ... existing code ...
|
|
||||||
|
|
||||||
final runtime = Runtime(ByteData.sublistView(bytecode));
|
|
||||||
runtime.addPlugin(flutterEvalPlugin);
|
|
||||||
runtime.addPlugin(PaymentEvalPlugin()); // Add payment API plugin
|
|
||||||
|
|
||||||
// ... rest of loading code
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Mini-app Usage**
|
|
||||||
```dart
|
|
||||||
// mini_app/main.dart
|
|
||||||
// Once bridge is complete, miniapps can access:
|
|
||||||
final paymentBridge = PaymentBridgePlugin.instance;
|
|
||||||
final result = await paymentBridge.processDirectPayment(
|
|
||||||
orderId: 'order_123',
|
|
||||||
amount: 1000,
|
|
||||||
currency: 'USD',
|
|
||||||
pinCode: '123456',
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Simplified Alternative
|
|
||||||
|
|
||||||
For quick testing without full bridge setup, miniapps can use the example pattern:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Simulate API calls in miniapp for testing
|
|
||||||
Future<void> _testPayment() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
try {
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
setState(() => _status = 'Payment successful!');
|
|
||||||
} catch (e) {
|
|
||||||
setState(() => _status = 'Payment failed: $e');
|
|
||||||
} finally {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This pattern is demonstrated in `packages/miniapp-example/lib/main.dart`.
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
- **Never hardcode PIN codes**: Always get from user input
|
|
||||||
- **Use secure storage**: App manages PIN storage securely
|
|
||||||
- **Validate amounts**: Ensure amounts are reasonable
|
|
||||||
- **Handle errors gracefully**: Show user-friendly messages
|
|
||||||
- **Biometric is optional**: Some devices may not support it
|
|
||||||
@@ -1,284 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:island/models/auth.dart';
|
|
||||||
import 'package:island/models/wallet.dart';
|
|
||||||
import 'package:island/widgets/payment/payment_overlay.dart';
|
|
||||||
import 'package:island/pods/config.dart';
|
|
||||||
import 'package:island/pods/network.dart';
|
|
||||||
|
|
||||||
part 'payment.freezed.dart';
|
|
||||||
part 'payment.g.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class PaymentRequest with _$PaymentRequest {
|
|
||||||
const factory PaymentRequest({
|
|
||||||
required String orderId,
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
String? pinCode,
|
|
||||||
@Default(true) bool showOverlay,
|
|
||||||
@Default(true) bool enableBiometric,
|
|
||||||
}) = _PaymentRequest;
|
|
||||||
|
|
||||||
factory PaymentRequest.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PaymentRequestFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class PaymentResult with _$PaymentResult {
|
|
||||||
const factory PaymentResult({
|
|
||||||
required bool success,
|
|
||||||
SnWalletOrder? order,
|
|
||||||
String? error,
|
|
||||||
String? errorCode,
|
|
||||||
}) = _PaymentResult;
|
|
||||||
|
|
||||||
factory PaymentResult.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PaymentResultFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class CreateOrderRequest with _$CreateOrderRequest {
|
|
||||||
const factory CreateOrderRequest({
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
String? appIdentifier,
|
|
||||||
@Default({}) Map<String, dynamic> meta,
|
|
||||||
}) = _CreateOrderRequest;
|
|
||||||
|
|
||||||
factory CreateOrderRequest.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$CreateOrderRequestFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
class PaymentApi {
|
|
||||||
static PaymentApi? _instance;
|
|
||||||
late Dio? _dio;
|
|
||||||
late String _serverUrl;
|
|
||||||
String? _token;
|
|
||||||
|
|
||||||
PaymentApi._internal();
|
|
||||||
|
|
||||||
static PaymentApi get instance {
|
|
||||||
_instance ??= PaymentApi._internal();
|
|
||||||
return _instance!;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _initialize() async {
|
|
||||||
if (_dio == null) {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
_serverUrl =
|
|
||||||
prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
|
||||||
|
|
||||||
final tokenString = prefs.getString(kTokenPairStoreKey);
|
|
||||||
if (tokenString != null) {
|
|
||||||
final appToken = AppToken.fromJson(jsonDecode(tokenString));
|
|
||||||
_token = await getToken(appToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
_dio = Dio(
|
|
||||||
BaseOptions(
|
|
||||||
baseUrl: _serverUrl,
|
|
||||||
connectTimeout: const Duration(seconds: 10),
|
|
||||||
receiveTimeout: const Duration(seconds: 10),
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
_dio?.interceptors.add(
|
|
||||||
InterceptorsWrapper(
|
|
||||||
onRequest: (options, handler) async {
|
|
||||||
if (_token != null) {
|
|
||||||
options.headers['Authorization'] = 'AtField $_token';
|
|
||||||
}
|
|
||||||
return handler.next(options);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SnWalletOrder?> createOrder(CreateOrderRequest request) async {
|
|
||||||
await _initialize();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (_dio == null) return null;
|
|
||||||
final response = await _dio!.post('/pass/orders', data: request.toJson());
|
|
||||||
|
|
||||||
return SnWalletOrder.fromJson(response.data);
|
|
||||||
} catch (e) {
|
|
||||||
throw _parsePaymentError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SnWalletOrder?> processPayment({
|
|
||||||
required String orderId,
|
|
||||||
required String pinCode,
|
|
||||||
bool enableBiometric = true,
|
|
||||||
}) async {
|
|
||||||
await _initialize();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (_dio == null) return null;
|
|
||||||
final response = await _dio!.post(
|
|
||||||
'/pass/orders/$orderId/pay',
|
|
||||||
data: {'pin_code': pinCode},
|
|
||||||
);
|
|
||||||
|
|
||||||
return SnWalletOrder.fromJson(response.data);
|
|
||||||
} catch (e) {
|
|
||||||
throw _parsePaymentError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<PaymentResult> processPaymentWithOverlay({
|
|
||||||
required BuildContext context,
|
|
||||||
PaymentRequest? request,
|
|
||||||
CreateOrderRequest? createOrderRequest,
|
|
||||||
bool enableBiometric = true,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
await _initialize();
|
|
||||||
|
|
||||||
SnWalletOrder order;
|
|
||||||
|
|
||||||
if (request == null && createOrderRequest == null) {
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: 'Either request or createOrderRequest must be provided',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request != null) {
|
|
||||||
order = (await createOrder(createOrderRequest!))!;
|
|
||||||
} else {
|
|
||||||
order = SnWalletOrder(
|
|
||||||
id: request!.orderId,
|
|
||||||
status: 0,
|
|
||||||
currency: request.currency,
|
|
||||||
remarks: request.remarks,
|
|
||||||
appIdentifier: 'mini-app',
|
|
||||||
meta: {},
|
|
||||||
amount: request.amount,
|
|
||||||
expiredAt: DateTime.now().add(const Duration(hours: 1)),
|
|
||||||
payeeWalletId: request.payeeWalletId,
|
|
||||||
transactionId: null,
|
|
||||||
issuerAppId: null,
|
|
||||||
createdAt: DateTime.now(),
|
|
||||||
updatedAt: DateTime.now(),
|
|
||||||
deletedAt: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.mounted) throw PaymentResult(success: false);
|
|
||||||
final result = await PaymentOverlay.show(
|
|
||||||
context: context,
|
|
||||||
order: order,
|
|
||||||
enableBiometric: enableBiometric,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result != null) {
|
|
||||||
return PaymentResult(success: true, order: result);
|
|
||||||
} else {
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: 'Payment was cancelled by user',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
final errorMessage = _parsePaymentError(e);
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: errorMessage,
|
|
||||||
errorCode: e is DioException ? e.response?.statusCode.toString() : null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<PaymentResult> processDirectPayment(PaymentRequest request) async {
|
|
||||||
await _initialize();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (request.pinCode == null) {
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: 'PIN code is required for direct payment processing',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await processPayment(
|
|
||||||
orderId: request.orderId,
|
|
||||||
pinCode: request.pinCode!,
|
|
||||||
enableBiometric: request.enableBiometric,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result != null) {
|
|
||||||
return PaymentResult(success: true, order: result);
|
|
||||||
} else {
|
|
||||||
return PaymentResult(success: false, error: 'Payment failed');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
final errorMessage = _parsePaymentError(e);
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: errorMessage,
|
|
||||||
errorCode: e is DioException ? e.response?.statusCode.toString() : null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _parsePaymentError(dynamic error) {
|
|
||||||
if (error is DioException) {
|
|
||||||
final dioError = error;
|
|
||||||
|
|
||||||
if (dioError.response?.statusCode == 403 ||
|
|
||||||
dioError.response?.statusCode == 401) {
|
|
||||||
return 'invalidPin'.tr();
|
|
||||||
} else if (dioError.response?.statusCode == 400) {
|
|
||||||
return dioError.response?.data?['error'] ?? 'paymentFailed'.tr();
|
|
||||||
} else if (dioError.response?.statusCode == 503) {
|
|
||||||
return 'serviceUnavailable'.tr();
|
|
||||||
} else if (dioError.response?.statusCode == 404) {
|
|
||||||
return 'orderNotFound'.tr();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'networkError'.tr();
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateServerUrl() async {
|
|
||||||
if (_dio == null) return;
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
_serverUrl =
|
|
||||||
prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
|
||||||
_dio!.options.baseUrl = _serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateToken() async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final tokenString = prefs.getString(kTokenPairStoreKey);
|
|
||||||
if (tokenString != null) {
|
|
||||||
final appToken = AppToken.fromJson(jsonDecode(tokenString));
|
|
||||||
_token = await getToken(appToken);
|
|
||||||
} else {
|
|
||||||
_token = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
_dio?.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,860 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'payment.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$PaymentRequest {
|
|
||||||
|
|
||||||
String get orderId; int get amount; String get currency; String? get remarks; String? get payeeWalletId; String? get pinCode; bool get showOverlay; bool get enableBiometric;
|
|
||||||
/// Create a copy of PaymentRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$PaymentRequestCopyWith<PaymentRequest> get copyWith => _$PaymentRequestCopyWithImpl<PaymentRequest>(this as PaymentRequest, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this PaymentRequest to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentRequest&&(identical(other.orderId, orderId) || other.orderId == orderId)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.remarks, remarks) || other.remarks == remarks)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.pinCode, pinCode) || other.pinCode == pinCode)&&(identical(other.showOverlay, showOverlay) || other.showOverlay == showOverlay)&&(identical(other.enableBiometric, enableBiometric) || other.enableBiometric == enableBiometric));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,orderId,amount,currency,remarks,payeeWalletId,pinCode,showOverlay,enableBiometric);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PaymentRequest(orderId: $orderId, amount: $amount, currency: $currency, remarks: $remarks, payeeWalletId: $payeeWalletId, pinCode: $pinCode, showOverlay: $showOverlay, enableBiometric: $enableBiometric)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $PaymentRequestCopyWith<$Res> {
|
|
||||||
factory $PaymentRequestCopyWith(PaymentRequest value, $Res Function(PaymentRequest) _then) = _$PaymentRequestCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String orderId, int amount, String currency, String? remarks, String? payeeWalletId, String? pinCode, bool showOverlay, bool enableBiometric
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$PaymentRequestCopyWithImpl<$Res>
|
|
||||||
implements $PaymentRequestCopyWith<$Res> {
|
|
||||||
_$PaymentRequestCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final PaymentRequest _self;
|
|
||||||
final $Res Function(PaymentRequest) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? orderId = null,Object? amount = null,Object? currency = null,Object? remarks = freezed,Object? payeeWalletId = freezed,Object? pinCode = freezed,Object? showOverlay = null,Object? enableBiometric = null,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
orderId: null == orderId ? _self.orderId : orderId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,pinCode: freezed == pinCode ? _self.pinCode : pinCode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,showOverlay: null == showOverlay ? _self.showOverlay : showOverlay // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,enableBiometric: null == enableBiometric ? _self.enableBiometric : enableBiometric // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [PaymentRequest].
|
|
||||||
extension PaymentRequestPatterns on PaymentRequest {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PaymentRequest value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PaymentRequest value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PaymentRequest value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String orderId, int amount, String currency, String? remarks, String? payeeWalletId, String? pinCode, bool showOverlay, bool enableBiometric)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest() when $default != null:
|
|
||||||
return $default(_that.orderId,_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.pinCode,_that.showOverlay,_that.enableBiometric);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String orderId, int amount, String currency, String? remarks, String? payeeWalletId, String? pinCode, bool showOverlay, bool enableBiometric) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest():
|
|
||||||
return $default(_that.orderId,_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.pinCode,_that.showOverlay,_that.enableBiometric);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String orderId, int amount, String currency, String? remarks, String? payeeWalletId, String? pinCode, bool showOverlay, bool enableBiometric)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentRequest() when $default != null:
|
|
||||||
return $default(_that.orderId,_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.pinCode,_that.showOverlay,_that.enableBiometric);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _PaymentRequest implements PaymentRequest {
|
|
||||||
const _PaymentRequest({required this.orderId, required this.amount, required this.currency, this.remarks, this.payeeWalletId, this.pinCode, this.showOverlay = true, this.enableBiometric = true});
|
|
||||||
factory _PaymentRequest.fromJson(Map<String, dynamic> json) => _$PaymentRequestFromJson(json);
|
|
||||||
|
|
||||||
@override final String orderId;
|
|
||||||
@override final int amount;
|
|
||||||
@override final String currency;
|
|
||||||
@override final String? remarks;
|
|
||||||
@override final String? payeeWalletId;
|
|
||||||
@override final String? pinCode;
|
|
||||||
@override@JsonKey() final bool showOverlay;
|
|
||||||
@override@JsonKey() final bool enableBiometric;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$PaymentRequestCopyWith<_PaymentRequest> get copyWith => __$PaymentRequestCopyWithImpl<_PaymentRequest>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$PaymentRequestToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentRequest&&(identical(other.orderId, orderId) || other.orderId == orderId)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.remarks, remarks) || other.remarks == remarks)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.pinCode, pinCode) || other.pinCode == pinCode)&&(identical(other.showOverlay, showOverlay) || other.showOverlay == showOverlay)&&(identical(other.enableBiometric, enableBiometric) || other.enableBiometric == enableBiometric));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,orderId,amount,currency,remarks,payeeWalletId,pinCode,showOverlay,enableBiometric);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PaymentRequest(orderId: $orderId, amount: $amount, currency: $currency, remarks: $remarks, payeeWalletId: $payeeWalletId, pinCode: $pinCode, showOverlay: $showOverlay, enableBiometric: $enableBiometric)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$PaymentRequestCopyWith<$Res> implements $PaymentRequestCopyWith<$Res> {
|
|
||||||
factory _$PaymentRequestCopyWith(_PaymentRequest value, $Res Function(_PaymentRequest) _then) = __$PaymentRequestCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
String orderId, int amount, String currency, String? remarks, String? payeeWalletId, String? pinCode, bool showOverlay, bool enableBiometric
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$PaymentRequestCopyWithImpl<$Res>
|
|
||||||
implements _$PaymentRequestCopyWith<$Res> {
|
|
||||||
__$PaymentRequestCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _PaymentRequest _self;
|
|
||||||
final $Res Function(_PaymentRequest) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? orderId = null,Object? amount = null,Object? currency = null,Object? remarks = freezed,Object? payeeWalletId = freezed,Object? pinCode = freezed,Object? showOverlay = null,Object? enableBiometric = null,}) {
|
|
||||||
return _then(_PaymentRequest(
|
|
||||||
orderId: null == orderId ? _self.orderId : orderId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,pinCode: freezed == pinCode ? _self.pinCode : pinCode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,showOverlay: null == showOverlay ? _self.showOverlay : showOverlay // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,enableBiometric: null == enableBiometric ? _self.enableBiometric : enableBiometric // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$PaymentResult {
|
|
||||||
|
|
||||||
bool get success; SnWalletOrder? get order; String? get error; String? get errorCode;
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$PaymentResultCopyWith<PaymentResult> get copyWith => _$PaymentResultCopyWithImpl<PaymentResult>(this as PaymentResult, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this PaymentResult to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentResult&&(identical(other.success, success) || other.success == success)&&(identical(other.order, order) || other.order == order)&&(identical(other.error, error) || other.error == error)&&(identical(other.errorCode, errorCode) || other.errorCode == errorCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,order,error,errorCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PaymentResult(success: $success, order: $order, error: $error, errorCode: $errorCode)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $PaymentResultCopyWith<$Res> {
|
|
||||||
factory $PaymentResultCopyWith(PaymentResult value, $Res Function(PaymentResult) _then) = _$PaymentResultCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
bool success, SnWalletOrder? order, String? error, String? errorCode
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$SnWalletOrderCopyWith<$Res>? get order;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$PaymentResultCopyWithImpl<$Res>
|
|
||||||
implements $PaymentResultCopyWith<$Res> {
|
|
||||||
_$PaymentResultCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final PaymentResult _self;
|
|
||||||
final $Res Function(PaymentResult) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? success = null,Object? order = freezed,Object? error = freezed,Object? errorCode = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,order: freezed == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
|
||||||
as SnWalletOrder?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,errorCode: freezed == errorCode ? _self.errorCode : errorCode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnWalletOrderCopyWith<$Res>? get order {
|
|
||||||
if (_self.order == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnWalletOrderCopyWith<$Res>(_self.order!, (value) {
|
|
||||||
return _then(_self.copyWith(order: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [PaymentResult].
|
|
||||||
extension PaymentResultPatterns on PaymentResult {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PaymentResult value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PaymentResult value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PaymentResult value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool success, SnWalletOrder? order, String? error, String? errorCode)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult() when $default != null:
|
|
||||||
return $default(_that.success,_that.order,_that.error,_that.errorCode);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool success, SnWalletOrder? order, String? error, String? errorCode) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult():
|
|
||||||
return $default(_that.success,_that.order,_that.error,_that.errorCode);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool success, SnWalletOrder? order, String? error, String? errorCode)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PaymentResult() when $default != null:
|
|
||||||
return $default(_that.success,_that.order,_that.error,_that.errorCode);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _PaymentResult implements PaymentResult {
|
|
||||||
const _PaymentResult({required this.success, this.order, this.error, this.errorCode});
|
|
||||||
factory _PaymentResult.fromJson(Map<String, dynamic> json) => _$PaymentResultFromJson(json);
|
|
||||||
|
|
||||||
@override final bool success;
|
|
||||||
@override final SnWalletOrder? order;
|
|
||||||
@override final String? error;
|
|
||||||
@override final String? errorCode;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$PaymentResultCopyWith<_PaymentResult> get copyWith => __$PaymentResultCopyWithImpl<_PaymentResult>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$PaymentResultToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentResult&&(identical(other.success, success) || other.success == success)&&(identical(other.order, order) || other.order == order)&&(identical(other.error, error) || other.error == error)&&(identical(other.errorCode, errorCode) || other.errorCode == errorCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,order,error,errorCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PaymentResult(success: $success, order: $order, error: $error, errorCode: $errorCode)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$PaymentResultCopyWith<$Res> implements $PaymentResultCopyWith<$Res> {
|
|
||||||
factory _$PaymentResultCopyWith(_PaymentResult value, $Res Function(_PaymentResult) _then) = __$PaymentResultCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
bool success, SnWalletOrder? order, String? error, String? errorCode
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@override $SnWalletOrderCopyWith<$Res>? get order;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$PaymentResultCopyWithImpl<$Res>
|
|
||||||
implements _$PaymentResultCopyWith<$Res> {
|
|
||||||
__$PaymentResultCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _PaymentResult _self;
|
|
||||||
final $Res Function(_PaymentResult) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? success = null,Object? order = freezed,Object? error = freezed,Object? errorCode = freezed,}) {
|
|
||||||
return _then(_PaymentResult(
|
|
||||||
success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,order: freezed == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
|
||||||
as SnWalletOrder?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,errorCode: freezed == errorCode ? _self.errorCode : errorCode // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of PaymentResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnWalletOrderCopyWith<$Res>? get order {
|
|
||||||
if (_self.order == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnWalletOrderCopyWith<$Res>(_self.order!, (value) {
|
|
||||||
return _then(_self.copyWith(order: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$CreateOrderRequest {
|
|
||||||
|
|
||||||
int get amount; String get currency; String? get remarks; String? get payeeWalletId; String? get appIdentifier; Map<String, dynamic> get meta;
|
|
||||||
/// Create a copy of CreateOrderRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$CreateOrderRequestCopyWith<CreateOrderRequest> get copyWith => _$CreateOrderRequestCopyWithImpl<CreateOrderRequest>(this as CreateOrderRequest, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this CreateOrderRequest to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CreateOrderRequest&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.remarks, remarks) || other.remarks == remarks)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.appIdentifier, appIdentifier) || other.appIdentifier == appIdentifier)&&const DeepCollectionEquality().equals(other.meta, meta));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,amount,currency,remarks,payeeWalletId,appIdentifier,const DeepCollectionEquality().hash(meta));
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'CreateOrderRequest(amount: $amount, currency: $currency, remarks: $remarks, payeeWalletId: $payeeWalletId, appIdentifier: $appIdentifier, meta: $meta)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $CreateOrderRequestCopyWith<$Res> {
|
|
||||||
factory $CreateOrderRequestCopyWith(CreateOrderRequest value, $Res Function(CreateOrderRequest) _then) = _$CreateOrderRequestCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
int amount, String currency, String? remarks, String? payeeWalletId, String? appIdentifier, Map<String, dynamic> meta
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$CreateOrderRequestCopyWithImpl<$Res>
|
|
||||||
implements $CreateOrderRequestCopyWith<$Res> {
|
|
||||||
_$CreateOrderRequestCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final CreateOrderRequest _self;
|
|
||||||
final $Res Function(CreateOrderRequest) _then;
|
|
||||||
|
|
||||||
/// Create a copy of CreateOrderRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? amount = null,Object? currency = null,Object? remarks = freezed,Object? payeeWalletId = freezed,Object? appIdentifier = freezed,Object? meta = null,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,appIdentifier: freezed == appIdentifier ? _self.appIdentifier : appIdentifier // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,meta: null == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [CreateOrderRequest].
|
|
||||||
extension CreateOrderRequestPatterns on CreateOrderRequest {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CreateOrderRequest value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CreateOrderRequest value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CreateOrderRequest value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int amount, String currency, String? remarks, String? payeeWalletId, String? appIdentifier, Map<String, dynamic> meta)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest() when $default != null:
|
|
||||||
return $default(_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.appIdentifier,_that.meta);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int amount, String currency, String? remarks, String? payeeWalletId, String? appIdentifier, Map<String, dynamic> meta) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest():
|
|
||||||
return $default(_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.appIdentifier,_that.meta);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int amount, String currency, String? remarks, String? payeeWalletId, String? appIdentifier, Map<String, dynamic> meta)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _CreateOrderRequest() when $default != null:
|
|
||||||
return $default(_that.amount,_that.currency,_that.remarks,_that.payeeWalletId,_that.appIdentifier,_that.meta);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _CreateOrderRequest implements CreateOrderRequest {
|
|
||||||
const _CreateOrderRequest({required this.amount, required this.currency, this.remarks, this.payeeWalletId, this.appIdentifier, final Map<String, dynamic> meta = const {}}): _meta = meta;
|
|
||||||
factory _CreateOrderRequest.fromJson(Map<String, dynamic> json) => _$CreateOrderRequestFromJson(json);
|
|
||||||
|
|
||||||
@override final int amount;
|
|
||||||
@override final String currency;
|
|
||||||
@override final String? remarks;
|
|
||||||
@override final String? payeeWalletId;
|
|
||||||
@override final String? appIdentifier;
|
|
||||||
final Map<String, dynamic> _meta;
|
|
||||||
@override@JsonKey() Map<String, dynamic> get meta {
|
|
||||||
if (_meta is EqualUnmodifiableMapView) return _meta;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableMapView(_meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Create a copy of CreateOrderRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$CreateOrderRequestCopyWith<_CreateOrderRequest> get copyWith => __$CreateOrderRequestCopyWithImpl<_CreateOrderRequest>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$CreateOrderRequestToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CreateOrderRequest&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.remarks, remarks) || other.remarks == remarks)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.appIdentifier, appIdentifier) || other.appIdentifier == appIdentifier)&&const DeepCollectionEquality().equals(other._meta, _meta));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,amount,currency,remarks,payeeWalletId,appIdentifier,const DeepCollectionEquality().hash(_meta));
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'CreateOrderRequest(amount: $amount, currency: $currency, remarks: $remarks, payeeWalletId: $payeeWalletId, appIdentifier: $appIdentifier, meta: $meta)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$CreateOrderRequestCopyWith<$Res> implements $CreateOrderRequestCopyWith<$Res> {
|
|
||||||
factory _$CreateOrderRequestCopyWith(_CreateOrderRequest value, $Res Function(_CreateOrderRequest) _then) = __$CreateOrderRequestCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
int amount, String currency, String? remarks, String? payeeWalletId, String? appIdentifier, Map<String, dynamic> meta
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$CreateOrderRequestCopyWithImpl<$Res>
|
|
||||||
implements _$CreateOrderRequestCopyWith<$Res> {
|
|
||||||
__$CreateOrderRequestCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _CreateOrderRequest _self;
|
|
||||||
final $Res Function(_CreateOrderRequest) _then;
|
|
||||||
|
|
||||||
/// Create a copy of CreateOrderRequest
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? amount = null,Object? currency = null,Object? remarks = freezed,Object? payeeWalletId = freezed,Object? appIdentifier = freezed,Object? meta = null,}) {
|
|
||||||
return _then(_CreateOrderRequest(
|
|
||||||
amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,appIdentifier: freezed == appIdentifier ? _self.appIdentifier : appIdentifier // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,meta: null == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'payment.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_PaymentRequest _$PaymentRequestFromJson(Map<String, dynamic> json) =>
|
|
||||||
_PaymentRequest(
|
|
||||||
orderId: json['order_id'] as String,
|
|
||||||
amount: (json['amount'] as num).toInt(),
|
|
||||||
currency: json['currency'] as String,
|
|
||||||
remarks: json['remarks'] as String?,
|
|
||||||
payeeWalletId: json['payee_wallet_id'] as String?,
|
|
||||||
pinCode: json['pin_code'] as String?,
|
|
||||||
showOverlay: json['show_overlay'] as bool? ?? true,
|
|
||||||
enableBiometric: json['enable_biometric'] as bool? ?? true,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PaymentRequestToJson(_PaymentRequest instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'order_id': instance.orderId,
|
|
||||||
'amount': instance.amount,
|
|
||||||
'currency': instance.currency,
|
|
||||||
'remarks': instance.remarks,
|
|
||||||
'payee_wallet_id': instance.payeeWalletId,
|
|
||||||
'pin_code': instance.pinCode,
|
|
||||||
'show_overlay': instance.showOverlay,
|
|
||||||
'enable_biometric': instance.enableBiometric,
|
|
||||||
};
|
|
||||||
|
|
||||||
_PaymentResult _$PaymentResultFromJson(Map<String, dynamic> json) =>
|
|
||||||
_PaymentResult(
|
|
||||||
success: json['success'] as bool,
|
|
||||||
order: json['order'] == null
|
|
||||||
? null
|
|
||||||
: SnWalletOrder.fromJson(json['order'] as Map<String, dynamic>),
|
|
||||||
error: json['error'] as String?,
|
|
||||||
errorCode: json['error_code'] as String?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PaymentResultToJson(_PaymentResult instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'success': instance.success,
|
|
||||||
'order': instance.order?.toJson(),
|
|
||||||
'error': instance.error,
|
|
||||||
'error_code': instance.errorCode,
|
|
||||||
};
|
|
||||||
|
|
||||||
_CreateOrderRequest _$CreateOrderRequestFromJson(Map<String, dynamic> json) =>
|
|
||||||
_CreateOrderRequest(
|
|
||||||
amount: (json['amount'] as num).toInt(),
|
|
||||||
currency: json['currency'] as String,
|
|
||||||
remarks: json['remarks'] as String?,
|
|
||||||
payeeWalletId: json['payee_wallet_id'] as String?,
|
|
||||||
appIdentifier: json['app_identifier'] as String?,
|
|
||||||
meta: json['meta'] as Map<String, dynamic>? ?? const {},
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$CreateOrderRequestToJson(_CreateOrderRequest instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'amount': instance.amount,
|
|
||||||
'currency': instance.currency,
|
|
||||||
'remarks': instance.remarks,
|
|
||||||
'payee_wallet_id': instance.payeeWalletId,
|
|
||||||
'app_identifier': instance.appIdentifier,
|
|
||||||
'meta': instance.meta,
|
|
||||||
};
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
class PaymentBridgePlugin {
|
|
||||||
static final instance = PaymentBridgePlugin._internal();
|
|
||||||
PaymentBridgePlugin._internal();
|
|
||||||
|
|
||||||
Future<PaymentResult> createOrder({
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
String? appIdentifier,
|
|
||||||
Map<String, dynamic>? meta,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final request = CreateOrderRequest(
|
|
||||||
amount: amount,
|
|
||||||
currency: currency,
|
|
||||||
remarks: remarks,
|
|
||||||
payeeWalletId: payeeWalletId,
|
|
||||||
appIdentifier: appIdentifier,
|
|
||||||
meta: meta ?? {},
|
|
||||||
);
|
|
||||||
final order = await PaymentApi.instance.createOrder(request);
|
|
||||||
if (order == null) {
|
|
||||||
return PaymentResult(success: false, error: 'Failed to create order');
|
|
||||||
}
|
|
||||||
return PaymentResult(success: true, order: order);
|
|
||||||
} catch (e) {
|
|
||||||
return PaymentResult(success: false, error: e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<PaymentResult> processPayment({
|
|
||||||
required String orderId,
|
|
||||||
required String pinCode,
|
|
||||||
bool enableBiometric = true,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final order = await PaymentApi.instance.processPayment(
|
|
||||||
orderId: orderId,
|
|
||||||
pinCode: pinCode,
|
|
||||||
enableBiometric: enableBiometric,
|
|
||||||
);
|
|
||||||
if (order == null) {
|
|
||||||
return PaymentResult(
|
|
||||||
success: false,
|
|
||||||
error: 'Failed to process payment',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return PaymentResult(success: true, order: order);
|
|
||||||
} catch (e) {
|
|
||||||
return PaymentResult(success: false, error: e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<PaymentResult> processDirectPayment({
|
|
||||||
required String orderId,
|
|
||||||
required int amount,
|
|
||||||
required String currency,
|
|
||||||
required String pinCode,
|
|
||||||
String? remarks,
|
|
||||||
String? payeeWalletId,
|
|
||||||
bool enableBiometric = true,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final request = PaymentRequest(
|
|
||||||
orderId: orderId,
|
|
||||||
amount: amount,
|
|
||||||
currency: currency,
|
|
||||||
remarks: remarks,
|
|
||||||
payeeWalletId: payeeWalletId,
|
|
||||||
pinCode: pinCode,
|
|
||||||
showOverlay: false,
|
|
||||||
enableBiometric: enableBiometric,
|
|
||||||
);
|
|
||||||
return await PaymentApi.instance.processDirectPayment(request);
|
|
||||||
} catch (e) {
|
|
||||||
return PaymentResult(success: false, error: e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'interface.freezed.dart';
|
|
||||||
part 'interface.g.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class PluginMetadata with _$PluginMetadata {
|
|
||||||
const factory PluginMetadata({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String version,
|
|
||||||
required String description,
|
|
||||||
String? author,
|
|
||||||
DateTime? updatedAt,
|
|
||||||
}) = _PluginMetadata;
|
|
||||||
|
|
||||||
factory PluginMetadata.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PluginMetadataFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Plugin {
|
|
||||||
PluginMetadata get metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class RawPlugin extends Plugin {}
|
|
||||||
|
|
||||||
abstract class MiniApp extends Plugin {
|
|
||||||
Widget buildEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class MiniAppMetadata with _$MiniAppMetadata {
|
|
||||||
const factory MiniAppMetadata({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String version,
|
|
||||||
required String description,
|
|
||||||
String? author,
|
|
||||||
String? iconUrl,
|
|
||||||
required String downloadUrl,
|
|
||||||
String? localCachePath,
|
|
||||||
DateTime? lastUpdated,
|
|
||||||
DateTime? lastChecked,
|
|
||||||
@Default(false) bool isEnabled,
|
|
||||||
@Default(0) int localVersion,
|
|
||||||
int? sizeBytes,
|
|
||||||
}) = _MiniAppMetadata;
|
|
||||||
|
|
||||||
factory MiniAppMetadata.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$MiniAppMetadataFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class MiniAppServerInfo with _$MiniAppServerInfo {
|
|
||||||
const factory MiniAppServerInfo({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String version,
|
|
||||||
required String description,
|
|
||||||
String? author,
|
|
||||||
String? iconUrl,
|
|
||||||
required String downloadUrl,
|
|
||||||
required DateTime updatedAt,
|
|
||||||
required int sizeBytes,
|
|
||||||
}) = _MiniAppServerInfo;
|
|
||||||
|
|
||||||
factory MiniAppServerInfo.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$MiniAppServerInfoFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PluginLoadResult { success, failed, alreadyLoaded }
|
|
||||||
@@ -1,860 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'interface.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$PluginMetadata {
|
|
||||||
|
|
||||||
String get id; String get name; String get version; String get description; String? get author; DateTime? get updatedAt;
|
|
||||||
/// Create a copy of PluginMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$PluginMetadataCopyWith<PluginMetadata> get copyWith => _$PluginMetadataCopyWithImpl<PluginMetadata>(this as PluginMetadata, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this PluginMetadata to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PluginMetadata&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,updatedAt);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PluginMetadata(id: $id, name: $name, version: $version, description: $description, author: $author, updatedAt: $updatedAt)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $PluginMetadataCopyWith<$Res> {
|
|
||||||
factory $PluginMetadataCopyWith(PluginMetadata value, $Res Function(PluginMetadata) _then) = _$PluginMetadataCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, DateTime? updatedAt
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$PluginMetadataCopyWithImpl<$Res>
|
|
||||||
implements $PluginMetadataCopyWith<$Res> {
|
|
||||||
_$PluginMetadataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final PluginMetadata _self;
|
|
||||||
final $Res Function(PluginMetadata) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PluginMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? updatedAt = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [PluginMetadata].
|
|
||||||
extension PluginMetadataPatterns on PluginMetadata {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PluginMetadata value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PluginMetadata value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PluginMetadata value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, DateTime? updatedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.updatedAt);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, DateTime? updatedAt) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata():
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.updatedAt);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String version, String description, String? author, DateTime? updatedAt)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _PluginMetadata() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.updatedAt);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _PluginMetadata implements PluginMetadata {
|
|
||||||
const _PluginMetadata({required this.id, required this.name, required this.version, required this.description, this.author, this.updatedAt});
|
|
||||||
factory _PluginMetadata.fromJson(Map<String, dynamic> json) => _$PluginMetadataFromJson(json);
|
|
||||||
|
|
||||||
@override final String id;
|
|
||||||
@override final String name;
|
|
||||||
@override final String version;
|
|
||||||
@override final String description;
|
|
||||||
@override final String? author;
|
|
||||||
@override final DateTime? updatedAt;
|
|
||||||
|
|
||||||
/// Create a copy of PluginMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$PluginMetadataCopyWith<_PluginMetadata> get copyWith => __$PluginMetadataCopyWithImpl<_PluginMetadata>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$PluginMetadataToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PluginMetadata&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,updatedAt);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PluginMetadata(id: $id, name: $name, version: $version, description: $description, author: $author, updatedAt: $updatedAt)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$PluginMetadataCopyWith<$Res> implements $PluginMetadataCopyWith<$Res> {
|
|
||||||
factory _$PluginMetadataCopyWith(_PluginMetadata value, $Res Function(_PluginMetadata) _then) = __$PluginMetadataCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, DateTime? updatedAt
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$PluginMetadataCopyWithImpl<$Res>
|
|
||||||
implements _$PluginMetadataCopyWith<$Res> {
|
|
||||||
__$PluginMetadataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _PluginMetadata _self;
|
|
||||||
final $Res Function(_PluginMetadata) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PluginMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? updatedAt = freezed,}) {
|
|
||||||
return _then(_PluginMetadata(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$MiniAppMetadata {
|
|
||||||
|
|
||||||
String get id; String get name; String get version; String get description; String? get author; String? get iconUrl; String get downloadUrl; String? get localCachePath; DateTime? get lastUpdated; DateTime? get lastChecked; bool get isEnabled; int get localVersion; int? get sizeBytes;
|
|
||||||
/// Create a copy of MiniAppMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$MiniAppMetadataCopyWith<MiniAppMetadata> get copyWith => _$MiniAppMetadataCopyWithImpl<MiniAppMetadata>(this as MiniAppMetadata, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this MiniAppMetadata to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MiniAppMetadata&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.iconUrl, iconUrl) || other.iconUrl == iconUrl)&&(identical(other.downloadUrl, downloadUrl) || other.downloadUrl == downloadUrl)&&(identical(other.localCachePath, localCachePath) || other.localCachePath == localCachePath)&&(identical(other.lastUpdated, lastUpdated) || other.lastUpdated == lastUpdated)&&(identical(other.lastChecked, lastChecked) || other.lastChecked == lastChecked)&&(identical(other.isEnabled, isEnabled) || other.isEnabled == isEnabled)&&(identical(other.localVersion, localVersion) || other.localVersion == localVersion)&&(identical(other.sizeBytes, sizeBytes) || other.sizeBytes == sizeBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,iconUrl,downloadUrl,localCachePath,lastUpdated,lastChecked,isEnabled,localVersion,sizeBytes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppMetadata(id: $id, name: $name, version: $version, description: $description, author: $author, iconUrl: $iconUrl, downloadUrl: $downloadUrl, localCachePath: $localCachePath, lastUpdated: $lastUpdated, lastChecked: $lastChecked, isEnabled: $isEnabled, localVersion: $localVersion, sizeBytes: $sizeBytes)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $MiniAppMetadataCopyWith<$Res> {
|
|
||||||
factory $MiniAppMetadataCopyWith(MiniAppMetadata value, $Res Function(MiniAppMetadata) _then) = _$MiniAppMetadataCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, String? localCachePath, DateTime? lastUpdated, DateTime? lastChecked, bool isEnabled, int localVersion, int? sizeBytes
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$MiniAppMetadataCopyWithImpl<$Res>
|
|
||||||
implements $MiniAppMetadataCopyWith<$Res> {
|
|
||||||
_$MiniAppMetadataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final MiniAppMetadata _self;
|
|
||||||
final $Res Function(MiniAppMetadata) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? iconUrl = freezed,Object? downloadUrl = null,Object? localCachePath = freezed,Object? lastUpdated = freezed,Object? lastChecked = freezed,Object? isEnabled = null,Object? localVersion = null,Object? sizeBytes = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,iconUrl: freezed == iconUrl ? _self.iconUrl : iconUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,downloadUrl: null == downloadUrl ? _self.downloadUrl : downloadUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,localCachePath: freezed == localCachePath ? _self.localCachePath : localCachePath // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,lastUpdated: freezed == lastUpdated ? _self.lastUpdated : lastUpdated // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,lastChecked: freezed == lastChecked ? _self.lastChecked : lastChecked // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,isEnabled: null == isEnabled ? _self.isEnabled : isEnabled // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,localVersion: null == localVersion ? _self.localVersion : localVersion // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,sizeBytes: freezed == sizeBytes ? _self.sizeBytes : sizeBytes // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [MiniAppMetadata].
|
|
||||||
extension MiniAppMetadataPatterns on MiniAppMetadata {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MiniAppMetadata value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MiniAppMetadata value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MiniAppMetadata value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, String? localCachePath, DateTime? lastUpdated, DateTime? lastChecked, bool isEnabled, int localVersion, int? sizeBytes)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.localCachePath,_that.lastUpdated,_that.lastChecked,_that.isEnabled,_that.localVersion,_that.sizeBytes);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, String? localCachePath, DateTime? lastUpdated, DateTime? lastChecked, bool isEnabled, int localVersion, int? sizeBytes) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata():
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.localCachePath,_that.lastUpdated,_that.lastChecked,_that.isEnabled,_that.localVersion,_that.sizeBytes);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, String? localCachePath, DateTime? lastUpdated, DateTime? lastChecked, bool isEnabled, int localVersion, int? sizeBytes)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppMetadata() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.localCachePath,_that.lastUpdated,_that.lastChecked,_that.isEnabled,_that.localVersion,_that.sizeBytes);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _MiniAppMetadata implements MiniAppMetadata {
|
|
||||||
const _MiniAppMetadata({required this.id, required this.name, required this.version, required this.description, this.author, this.iconUrl, required this.downloadUrl, this.localCachePath, this.lastUpdated, this.lastChecked, this.isEnabled = false, this.localVersion = 0, this.sizeBytes});
|
|
||||||
factory _MiniAppMetadata.fromJson(Map<String, dynamic> json) => _$MiniAppMetadataFromJson(json);
|
|
||||||
|
|
||||||
@override final String id;
|
|
||||||
@override final String name;
|
|
||||||
@override final String version;
|
|
||||||
@override final String description;
|
|
||||||
@override final String? author;
|
|
||||||
@override final String? iconUrl;
|
|
||||||
@override final String downloadUrl;
|
|
||||||
@override final String? localCachePath;
|
|
||||||
@override final DateTime? lastUpdated;
|
|
||||||
@override final DateTime? lastChecked;
|
|
||||||
@override@JsonKey() final bool isEnabled;
|
|
||||||
@override@JsonKey() final int localVersion;
|
|
||||||
@override final int? sizeBytes;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$MiniAppMetadataCopyWith<_MiniAppMetadata> get copyWith => __$MiniAppMetadataCopyWithImpl<_MiniAppMetadata>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$MiniAppMetadataToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MiniAppMetadata&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.iconUrl, iconUrl) || other.iconUrl == iconUrl)&&(identical(other.downloadUrl, downloadUrl) || other.downloadUrl == downloadUrl)&&(identical(other.localCachePath, localCachePath) || other.localCachePath == localCachePath)&&(identical(other.lastUpdated, lastUpdated) || other.lastUpdated == lastUpdated)&&(identical(other.lastChecked, lastChecked) || other.lastChecked == lastChecked)&&(identical(other.isEnabled, isEnabled) || other.isEnabled == isEnabled)&&(identical(other.localVersion, localVersion) || other.localVersion == localVersion)&&(identical(other.sizeBytes, sizeBytes) || other.sizeBytes == sizeBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,iconUrl,downloadUrl,localCachePath,lastUpdated,lastChecked,isEnabled,localVersion,sizeBytes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppMetadata(id: $id, name: $name, version: $version, description: $description, author: $author, iconUrl: $iconUrl, downloadUrl: $downloadUrl, localCachePath: $localCachePath, lastUpdated: $lastUpdated, lastChecked: $lastChecked, isEnabled: $isEnabled, localVersion: $localVersion, sizeBytes: $sizeBytes)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$MiniAppMetadataCopyWith<$Res> implements $MiniAppMetadataCopyWith<$Res> {
|
|
||||||
factory _$MiniAppMetadataCopyWith(_MiniAppMetadata value, $Res Function(_MiniAppMetadata) _then) = __$MiniAppMetadataCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, String? localCachePath, DateTime? lastUpdated, DateTime? lastChecked, bool isEnabled, int localVersion, int? sizeBytes
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$MiniAppMetadataCopyWithImpl<$Res>
|
|
||||||
implements _$MiniAppMetadataCopyWith<$Res> {
|
|
||||||
__$MiniAppMetadataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _MiniAppMetadata _self;
|
|
||||||
final $Res Function(_MiniAppMetadata) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppMetadata
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? iconUrl = freezed,Object? downloadUrl = null,Object? localCachePath = freezed,Object? lastUpdated = freezed,Object? lastChecked = freezed,Object? isEnabled = null,Object? localVersion = null,Object? sizeBytes = freezed,}) {
|
|
||||||
return _then(_MiniAppMetadata(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,iconUrl: freezed == iconUrl ? _self.iconUrl : iconUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,downloadUrl: null == downloadUrl ? _self.downloadUrl : downloadUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,localCachePath: freezed == localCachePath ? _self.localCachePath : localCachePath // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,lastUpdated: freezed == lastUpdated ? _self.lastUpdated : lastUpdated // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,lastChecked: freezed == lastChecked ? _self.lastChecked : lastChecked // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,isEnabled: null == isEnabled ? _self.isEnabled : isEnabled // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,localVersion: null == localVersion ? _self.localVersion : localVersion // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,sizeBytes: freezed == sizeBytes ? _self.sizeBytes : sizeBytes // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$MiniAppServerInfo {
|
|
||||||
|
|
||||||
String get id; String get name; String get version; String get description; String? get author; String? get iconUrl; String get downloadUrl; DateTime get updatedAt; int get sizeBytes;
|
|
||||||
/// Create a copy of MiniAppServerInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$MiniAppServerInfoCopyWith<MiniAppServerInfo> get copyWith => _$MiniAppServerInfoCopyWithImpl<MiniAppServerInfo>(this as MiniAppServerInfo, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this MiniAppServerInfo to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MiniAppServerInfo&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.iconUrl, iconUrl) || other.iconUrl == iconUrl)&&(identical(other.downloadUrl, downloadUrl) || other.downloadUrl == downloadUrl)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.sizeBytes, sizeBytes) || other.sizeBytes == sizeBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,iconUrl,downloadUrl,updatedAt,sizeBytes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppServerInfo(id: $id, name: $name, version: $version, description: $description, author: $author, iconUrl: $iconUrl, downloadUrl: $downloadUrl, updatedAt: $updatedAt, sizeBytes: $sizeBytes)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $MiniAppServerInfoCopyWith<$Res> {
|
|
||||||
factory $MiniAppServerInfoCopyWith(MiniAppServerInfo value, $Res Function(MiniAppServerInfo) _then) = _$MiniAppServerInfoCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, DateTime updatedAt, int sizeBytes
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$MiniAppServerInfoCopyWithImpl<$Res>
|
|
||||||
implements $MiniAppServerInfoCopyWith<$Res> {
|
|
||||||
_$MiniAppServerInfoCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final MiniAppServerInfo _self;
|
|
||||||
final $Res Function(MiniAppServerInfo) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppServerInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? iconUrl = freezed,Object? downloadUrl = null,Object? updatedAt = null,Object? sizeBytes = null,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,iconUrl: freezed == iconUrl ? _self.iconUrl : iconUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,downloadUrl: null == downloadUrl ? _self.downloadUrl : downloadUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime,sizeBytes: null == sizeBytes ? _self.sizeBytes : sizeBytes // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [MiniAppServerInfo].
|
|
||||||
extension MiniAppServerInfoPatterns on MiniAppServerInfo {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MiniAppServerInfo value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MiniAppServerInfo value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MiniAppServerInfo value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, DateTime updatedAt, int sizeBytes)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.updatedAt,_that.sizeBytes);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, DateTime updatedAt, int sizeBytes) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo():
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.updatedAt,_that.sizeBytes);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, DateTime updatedAt, int sizeBytes)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppServerInfo() when $default != null:
|
|
||||||
return $default(_that.id,_that.name,_that.version,_that.description,_that.author,_that.iconUrl,_that.downloadUrl,_that.updatedAt,_that.sizeBytes);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _MiniAppServerInfo implements MiniAppServerInfo {
|
|
||||||
const _MiniAppServerInfo({required this.id, required this.name, required this.version, required this.description, this.author, this.iconUrl, required this.downloadUrl, required this.updatedAt, required this.sizeBytes});
|
|
||||||
factory _MiniAppServerInfo.fromJson(Map<String, dynamic> json) => _$MiniAppServerInfoFromJson(json);
|
|
||||||
|
|
||||||
@override final String id;
|
|
||||||
@override final String name;
|
|
||||||
@override final String version;
|
|
||||||
@override final String description;
|
|
||||||
@override final String? author;
|
|
||||||
@override final String? iconUrl;
|
|
||||||
@override final String downloadUrl;
|
|
||||||
@override final DateTime updatedAt;
|
|
||||||
@override final int sizeBytes;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppServerInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$MiniAppServerInfoCopyWith<_MiniAppServerInfo> get copyWith => __$MiniAppServerInfoCopyWithImpl<_MiniAppServerInfo>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$MiniAppServerInfoToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MiniAppServerInfo&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.version, version) || other.version == version)&&(identical(other.description, description) || other.description == description)&&(identical(other.author, author) || other.author == author)&&(identical(other.iconUrl, iconUrl) || other.iconUrl == iconUrl)&&(identical(other.downloadUrl, downloadUrl) || other.downloadUrl == downloadUrl)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.sizeBytes, sizeBytes) || other.sizeBytes == sizeBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,version,description,author,iconUrl,downloadUrl,updatedAt,sizeBytes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppServerInfo(id: $id, name: $name, version: $version, description: $description, author: $author, iconUrl: $iconUrl, downloadUrl: $downloadUrl, updatedAt: $updatedAt, sizeBytes: $sizeBytes)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$MiniAppServerInfoCopyWith<$Res> implements $MiniAppServerInfoCopyWith<$Res> {
|
|
||||||
factory _$MiniAppServerInfoCopyWith(_MiniAppServerInfo value, $Res Function(_MiniAppServerInfo) _then) = __$MiniAppServerInfoCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
String id, String name, String version, String description, String? author, String? iconUrl, String downloadUrl, DateTime updatedAt, int sizeBytes
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$MiniAppServerInfoCopyWithImpl<$Res>
|
|
||||||
implements _$MiniAppServerInfoCopyWith<$Res> {
|
|
||||||
__$MiniAppServerInfoCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _MiniAppServerInfo _self;
|
|
||||||
final $Res Function(_MiniAppServerInfo) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppServerInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? version = null,Object? description = null,Object? author = freezed,Object? iconUrl = freezed,Object? downloadUrl = null,Object? updatedAt = null,Object? sizeBytes = null,}) {
|
|
||||||
return _then(_MiniAppServerInfo(
|
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,iconUrl: freezed == iconUrl ? _self.iconUrl : iconUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,downloadUrl: null == downloadUrl ? _self.downloadUrl : downloadUrl // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime,sizeBytes: null == sizeBytes ? _self.sizeBytes : sizeBytes // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'interface.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_PluginMetadata _$PluginMetadataFromJson(Map<String, dynamic> json) =>
|
|
||||||
_PluginMetadata(
|
|
||||||
id: json['id'] as String,
|
|
||||||
name: json['name'] as String,
|
|
||||||
version: json['version'] as String,
|
|
||||||
description: json['description'] as String,
|
|
||||||
author: json['author'] as String?,
|
|
||||||
updatedAt: json['updated_at'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PluginMetadataToJson(_PluginMetadata instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'name': instance.name,
|
|
||||||
'version': instance.version,
|
|
||||||
'description': instance.description,
|
|
||||||
'author': instance.author,
|
|
||||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
|
||||||
};
|
|
||||||
|
|
||||||
_MiniAppMetadata _$MiniAppMetadataFromJson(Map<String, dynamic> json) =>
|
|
||||||
_MiniAppMetadata(
|
|
||||||
id: json['id'] as String,
|
|
||||||
name: json['name'] as String,
|
|
||||||
version: json['version'] as String,
|
|
||||||
description: json['description'] as String,
|
|
||||||
author: json['author'] as String?,
|
|
||||||
iconUrl: json['icon_url'] as String?,
|
|
||||||
downloadUrl: json['download_url'] as String,
|
|
||||||
localCachePath: json['local_cache_path'] as String?,
|
|
||||||
lastUpdated: json['last_updated'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['last_updated'] as String),
|
|
||||||
lastChecked: json['last_checked'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['last_checked'] as String),
|
|
||||||
isEnabled: json['is_enabled'] as bool? ?? false,
|
|
||||||
localVersion: (json['local_version'] as num?)?.toInt() ?? 0,
|
|
||||||
sizeBytes: (json['size_bytes'] as num?)?.toInt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$MiniAppMetadataToJson(_MiniAppMetadata instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'name': instance.name,
|
|
||||||
'version': instance.version,
|
|
||||||
'description': instance.description,
|
|
||||||
'author': instance.author,
|
|
||||||
'icon_url': instance.iconUrl,
|
|
||||||
'download_url': instance.downloadUrl,
|
|
||||||
'local_cache_path': instance.localCachePath,
|
|
||||||
'last_updated': instance.lastUpdated?.toIso8601String(),
|
|
||||||
'last_checked': instance.lastChecked?.toIso8601String(),
|
|
||||||
'is_enabled': instance.isEnabled,
|
|
||||||
'local_version': instance.localVersion,
|
|
||||||
'size_bytes': instance.sizeBytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
_MiniAppServerInfo _$MiniAppServerInfoFromJson(Map<String, dynamic> json) =>
|
|
||||||
_MiniAppServerInfo(
|
|
||||||
id: json['id'] as String,
|
|
||||||
name: json['name'] as String,
|
|
||||||
version: json['version'] as String,
|
|
||||||
description: json['description'] as String,
|
|
||||||
author: json['author'] as String?,
|
|
||||||
iconUrl: json['icon_url'] as String?,
|
|
||||||
downloadUrl: json['download_url'] as String,
|
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
|
||||||
sizeBytes: (json['size_bytes'] as num).toInt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$MiniAppServerInfoToJson(_MiniAppServerInfo instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'name': instance.name,
|
|
||||||
'version': instance.version,
|
|
||||||
'description': instance.description,
|
|
||||||
'author': instance.author,
|
|
||||||
'icon_url': instance.iconUrl,
|
|
||||||
'download_url': instance.downloadUrl,
|
|
||||||
'updated_at': instance.updatedAt.toIso8601String(),
|
|
||||||
'size_bytes': instance.sizeBytes,
|
|
||||||
};
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:island/modular/interface.dart';
|
|
||||||
import 'package:island/modular/registry.dart';
|
|
||||||
import 'package:island/pods/modular/plugin_registry.dart';
|
|
||||||
import 'package:island/talker.dart';
|
|
||||||
import 'package:island/widgets/alert.dart';
|
|
||||||
import 'package:island/widgets/dart_miniapp_display.dart';
|
|
||||||
import 'package:island/widgets/miniapp_modal.dart';
|
|
||||||
|
|
||||||
typedef ProgressCallback = void Function(double progress, String message);
|
|
||||||
|
|
||||||
class MiniappLoader {
|
|
||||||
static Future<void> loadMiniappFromSource(
|
|
||||||
BuildContext context,
|
|
||||||
WidgetRef ref, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final result = await FilePicker.platform.pickFiles(
|
|
||||||
type: FileType.custom,
|
|
||||||
allowedExtensions: ['dart', 'evc'],
|
|
||||||
withData: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == null || result.files.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final file = result.files.first;
|
|
||||||
final fileName = file.name;
|
|
||||||
|
|
||||||
if (!context.mounted) return;
|
|
||||||
if (fileName.endsWith('.dart')) {
|
|
||||||
await _loadDartSource(context, file, fileName);
|
|
||||||
} else if (fileName.endsWith('.evc')) {
|
|
||||||
await _loadBytecodeFile(context, ref, file, fileName, onProgress);
|
|
||||||
} else {
|
|
||||||
showErrorAlert('Unsupported file type. Please use .dart or .evc files');
|
|
||||||
}
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error('[MiniappLoader] Failed to load from source', e, stackTrace);
|
|
||||||
showErrorAlert('Failed to load miniapp: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<void> _loadDartSource(
|
|
||||||
BuildContext context,
|
|
||||||
PlatformFile file,
|
|
||||||
String fileName,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
final sourceCode = file.bytes;
|
|
||||||
|
|
||||||
if (sourceCode == null) {
|
|
||||||
showErrorAlert('Unable to read file contents');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final package = _generatePackageName(fileName);
|
|
||||||
|
|
||||||
await showModalBottomSheet<void>(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
useSafeArea: true,
|
|
||||||
builder: (modalContext) {
|
|
||||||
return DartMiniappDisplay(
|
|
||||||
package: package,
|
|
||||||
sourceCode: String.fromCharCodes(sourceCode),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error('[MiniappLoader] Failed to load dart source', e, stackTrace);
|
|
||||||
showErrorAlert('Failed to load miniapp: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<void> _loadBytecodeFile(
|
|
||||||
BuildContext context,
|
|
||||||
WidgetRef ref,
|
|
||||||
PlatformFile file,
|
|
||||||
String fileName,
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
final bytecode = file.bytes;
|
|
||||||
|
|
||||||
if (bytecode == null) {
|
|
||||||
showErrorAlert('Unable to read file contents');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.3, 'Saving bytecode...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final registryNotifier = ref.read(pluginRegistryProvider.notifier);
|
|
||||||
final cacheDirPath = await registryNotifier.getMiniAppCacheDirectory();
|
|
||||||
final appId = _generateAppId(fileName);
|
|
||||||
final cachePath = '$cacheDirPath/$appId.evc';
|
|
||||||
|
|
||||||
final cacheFile = File(cachePath);
|
|
||||||
await cacheFile.writeAsBytes(bytecode);
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.5, 'Loading miniapp...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final metadata = MiniAppMetadata(
|
|
||||||
id: appId,
|
|
||||||
name: appId,
|
|
||||||
version: '0.0.1-dev',
|
|
||||||
description: 'Loaded from file: $fileName',
|
|
||||||
downloadUrl: '',
|
|
||||||
localCachePath: cachePath,
|
|
||||||
lastUpdated: DateTime.now(),
|
|
||||||
isEnabled: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
final success = await registryNotifier.loadMiniappFromCache(
|
|
||||||
metadata,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (success && context.mounted) {
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(1.0, 'Loaded successfully');
|
|
||||||
}
|
|
||||||
showInfoAlert('Miniapp loaded successfully from file', 'Load Complete');
|
|
||||||
await showMiniappModal(context, ref, appId);
|
|
||||||
} else {
|
|
||||||
showErrorAlert('Failed to load miniapp');
|
|
||||||
}
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[MiniappLoader] Failed to load bytecode file',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
showErrorAlert('Failed to load miniapp: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<void> loadMiniappFromUrl(
|
|
||||||
BuildContext context,
|
|
||||||
WidgetRef ref,
|
|
||||||
String url, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.1, 'Downloading from URL...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final registryNotifier = ref.read(pluginRegistryProvider.notifier);
|
|
||||||
final success = await registryNotifier.loadMiniappFromUrl(
|
|
||||||
url,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(1.0, 'Loaded successfully');
|
|
||||||
}
|
|
||||||
showInfoAlert('Miniapp loaded successfully from URL', 'Load Complete');
|
|
||||||
|
|
||||||
if (context.mounted) {
|
|
||||||
final appId = registryNotifier.generateAppIdFromUrl(url);
|
|
||||||
await showMiniappModal(context, ref, appId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showErrorAlert('Failed to load miniapp');
|
|
||||||
}
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error('[MiniappLoader] Failed to load from URL', e, stackTrace);
|
|
||||||
showErrorAlert('Failed to load miniapp: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _generateAppId(String fileName) {
|
|
||||||
final baseName = fileName
|
|
||||||
.replaceAll('.dart', '')
|
|
||||||
.replaceAll('.evc', '')
|
|
||||||
.replaceAll(RegExp(r'[^\w-]'), '_');
|
|
||||||
return 'dev_${baseName}_${DateTime.now().millisecondsSinceEpoch}';
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _generatePackageName(String fileName) {
|
|
||||||
final baseName = fileName
|
|
||||||
.replaceAll('.dart', '')
|
|
||||||
.replaceAll(RegExp(r'[^\w-]'), '_');
|
|
||||||
return 'dev_${baseName}_${DateTime.now().millisecondsSinceEpoch}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:island/modular/interface.dart';
|
|
||||||
import 'package:island/talker.dart';
|
|
||||||
|
|
||||||
typedef ProgressCallback = void Function(double progress, String message);
|
|
||||||
|
|
||||||
dynamic flutterEvalPlugin;
|
|
||||||
|
|
||||||
class Runtime {
|
|
||||||
final ByteData bytecode;
|
|
||||||
|
|
||||||
Runtime(this.bytecode);
|
|
||||||
|
|
||||||
void addPlugin(dynamic plugin) {}
|
|
||||||
|
|
||||||
dynamic executeLib(String package, String function) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PluginRegistry {
|
|
||||||
final Map<String, RawPlugin> _rawPlugins = {};
|
|
||||||
final Map<String, MiniApp> _miniApps = {};
|
|
||||||
|
|
||||||
Map<String, RawPlugin> get rawPlugins => Map.unmodifiable(_rawPlugins);
|
|
||||||
Map<String, MiniApp> get miniApps => Map.unmodifiable(_miniApps);
|
|
||||||
|
|
||||||
void registerRawPlugin(RawPlugin plugin) {
|
|
||||||
_rawPlugins[plugin.metadata.id] = plugin;
|
|
||||||
talker.info(
|
|
||||||
'[PluginRegistry] Registered raw plugin: ${plugin.metadata.id}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unregisterRawPlugin(String id) {
|
|
||||||
_rawPlugins.remove(id);
|
|
||||||
talker.info('[PluginRegistry] Unregistered raw plugin: $id');
|
|
||||||
}
|
|
||||||
|
|
||||||
RawPlugin? getRawPlugin(String id) {
|
|
||||||
return _rawPlugins[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<PluginLoadResult> loadMiniApp(
|
|
||||||
MiniAppMetadata metadata, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
if (_miniApps.containsKey(metadata.id)) {
|
|
||||||
return PluginLoadResult.alreadyLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
talker.info('[PluginRegistry] Loading mini-app: ${metadata.id}');
|
|
||||||
|
|
||||||
if (metadata.localCachePath == null) {
|
|
||||||
talker.warning(
|
|
||||||
'[PluginRegistry] Mini-app has no cache path: ${metadata.id}',
|
|
||||||
);
|
|
||||||
return PluginLoadResult.failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
final file = File(metadata.localCachePath!);
|
|
||||||
if (!await file.exists()) {
|
|
||||||
talker.warning(
|
|
||||||
'[PluginRegistry] Mini-app cache file not found: ${metadata.localCachePath}',
|
|
||||||
);
|
|
||||||
return PluginLoadResult.failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
final bytecode = await file.readAsBytes();
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.5, 'Initializing runtime...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final runtime = Runtime(ByteData.sublistView(bytecode));
|
|
||||||
if (flutterEvalPlugin != null) {
|
|
||||||
try {
|
|
||||||
runtime.addPlugin(flutterEvalPlugin);
|
|
||||||
talker.info('[PluginRegistry] FlutterEvalPlugin added to runtime');
|
|
||||||
} catch (e) {
|
|
||||||
talker.error('[PluginRegistry] Failed to add FlutterEvalPlugin: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.8, 'Building entry widget...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final entryFunction = runtime.executeLib(
|
|
||||||
'package:mini_app/main.dart',
|
|
||||||
'buildEntry',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (entryFunction == null) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to get entry function for mini-app: ${metadata.id}',
|
|
||||||
);
|
|
||||||
return PluginLoadResult.failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
final miniApp = EvaluatedMiniApp(
|
|
||||||
appMetadata: metadata,
|
|
||||||
entryFunction: entryFunction,
|
|
||||||
runtime: runtime,
|
|
||||||
);
|
|
||||||
|
|
||||||
_miniApps[metadata.id] = miniApp;
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(1.0, 'Loaded successfully');
|
|
||||||
}
|
|
||||||
|
|
||||||
talker.info(
|
|
||||||
'[PluginRegistry] Successfully loaded mini-app: ${metadata.id}',
|
|
||||||
);
|
|
||||||
return PluginLoadResult.success;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to load mini-app: ${metadata.id}',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
return PluginLoadResult.failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unloadMiniApp(String id) {
|
|
||||||
final miniApp = _miniApps[id];
|
|
||||||
if (miniApp != null) {
|
|
||||||
if (miniApp is EvaluatedMiniApp) {
|
|
||||||
miniApp.runtime.dispose();
|
|
||||||
}
|
|
||||||
_miniApps.remove(id);
|
|
||||||
talker.info('[PluginRegistry] Unloaded mini-app: $id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MiniApp? getMiniApp(String id) {
|
|
||||||
return _miniApps[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> getMiniAppCacheDirectory() async {
|
|
||||||
final appDocDir = await getApplicationDocumentsDirectory();
|
|
||||||
final cacheDir = Directory('${appDocDir.path}/mini_apps');
|
|
||||||
if (!await cacheDir.exists()) {
|
|
||||||
await cacheDir.create(recursive: true);
|
|
||||||
}
|
|
||||||
return cacheDir.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getMiniAppCachePath(String id) {
|
|
||||||
return '$id.evc';
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> clearMiniAppCache() async {
|
|
||||||
final cacheDirPath = await getMiniAppCacheDirectory();
|
|
||||||
final cacheDir = Directory(cacheDirPath);
|
|
||||||
if (await cacheDir.exists()) {
|
|
||||||
await cacheDir.delete(recursive: true);
|
|
||||||
await cacheDir.create(recursive: true);
|
|
||||||
talker.info('[PluginRegistry] Cleared mini-app cache');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
for (final miniApp in _miniApps.values) {
|
|
||||||
if (miniApp is EvaluatedMiniApp) {
|
|
||||||
miniApp.runtime.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_miniApps.clear();
|
|
||||||
_rawPlugins.clear();
|
|
||||||
talker.info('[PluginRegistry] Disposed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EvaluatedMiniApp extends MiniApp {
|
|
||||||
final MiniAppMetadata appMetadata;
|
|
||||||
final dynamic entryFunction;
|
|
||||||
final Runtime runtime;
|
|
||||||
|
|
||||||
EvaluatedMiniApp({
|
|
||||||
required this.appMetadata,
|
|
||||||
required this.entryFunction,
|
|
||||||
required this.runtime,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
PluginMetadata get metadata => appMetadata as PluginMetadata;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildEntry() {
|
|
||||||
try {
|
|
||||||
final result = entryFunction();
|
|
||||||
if (result is Widget) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
talker.warning(
|
|
||||||
'[MiniApp] Entry function did not return a Widget: $result',
|
|
||||||
);
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error('[MiniApp] Failed to build entry widget', e, stackTrace);
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:dart_midi_pro/dart_midi_pro.dart';
|
|
||||||
|
|
||||||
final sfxPlayerProvider = Provider<AudioPlayer>((ref) {
|
final sfxPlayerProvider = Provider<AudioPlayer>((ref) {
|
||||||
final player = AudioPlayer();
|
final player = AudioPlayer();
|
||||||
@@ -67,111 +63,3 @@ void playMessageSfx(WidgetRef ref) {
|
|||||||
if (!settings.soundEffects) return;
|
if (!settings.soundEffects) return;
|
||||||
_playSfx('assets/audio/messages.mp3', 0.75);
|
_playSfx('assets/audio/messages.mp3', 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MiniSampleSynth {
|
|
||||||
final String sampleAsset;
|
|
||||||
final int
|
|
||||||
baseNote; // MIDI note of the sample (usually 72 = C5 for lower pitch playback)
|
|
||||||
|
|
||||||
AudioPlayer? currentPlayer;
|
|
||||||
|
|
||||||
MiniSampleSynth({required this.sampleAsset, this.baseNote = 72});
|
|
||||||
|
|
||||||
Future<void> playMidiAsset(String midiAsset) async {
|
|
||||||
final data = await rootBundle.load(midiAsset);
|
|
||||||
final midi = MidiParser().parseMidiFromBuffer(data.buffer.asUint8List());
|
|
||||||
|
|
||||||
for (final track in midi.tracks) {
|
|
||||||
int currentTick = 0;
|
|
||||||
|
|
||||||
for (final event in track) {
|
|
||||||
currentTick += event.deltaTime;
|
|
||||||
|
|
||||||
if (event is NoteOnEvent && event.velocity > 0) {
|
|
||||||
final note = event.noteNumber;
|
|
||||||
final durationTicks = _estimateDuration(track, event);
|
|
||||||
final durationMs = _ticksToMs(durationTicks, midi);
|
|
||||||
|
|
||||||
_scheduleNote(
|
|
||||||
note: note,
|
|
||||||
startMs: _ticksToMs(currentTick, midi),
|
|
||||||
durationMs: durationMs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scheduleNote({
|
|
||||||
required int note,
|
|
||||||
required int startMs,
|
|
||||||
required int durationMs,
|
|
||||||
}) {
|
|
||||||
Future.delayed(Duration(milliseconds: startMs), () async {
|
|
||||||
// Stop any currently playing note
|
|
||||||
if (currentPlayer != null) {
|
|
||||||
await currentPlayer!.stop();
|
|
||||||
await currentPlayer!.dispose();
|
|
||||||
currentPlayer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final player = AudioPlayer();
|
|
||||||
currentPlayer = player;
|
|
||||||
|
|
||||||
await player.setAudioSource(AudioSource.asset(sampleAsset));
|
|
||||||
final speed = _noteToSpeed(note);
|
|
||||||
await player.setSpeed(speed);
|
|
||||||
await player.play();
|
|
||||||
|
|
||||||
Future.delayed(Duration(milliseconds: durationMs), () async {
|
|
||||||
if (currentPlayer == player) {
|
|
||||||
await player.stop();
|
|
||||||
await player.dispose();
|
|
||||||
currentPlayer = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
double _noteToSpeed(int note) {
|
|
||||||
return math.pow(2, (note - baseNote) / 12).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _getTempo(MidiFile midi) {
|
|
||||||
for (var track in midi.tracks) {
|
|
||||||
for (var event in track) {
|
|
||||||
if (event is SetTempoEvent) {
|
|
||||||
return event.microsecondsPerBeat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 500000; // default 120 BPM
|
|
||||||
}
|
|
||||||
|
|
||||||
int _ticksToMs(int ticks, MidiFile midi) {
|
|
||||||
final tempo = _getTempo(midi);
|
|
||||||
final ticksPerBeat = midi.header.ticksPerBeat ?? 480;
|
|
||||||
|
|
||||||
return ((ticks * tempo) / ticksPerBeat / 1000).round();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _estimateDuration(List<MidiEvent> track, NoteOnEvent on) {
|
|
||||||
int ticks = 0;
|
|
||||||
bool started = false;
|
|
||||||
|
|
||||||
for (final e in track) {
|
|
||||||
if (e == on) {
|
|
||||||
started = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!started) continue;
|
|
||||||
|
|
||||||
ticks += e.deltaTime;
|
|
||||||
|
|
||||||
if (e is NoteOffEvent && e.noteNumber == on.noteNumber) {
|
|
||||||
return ticks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 200; // fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,440 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
import 'package:island/modular/interface.dart';
|
|
||||||
import 'package:island/modular/registry.dart';
|
|
||||||
import 'package:island/pods/config.dart';
|
|
||||||
import 'package:island/talker.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
||||||
|
|
||||||
part 'plugin_registry.freezed.dart';
|
|
||||||
part 'plugin_registry.g.dart';
|
|
||||||
|
|
||||||
const kMiniAppsRegistryKey = 'mini_apps_registry';
|
|
||||||
const kMiniAppsLastSyncKey = 'mini_apps_last_sync';
|
|
||||||
const kRawPluginsRegistryKey = 'raw_plugins_registry';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class MiniAppSyncResult with _$MiniAppSyncResult {
|
|
||||||
const factory MiniAppSyncResult({
|
|
||||||
required bool success,
|
|
||||||
List<String>? added,
|
|
||||||
List<String>? updated,
|
|
||||||
List<String>? removed,
|
|
||||||
String? error,
|
|
||||||
}) = _MiniAppSyncResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
class PluginRegistryNotifier extends _$PluginRegistryNotifier {
|
|
||||||
late final PluginRegistry _registry;
|
|
||||||
late final Dio _dio;
|
|
||||||
|
|
||||||
@override
|
|
||||||
PluginRegistry build() {
|
|
||||||
_registry = PluginRegistry();
|
|
||||||
_dio = Dio();
|
|
||||||
|
|
||||||
ref.onDispose(() {
|
|
||||||
_registry.dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
_loadFromPrefs();
|
|
||||||
|
|
||||||
return _registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadFromPrefs() async {
|
|
||||||
try {
|
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
|
||||||
final registryJson = prefs.getString(kMiniAppsRegistryKey);
|
|
||||||
if (registryJson != null) {
|
|
||||||
final List<dynamic> decoded = jsonDecode(registryJson);
|
|
||||||
for (final item in decoded) {
|
|
||||||
final metadata = MiniAppMetadata.fromJson(
|
|
||||||
item as Map<String, dynamic>,
|
|
||||||
);
|
|
||||||
if (metadata.isEnabled && metadata.localCachePath != null) {
|
|
||||||
await _registry.loadMiniApp(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
talker.info('[PluginRegistry] Loaded registry from preferences');
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to load from preferences',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _saveToPrefs() async {
|
|
||||||
try {
|
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
|
||||||
final miniAppsData = _registry.miniApps.values
|
|
||||||
.map((app) => (app as EvaluatedMiniApp).appMetadata.toJson())
|
|
||||||
.toList();
|
|
||||||
await prefs.setString(kMiniAppsRegistryKey, jsonEncode(miniAppsData));
|
|
||||||
talker.info('[PluginRegistry] Saved registry to preferences');
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to save to preferences',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerRawPlugin(RawPlugin plugin) {
|
|
||||||
_registry.registerRawPlugin(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unregisterRawPlugin(String id) {
|
|
||||||
_registry.unregisterRawPlugin(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<MiniAppSyncResult> syncMiniAppsFromServer(String apiEndpoint) async {
|
|
||||||
try {
|
|
||||||
talker.info(
|
|
||||||
'[PluginRegistry] Syncing mini-apps from server: $apiEndpoint',
|
|
||||||
);
|
|
||||||
|
|
||||||
final response = await _dio.get(apiEndpoint);
|
|
||||||
if (response.statusCode != 200) {
|
|
||||||
throw Exception('Failed to fetch mini-apps: ${response.statusCode}');
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<dynamic> data = response.data['mini_apps'] ?? [];
|
|
||||||
final serverApps = data
|
|
||||||
.map(
|
|
||||||
(item) => MiniAppServerInfo.fromJson(item as Map<String, dynamic>),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final currentApps = _registry.miniApps;
|
|
||||||
final added = <String>[];
|
|
||||||
final updated = <String>[];
|
|
||||||
|
|
||||||
for (final serverApp in serverApps) {
|
|
||||||
final existingApp = currentApps[serverApp.id];
|
|
||||||
if (existingApp == null) {
|
|
||||||
added.add(serverApp.id);
|
|
||||||
talker.info('[PluginRegistry] Found new mini-app: ${serverApp.name}');
|
|
||||||
} else {
|
|
||||||
final currentMetadata = (existingApp as EvaluatedMiniApp).appMetadata;
|
|
||||||
if (currentMetadata.version != serverApp.version) {
|
|
||||||
updated.add(serverApp.id);
|
|
||||||
talker.info(
|
|
||||||
'[PluginRegistry] Found update for mini-app: ${serverApp.name}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
|
||||||
await prefs.setString(
|
|
||||||
kMiniAppsLastSyncKey,
|
|
||||||
DateTime.now().toIso8601String(),
|
|
||||||
);
|
|
||||||
|
|
||||||
final syncResult = MiniAppSyncResult(
|
|
||||||
success: true,
|
|
||||||
added: added,
|
|
||||||
updated: updated,
|
|
||||||
);
|
|
||||||
|
|
||||||
await _saveToPrefs();
|
|
||||||
return syncResult;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error('[PluginRegistry] Failed to sync mini-apps', e, stackTrace);
|
|
||||||
return MiniAppSyncResult(success: false, error: e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> downloadMiniApp(
|
|
||||||
String id,
|
|
||||||
String downloadUrl, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
talker.info(
|
|
||||||
'[PluginRegistry] Downloading mini-app: $id from $downloadUrl',
|
|
||||||
);
|
|
||||||
|
|
||||||
final cacheDirPath = await _registry.getMiniAppCacheDirectory();
|
|
||||||
final cachePath = '$cacheDirPath/${_registry.getMiniAppCachePath(id)}';
|
|
||||||
|
|
||||||
await _dio.download(
|
|
||||||
downloadUrl,
|
|
||||||
cachePath,
|
|
||||||
onReceiveProgress: (received, total) {
|
|
||||||
if (total != -1 && onProgress != null) {
|
|
||||||
final progress = received / total;
|
|
||||||
onProgress(
|
|
||||||
progress,
|
|
||||||
'Downloading... ${((progress * 100).toStringAsFixed(0))}%',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
talker.info('[PluginRegistry] Downloaded mini-app: $id to $cachePath');
|
|
||||||
|
|
||||||
final metadata = MiniAppMetadata(
|
|
||||||
id: id,
|
|
||||||
name: id,
|
|
||||||
version: '1.0.0',
|
|
||||||
description: 'Downloaded mini-app',
|
|
||||||
downloadUrl: downloadUrl,
|
|
||||||
localCachePath: cachePath,
|
|
||||||
lastUpdated: DateTime.now(),
|
|
||||||
isEnabled: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await _registry.loadMiniApp(
|
|
||||||
metadata,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == PluginLoadResult.success) {
|
|
||||||
await _saveToPrefs();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to download mini-app: $id',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> updateMiniApp(String id, {ProgressCallback? onProgress}) async {
|
|
||||||
try {
|
|
||||||
final miniApp = _registry.getMiniApp(id);
|
|
||||||
if (miniApp == null) {
|
|
||||||
talker.warning('[PluginRegistry] Mini-app not found for update: $id');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final appMetadata = (miniApp as EvaluatedMiniApp).appMetadata;
|
|
||||||
|
|
||||||
_registry.unloadMiniApp(id);
|
|
||||||
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(0.0, 'Downloading update...');
|
|
||||||
}
|
|
||||||
|
|
||||||
final success = await downloadMiniApp(
|
|
||||||
id,
|
|
||||||
appMetadata.downloadUrl,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
talker.info('[PluginRegistry] Successfully updated mini-app: $id');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to update mini-app: $id',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> enableMiniApp(String id, bool enabled) async {
|
|
||||||
final miniApp = _registry.getMiniApp(id);
|
|
||||||
if (miniApp != null) {
|
|
||||||
final appMetadata = (miniApp as EvaluatedMiniApp).appMetadata;
|
|
||||||
if (enabled && appMetadata.isEnabled == false) {
|
|
||||||
await _registry.loadMiniApp(appMetadata.copyWith(isEnabled: true));
|
|
||||||
} else if (!enabled && appMetadata.isEnabled == true) {
|
|
||||||
_registry.unloadMiniApp(id);
|
|
||||||
final updatedMetadata = appMetadata.copyWith(isEnabled: false);
|
|
||||||
final evaluatedMiniApp = miniApp;
|
|
||||||
final updatedMiniApp = EvaluatedMiniApp(
|
|
||||||
appMetadata: updatedMetadata,
|
|
||||||
entryFunction: evaluatedMiniApp.entryFunction,
|
|
||||||
runtime: evaluatedMiniApp.runtime,
|
|
||||||
);
|
|
||||||
_registry.miniApps[id] = updatedMiniApp;
|
|
||||||
}
|
|
||||||
await _saveToPrefs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteMiniApp(String id, {bool deleteCache = true}) async {
|
|
||||||
try {
|
|
||||||
_registry.unloadMiniApp(id);
|
|
||||||
|
|
||||||
if (deleteCache) {
|
|
||||||
final cacheDirPath = await _registry.getMiniAppCacheDirectory();
|
|
||||||
final cachePath = '$cacheDirPath/${_registry.getMiniAppCachePath(id)}';
|
|
||||||
final file = File(cachePath);
|
|
||||||
if (await file.exists()) {
|
|
||||||
await file.delete();
|
|
||||||
talker.info('[PluginRegistry] Deleted cache for mini-app: $id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _saveToPrefs();
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to delete mini-app: $id',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> clearMiniAppCache() async {
|
|
||||||
await _registry.clearMiniAppCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MiniApp> getAvailableMiniApps() {
|
|
||||||
return _registry.miniApps.values.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, RawPlugin> getAvailableRawPlugins() {
|
|
||||||
return _registry.rawPlugins;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<MiniApp?> getMiniApp(String id) async {
|
|
||||||
return _registry.getMiniApp(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DateTime?> getLastSyncTime() async {
|
|
||||||
final prefs = ref.read(sharedPreferencesProvider);
|
|
||||||
final lastSyncStr = prefs.getString(kMiniAppsLastSyncKey);
|
|
||||||
if (lastSyncStr != null) {
|
|
||||||
return DateTime.tryParse(lastSyncStr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> getMiniAppCacheDirectory() async {
|
|
||||||
return _registry.getMiniAppCacheDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> loadMiniappFromCache(
|
|
||||||
MiniAppMetadata metadata, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final result = await _registry.loadMiniApp(
|
|
||||||
metadata,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == PluginLoadResult.success) {
|
|
||||||
await _saveToPrefs();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to load miniapp from cache',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> loadMiniappFromUrl(
|
|
||||||
String url, {
|
|
||||||
ProgressCallback? onProgress,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final appId = generateAppIdFromUrl(url);
|
|
||||||
final cacheDirPath = await _registry.getMiniAppCacheDirectory();
|
|
||||||
final cachePath = '$cacheDirPath/${_registry.getMiniAppCachePath(appId)}';
|
|
||||||
|
|
||||||
await _dio.download(
|
|
||||||
url,
|
|
||||||
cachePath,
|
|
||||||
onReceiveProgress: (received, total) {
|
|
||||||
if (total != -1 && onProgress != null) {
|
|
||||||
final progress = received / total;
|
|
||||||
onProgress(
|
|
||||||
progress,
|
|
||||||
'Downloading... ${((progress * 100).toStringAsFixed(0))}%',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
talker.info('[PluginRegistry] Downloaded mini-app: $appId to $cachePath');
|
|
||||||
|
|
||||||
final metadata = MiniAppMetadata(
|
|
||||||
id: appId,
|
|
||||||
name: appId,
|
|
||||||
version: '1.0.0',
|
|
||||||
description: 'Loaded from URL',
|
|
||||||
downloadUrl: url,
|
|
||||||
localCachePath: cachePath,
|
|
||||||
lastUpdated: DateTime.now(),
|
|
||||||
isEnabled: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await _registry.loadMiniApp(
|
|
||||||
metadata,
|
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == PluginLoadResult.success) {
|
|
||||||
await _saveToPrefs();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
talker.error(
|
|
||||||
'[PluginRegistry] Failed to load miniapp from URL',
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateAppIdFromUrl(String url) {
|
|
||||||
final uri = Uri.tryParse(url);
|
|
||||||
if (uri == null) {
|
|
||||||
return 'dev_miniapp_${DateTime.now().millisecondsSinceEpoch}';
|
|
||||||
}
|
|
||||||
final path = uri.pathSegments.lastWhere(
|
|
||||||
(s) => s.isNotEmpty,
|
|
||||||
orElse: () => 'miniapp',
|
|
||||||
);
|
|
||||||
final baseName = path
|
|
||||||
.replaceAll('.evc', '')
|
|
||||||
.replaceAll(RegExp(r'[^\w-]'), '_');
|
|
||||||
return 'dev_$baseName';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final miniAppsProvider = Provider.autoDispose((ref) {
|
|
||||||
final registry = ref.watch(pluginRegistryProvider);
|
|
||||||
return registry.miniApps.values.toList();
|
|
||||||
});
|
|
||||||
|
|
||||||
final rawPluginsProvider = Provider.autoDispose((ref) {
|
|
||||||
final registry = ref.watch(pluginRegistryProvider);
|
|
||||||
return registry.rawPlugins;
|
|
||||||
});
|
|
||||||
|
|
||||||
typedef ProgressCallback = void Function(double progress, String message);
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'plugin_registry.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$MiniAppSyncResult {
|
|
||||||
|
|
||||||
bool get success; List<String>? get added; List<String>? get updated; List<String>? get removed; String? get error;
|
|
||||||
/// Create a copy of MiniAppSyncResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$MiniAppSyncResultCopyWith<MiniAppSyncResult> get copyWith => _$MiniAppSyncResultCopyWithImpl<MiniAppSyncResult>(this as MiniAppSyncResult, _$identity);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MiniAppSyncResult&&(identical(other.success, success) || other.success == success)&&const DeepCollectionEquality().equals(other.added, added)&&const DeepCollectionEquality().equals(other.updated, updated)&&const DeepCollectionEquality().equals(other.removed, removed)&&(identical(other.error, error) || other.error == error));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,const DeepCollectionEquality().hash(added),const DeepCollectionEquality().hash(updated),const DeepCollectionEquality().hash(removed),error);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppSyncResult(success: $success, added: $added, updated: $updated, removed: $removed, error: $error)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $MiniAppSyncResultCopyWith<$Res> {
|
|
||||||
factory $MiniAppSyncResultCopyWith(MiniAppSyncResult value, $Res Function(MiniAppSyncResult) _then) = _$MiniAppSyncResultCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
bool success, List<String>? added, List<String>? updated, List<String>? removed, String? error
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$MiniAppSyncResultCopyWithImpl<$Res>
|
|
||||||
implements $MiniAppSyncResultCopyWith<$Res> {
|
|
||||||
_$MiniAppSyncResultCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final MiniAppSyncResult _self;
|
|
||||||
final $Res Function(MiniAppSyncResult) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppSyncResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? success = null,Object? added = freezed,Object? updated = freezed,Object? removed = freezed,Object? error = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,added: freezed == added ? _self.added : added // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,updated: freezed == updated ? _self.updated : updated // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,removed: freezed == removed ? _self.removed : removed // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [MiniAppSyncResult].
|
|
||||||
extension MiniAppSyncResultPatterns on MiniAppSyncResult {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MiniAppSyncResult value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MiniAppSyncResult value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MiniAppSyncResult value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool success, List<String>? added, List<String>? updated, List<String>? removed, String? error)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult() when $default != null:
|
|
||||||
return $default(_that.success,_that.added,_that.updated,_that.removed,_that.error);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool success, List<String>? added, List<String>? updated, List<String>? removed, String? error) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult():
|
|
||||||
return $default(_that.success,_that.added,_that.updated,_that.removed,_that.error);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool success, List<String>? added, List<String>? updated, List<String>? removed, String? error)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _MiniAppSyncResult() when $default != null:
|
|
||||||
return $default(_that.success,_that.added,_that.updated,_that.removed,_that.error);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
|
|
||||||
class _MiniAppSyncResult implements MiniAppSyncResult {
|
|
||||||
const _MiniAppSyncResult({required this.success, final List<String>? added, final List<String>? updated, final List<String>? removed, this.error}): _added = added,_updated = updated,_removed = removed;
|
|
||||||
|
|
||||||
|
|
||||||
@override final bool success;
|
|
||||||
final List<String>? _added;
|
|
||||||
@override List<String>? get added {
|
|
||||||
final value = _added;
|
|
||||||
if (value == null) return null;
|
|
||||||
if (_added is EqualUnmodifiableListView) return _added;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String>? _updated;
|
|
||||||
@override List<String>? get updated {
|
|
||||||
final value = _updated;
|
|
||||||
if (value == null) return null;
|
|
||||||
if (_updated is EqualUnmodifiableListView) return _updated;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String>? _removed;
|
|
||||||
@override List<String>? get removed {
|
|
||||||
final value = _removed;
|
|
||||||
if (value == null) return null;
|
|
||||||
if (_removed is EqualUnmodifiableListView) return _removed;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override final String? error;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppSyncResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$MiniAppSyncResultCopyWith<_MiniAppSyncResult> get copyWith => __$MiniAppSyncResultCopyWithImpl<_MiniAppSyncResult>(this, _$identity);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MiniAppSyncResult&&(identical(other.success, success) || other.success == success)&&const DeepCollectionEquality().equals(other._added, _added)&&const DeepCollectionEquality().equals(other._updated, _updated)&&const DeepCollectionEquality().equals(other._removed, _removed)&&(identical(other.error, error) || other.error == error));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,const DeepCollectionEquality().hash(_added),const DeepCollectionEquality().hash(_updated),const DeepCollectionEquality().hash(_removed),error);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MiniAppSyncResult(success: $success, added: $added, updated: $updated, removed: $removed, error: $error)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$MiniAppSyncResultCopyWith<$Res> implements $MiniAppSyncResultCopyWith<$Res> {
|
|
||||||
factory _$MiniAppSyncResultCopyWith(_MiniAppSyncResult value, $Res Function(_MiniAppSyncResult) _then) = __$MiniAppSyncResultCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
bool success, List<String>? added, List<String>? updated, List<String>? removed, String? error
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$MiniAppSyncResultCopyWithImpl<$Res>
|
|
||||||
implements _$MiniAppSyncResultCopyWith<$Res> {
|
|
||||||
__$MiniAppSyncResultCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _MiniAppSyncResult _self;
|
|
||||||
final $Res Function(_MiniAppSyncResult) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MiniAppSyncResult
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? success = null,Object? added = freezed,Object? updated = freezed,Object? removed = freezed,Object? error = freezed,}) {
|
|
||||||
return _then(_MiniAppSyncResult(
|
|
||||||
success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,added: freezed == added ? _self._added : added // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,updated: freezed == updated ? _self._updated : updated // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,removed: freezed == removed ? _self._removed : removed // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<String>?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'plugin_registry.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// RiverpodGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
|
|
||||||
@ProviderFor(PluginRegistryNotifier)
|
|
||||||
final pluginRegistryProvider = PluginRegistryNotifierProvider._();
|
|
||||||
|
|
||||||
final class PluginRegistryNotifierProvider
|
|
||||||
extends $NotifierProvider<PluginRegistryNotifier, PluginRegistry> {
|
|
||||||
PluginRegistryNotifierProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'pluginRegistryProvider',
|
|
||||||
isAutoDispose: false,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$pluginRegistryNotifierHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
PluginRegistryNotifier create() => PluginRegistryNotifier();
|
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
|
||||||
Override overrideWithValue(PluginRegistry value) {
|
|
||||||
return $ProviderOverride(
|
|
||||||
origin: this,
|
|
||||||
providerOverride: $SyncValueProvider<PluginRegistry>(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$pluginRegistryNotifierHash() =>
|
|
||||||
r'287b3a9e5598b279b80293dd37fe5a825395c8e3';
|
|
||||||
|
|
||||||
abstract class _$PluginRegistryNotifier extends $Notifier<PluginRegistry> {
|
|
||||||
PluginRegistry build();
|
|
||||||
@$mustCallSuper
|
|
||||||
@override
|
|
||||||
void runBuild() {
|
|
||||||
final ref = this.ref as $Ref<PluginRegistry, PluginRegistry>;
|
|
||||||
final element =
|
|
||||||
ref.element
|
|
||||||
as $ClassProviderElement<
|
|
||||||
AnyNotifier<PluginRegistry, PluginRegistry>,
|
|
||||||
PluginRegistry,
|
|
||||||
Object?,
|
|
||||||
Object?
|
|
||||||
>;
|
|
||||||
element.handleCreate(ref, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_eval/flutter_eval.dart';
|
|
||||||
|
|
||||||
class DartMiniappDisplay extends StatelessWidget {
|
|
||||||
final String package;
|
|
||||||
final String sourceCode;
|
|
||||||
|
|
||||||
const DartMiniappDisplay({
|
|
||||||
super.key,
|
|
||||||
required this.package,
|
|
||||||
required this.sourceCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EvalWidget(
|
|
||||||
packages: {
|
|
||||||
package: {'main.dart': sourceCode},
|
|
||||||
},
|
|
||||||
library: 'package:$package/main.dart',
|
|
||||||
function: 'buildEntry',
|
|
||||||
args: [],
|
|
||||||
assetPath: 'assets/$package/main.evc',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,14 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/modular/miniapp_loader.dart';
|
|
||||||
import 'package:island/pods/message.dart';
|
import 'package:island/pods/message.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/modular/plugin_registry.dart';
|
|
||||||
import 'package:island/services/update_service.dart';
|
import 'package:island/services/update_service.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/network_status_sheet.dart';
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:island/widgets/miniapp_modal.dart';
|
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:talker_flutter/talker_flutter.dart';
|
import 'package:talker_flutter/talker_flutter.dart';
|
||||||
@@ -28,7 +22,7 @@ Future<void> _showSetTokenDialog(BuildContext context, WidgetRef ref) async {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Set Access Token'),
|
title: const Text('Set access token'),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@@ -139,7 +133,7 @@ class DebugSheet extends HookConsumerWidget {
|
|||||||
leading: const Icon(Symbols.error),
|
leading: const Icon(Symbols.error),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
title: const Text('Test Error Alert'),
|
title: const Text('Test error alert'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showErrorAlert(
|
showErrorAlert(
|
||||||
'This is a test error message for debugging purposes.',
|
'This is a test error message for debugging purposes.',
|
||||||
@@ -151,7 +145,7 @@ class DebugSheet extends HookConsumerWidget {
|
|||||||
leading: const Icon(Symbols.info),
|
leading: const Icon(Symbols.info),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
title: const Text('Test Info Alert'),
|
title: const Text('Test info alert'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showInfoAlert(
|
showInfoAlert(
|
||||||
'This is a test info message for debugging purposes.',
|
'This is a test info message for debugging purposes.',
|
||||||
@@ -202,136 +196,9 @@ class DebugSheet extends HookConsumerWidget {
|
|||||||
DefaultCacheManager().emptyCache();
|
DefaultCacheManager().emptyCache();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(height: 8),
|
|
||||||
if (!kIsWeb &&
|
|
||||||
(Platform.isMacOS || Platform.isWindows || Platform.isLinux))
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.file_upload),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Load Miniapp from File'),
|
|
||||||
onTap: () async {
|
|
||||||
await MiniappLoader.loadMiniappFromSource(context, ref);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.download),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Load Miniapp from URL'),
|
|
||||||
onTap: () async {
|
|
||||||
await _showLoadFromUrlDialog(context, ref);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.play_circle),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Launch Miniapp'),
|
|
||||||
onTap: () async {
|
|
||||||
await _showMiniappSelector(context, ref);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showLoadFromUrlDialog(
|
|
||||||
BuildContext context,
|
|
||||||
WidgetRef ref,
|
|
||||||
) async {
|
|
||||||
final TextEditingController controller = TextEditingController();
|
|
||||||
|
|
||||||
return showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('Load Miniapp from URL'),
|
|
||||||
content: TextField(
|
|
||||||
controller: controller,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Enter .evc file URL',
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
autofocus: true,
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: const Text('Cancel'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: const Text('Load'),
|
|
||||||
onPressed: () async {
|
|
||||||
final url = controller.text.trim();
|
|
||||||
if (url.isNotEmpty) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
showLoadingModal(context);
|
|
||||||
await MiniappLoader.loadMiniappFromUrl(context, ref, url);
|
|
||||||
if (context.mounted) {
|
|
||||||
hideLoadingModal(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _showMiniappSelector(BuildContext context, WidgetRef ref) async {
|
|
||||||
final registry = ref.read(pluginRegistryProvider);
|
|
||||||
final miniapps = registry.miniApps.values.toList();
|
|
||||||
|
|
||||||
if (miniapps.isEmpty) {
|
|
||||||
showErrorAlert('No miniapps loaded');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final selectedId = await showDialog<String>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('Select Miniapp'),
|
|
||||||
content: SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: miniapps.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final miniapp = miniapps[index];
|
|
||||||
return ListTile(
|
|
||||||
title: Text(miniapp.metadata.name),
|
|
||||||
subtitle: Text(miniapp.metadata.description),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop(miniapp.metadata.id);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: const Text('Cancel'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selectedId != null && context.mounted) {
|
|
||||||
await showMiniappModal(context, ref, selectedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:island/modular/interface.dart';
|
|
||||||
import 'package:island/pods/modular/plugin_registry.dart';
|
|
||||||
|
|
||||||
Future<void> showMiniappModal(
|
|
||||||
BuildContext context,
|
|
||||||
WidgetRef ref,
|
|
||||||
String miniappId,
|
|
||||||
) async {
|
|
||||||
final registry = ref.read(pluginRegistryProvider);
|
|
||||||
final miniapp = registry.getMiniApp(miniappId);
|
|
||||||
|
|
||||||
if (miniapp == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await showModalBottomSheet<void>(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
useSafeArea: true,
|
|
||||||
builder: (context) {
|
|
||||||
return MiniappDisplay(miniapp: miniapp);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MiniappDisplay extends StatelessWidget {
|
|
||||||
final MiniApp miniapp;
|
|
||||||
|
|
||||||
const MiniappDisplay({super.key, required this.miniapp});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxHeight: MediaQuery.of(context).size.height * 0.9,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text('Miniapp', style: Theme.of(context).textTheme.titleMedium),
|
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: ProviderScope(
|
|
||||||
child: Consumer(
|
|
||||||
builder: (context, ref, child) {
|
|
||||||
return miniapp.buildEntry();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
54
packages/miniapp-example/.gitignore
vendored
54
packages/miniapp-example/.gitignore
vendored
@@ -1,54 +0,0 @@
|
|||||||
# Miscellaneous
|
|
||||||
*.class
|
|
||||||
*.log
|
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
**/doc/api/
|
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
|
||||||
.flutter-plugins
|
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.packages
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
*.freezed.dart
|
|
||||||
*.g.dart
|
|
||||||
|
|
||||||
# Compiled bytecode files
|
|
||||||
*.evc
|
|
||||||
|
|
||||||
# Test coverage
|
|
||||||
coverage/
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
# Mini-App Example: Building for Production
|
|
||||||
|
|
||||||
This guide shows how to build and deploy the Payment Demo mini-app.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Flutter SDK installed
|
|
||||||
- flutter_eval package installed
|
|
||||||
- Access to the PluginRegistry in your main app
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Running the Example
|
|
||||||
|
|
||||||
Run the example app directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/miniapp-example
|
|
||||||
flutter run
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running Tests
|
|
||||||
|
|
||||||
Run the test suite:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building for Production
|
|
||||||
|
|
||||||
### Step 1: Compile to Bytecode
|
|
||||||
|
|
||||||
Use flutter_eval to compile the mini-app to bytecode (.evc file):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/miniapp-example
|
|
||||||
flutter pub get
|
|
||||||
dart run flutter_eval:compile -i lib/main.dart -o payment_demo.evc
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create `payment_demo.evc` in the current directory.
|
|
||||||
|
|
||||||
### Step 2: Upload to Server
|
|
||||||
|
|
||||||
Upload the `.evc` file to your server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Example: Upload to your mini-apps server
|
|
||||||
curl -X POST https://your-server.com/api/mini-apps/upload \
|
|
||||||
-F "file=@payment_demo.evc" \
|
|
||||||
-F "metadata={\"name\":\"Payment Demo\",\"version\":\"1.0.0\",\"description\":\"Example payment mini-app\"}"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Configure Server Metadata
|
|
||||||
|
|
||||||
Ensure your server returns the mini-app metadata in the correct format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"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": 12345,
|
|
||||||
"min_host_version": "1.0.0"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration with Main App
|
|
||||||
|
|
||||||
### Load the Mini-App
|
|
||||||
|
|
||||||
Use the PluginRegistry to load and run the mini-app:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:island/modular/registry.dart';
|
|
||||||
import 'package:island/pods/plugin_registry.dart';
|
|
||||||
|
|
||||||
// Get the registry
|
|
||||||
final registry = ref.read(pluginRegistryProvider.notifier);
|
|
||||||
|
|
||||||
// Load the mini-app from server
|
|
||||||
final miniApp = await registry.loadMiniApp(
|
|
||||||
'https://your-server.com/mini-apps/payment-demo.evc',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Enable the mini-app
|
|
||||||
await registry.enablePlugin(miniApp.id);
|
|
||||||
|
|
||||||
// Launch the mini-app
|
|
||||||
await registry.launchMiniApp(context, miniApp.id);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Provide PaymentAPI to Mini-App
|
|
||||||
|
|
||||||
Ensure the PaymentAPI is available to the mini-app through the eval bridge:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
// When loading the mini-app, register the PaymentAPI
|
|
||||||
final registry = PluginRegistry();
|
|
||||||
|
|
||||||
// Register PaymentAPI with the eval bridge
|
|
||||||
registry.registerBridge('PaymentAPI', PaymentAPI.instance);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mini-App Development Guide
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
packages/miniapp-example/
|
|
||||||
├── lib/
|
|
||||||
│ ├── main.dart # Main mini-app entry point
|
|
||||||
│ └── payment_api.dart # PaymentAPI interface (for reference)
|
|
||||||
├── test/
|
|
||||||
│ └── main_test.dart # Widget tests
|
|
||||||
├── pubspec.yaml # Package configuration
|
|
||||||
└── README.md # This file
|
|
||||||
```
|
|
||||||
|
|
||||||
### Key Files
|
|
||||||
|
|
||||||
#### `lib/main.dart`
|
|
||||||
- Contains the main mini-app widget
|
|
||||||
- Implements the UI for testing PaymentAPI
|
|
||||||
- Can be run standalone or compiled to bytecode
|
|
||||||
|
|
||||||
#### `lib/payment_api.dart`
|
|
||||||
- Shows the PaymentAPI interface
|
|
||||||
- Used as a reference for API usage
|
|
||||||
- Not compiled into the final .evc file
|
|
||||||
|
|
||||||
### Writing Your Own Mini-App
|
|
||||||
|
|
||||||
1. Create a new Flutter package:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir packages/my-miniapp
|
|
||||||
cd packages/my-miniapp
|
|
||||||
flutter create --org com.example my_miniapp
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add flutter_eval to `pubspec.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_eval: ^0.8.2
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Write your mini-app in `lib/main.dart`
|
|
||||||
4. Test with `flutter test`
|
|
||||||
5. Compile to `.evc`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dart run flutter_eval:compile -i lib/main.dart -o my_miniapp.evc
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Usage Examples
|
|
||||||
|
|
||||||
### Creating an Order
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final paymentApi = PaymentAPI.instance;
|
|
||||||
|
|
||||||
final order = await paymentApi.createOrder(
|
|
||||||
CreateOrderRequest(
|
|
||||||
amount: 19.99,
|
|
||||||
currency: 'USD',
|
|
||||||
description: 'Premium subscription',
|
|
||||||
metadata: {'plan': 'premium', 'duration': '1m'},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
print('Order created: ${order.orderId}');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Processing Payment with Overlay
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processPaymentWithOverlay(
|
|
||||||
orderId: order.orderId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
print('Payment successful: ${result.transactionId}');
|
|
||||||
// Navigate to success screen
|
|
||||||
} else {
|
|
||||||
print('Payment failed: ${result.errorMessage}');
|
|
||||||
// Show error to user
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Processing Direct Payment
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processDirectPayment(
|
|
||||||
orderId: order.orderId,
|
|
||||||
paymentMethod: 'credit_card',
|
|
||||||
paymentToken: 'tok_abc123',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
print('Payment successful: ${result.transactionId}');
|
|
||||||
} else {
|
|
||||||
print('Payment failed: ${result.errorMessage}');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
Test business logic and API interactions:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
test('should create order with correct amount', () {
|
|
||||||
final request = CreateOrderRequest(
|
|
||||||
amount: 19.99,
|
|
||||||
currency: 'USD',
|
|
||||||
description: 'Test order',
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(request.amount, 19.99);
|
|
||||||
expect(request.currency, 'USD');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Widget Tests
|
|
||||||
Test UI components:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter test test/main_test.dart
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
Test the mini-app with the eval bridge:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
testWidgets('should process payment through eval bridge', (tester) async {
|
|
||||||
// Setup eval bridge
|
|
||||||
final eval = EvalCompiler();
|
|
||||||
eval.addPlugin(PaymentAPIPlugin());
|
|
||||||
|
|
||||||
// Load mini-app
|
|
||||||
final program = eval.compile(await File('main.dart').readAsString());
|
|
||||||
|
|
||||||
// Run mini-app
|
|
||||||
await tester.pumpWidget(program.build());
|
|
||||||
|
|
||||||
// Test payment flow
|
|
||||||
await tester.tap(find.text('Pay with Overlay'));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.text('Payment successful!'), findsOneWidget);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Compilation Errors
|
|
||||||
|
|
||||||
**Error**: `Method not found in eval bridge`
|
|
||||||
**Solution**: Ensure PaymentAPI is registered with the eval bridge before loading the mini-app.
|
|
||||||
|
|
||||||
**Error**: `Permission denied` when accessing storage
|
|
||||||
**Solution**: Add the following to `AndroidManifest.xml` (Android):
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
```
|
|
||||||
|
|
||||||
And `Info.plist` (iOS):
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
|
||||||
<string>We need access to save payment receipts</string>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Runtime Errors
|
|
||||||
|
|
||||||
**Error**: `PaymentAPI.instance is null`
|
|
||||||
**Solution**: Ensure PaymentAPI is initialized before the mini-app starts.
|
|
||||||
|
|
||||||
**Error**: `Network timeout`
|
|
||||||
**Solution**: Increase timeout in PaymentAPI configuration or check network connectivity.
|
|
||||||
|
|
||||||
### Testing Errors
|
|
||||||
|
|
||||||
**Error**: `Widget not found`
|
|
||||||
**Solution**: Ensure you're pumping the widget tree with `await tester.pump()` after state changes.
|
|
||||||
|
|
||||||
**Error**: `Multiple widgets found`
|
|
||||||
**Solution**: Use more specific finders or add keys to widgets.
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Keep Mini-Apps Small**: Mini-apps should be focused and lightweight
|
|
||||||
2. **Handle Errors Gracefully**: Always wrap API calls in try-catch blocks
|
|
||||||
3. **Show Loading States**: Provide feedback for long-running operations
|
|
||||||
4. **Test Thoroughly**: Write comprehensive tests for all user flows
|
|
||||||
5. **Version Management**: Include version information in metadata
|
|
||||||
6. **Security**: Never store sensitive data in the mini-app
|
|
||||||
7. **Network Handling**: Implement retry logic for network failures
|
|
||||||
8. **User Feedback**: Always show success/error messages to users
|
|
||||||
|
|
||||||
## Performance Tips
|
|
||||||
|
|
||||||
1. **Lazy Loading**: Load resources only when needed
|
|
||||||
2. **Image Optimization**: Compress images before including in mini-app
|
|
||||||
3. **Code Splitting**: Split large mini-apps into smaller modules
|
|
||||||
4. **Caching**: Cache API responses to reduce network calls
|
|
||||||
5. **Debouncing**: Debounce user inputs to reduce API calls
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
1. **Input Validation**: Always validate user inputs
|
|
||||||
2. **API Keys**: Never include API keys in the mini-app code
|
|
||||||
3. **HTTPS Only**: Always use HTTPS for API calls
|
|
||||||
4. **Data Encryption**: Encrypt sensitive data stored locally
|
|
||||||
5. **Permissions**: Request minimum necessary permissions
|
|
||||||
6. **Updates**: Always update to the latest version of dependencies
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For issues or questions:
|
|
||||||
1. Check the main README in `lib/modular/README.md`
|
|
||||||
2. Review PaymentAPI documentation in `lib/modular/api/README.md`
|
|
||||||
3. Check test examples in `test/main_test.dart`
|
|
||||||
4. Review the PaymentAPI interface in `lib/payment_api.dart`
|
|
||||||
@@ -1,617 +0,0 @@
|
|||||||
# 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:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 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`
|
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
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
|
|
||||||
|
|
||||||
```dart
|
|
||||||
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**:
|
|
||||||
```yaml
|
|
||||||
dependencies:
|
|
||||||
flutter_eval: ^0.8.2
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Initialize PluginRegistry**:
|
|
||||||
```dart
|
|
||||||
final registry = ref.read(pluginRegistryProvider.notifier);
|
|
||||||
await registry.initialize();
|
|
||||||
registry.registerBridge('PaymentAPI', PaymentAPI.instance);
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Set up server**:
|
|
||||||
```dart
|
|
||||||
final serverInfo = MiniAppServerInfo(
|
|
||||||
baseUrl: 'https://your-server.com/api',
|
|
||||||
miniAppsPath: '/mini-apps',
|
|
||||||
);
|
|
||||||
registry.setServerInfo(serverInfo);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mini-App Setup
|
|
||||||
|
|
||||||
1. **Create package**:
|
|
||||||
```bash
|
|
||||||
mkdir packages/my-miniapp
|
|
||||||
cd packages/my-miniapp
|
|
||||||
flutter create --org com.example my_miniapp
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add flutter_eval**:
|
|
||||||
```yaml
|
|
||||||
dependencies:
|
|
||||||
flutter_eval: ^0.8.2
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Write mini-app**:
|
|
||||||
```dart
|
|
||||||
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());
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Test and build**:
|
|
||||||
```bash
|
|
||||||
flutter test
|
|
||||||
dart run flutter_eval:compile -i lib/main.dart -o my_miniapp.evc
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
|
|
||||||
Test business logic in isolation:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
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:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/miniapp-example
|
|
||||||
flutter test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
Test full flow:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
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**:
|
|
||||||
```dart
|
|
||||||
// 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**:
|
|
||||||
```dart
|
|
||||||
// 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**:
|
|
||||||
```dart
|
|
||||||
// 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
|
|
||||||
|
|
||||||
- **Build Script**: `packages/miniapp-example/build.sh`
|
|
||||||
- **Flutter Eval**: https://pub.dev/packages/flutter_eval
|
|
||||||
- **Flutter Docs**: https://flutter.dev/docs
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
# Mini-App Example Package
|
|
||||||
|
|
||||||
A complete example mini-app demonstrating how to use the PaymentAPI in a Flutter application loaded via flutter_eval.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/miniapp-example
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
flutter pub get
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
flutter test
|
|
||||||
|
|
||||||
# Run the example app
|
|
||||||
flutter run
|
|
||||||
|
|
||||||
# Build to bytecode (.evc)
|
|
||||||
./build.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## What's Included
|
|
||||||
|
|
||||||
### Files
|
|
||||||
- **`lib/main.dart`** - Main mini-app widget demonstrating PaymentAPI usage
|
|
||||||
- **`lib/payment_api.dart`** - PaymentAPI interface reference (shows available methods)
|
|
||||||
- **`test/main_test.dart`** - Comprehensive widget tests (17 tests, all passing)
|
|
||||||
- **`README.md`** - API usage documentation and examples
|
|
||||||
- **`BUILD_GUIDE.md`** - Complete build and deployment guide
|
|
||||||
- **`build.sh`** - Automated build and test script
|
|
||||||
- **`pubspec.yaml`** - Package configuration
|
|
||||||
|
|
||||||
### Features Demonstrated
|
|
||||||
1. ✅ Creating payment orders
|
|
||||||
2. ✅ Processing payments with overlay UI
|
|
||||||
3. ✅ Processing direct payments
|
|
||||||
4. ✅ Handling payment results and errors
|
|
||||||
5. ✅ Showing loading states
|
|
||||||
6. ✅ Displaying user feedback (SnackBars)
|
|
||||||
7. ✅ Status updates during operations
|
|
||||||
|
|
||||||
## Mini-App Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
PaymentExampleMiniApp
|
|
||||||
└── MaterialApp
|
|
||||||
└── PaymentDemoHome (StatefulWidget)
|
|
||||||
├── Header (Icon + Title + Description)
|
|
||||||
├── Status Card (Shows current operation status)
|
|
||||||
└── Action Buttons (3 payment methods)
|
|
||||||
├── Create Order
|
|
||||||
├── Pay with Overlay
|
|
||||||
└── Direct Payment
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Methods Demonstrated
|
|
||||||
|
|
||||||
### 1. Create Order
|
|
||||||
```dart
|
|
||||||
final order = await paymentApi.createOrder(
|
|
||||||
CreateOrderRequest(
|
|
||||||
amount: 19.99,
|
|
||||||
currency: 'USD',
|
|
||||||
description: 'Premium subscription',
|
|
||||||
metadata: {'plan': 'premium'},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Pay with Overlay
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processPaymentWithOverlay(
|
|
||||||
orderId: order.orderId,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Direct Payment
|
|
||||||
```dart
|
|
||||||
final result = await paymentApi.processDirectPayment(
|
|
||||||
orderId: order.orderId,
|
|
||||||
paymentMethod: 'credit_card',
|
|
||||||
paymentToken: 'token_abc123',
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Coverage
|
|
||||||
|
|
||||||
### Widget Tests (17 tests)
|
|
||||||
- ✅ App displays correctly
|
|
||||||
- ✅ Status card shows ready state
|
|
||||||
- ✅ Buttons are enabled initially
|
|
||||||
- ✅ Buttons disable during loading
|
|
||||||
- ✅ CircularProgressIndicator shows when loading
|
|
||||||
- ✅ Status updates correctly for each operation
|
|
||||||
- ✅ SnackBars show on success
|
|
||||||
- ✅ Status text has correct color
|
|
||||||
- ✅ All icons and UI elements present
|
|
||||||
|
|
||||||
### Running Tests
|
|
||||||
```bash
|
|
||||||
flutter test
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected output:
|
|
||||||
```
|
|
||||||
00:01 +17: All tests passed!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building for Production
|
|
||||||
|
|
||||||
### Automated Build
|
|
||||||
```bash
|
|
||||||
./build.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This will:
|
|
||||||
1. Check Flutter installation
|
|
||||||
2. Install dependencies
|
|
||||||
3. Run all tests
|
|
||||||
4. Optionally compile to .evc bytecode
|
|
||||||
|
|
||||||
### Manual Build
|
|
||||||
```bash
|
|
||||||
# Compile to bytecode
|
|
||||||
dart run flutter_eval:compile -i lib/main.dart -o payment_demo.evc
|
|
||||||
```
|
|
||||||
|
|
||||||
### Output
|
|
||||||
- `payment_demo.evc` - Compiled bytecode ready for deployment
|
|
||||||
- File size: ~2-5 KB (depending on Flutter version)
|
|
||||||
|
|
||||||
## Integration with Main App
|
|
||||||
|
|
||||||
### 1. Register PaymentAPI
|
|
||||||
```dart
|
|
||||||
import 'package:island/modular/api/payment.dart';
|
|
||||||
|
|
||||||
final registry = PluginRegistry();
|
|
||||||
registry.registerBridge('PaymentAPI', PaymentAPI.instance);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Load Mini-App
|
|
||||||
```dart
|
|
||||||
final registry = ref.read(pluginRegistryProvider.notifier);
|
|
||||||
|
|
||||||
final miniApp = await registry.loadMiniApp(
|
|
||||||
'https://your-server.com/mini-apps/payment_demo.evc',
|
|
||||||
);
|
|
||||||
|
|
||||||
await registry.enablePlugin(miniApp.id);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Launch Mini-App
|
|
||||||
```dart
|
|
||||||
await registry.launchMiniApp(context, miniApp.id);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Concepts
|
|
||||||
|
|
||||||
### Mini-App Design
|
|
||||||
- **Full-Screen**: Mini-apps are full-screen with their own navigation
|
|
||||||
- **Network-Loaded**: Downloaded from server and cached locally
|
|
||||||
- **Bytecode**: Compiled to .evc format for efficient loading
|
|
||||||
- **API Access**: Access PaymentAPI through eval bridge
|
|
||||||
|
|
||||||
### State Management
|
|
||||||
- Mini-apps manage their own state
|
|
||||||
- No Riverpod dependency required
|
|
||||||
- PaymentAPI uses singleton pattern for easy access
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
- All API calls wrapped in try-catch
|
|
||||||
- User-friendly error messages displayed
|
|
||||||
- Status updates provide real-time feedback
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### 1. Loading States
|
|
||||||
Always show loading indicators:
|
|
||||||
```dart
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
await paymentApi.processPaymentWithOverlay(...);
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. User Feedback
|
|
||||||
Always provide feedback:
|
|
||||||
```dart
|
|
||||||
if (result.success) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Payment successful!')),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Payment failed: ${result.errorMessage}')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Status Updates
|
|
||||||
Keep users informed:
|
|
||||||
```dart
|
|
||||||
_updateStatus('Processing payment...');
|
|
||||||
// ... process payment
|
|
||||||
_updateStatus('Payment successful!');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
### For API Details
|
|
||||||
See: `lib/modular/api/README.md`
|
|
||||||
|
|
||||||
### For Plugin System
|
|
||||||
See: `lib/modular/README.md`
|
|
||||||
|
|
||||||
### For Build & Deployment
|
|
||||||
See: `BUILD_GUIDE.md`
|
|
||||||
|
|
||||||
## Example Workflow
|
|
||||||
|
|
||||||
### Development
|
|
||||||
1. Edit `lib/main.dart`
|
|
||||||
2. Run `flutter run` to test
|
|
||||||
3. Run `flutter test` to verify
|
|
||||||
4. Commit changes
|
|
||||||
|
|
||||||
### Production
|
|
||||||
1. Update version in metadata
|
|
||||||
2. Run `./build.sh` to compile
|
|
||||||
3. Upload `payment_demo.evc` to server
|
|
||||||
4. Update server metadata
|
|
||||||
5. Test loading in main app
|
|
||||||
6. Deploy
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Compilation Fails
|
|
||||||
- Ensure flutter_eval is installed: `flutter pub get`
|
|
||||||
- Check Dart version: `dart --version` (should be >=3.0.0)
|
|
||||||
- Verify file paths are correct
|
|
||||||
|
|
||||||
### Tests Fail
|
|
||||||
- Run tests with verbose output: `flutter test --verbose`
|
|
||||||
- Check Flutter version: `flutter --version`
|
|
||||||
- Clean build: `flutter clean && flutter pub get`
|
|
||||||
|
|
||||||
### Mini-App Won't Load
|
|
||||||
- Verify PaymentAPI is registered with eval bridge
|
|
||||||
- Check .evc file is not corrupted
|
|
||||||
- Ensure server URL is accessible
|
|
||||||
- Review server metadata format
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### File Size
|
|
||||||
- Source code: ~5 KB
|
|
||||||
- Compiled bytecode: ~2-5 KB
|
|
||||||
- Small footprint for fast loading
|
|
||||||
|
|
||||||
### Load Time
|
|
||||||
- Download: <1 second on 4G
|
|
||||||
- Compilation: <500ms
|
|
||||||
- Initial render: <100ms
|
|
||||||
|
|
||||||
### Memory Usage
|
|
||||||
- Idle: ~5 MB
|
|
||||||
- During operation: ~10-15 MB
|
|
||||||
- Peak: ~20 MB
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
### What's Secure
|
|
||||||
- No API keys in code
|
|
||||||
- No sensitive data storage
|
|
||||||
- HTTPS-only API calls
|
|
||||||
- Sandboxed execution
|
|
||||||
|
|
||||||
### What to Watch
|
|
||||||
- Validate all user inputs
|
|
||||||
- Never store tokens locally
|
|
||||||
- Always use official PaymentAPI
|
|
||||||
- Keep dependencies updated
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
### Issues
|
|
||||||
1. Check documentation files
|
|
||||||
2. Review test examples
|
|
||||||
3. Check PaymentAPI interface
|
|
||||||
4. Review build script
|
|
||||||
|
|
||||||
### Resources
|
|
||||||
- `README.md` - API usage
|
|
||||||
- `BUILD_GUIDE.md` - Build process
|
|
||||||
- `test/main_test.dart` - Test examples
|
|
||||||
- `lib/payment_api.dart` - API reference
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### For This Mini-App
|
|
||||||
1. Customize the UI for your use case
|
|
||||||
2. Add more payment methods
|
|
||||||
3. Implement additional features
|
|
||||||
4. Write more tests
|
|
||||||
|
|
||||||
### For New Mini-Apps
|
|
||||||
1. Copy this package structure
|
|
||||||
2. Replace main.dart with your app
|
|
||||||
3. Add your dependencies
|
|
||||||
4. Test thoroughly
|
|
||||||
5. Build and deploy
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
Built as part of the Island plugin system demonstrating:
|
|
||||||
- Plugin Registry integration
|
|
||||||
- Mini-app loading and execution
|
|
||||||
- PaymentAPI usage in mini-apps
|
|
||||||
- Best practices for mini-app development
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Part of the Island project. See main project LICENSE for details.
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Mini-App Example: Simple Payment Demo
|
|
||||||
///
|
|
||||||
/// This demonstrates how a mini-app would use PaymentAPI.
|
|
||||||
/// In a real mini-app, PaymentAPI would be accessed through
|
|
||||||
/// eval bridge provided by flutter_eval.
|
|
||||||
Widget buildEntry() {
|
|
||||||
Widget result;
|
|
||||||
try {
|
|
||||||
result = const PaymentDemoHome();
|
|
||||||
} catch (e) {
|
|
||||||
result = Center(child: Text('Error: $e'));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PaymentDemoHome extends StatefulWidget {
|
|
||||||
const PaymentDemoHome({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
PaymentDemoHomeState createState() => PaymentDemoHomeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
|
||||||
String _status = 'Ready';
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
void _updateStatus(String status) {
|
|
||||||
setState(() {
|
|
||||||
_status = status;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _createOrder() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
try {
|
|
||||||
_updateStatus('Creating order...');
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
_updateStatus('Order created! Order ID: ORD-001\nAmount: 100 USD');
|
|
||||||
} catch (e) {
|
|
||||||
_updateStatus('Failed to create order: $e');
|
|
||||||
} finally {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _processPaymentWithOverlay() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
try {
|
|
||||||
_updateStatus('Processing payment with overlay...');
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
_updateStatus(
|
|
||||||
'Payment completed successfully!\nTransaction ID: TXN-12345',
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
_updateStatus('Payment failed: $e');
|
|
||||||
} finally {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _processDirectPayment() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
try {
|
|
||||||
_updateStatus('Processing direct payment...');
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
_updateStatus('Direct payment successful!\nTransaction ID: TXN-67890');
|
|
||||||
} catch (e) {
|
|
||||||
_updateStatus('Payment failed: $e');
|
|
||||||
} finally {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text('Payment API Demo')),
|
|
||||||
body: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(24.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.payment, size: 80, color: Colors.blue),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
const Text(
|
|
||||||
'Payment API Demo',
|
|
||||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Example mini-app demonstrating PaymentAPI usage',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.grey),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Card(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Status:',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
_status,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(fontSize: 14),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : () => _createOrder(),
|
|
||||||
child: _isLoading
|
|
||||||
? const SizedBox(
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
child: Text('Wait'),
|
|
||||||
)
|
|
||||||
: const Text('Create Order'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading
|
|
||||||
? null
|
|
||||||
: () => _processPaymentWithOverlay(),
|
|
||||||
child: const Text('Pay with Overlay'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : () => _processDirectPayment(),
|
|
||||||
child: const Text('Direct Payment'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,533 +0,0 @@
|
|||||||
# Generated by pub
|
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
|
||||||
packages:
|
|
||||||
_fe_analyzer_shared:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: _fe_analyzer_shared
|
|
||||||
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "85.0.0"
|
|
||||||
analyzer:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: analyzer
|
|
||||||
sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.7.1"
|
|
||||||
args:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: args
|
|
||||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.7.0"
|
|
||||||
async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: async
|
|
||||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.13.0"
|
|
||||||
boolean_selector:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: boolean_selector
|
|
||||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.2"
|
|
||||||
change_case:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: change_case
|
|
||||||
sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
characters:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: characters
|
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.0"
|
|
||||||
checked_yaml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: checked_yaml
|
|
||||||
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.4"
|
|
||||||
clock:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: clock
|
|
||||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.2"
|
|
||||||
code_assets:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: code_assets
|
|
||||||
sha256: ae0db647e668cbb295a3527f0938e4039e004c80099dce2f964102373f5ce0b5
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.19.10"
|
|
||||||
collection:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: collection
|
|
||||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.19.1"
|
|
||||||
convert:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: convert
|
|
||||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.2"
|
|
||||||
crypto:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: crypto
|
|
||||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.7"
|
|
||||||
dart_eval:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dart_eval
|
|
||||||
sha256: c541adaa17530870b93fc853b5481382163ad44a246df3582c59afd899bc8214
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.3"
|
|
||||||
dart_style:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dart_style
|
|
||||||
sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.1"
|
|
||||||
directed_graph:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: directed_graph
|
|
||||||
sha256: "6582283519fc08a6a14172d8a3798238f09275a1147e7599baf09c6245e3104a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.5"
|
|
||||||
exception_templates:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: exception_templates
|
|
||||||
sha256: "57adef649aa2a99a5b324a921355ee9214472a007ca257cbec2f3abae005c93e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.2"
|
|
||||||
fake_async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fake_async
|
|
||||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.3"
|
|
||||||
ffi:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: ffi
|
|
||||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.5"
|
|
||||||
file:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file
|
|
||||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.1"
|
|
||||||
flutter:
|
|
||||||
dependency: "direct main"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
flutter_eval:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_eval
|
|
||||||
sha256: "1aeba5ecc5bbafc560d6a48bb55b4c19051f596fc67944f4822054cf0005ef69"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.2"
|
|
||||||
flutter_lints:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: flutter_lints
|
|
||||||
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.0.0"
|
|
||||||
flutter_test:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
glob:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: glob
|
|
||||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.3"
|
|
||||||
hooks:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: hooks
|
|
||||||
sha256: "5410b9f4f6c9f01e8ff0eb81c9801ea13a3c3d39f8f0b1613cda08e27eab3c18"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.20.5"
|
|
||||||
http:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.6.0"
|
|
||||||
http_parser:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http_parser
|
|
||||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.2"
|
|
||||||
json_annotation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: json_annotation
|
|
||||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.9.0"
|
|
||||||
lazy_memo:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lazy_memo
|
|
||||||
sha256: f3f4afe9c4ccf0f29082213c5319a3711041446fc41cd325a9bf91724d4ea9c8
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.5"
|
|
||||||
leak_tracker:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker
|
|
||||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "11.0.2"
|
|
||||||
leak_tracker_flutter_testing:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker_flutter_testing
|
|
||||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.10"
|
|
||||||
leak_tracker_testing:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker_testing
|
|
||||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.2"
|
|
||||||
lints:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lints
|
|
||||||
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.0.0"
|
|
||||||
logging:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: logging
|
|
||||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
matcher:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: matcher
|
|
||||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.12.17"
|
|
||||||
material_color_utilities:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: material_color_utilities
|
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.11.1"
|
|
||||||
meta:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: meta
|
|
||||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.17.0"
|
|
||||||
native_toolchain_c:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: native_toolchain_c
|
|
||||||
sha256: f8872ea6c7a50ce08db9ae280ca2b8efdd973157ce462826c82f3c3051d154ce
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.17.2"
|
|
||||||
objective_c:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: objective_c
|
|
||||||
sha256: "55eb67ede1002d9771b3f9264d2c9d30bc364f0267bc1c6cc0883280d5f0c7cb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "9.2.2"
|
|
||||||
package_config:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_config
|
|
||||||
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
path:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path
|
|
||||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.9.1"
|
|
||||||
path_provider:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider
|
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.5"
|
|
||||||
path_provider_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_android
|
|
||||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.22"
|
|
||||||
path_provider_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_foundation
|
|
||||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.6.0"
|
|
||||||
path_provider_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_linux
|
|
||||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.1"
|
|
||||||
path_provider_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_platform_interface
|
|
||||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.2"
|
|
||||||
path_provider_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_windows
|
|
||||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.0"
|
|
||||||
platform:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: platform
|
|
||||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.6"
|
|
||||||
plugin_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: plugin_platform_interface
|
|
||||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.8"
|
|
||||||
pub_semver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pub_semver
|
|
||||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
pubspec_parse:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pubspec_parse
|
|
||||||
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.5.0"
|
|
||||||
quote_buffer:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: quote_buffer
|
|
||||||
sha256: "5be4662a87aac8152aa05cdcf467e421aa2edc3b147f069798e2d26539b7ed0a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.7"
|
|
||||||
sky_engine:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
source_span:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: source_span
|
|
||||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.10.1"
|
|
||||||
stack_trace:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: stack_trace
|
|
||||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.12.1"
|
|
||||||
stream_channel:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: stream_channel
|
|
||||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
string_scanner:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: string_scanner
|
|
||||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.1"
|
|
||||||
term_glyph:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: term_glyph
|
|
||||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.2"
|
|
||||||
test_api:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: test_api
|
|
||||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.7.7"
|
|
||||||
typed_data:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: typed_data
|
|
||||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.0"
|
|
||||||
vector_math:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_math
|
|
||||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
vm_service:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vm_service
|
|
||||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "15.0.2"
|
|
||||||
watcher:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: watcher
|
|
||||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.1"
|
|
||||||
web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: web
|
|
||||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
xdg_directories:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xdg_directories
|
|
||||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
yaml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: yaml
|
|
||||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.3"
|
|
||||||
sdks:
|
|
||||||
dart: ">=3.10.3 <4.0.0"
|
|
||||||
flutter: ">=3.38.4"
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
name: miniapp_example
|
|
||||||
description: An example mini-app demonstrating PaymentAPI usage
|
|
||||||
version: 1.0.0
|
|
||||||
publish_to: none
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=3.10.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_eval: ^0.8.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^4.0.0
|
|
||||||
|
|
||||||
analyzer:
|
|
||||||
enable-experiment:
|
|
||||||
- dot-shorthands
|
|
||||||
File diff suppressed because one or more lines are too long
31
packages/miniapp-minimal/.gitignore
vendored
31
packages/miniapp-minimal/.gitignore
vendored
@@ -1,31 +0,0 @@
|
|||||||
# Miscellaneous
|
|
||||||
*.class
|
|
||||||
*.log
|
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
|
||||||
/pubspec.lock
|
|
||||||
**/doc/api/
|
|
||||||
.dart_tool/
|
|
||||||
.flutter-plugins-dependencies
|
|
||||||
/build/
|
|
||||||
/coverage/
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled and should not be manually edited.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: "8b872868494e429d94fa06dca855c306438b22c0"
|
|
||||||
channel: "stable"
|
|
||||||
|
|
||||||
project_type: package
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
Widget buildEntry() {
|
|
||||||
return const MinimalTextWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
class MinimalTextWidget extends StatefulWidget {
|
|
||||||
const MinimalTextWidget({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MinimalTextWidget> createState() => _MinimalTextWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MinimalTextWidgetState extends State<MinimalTextWidget> {
|
|
||||||
int _tapCount = 0;
|
|
||||||
|
|
||||||
final List<String> _messages = ['Tap me!', 'Hello!', 'Thanks for tapping!'];
|
|
||||||
|
|
||||||
String get _currentMessage => _messages[_tapCount % _messages.length];
|
|
||||||
|
|
||||||
void _handleTap() {
|
|
||||||
setState(() {
|
|
||||||
_tapCount++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Center(
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: _handleTap,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),
|
|
||||||
child: Text(
|
|
||||||
_currentMessage,
|
|
||||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
name: miniapp_minimal
|
|
||||||
description: "A new Flutter package project."
|
|
||||||
version: 0.0.1
|
|
||||||
homepage:
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ^3.10.7
|
|
||||||
flutter: ">=1.17.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^6.0.0
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
|
||||||
flutter:
|
|
||||||
|
|
||||||
# To add assets to your package, add an assets section, like this:
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
#
|
|
||||||
# For details regarding assets in packages, see
|
|
||||||
# https://flutter.dev/to/asset-from-package
|
|
||||||
#
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
|
||||||
# https://flutter.dev/to/resolution-aware-images
|
|
||||||
|
|
||||||
# To add custom fonts to your package, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts in packages, see
|
|
||||||
# https://flutter.dev/to/font-from-package
|
|
||||||
90
pubspec.lock
90
pubspec.lock
@@ -34,7 +34,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.3.3"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0
|
sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0
|
||||||
@@ -217,14 +217,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.5"
|
||||||
change_case:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: change_case
|
|
||||||
sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -321,14 +313,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
console:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: console
|
|
||||||
sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
convert:
|
convert:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -409,14 +393,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0+8.4.0"
|
version: "1.0.0+8.4.0"
|
||||||
dart_eval:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dart_eval
|
|
||||||
sha256: c541adaa17530870b93fc853b5481382163ad44a246df3582c59afd899bc8214
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.3"
|
|
||||||
dart_ipc:
|
dart_ipc:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -433,14 +409,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.3.1"
|
||||||
dart_midi_pro:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: dart_midi_pro
|
|
||||||
sha256: "9a0273c92c0336e5694c7318fd936f64f06e938a936dd5fac6563c39954b7f6d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.4+2"
|
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -521,14 +489,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
directed_graph:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: directed_graph
|
|
||||||
sha256: "6582283519fc08a6a14172d8a3798238f09275a1147e7599baf09c6245e3104a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.5"
|
|
||||||
dismissible_page:
|
dismissible_page:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -617,14 +577,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
exception_templates:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: exception_templates
|
|
||||||
sha256: "57adef649aa2a99a5b324a921355ee9214472a007ca257cbec2f3abae005c93e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.2"
|
|
||||||
expandable:
|
expandable:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -870,14 +822,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
flutter_eval:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_eval
|
|
||||||
sha256: "1aeba5ecc5bbafc560d6a48bb55b4c19051f596fc67944f4822054cf0005ef69"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.2"
|
|
||||||
flutter_expandable_fab:
|
flutter_expandable_fab:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1293,14 +1237,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
get_it:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: get_it
|
|
||||||
sha256: ae78de7c3f2304b8d81f2bb6e320833e5e81de942188542328f074978cc0efa9
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "8.3.0"
|
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1621,14 +1557,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.16"
|
||||||
lazy_memo:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lazy_memo
|
|
||||||
sha256: f3f4afe9c4ccf0f29082213c5319a3711041446fc41cd325a9bf91724d4ea9c8
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.5"
|
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1909,14 +1837,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
msix:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: msix
|
|
||||||
sha256: f88033fcb9e0dd8de5b18897cbebbd28ea30596810f4a7c86b12b0c03ace87e5
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.16.12"
|
|
||||||
native_exif:
|
native_exif:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2365,14 +2285,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
quote_buffer:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: quote_buffer
|
|
||||||
sha256: "5be4662a87aac8152aa05cdcf467e421aa2edc3b147f069798e2d26539b7ed0a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.7"
|
|
||||||
recase:
|
recase:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
19
pubspec.yaml
19
pubspec.yaml
@@ -19,7 +19,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
version: 3.5.0+164
|
version: 3.5.0+164
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>3.10.0'
|
sdk: ">3.10.0"
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
@@ -71,7 +71,6 @@ dependencies:
|
|||||||
cross_file: ^0.3.5+1
|
cross_file: ^0.3.5+1
|
||||||
image_picker: ^1.2.1
|
image_picker: ^1.2.1
|
||||||
file_picker: ^10.3.8
|
file_picker: ^10.3.8
|
||||||
flutter_eval: ^0.8.2
|
|
||||||
riverpod_annotation: ^4.0.0
|
riverpod_annotation: ^4.0.0
|
||||||
image_picker_platform_interface: ^2.11.1
|
image_picker_platform_interface: ^2.11.1
|
||||||
image_picker_android: ^0.8.13+10
|
image_picker_android: ^0.8.13+10
|
||||||
@@ -175,7 +174,6 @@ dependencies:
|
|||||||
video_thumbnail: ^0.5.6
|
video_thumbnail: ^0.5.6
|
||||||
just_audio: ^0.10.5
|
just_audio: ^0.10.5
|
||||||
audio_session: ^0.2.2
|
audio_session: ^0.2.2
|
||||||
dart_midi_pro: ^1.0.4+2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -195,10 +193,6 @@ dev_dependencies:
|
|||||||
riverpod_lint: ^3.1.0
|
riverpod_lint: ^3.1.0
|
||||||
drift_dev: ^2.30.1
|
drift_dev: ^2.30.1
|
||||||
flutter_launcher_icons: ^0.14.4
|
flutter_launcher_icons: ^0.14.4
|
||||||
msix: ^3.16.12
|
|
||||||
|
|
||||||
dependency_overrides:
|
|
||||||
analyzer: ^8.0.0
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
@@ -212,7 +206,6 @@ flutter:
|
|||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
assets:
|
||||||
- assets/midi/
|
|
||||||
- assets/i18n/
|
- assets/i18n/
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/images/oidc/
|
- assets/images/oidc/
|
||||||
@@ -271,13 +264,3 @@ flutter_native_splash:
|
|||||||
image_dark: "assets/icons/icon-dark.png"
|
image_dark: "assets/icons/icon-dark.png"
|
||||||
color: "#ffffff"
|
color: "#ffffff"
|
||||||
color_dark: "#121212"
|
color_dark: "#121212"
|
||||||
|
|
||||||
msix_config:
|
|
||||||
display_name: Solian
|
|
||||||
publisher_display_name: Solsynth LLC
|
|
||||||
identity_name: dev.solian.app
|
|
||||||
msix_version: 3.2.0.0
|
|
||||||
logo_path: .\assets\icons\icon.png
|
|
||||||
protocol_activation: solian, https
|
|
||||||
app_uri_handler_hosts: solian.app
|
|
||||||
capabilities: internetClientServer, location, microphone, webcam
|
|
||||||
|
|||||||
Reference in New Issue
Block a user