easy_thermal_printer 1.0.1 copy "easy_thermal_printer: ^1.0.1" to clipboard
easy_thermal_printer: ^1.0.1 copied to clipboard

A simple Flutter library for Bluetooth thermal printing using ESC/POS commands. Supports text, images, QR codes, barcodes, and HTML receipt preview.

example/lib/main.dart

import 'dart:convert';
import 'dart:math' as math;

import 'package:easy_thermal_printer/easy_thermal_printer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:intl/intl.dart';

import 'int_extension.dart';

void main() => runApp(const EasyPrinterDemoApp());

class EasyPrinterDemoApp extends StatelessWidget {
  const EasyPrinterDemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Easy Thermal Printer Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
      ),
      home: const PrinterHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class PrinterHomePage extends StatefulWidget {
  const PrinterHomePage({super.key});

  @override
  State<PrinterHomePage> createState() => _PrinterHomePageState();
}

class _PrinterHomePageState extends State<PrinterHomePage> {
  bool _isBusy = false;
  List<FluetoothDevice> _devices = [];
  List<FluetoothDevice> _connectedDevices = [];
  final _printer = EasyThermalPrinter.instance;

  String _htmlPreview = '';

  @override
  void initState() {
    super.initState();
    _refreshPrinters();
    _printer.setPaperSize(PaperSize.mm58);
  }

  Future<void> _refreshPrinters() async {
    if (_isBusy) return;
    setState(() => _isBusy = true);
    final devices = await _printer.scan();
    final connected = await _printer.getConnectedDevices();
    setState(() {
      _devices = devices;
      _connectedDevices = connected;
      _isBusy = false;
    });
  }

  Future<void> _connect(FluetoothDevice device) async {
    if (_isBusy) return;
    setState(() => _isBusy = true);
    await _printer.connect(device);
    final connected = await _printer.getConnectedDevices();
    setState(() {
      _connectedDevices = connected;
      _isBusy = false;
    });
  }

  Future<void> _disconnect(FluetoothDevice device) async {
    if (_isBusy) return;
    setState(() => _isBusy = true);
    await _printer.disconnect(device.id);
    final connected = await _printer.getConnectedDevices();
    setState(() {
      _connectedDevices = connected;
      _isBusy = false;
    });
  }

  bool _isConnected(FluetoothDevice device) =>
      _connectedDevices.any((d) => d.id == device.id);

  /// Build example receipt
  Future<ReceiptSectionText> _buildReceipt({bool useLogo = true}) async {
    final logoBytes = await rootBundle.load('assets/image.png');
    final receipt = ReceiptSectionText();

    if (useLogo) {
      receipt.addImage(
        base64.encode(Uint8List.view(logoBytes.buffer)),
        width: 330,
      );
      receipt.addSpacer();
    }

    receipt.addText('EASY STORE',
        size: ReceiptTextSizeType.large, style: ReceiptTextStyleType.bold);
    receipt.addText('Wisma 46, Jakarta, Indonesia',
        size: ReceiptTextSizeType.small);
    receipt.addSpacer();

    receipt.addLeftRightText('Order No', '123456');
    receipt.addLeftRightText(
      'Time',
      DateFormat('H:mm, dd/MM/yy').format(DateTime.now()),
    );
    receipt.addSpacer(useDashed: true);

    int total = 0;
    for (int i = 0; i < 5; i++) {
      final qty = math.Random().nextInt(5) + 1;
      final price = 1000 + (i * 500);
      receipt.addText('Item ${i + 1}', style: ReceiptTextStyleType.bold);
      receipt.addLeftRightText('$qty × ${price.inIDR}', (qty * price).inIDR);
      total += qty * price;
    }

    receipt.addSpacer(useDashed: true);
    receipt.addLeftRightText('Total', total.inIDR,
        rightStyle: ReceiptTextStyleType.bold);
    receipt.addLeftRightText('Payment', 'Cash');
    receipt.addSpacer();
    receipt.addText('Scan QR to pay:');
    receipt.addQR('easythermalprinter', size: 300);
    receipt.addText('Thank you for your purchase!');
    return receipt;
  }

  Future<void> _showPreview(ReceiptSectionText receipt) async {
    final html = _printer.convertToHtml(receipt.getContent());
    setState(() => _htmlPreview = html);
  }

