Dollar StarXpand
Seamlessly Integrate Star Micronics Hardware with Dollar POS
Overview
Dollar StarXpand is a Flutter plugin designed to integrate Star Micronics printers and cash drawers into the Dollar POS system. This plugin provides platform-specific implementations for iOS and Android, enabling smooth communication with Star Micronics devices for printing receipts, opening cash drawers, and more.
Features
- Discover and connect to Star Micronics printers.
- Print receipts with customizable templates.
- Open cash drawers programmatically.
- Stream printer discovery events.
Installation
Add the plugin to your Flutter project by including it in your pubspec.yaml
:
dependencies:
dollar_starxpand: latest
Run flutter pub get
to fetch the package.
iOS Setup
To use the plugin on iOS, update your Info.plist
file to include the required permissions:
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Use Bluetooth for communication with the printer.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Use Bluetooth for communication with the printer.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Use Local Network for communication with the printer or discovery the printers.</string>
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>jp.star-m.starpro</string>
</array>
Android Setup
Ensure the following permissions are added to your AndroidManifest.xml
file for Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Example Usage
Import the Plugin
import 'package:dollar_starxpand/dollar_starxpand.dart';
Discover Printers
final plugin = DollarStarxpand();
await plugin.startDiscovery();
plugin.onPrinterDiscovered((identifier, interfaceType, model) {
print("Printer found: $model ($interfaceType)");
});
Connect to a Printer
await plugin.connectPrinter(identifier: "printer_identifier", interface: "bluetooth");
Print a Receipt
await plugin.printReceipt(
identifier: "printer_identifier",
interface: "bluetooth",
printData: {
"id": "12345",
"date": "DEC 17, 2024 12:31 AM",
"store": {"name": "Store Name", "address": "123 Main St", "phone": "123-456-7890"},
// Additional receipt data here
},
);
Open Cash Drawer
await plugin.openCashDrawer(identifier: "printer_identifier", interface: "bluetooth");
Method Channels
The plugin uses the following method channels for platform communication:
-
Method Channel:
dollar_starxpand
Handles printer connection, printing, and drawer operations. -
Event Channel:
printerDiscovered
Streams discovered printer details to Flutter.
Example App
Here’s a complete example showcasing printer discovery, connection, and operations:
import 'package:dollar_starxpand/dollar_starxpand.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// Instance of the plugin
final _dollarStarxpandPlugin = DollarStarxpand();
// State variables
List<Map<String, dynamic>> discoveredPrinters = [];
bool isDiscovering = false;
String statusMessage = 'Press "Discover Printers" to search.';
@override
void initState() {
super.initState();
setupPrinterDiscoveryListener();
}
// Start discovery of printers
Future<void> startDiscovery() async {
setState(() {
isDiscovering = true;
discoveredPrinters.clear();
statusMessage = "Starting discovery...";
});
try {
await _dollarStarxpandPlugin.startDiscovery();
} catch (error) {
if (!mounted) return;
setState(() {
statusMessage = "Discovery failed: $error";
});
_showSnackBar('Discovery failed: $error');
} finally {
setState(() {
isDiscovering = false;
});
}
}
// Set up listener for printer discovery events
void setupPrinterDiscoveryListener() {
_dollarStarxpandPlugin.onPrinterDiscovered(
(identifier, interfaceType, model) {
if (!mounted) return;
setState(() {
discoveredPrinters.add({
"identifier": identifier,
"interface": interfaceType,
"model": model,
});
statusMessage = "Printer found: $identifier $interfaceType $model";
});
},
);
}
// Connect to a printer
Future<void> connectToPrinter(String identifier, String interface) async {
setState(() {
statusMessage = "Connecting to printer: $identifier...";
});
try {
await _dollarStarxpandPlugin.connectPrinter(identifier, interface);
if (!mounted) return;
setState(() {
statusMessage = "Connected to printer: $identifier";
});
_showSnackBar('Connected to printer: $identifier');
} catch (error) {
if (!mounted) return;
setState(() {
statusMessage = "Failed to connect: $error";
});
_showSnackBar('Failed to connect: $error');
}
}
// Helper method to show a SnackBar
void _showSnackBar(String message) {
if (!mounted) return;
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Printer Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Status message
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
statusMessage,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16.0),
),
),
// Discover Printers button
Padding(
padding: const EdgeInsets.all(16.0),
child: ElevatedButton(
onPressed: isDiscovering ? null : startDiscovery,
child: isDiscovering
? const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
)
: const Text('Discover Printers'),
),
),
// List of discovered printers
Expanded(
child: discoveredPrinters.isEmpty
? const Center(child: Text('No printers found.'))
: ListView.builder(
itemCount: discoveredPrinters.length,
itemBuilder: (context, index) {
final printer = discoveredPrinters[index];
return PrinterTile(
printer: printer,
onConnect: () =>
connectToPrinter(
printer["identifier"], printer["interface"]),
onPrint: () {
_dollarStarxpandPlugin.printReceipt(
printer["identifier"],
printer["interface"],
printData,
);
},
onOpenDrawer: () =>
_dollarStarxpandPlugin.openCashDrawer(
printer["identifier"],
printer["interface"],
),
onDisconnect:
_dollarStarxpandPlugin.disconnectPrinter,
);
},
),
),
],
),
),
);
}
}
class PrinterTile extends StatelessWidget {
final Map<String, dynamic> printer;
final VoidCallback onConnect;
final VoidCallback onPrint;
final VoidCallback onOpenDrawer;
final VoidCallback onDisconnect;
const PrinterTile({
super.key,
required this.printer,
required this.onConnect,
required this.onPrint,
required this.onOpenDrawer,
required this.onDisconnect,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.print),
title: Text('${printer["model"]}: ${printer["interface"]}'),
subtitle: Text(printer["identifier"]),
trailing: SizedBox(
width: 180,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.print, size: 40.0), onPressed: onPrint),
IconButton(
icon: const Icon(Icons.dashboard, size: 40.0),
onPressed: onOpenDrawer),
IconButton(
icon: const Icon(Icons.close, size: 40.0),
onPressed: onDisconnect),
],
),
),
onTap: onConnect,
);
}
}
final printData = {
"id": "12345",
"status": "completed",
"date": "DEC 17, 2024 12:31 AM",
"registerNo": "1",
"employee": {"id": 1, "firstName": "John", "lastName": "Doe"},
"subTotal": 100.0,
"tax": 10.0,
"taxBreakdown": [
{"name": "State Tax", "value": 6.0},
{"name": "County Tax", "value": 4.0}
],
"surcharge": 2.0,
"discount": 5.0,
"total": 107.0,
"changeDue": 0.0,
"tenders": [
{
"method": "Card",
"amount": 107.0,
"details": {
"authCode": "123456",
"referenceId": "ABC123",
"amount": 107.0,
"cardData": {
"cardType": "VISA",
"entryType": "Contactless",
"last4": "5678",
"expirationDate": "12/24",
"name": "John Doe"
}
},
}
],
"store": {
"name": "Store Name",
"address": "123 Main St",
"phone": "123-456-7890"
},
"items": [
{
"item": {
"name": "Product 1",
"upc": "123456789",
"pack": "Box",
"size": "500g",
"price": 10.0
},
"qty": "1",
"price": 10.0,
"taxable": true
},
{
"item": {
"name": "Product 2",
"upc": "987654321",
"pack": "Pack",
"size": "1kg",
"price": 20.0
},
"qty": "2",
"price": 40.0,
"taxable": false
}
]
};
Troubleshooting
Common Issues
- Printer Not Found: Ensure the printer is powered on and discoverable.
- Connection Failed: Verify the
identifier
andinterface
match the discovered printer details.
License
This plugin is licensed under the MIT License.