nebula_flutter_plugin 1.0.3
nebula_flutter_plugin: ^1.0.3 copied to clipboard
Flutter wrapper for PAX Nebula SDK: connect to PAX POS terminals and perform Sale, Refund, Void, Settlement and custom txn transactions easily over WIFI, USB, BLuetooth and Cloud
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:nebula_flutter_plugin/nebula_flutter_plugin.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nebula Plugin Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final NebulaFlutterPlugin _plugin = NebulaFlutterPlugin();
final List<String> _logs = [];
bool _isConnected = false;
@override
void initState() {
super.initState();
_plugin.setOnConnectionStatusCallback((connected) {
setState(() {
_isConnected = connected;
_addLog('Connection status changed: ${connected ? "Connected" : "Disconnected"}');
});
});
_plugin.setOnMessageReceivedCallback((packageName, message) {
_addLog('Message received: pkg=$packageName, msg=$message');
});
_plugin.setOnChooseAppCallback((apps) async {
return await showDialog<String>(
context: context,
barrierDismissible: false,
builder: (context) {
return SimpleDialog(
title: const Text('Select Application'),
children: apps.map((app) {
return SimpleDialogOption(
onPressed: () {
Navigator.pop(context, app['packageName']);
},
child: Text(app['appName'] ?? (app['packageName'] ?? 'Unknown App')),
);
}).toList(),
);
},
);
});
}
Future<Map<String, String>?> _showInputsDialog(
String title, Map<String, String> defaultValues) async {
final controllers = <String, TextEditingController>{};
for (var entry in defaultValues.entries) {
controllers[entry.key] = TextEditingController(text: entry.value);
}
return await showDialog<Map<String, String>>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: defaultValues.keys.map((key) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: TextField(
controller: controllers[key],
decoration: InputDecoration(
labelText: key,
border: const OutlineInputBorder(),
),
),
);
}).toList(),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, null),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final result = <String, String>{};
for (var key in controllers.keys) {
result[key] = controllers[key]!.text;
}
Navigator.pop(context, result);
},
child: const Text('OK'),
),
],
);
},
);
}
void _addLog(String log) {
setState(() {
_logs.insert(0, '[${DateTime.now().toString().substring(11, 19)}] $log');
if (_logs.length > 100) _logs.removeLast();
});
}
Future<void> _connectTcp() async {
final inputs = await _showInputsDialog('TCP Connect', {
'host': '192.168.8.101',
'port': '30999',
});
if (inputs == null) return;
_addLog('Connecting via TCP to ${inputs['host']}:${inputs['port']}...');
try {
final result = await _plugin.connectTcp(
inputs['host']!,
port: int.tryParse(inputs['port']!) ?? 30999,
timeout: 20000,
);
_addLog('TCP Connection result: $result');
} catch (e) {
_addLog('TCP Connection failed: $e');
}
}
Future<void> _connectBluetooth() async {
final inputs = await _showInputsDialog('Bluetooth Connect', {
'identifier': 'A0:44:B7:DC:3E:0D',
});
if (inputs == null) return;
_addLog('Connecting via Bluetooth to ${inputs['identifier']}...');
try {
final result = await _plugin.connectBluetooth(
inputs['identifier']!,
timeout: 20000,
);
_addLog('Bluetooth Connection result: $result');
} catch (e) {
_addLog('Bluetooth Connection failed: $e');
}
}
Future<void> _connectUsb() async {
_addLog('Connecting via USB...');
try {
final result = await _plugin.connectUsb(timeout: 20000);
_addLog('USB Connection result: $result');
} catch (e) {
_addLog('USB Connection failed: $e');
}
}
Future<void> _bindCloud() async {
final inputs = await _showInputsDialog('Bind Cloud', {
'baseUrl': 'https://uat.posnebula.com/nebula-acs/',
'bindCode': '62719',
'eid': '495572',
'appId': '600052',
});
if (inputs == null) return;
_addLog('Binding via Cloud...');
try {
final config = CloudConfig(
baseUrl: inputs['baseUrl']!,
bindCode: inputs['bindCode']!,
eid: inputs['eid']!,
appId: inputs['appId']!,
);
final result = await _plugin.bindDeviceByCloud(config, timeout: 20000);
_addLog('Cloud Bind result: $result');
} catch (e) {
_addLog('Cloud Bind failed: $e');
}
}
Future<void> _connectCloud() async {
_addLog('Connecting via Cloud...');
try {
final result = await _plugin.connectCloud(timeout: 20000);
_addLog('Cloud Connection result: $result');
} catch (e) {
_addLog('Cloud Connection failed: $e');
}
}
Future<void> _sendSaleTransaction() async {
final inputs = await _showInputsDialog('Sale Transaction', {
'amount': '1000',
});
if (inputs == null) return;
final amount = int.tryParse(inputs['amount'] ?? '');
if (amount == null) {
_addLog('Invalid amount entered');
return;
}
_addLog('Sending Sale transaction...');
try {
final request = SaleRequest(
amount: amount,
tipAmount: 0,
currencyCode: 'BDT',
);
final response = await _plugin.startSaleTransaction(request);
_addLog('Sale Transaction result: $response');
} catch (e) {
_addLog('Sale Transaction failed: $e');
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Nebula Plugin Demo'),
backgroundColor: theme.colorScheme.inversePrimary,
actions: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Row(
children: [
Icon(
Icons.circle,
size: 12,
color: _isConnected ? Colors.green : Colors.red,
),
const SizedBox(width: 6),
Text(
_isConnected ? 'Connected' : 'Disconnected',
style: TextStyle(
fontSize: 14,
color: _isConnected ? Colors.green : Colors.red,
),
),
],
),
),
],
),
body: Column(
children: [
// Button area
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child:_buildButton(
icon: Icons.usb,
label: 'USB Connect',
onPressed: _connectUsb,
color: Colors.teal,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildButton(
icon: Icons.bluetooth,
label: 'BT Connect',
onPressed: _connectBluetooth,
color: Colors.indigo,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: _buildButton(
icon: Icons.cloud_outlined,
label: 'Bind Cloud',
onPressed: _bindCloud,
color: Colors.deepPurple,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildButton(
icon: Icons.cloud_done,
label: 'Connect Cloud',
onPressed: _connectCloud,
color: Colors.blueGrey,
),
),
],
),
const SizedBox(height: 8),
const SizedBox(height: 12),
_buildButton(
icon: Icons.payment,
label: 'Send Sale Transaction',
onPressed: _sendSaleTransaction,
color: Colors.orange,
),
if (_isConnected) ...[
const SizedBox(height: 8),
OutlinedButton.icon(
onPressed: () async {
await _plugin.disconnect();
_addLog('Disconnected');
},
icon: const Icon(Icons.link_off),
label: const Text('Disconnect'),
style: OutlinedButton.styleFrom(
foregroundColor: Colors.red,
side: const BorderSide(color: Colors.red),
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
],
],
),
),
const Divider(height: 1),
// Log area
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Logs',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
TextButton(
onPressed: () => setState(() => _logs.clear()),
child: const Text('Clear'),
),
],
),
),
Expanded(
child: _logs.isEmpty
? const Center(
child: Text(
'No Logs Available',
style: TextStyle(color: Colors.grey),
),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _logs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
_logs[index],
style: const TextStyle(
fontSize: 13,
fontFamily: 'monospace',
),
),
);
},
),
),
],
),
);
}
Widget _buildButton({
required IconData icon,
required String label,
required VoidCallback onPressed,
required Color color,
}) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, size: 18),
label: Text(label),
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
}
}