  Future<void> _printNow(FluetoothDevice device) async {
    final receipt = await _buildReceipt();
    await _printer.printReceiptText(receipt, device.id, feedCount: 2);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Easy Thermal Printer'),
        actions: [
          IconButton(
            onPressed: _isBusy ? null : _refreshPrinters,
            icon: const Icon(Icons.refresh),
          ),
        ],
      ),
      body: Row(
        children: [
          // Printer List Panel
          Expanded(
            flex: 1,
            child: _isBusy
                ? const Center(child: CircularProgressIndicator())
                : _devices.isEmpty
                    ? const Center(child: Text('No printers found'))
                    : ListView.separated(
                        padding: const EdgeInsets.all(8),
                        separatorBuilder: (_, __) => const Divider(height: 1),
                        itemCount: _devices.length,
                        itemBuilder: (context, index) {
                          final device = _devices[index];
                          final isConnected = _isConnected(device);
                          return Card(
                            shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(12)),
                            child: ListTile(
                              leading: Icon(
                                isConnected
                                    ? Icons.print
                                    : Icons.print_disabled,
                                color: isConnected ? Colors.green : Colors.grey,
                              ),
                              title: Text(device.name),
                              subtitle: Text(device.id),
                              trailing: Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  IconButton(
                                    icon: const Icon(Icons.visibility),
                                    tooltip: 'Preview Receipt',
                                    onPressed: () async {
                                      final receipt = await _buildReceipt();
                                      _showPreview(receipt);
                                    },
                                  ),
                                  IconButton(
                                    icon: const Icon(Icons.print),
                                    tooltip: 'Print Receipt',
                                    onPressed: isConnected
                                        ? () => _printNow(device)
                                        : null,
                                  ),
                                  FilledButton(
                                    style: FilledButton.styleFrom(
                                      backgroundColor: isConnected
                                          ? Colors.redAccent
                                          : Colors.blueAccent,
                                    ),
                                    onPressed: isConnected
                                        ? () => _disconnect(device)
                                        : () => _connect(device),
                                    child: Text(
                                        isConnected ? 'Disconnect' : 'Connect'),
                                  ),
                                ],
                              ),
                            ),
                          );
                        },
                      ),
          ),

          // HTML Preview Panel
          Expanded(
            flex: 1,
            child: Column(
              children: [
                Container(
                  color: Theme.of(context).colorScheme.surfaceContainerHighest,
                  padding: const EdgeInsets.all(8),
                  child: const Row(
                    children: [
                      Icon(Icons.receipt_long),
                      SizedBox(width: 8),
                      Text('Receipt Preview',
                          style: TextStyle(
                              fontWeight: FontWeight.bold, fontSize: 16)),
                    ],
                  ),
                ),
                Expanded(
                  child: _htmlPreview.isEmpty
                      ? const Center(child: Text('No preview available'))
                      : SingleChildScrollView(
                          child: Html(
                            data: _htmlPreview,
                            style: {
                              "body": Style(
                                backgroundColor: Colors.white,
                                margin: Margins.zero,
                                padding: HtmlPaddings.zero,
                                fontFamily: 'Courier New',
                                fontSize: FontSize.medium,
                              ),
                              "img": Style(
                                alignment: Alignment.center,
                                margin: Margins.symmetric(vertical: 8),
                              ),
                              "hr": Style(
                                border: const Border(
                                  top: BorderSide(
                                      color: Colors.black,
                                      width: 1.5,
                                      style: BorderStyle.solid),
                                ),
                              ),
                              ".text-center":
                                  Style(textAlign: TextAlign.center),
                              ".text-left": Style(textAlign: TextAlign.left),
                              ".text-right": Style(textAlign: TextAlign.right),
                            },
                          ),
                        ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
140
points
17
downloads

Publisher

verified publisherrobbysoerya.dev

Weekly Downloads

A simple Flutter library for Bluetooth thermal printing using ESC/POS commands. Supports text, images, QR codes, barcodes, and HTML receipt preview.

Repository (GitHub)
View/report issues

Topics

#bluetooth #printer #escpos #thermal #receipt

Documentation

API reference

License

MIT (license)

Dependencies

barcode_image, fluetooth_plus, flutter, image, zxing_lib

More

Packages that depend on easy_thermal_printer