Flutter Platform Guide
Complete guide for integrating the MOSA SDK into Flutter applications.
Requirements
- Flutter: 3.0 or higher
- Dart: 2.17 or higher
- Platform Requirements: Same as native platform requirements
- Android: API Level 24+ (Android 7.0+)
- iOS: iOS 13.0+
Installation
Add the plugin to your pubspec.yaml:
dependencies:
mosa_dock_sdk_flutter: ^1.0.0 # Replace with actual version
Then run:
flutter pub get
Configuration
Android
See Android Platform Guide for AndroidManifest.xml configuration.
iOS
See iOS Platform Guide for Info.plist configuration.
Initialization
Initialize SDK
import 'package:mosa_dock_sdk_flutter/mosa_dock_sdk_flutter.dart';
final mosaDockSdk = MosaDockSdk();
// IMPORTANT: Subscribe to callbacks BEFORE initializing SDK
mosaDockSdk.dockCallbackEvents.listen((event) {
// Handle callbacks
});
// Initialize SDK
await mosaDockSdk.initialize(
apiKey: 'your-api-key',
userId: 'user-123',
);
Critical: Subscribe to event streams before calling initialize() to ensure you receive all callbacks.
Permissions
Request Permissions
final hasPermissions = await mosaDockSdk.checkAndRequestPermissions();
if (!hasPermissions) {
// Handle permission denial
showPermissionDialog();
}
Check Device Capabilities
final bleSupported = await mosaDockSdk.isBleSupported();
final nfcSupported = await mosaDockSdk.isNfcSupported();
final bleEnabled = await mosaDockSdk.isBleEnabled();
final nfcEnabled = await mosaDockSdk.isNfcEnabled();
if (!bleSupported || !nfcSupported) {
showError('Device doesn\'t support NFC or BLE');
return;
}
if (!bleEnabled || !nfcEnabled) {
showEnableFeaturesDialog();
return;
}
Basic Usage
Request Unlock Key
try {
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.bikeLock,
areaId: 'area-123',
userId: 'user-123',
);
// Use key for dock operation
await performDockOperation(unlockKey);
} catch (e) {
// Handle error
showError('Failed to request unlock key: $e');
}
Perform Dock Operation
try {
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.parking.value,
);
} catch (e) {
// Handle error
showError('Dock operation failed: $e');
}
Callbacks
Subscribe to Dock Callback Events
StreamSubscription<DockCallbackEvent>? _callbackSubscription;
@override
void initState() {
super.initState();
_setupCallbacks();
}
void _setupCallbacks() {
// Subscribe BEFORE initializing SDK
_callbackSubscription = mosaDockSdk.dockCallbackEvents.listen(
(DockCallbackEvent event) {
setState(() {
switch (event.type) {
case DockCallbackType.nfcScanCompleted:
statusText = 'Dock found: ${event.data?['macAddress']}';
break;
case DockCallbackType.bleConnecting:
statusText = 'Connecting...';
isLoading = true;
break;
case DockCallbackType.bleConnected:
statusText = 'Connected';
break;
case DockCallbackType.keySent:
statusText = 'Key sent, unlocking...';
break;
case DockCallbackType.dockUnlocked:
statusText = 'Dock unlocked!';
break;
case DockCallbackType.dockChainRemoved:
statusText = 'Chain removed - bike ready!';
break;
case DockCallbackType.dockChainInserted:
statusText = 'Chain inserted, securing dock...';
break;
case DockCallbackType.dockSecured:
statusText = 'Dock secured - operation complete!';
isLoading = false;
break;
case DockCallbackType.bleDisconnected:
statusText = 'Disconnected';
break;
case DockCallbackType.error:
statusText = 'Error: ${event.message ?? "Unknown error"}';
isLoading = false;
_handleError(event.error);
break;
default:
break;
}
});
},
onError: (error) {
print('Callback error: $error');
},
);
// Now initialize SDK
mosaDockSdk.initialize(
apiKey: 'your-api-key',
userId: 'user-123',
);
}
@override
void dispose() {
_callbackSubscription?.cancel();
super.dispose();
}
Additional Event Streams
The Flutter SDK provides additional event streams:
// Session state events
mosaDockSdk.sessionStateEvents.listen((event) {
// Handle session state changes
});
// BLE adapter state events
mosaDockSdk.bleAdapterStateEvents.listen((event) {
final isEnabled = event.enabled;
// Handle BLE adapter state
});
// BLE connection state events
mosaDockSdk.bleConnectionStateEvents.listen((event) {
final isConnected = event.connected;
// Handle BLE connection state
});
Complete Example
import 'package:flutter/material.dart';
import 'package:mosa_dock_sdk_flutter/mosa_dock_sdk_flutter.dart';
import 'dart:async';
class UnlockScreen extends StatefulWidget {
@override
_UnlockScreenState createState() => _UnlockScreenState();
}
class _UnlockScreenState extends State<UnlockScreen> {
final mosaDockSdk = MosaDockSdk();
StreamSubscription<DockCallbackEvent>? _callbackSubscription;
String statusText = 'Ready';
bool isLoading = false;
bool isConnected = false;
@override
void initState() {
super.initState();
_setupSDK();
}
void _setupSDK() {
// Subscribe to callbacks BEFORE initializing
_callbackSubscription = mosaDockSdk.dockCallbackEvents.listen(
(DockCallbackEvent event) {
setState(() {
switch (event.type) {
case DockCallbackType.nfcScanCompleted:
statusText = 'Dock found';
break;
case DockCallbackType.bleConnecting:
statusText = 'Connecting...';
isLoading = true;
break;
case DockCallbackType.bleConnected:
statusText = 'Connected';
isConnected = true;
break;
case DockCallbackType.keySent:
statusText = 'Key sent, unlocking...';
break;
case DockCallbackType.dockUnlocked:
statusText = 'Dock unlocked!';
break;
case DockCallbackType.dockChainRemoved:
statusText = 'Chain removed - bike ready!';
isLoading = false;
break;
case DockCallbackType.error:
statusText = 'Error: ${event.message ?? "Unknown"}';
isLoading = false;
break;
default:
break;
}
});
},
);
// Initialize SDK
mosaDockSdk.initialize(
apiKey: 'your-api-key',
userId: 'user-123',
);
}
Future<void> _unlockBike() async {
try {
// Check capabilities
final bleSupported = await mosaDockSdk.isBleSupported();
final nfcSupported = await mosaDockSdk.isNfcSupported();
if (!bleSupported || !nfcSupported) {
setState(() {
statusText = 'Device doesn\'t support NFC or BLE';
});
return;
}
// Request permissions
final hasPermissions = await mosaDockSdk.checkAndRequestPermissions();
if (!hasPermissions) {
setState(() {
statusText = 'Permissions denied';
});
return;
}
// Request unlock key
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.bikeUnlock,
areaId: 'area-123',
userId: 'user-123',
);
// Perform unlock operation
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.parking.value,
);
} catch (e) {
setState(() {
statusText = 'Error: $e';
isLoading = false;
});
}
}
@override
void dispose() {
_callbackSubscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Unlock Bike')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(statusText, style: TextStyle(fontSize: 18)),
if (isLoading) CircularProgressIndicator(),
SizedBox(height: 20),
ElevatedButton(
onPressed: isLoading ? null : _unlockBike,
child: Text('Unlock Bike'),
),
],
),
);
}
}
Error Handling
Errors are reported via the error callback event:
if (event.type == DockCallbackType.error) {
final error = event.error;
if (error is Map<String, dynamic>) {
final errorType = error['errorType'];
final message = error['message'];
switch (errorType) {
case 'nfc_disabled':
showError('Please enable NFC in settings');
break;
case 'ble_disabled':
showError('Please enable Bluetooth in settings');
break;
case 'dock_timeout':
showError('Dock operation timed out. Please try again.');
break;
default:
showError('Error: $message');
}
}
}
Platform Channels
The Flutter SDK uses platform channels to communicate with native SDKs:
- Method Channel:
com.mosa/sdk- For method calls - Event Channel:
com.mosa/sdk/events- For callback events - Dock Callback Channel:
com.mosa/sdk/dock_callbacks- For dock operation callbacks
These are handled automatically by the SDK - you don't need to interact with them directly.
Best Practices
- Subscribe Early: Subscribe to event streams before initializing SDK
- Check Capabilities: Always check device capabilities before operations
- Handle Permissions: Request permissions before dock operations
- Error Handling: Always handle errors from callbacks and method calls
- Clean Up: Cancel subscriptions in
dispose()method - State Management: Use
setState()or state management solution for UI updates
Next Steps
- SDK Callbacks - Complete callback documentation
- Use Case Guides - Bike parking and sharing examples
- API Reference - Complete API documentation
For native platform details, see Android Platform Guide and iOS Platform Guide.