totem_alecto 0.0.1
totem_alecto: ^0.0.1 copied to clipboard
Package to interface with Totem Alecto printers
Totem Alecto #
A Flutter plugin for interfacing with thermal printers via USB on Android devices. This plugin uses the CSN Printer SDK to provide full control over USB thermal printers, including text printing, image printing, paper cutting, cash drawer control, and more.
Platform Support #
| Android | iOS |
|---|---|
| ✅ | ❌ |
Note: iOS does not support USB printing due to platform limitations.
Features #
- 🔌 USB Device Management - List, connect, and manage USB thermal printers
- 📄 Text Printing - Simple and formatted text printing with alignment options
- 🖼️ Image Printing - Print images from bytes or Base64 strings
- ✂️ Paper Control - Cut paper (full/partial) and feed lines
- 💰 Cash Drawer - Open cash drawer with configurable settings
- 🔔 Beeper Control - Emit beeps for notifications
- 📡 Real-time Events - Stream of printer events (connection, disconnection, print completion)
- 🎨 Font Formatting - Bold, underline, double width/height, and custom font sizes
Requirements #
- Flutter 3.3.0 or higher
- Dart SDK 3.10.4 or higher
- Android API 12 (Android 3.1 - Honeycomb MR1) or higher
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
totem_alecto: ^0.0.1
Then run:
flutter pub get
Android Setup #
Add USB permissions to your AndroidManifest.xml:
<manifest>
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<application>
<!-- Your existing application configuration -->
</application>
</manifest>
Usage #
1. Import the package #
import 'package:totem_alecto/totem_alecto.dart';
2. Initialize #
final printer = TotemAlecto();
3. Check USB Support #
bool isSupported = await printer.isSupported();
if (!isSupported) {
print('USB Host mode not supported on this device');
return;
}
4. List USB Devices #
// List all connected USB devices
List<UsbDeviceInfo> devices = await printer.listDevices();
for (var device in devices) {
print('Device: ${device.displayName}');
print('Vendor ID: ${device.vendorId}');
print('Product ID: ${device.productId}');
}
5. Request Permission & Connect #
Option A: Connect by Device Name
if (devices.isNotEmpty) {
final device = devices.first;
// Check permission
bool hasPermission = await printer.hasPermission(device.deviceName);
if (!hasPermission) {
// Request permission (shows system dialog)
bool granted = await printer.requestPermission(device.deviceName);
if (!granted) {
print('Permission denied');
return;
}
}
// Connect to printer
bool connected = await printer.connect(device.deviceName);
if (connected) {
print('Connection initiated');
}
}
Option B: Connect by Vendor/Product ID (if known)
// Example: Connect to a specific printer model
int vendorId = 0x0519; // Your printer's vendor ID
int productId = 0x0003; // Your printer's product ID
bool connected = await printer.connectByIds(vendorId, productId);
if (connected) {
print('Connection initiated');
}
6. Listen to Printer Events #
printer.printerEvents.listen((event) {
switch (event.type) {
case PrinterEventType.onOpen:
print('✅ Printer connected');
break;
case PrinterEventType.onOpenFailed:
print('❌ Connection failed');
break;
case PrinterEventType.onClose:
print('🔌 Printer disconnected');
break;
case PrinterEventType.onPrintComplete:
if (event.success == true) {
print('✅ Print successful (code: ${event.resultCode})');
} else {
print('❌ Print failed (code: ${event.resultCode})');
}
break;
default:
break;
}
});
7. Check Connection Status #
bool isConnected = await printer.isConnected();
print('Printer connected: $isConnected');
Printing Examples #
Simple Text #
await printer.printText(
'Hello World!',
alignment: TextAlignment.center,
cutPaper: true,
);
Formatted Text #
// Print header with bold and double height
await printer.printTextFormatted(
'=== RECEIPT ===\n',
alignment: TextAlignment.center,
fontAttributes: FontAttributes(
bold: true,
doubleHeight: true,
doubleWidth: true,
),
cutPaper: false,
);
// Print normal text
await printer.printText(
'Date: ${DateTime.now()}\n',
alignment: TextAlignment.left,
cutPaper: false,
);
// Print footer with underline
await printer.printTextFormatted(
'\nThank you!\n\n',
alignment: TextAlignment.center,
fontAttributes: FontAttributes(
underline: true,
),
cutPaper: true,
);
Print Image from Bytes #
import 'dart:io';
// Load image from file
File imageFile = File('path/to/image.png');
Uint8List imageBytes = await imageFile.readAsBytes();
// Print image
PrintResult result = await printer.printPicture(
imageBytes,
alignment: TextAlignment.center,
width: 384, // Optional: specify width in pixels
cutPaper: true,
);
if (result.success) {
print('Image printed successfully');
}
Print Image from Base64 #
import 'dart:convert';
// Convert bytes to Base64
String base64Image = base64Encode(imageBytes);
// Print image
await printer.printPictureBase64(
base64Image,
alignment: TextAlignment.center,
cutPaper: true,
);
Print Image from Network #
import 'package:http/http.dart' as http;
// Download image
final response = await http.get(Uri.parse('https://example.com/logo.png'));
Uint8List imageBytes = response.bodyBytes;
// Print
await printer.printPicture(
imageBytes,
alignment: TextAlignment.center,
cutPaper: true,
);
Additional Features #
Paper Control #
// Full cut
await printer.cutPaper();
// Partial cut (perforated)
await printer.partialCutPaper();
// Feed 3 lines
await printer.feedPaper(lines: 3);
Cash Drawer #
// Open cash drawer
await printer.openCashDrawer(
pin: 2, // Pin number (2 or 5)
onTime: 100, // Duration in milliseconds
);
Beeper #
// Beep 3 times
await printer.beep(
times: 3,
duration: 100, // Duration of each beep in milliseconds
);
Disconnect #
await printer.disconnect();
Complete Example #
import 'package:flutter/material.dart';
import 'package:totem_alecto/totem_alecto.dart';
class PrinterExample extends StatefulWidget {
@override
_PrinterExampleState createState() => _PrinterExampleState();
}
class _PrinterExampleState extends State<PrinterExample> {
final printer = TotemAlecto();
bool isConnected = false;
@override
void initState() {
super.initState();
_listenToPrinterEvents();
}
void _listenToPrinterEvents() {
printer.printerEvents.listen((event) {
if (event.type == PrinterEventType.onOpen) {
setState(() => isConnected = true);
} else if (event.type == PrinterEventType.onClose) {
setState(() => isConnected = false);
}
});
}
Future<void> _connectToPrinter() async {
final devices = await printer.listDevices();
if (devices.isEmpty) return;
final device = devices.first;
final hasPermission = await printer.hasPermission(device.deviceName);
if (!hasPermission) {
final granted = await printer.requestPermission(device.deviceName);
if (!granted) return;
}
await printer.connect(device.deviceName);
}
Future<void> _printReceipt() async {
// Header
await printer.printTextFormatted(
'\n*** MY STORE ***\n',
alignment: TextAlignment.center,
fontAttributes: FontAttributes(bold: true, doubleHeight: true),
cutPaper: false,
);
// Content
await printer.printText(
'\nDate: ${DateTime.now()}\n'
'Item 1 ............ \$10.00\n'
'Item 2 ............ \$15.00\n'
'------------------------\n'
'Total ............. \$25.00\n\n',
alignment: TextAlignment.left,
cutPaper: false,
);
// Footer
await printer.printText(
'Thank you for your purchase!\n\n\n',
alignment: TextAlignment.center,
cutPaper: true,
);
// Open drawer and beep
await printer.openCashDrawer();
await printer.beep(times: 2);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Printer Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
isConnected ? 'Connected' : 'Disconnected',
style: TextStyle(
fontSize: 24,
color: isConnected ? Colors.green : Colors.red,
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _connectToPrinter,
child: Text('Connect'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: isConnected ? _printReceipt : null,
child: Text('Print Receipt'),
),
],
),
),
);
}
}
Font Attributes #
The FontAttributes class supports the following properties:
FontAttributes(
bold: true, // Bold text
underline: true, // Underlined text
doubleWidth: true, // Double width characters
doubleHeight: true, // Double height characters
)
Text Alignment #
Available alignment options:
TextAlignment.left- Left aligned (default)TextAlignment.center- Center alignedTextAlignment.right- Right aligned
Error Handling #
try {
final result = await printer.printText('Test');
if (!result.success) {
print('Print failed with code: ${result.resultCode}');
}
} catch (e) {
print('Error: $e');
}
Troubleshooting #
Device Not Found #
- Ensure the USB printer is properly connected
- Check if USB Host mode is supported:
await printer.isSupported() - Try disconnecting and reconnecting the USB cable
Permission Denied #
- Make sure to call
requestPermission()before connecting - Check Android manifest has USB host permission
- Some devices may require manual permission grant in system settings
Print Not Working #
- Verify the printer is connected:
await printer.isConnected() - Check printer has paper and is powered on
- Ensure you're using the correct printer SDK (CSN Printer SDK)
- Listen to
printerEventsto debug connection issues
Connection Timeout #
- Some printers may take longer to connect
- Try disconnecting and reconnecting
- Restart the printer if issues persist
Supported Printer Models #
This plugin works with thermal printers that support the CSN Printer SDK. Commonly supported models include:
- REGO POS printers
- Various 58mm and 80mm thermal printers with USB interface
- Printers with CSN chipsets
Note: Always test with your specific printer model to ensure compatibility.
Testing #
Run the connection test:
await printer.runTest(iterations: 10);
This will perform multiple connection and print cycles to test stability.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
Support #
For issues, questions, or suggestions, please file an issue on the GitHub repository.