Skip to main content

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​

  1. Subscribe Early: Subscribe to event streams before initializing SDK
  2. Check Capabilities: Always check device capabilities before operations
  3. Handle Permissions: Request permissions before dock operations
  4. Error Handling: Always handle errors from callbacks and method calls
  5. Clean Up: Cancel subscriptions in dispose() method
  6. State Management: Use setState() or state management solution for UI updates

Next Steps​


For native platform details, see Android Platform Guide and iOS Platform Guide.