flutter_dothantech_lpapi_thermal_printer 1.0.0
flutter_dothantech_lpapi_thermal_printer: ^1.0.0 copied to clipboard
Flutter plugin for Dothantech LPAPI thermal label printers. Supports Bluetooth discovery, direct connection without pairing, and printing of text, barcodes, QR codes, and images.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:flutter_dothantech_lpapi_thermal_printer/flutter_dothantech_lpapi_thermal_printer.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LPAPI Thermal Printer Demo',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const PrinterDemoPage(),
);
}
}
class PrinterDemoPage extends StatefulWidget {
const PrinterDemoPage({super.key});
@override
State<PrinterDemoPage> createState() => _PrinterDemoPageState();
}
class _PrinterDemoPageState extends State<PrinterDemoPage> {
final _printer = LpapiThermalPrinter();
List<PrinterInfo> _printers = [];
String _connectionStatus = 'Disconnected';
String? _connectedPrinterAddress;
bool _isSearching = false;
// Text controllers
final _textController = TextEditingController(
text: 'Hello from LPAPI Thermal Printer!',
);
final _barcodeTextController = TextEditingController(text: 'Product Label');
final _barcodeDataController = TextEditingController(text: '1234567890');
final _qrcodeController = TextEditingController(text: 'https://example.com');
// Print settings
int _printDensity = 6; // Default normal density
int _printSpeed = 3; // Default normal speed
@override
void initState() {
super.initState();
_checkPrinterStatus();
}
@override
void dispose() {
_textController.dispose();
_barcodeTextController.dispose();
_barcodeDataController.dispose();
_qrcodeController.dispose();
super.dispose();
}
Future<void> _checkPrinterStatus() async {
try {
final status = await _printer.getPrinterStatus();
setState(() {
_connectionStatus = status;
});
} catch (e) {
_showError('Failed to check printer status: $e');
}
}
Future<void> _requestPermissions() async {
if (Platform.isAndroid) {
// Request multiple permissions
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetooth,
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
// Check if all permissions are granted
bool allGranted = statuses.values.every((status) => status.isGranted);
if (!allGranted) {
_showError('Please grant all permissions to use the printer');
return;
}
}
}
Future<void> _searchPrinters() async {
// Request permissions first
await _requestPermissions();
setState(() {
_isSearching = true;
_printers = [];
});
try {
final printers = await _printer.searchPrinters();
setState(() {
_printers = printers;
_isSearching = false;
});
if (printers.isEmpty) {
_showMessage(
'No paired printers found. Try "Discover All" to find nearby printers.',
);
}
} catch (e) {
setState(() {
_isSearching = false;
});
_showError('Failed to search printers: $e');
}
}
Future<void> _discoverPrinters() async {
// Request permissions first
await _requestPermissions();
setState(() {
_isSearching = true;
_printers = [];
});
_showMessage('Discovering printers... This may take up to 12 seconds.');
try {
final printers = await _printer.discoverPrinters();
setState(() {
_printers = printers;
_isSearching = false;
});
if (printers.isEmpty) {
_showMessage(
'No printers found. Make sure Bluetooth is enabled and printers are turned on.',
);
} else {
_showMessage('Found ${printers.length} printer(s)');
}
} catch (e) {
setState(() {
_isSearching = false;
});
_showError('Failed to discover printers: $e');
}
}
Future<void> _connectPrinter(String address) async {
try {
_showMessage('Connecting to printer...');
final success = await _printer.connectPrinter(address);
if (success) {
setState(() {
_connectedPrinterAddress = address;
_connectionStatus = 'Connected';
});
_showMessage('Printer connected successfully!');
} else {
_showError('Failed to connect to printer');
}
} catch (e) {
_showError('Connection error: $e');
}
await _checkPrinterStatus();
}
Future<void> _connectFirstPrinter() async {
// Request permissions first
await _requestPermissions();
try {
_showMessage('Connecting to first available printer...');
final success = await _printer.connectFirstPrinter();
if (success) {
setState(() {
_connectedPrinterAddress = 'First Printer';
_connectionStatus = 'Connected';
});
_showMessage('Printer connected successfully!');
} else {
_showError(
'Failed to connect to printer. Make sure a printer is paired in Bluetooth settings.',
);
}
} catch (e) {
_showError('Connection error: $e');
}
await _checkPrinterStatus();
}
Future<void> _disconnectPrinter() async {
try {
final success = await _printer.disconnectPrinter();
if (success) {
setState(() {
_connectedPrinterAddress = null;
_connectionStatus = 'Disconnected';
});
_showMessage('Printer disconnected');
}
} catch (e) {
_showError('Failed to disconnect: $e');
}
}
Future<void> _printText() async {
if (_connectionStatus != 'connected') {
_showError('Please connect to a printer first');
return;
}
try {
final success = await _printer.printText(_textController.text);
if (success) {
_showMessage('Text printed successfully!');
} else {
_showError('Failed to print text');
}
} catch (e) {
_showError('Print error: $e');
}
}
Future<void> _print1DBarcode() async {
if (_connectionStatus != 'connected') {
_showError('Please connect to a printer first');
return;
}
try {
final success = await _printer.print1DBarcode(
_barcodeDataController.text,
text: _barcodeTextController.text,
);
if (success) {
_showMessage('Barcode printed successfully!');
} else {
_showError('Failed to print barcode');
}
} catch (e) {
_showError('Print error: $e');
}
}
Future<void> _print2DBarcode() async {
if (_connectionStatus != 'connected') {
_showError('Please connect to a printer first');
return;
}
try {
final success = await _printer.print2DBarcode(_qrcodeController.text);
if (success) {
_showMessage('QR Code printed successfully!');
} else {
_showError('Failed to print QR code');
}
} catch (e) {
_showError('Print error: $e');
}
}
Future<void> _setPrintDensity(int density) async {
if (_connectionStatus != 'connected') {
_showError('Please connect to a printer first');
return;
}
try {
final success = await _printer.setPrintDensity(density);
if (success) {
setState(() {
_printDensity = density;
});
_showMessage('Print density set to $density');
}
} catch (e) {
_showError('Failed to set density: $e');
}
}
Future<void> _setPrintSpeed(int speed) async {
if (_connectionStatus != 'connected') {
_showError('Please connect to a printer first');
return;
}
try {
final success = await _printer.setPrintSpeed(speed);
if (success) {
setState(() {
_printSpeed = speed;
});
_showMessage('Print speed set to $speed');
}
} catch (e) {
_showError('Failed to set speed: $e');
}
}
void _showMessage(String message) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(message)));
}
void _showError(String error) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(error), backgroundColor: Colors.red));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('LPAPI Thermal Printer Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Connection Status Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Printer Connection',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Row(
children: [
Icon(
_connectionStatus == 'connected'
? Icons.check_circle
: Icons.cancel,
color: _connectionStatus == 'connected'
? Colors.green
: Colors.red,
),
const SizedBox(width: 8),
Text('Status: $_connectionStatus'),
],
),
if (_connectedPrinterAddress != null) ...[
const SizedBox(height: 4),
Text('Address: $_connectedPrinterAddress'),
],
const SizedBox(height: 16),
Row(
children: [
ElevatedButton.icon(
onPressed: _isSearching ? null : _discoverPrinters,
icon: _isSearching
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Icon(Icons.bluetooth_searching),
label: const Text('Discover All'),
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: _isSearching ? null : _searchPrinters,
icon: const Icon(Icons.search),
label: const Text('Paired Only'),
),
],
),
const SizedBox(height: 8),
Row(
children: [
ElevatedButton.icon(
onPressed: _connectionStatus == 'disconnected'
? _connectFirstPrinter
: null,
icon: const Icon(Icons.print),
label: const Text('Quick Connect'),
),
const SizedBox(width: 8),
if (_connectedPrinterAddress != null)
ElevatedButton.icon(
onPressed: _disconnectPrinter,
icon: const Icon(Icons.bluetooth_disabled),
label: const Text('Disconnect'),
),
],
),
],
),
),
),
// Printers List
if (_printers.isNotEmpty) ...[
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Available Printers',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
..._printers.map(
(printer) => ListTile(
leading: const Icon(Icons.print),
title: Text(printer.name),
subtitle: Text(
'${printer.address} (${printer.type})',
),
trailing: printer.address == _connectedPrinterAddress
? const Icon(Icons.check, color: Colors.green)
: null,
onTap: printer.address == _connectedPrinterAddress
? null
: () => _connectPrinter(printer.address),
),
),
],
),
),
),
],
// Print Settings
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Print Settings',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text('Print Density: $_printDensity'),
Slider(
value: _printDensity.toDouble(),
min: 0,
max: 20,
divisions: 20,
label: _printDensity.toString(),
onChanged: (value) => _setPrintDensity(value.toInt()),
),
const SizedBox(height: 8),
Text('Print Speed: $_printSpeed'),
Slider(
value: _printSpeed.toDouble(),
min: 1,
max: 5,
divisions: 4,
label: _printSpeed.toString(),
onChanged: (value) => _setPrintSpeed(value.toInt()),
),
],
),
),
),
// Print Actions
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Print Actions',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
// Text printing
TextField(
controller: _textController,
decoration: const InputDecoration(
labelText: 'Text to Print',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _connectionStatus == 'connected'
? _printText
: null,
icon: const Icon(Icons.text_fields),
label: const Text('Print Text'),
),
const Divider(height: 32),
// 1D Barcode
TextField(
controller: _barcodeTextController,
decoration: const InputDecoration(
labelText: 'Barcode Label Text',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextField(
controller: _barcodeDataController,
decoration: const InputDecoration(
labelText: 'Barcode Data',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _connectionStatus == 'connected'
? _print1DBarcode
: null,
icon: const Icon(Icons.barcode_reader),
label: const Text('Print 1D Barcode'),
),
const Divider(height: 32),
// QR Code
TextField(
controller: _qrcodeController,
decoration: const InputDecoration(
labelText: 'QR Code Data',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _connectionStatus == 'connected'
? _print2DBarcode
: null,
icon: const Icon(Icons.qr_code),
label: const Text('Print QR Code'),
),
],
),
),
),
],
),
),
);
}
}