bluetooth_pos_printer 1.1.1 copy "bluetooth_pos_printer: ^1.1.1" to clipboard
bluetooth_pos_printer: ^1.1.1 copied to clipboard

A robust Flutter plugin for communicating with Bluetooth thermal (POS) printers. Supports Bluetooth Classic (SPP) on Android and Bluetooth Low Energy (BLE) on iOS.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter_esc_pos_utils/flutter_esc_pos_utils.dart';
import 'package:bluetooth_pos_printer/bluetooth_pos_printer.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bluetooth POS Printer Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueAccent),
        useMaterial3: true,
      ),
      home: const PrinterScreen(),
    );
  }
}

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

  @override
  State<PrinterScreen> createState() => _PrinterScreenState();
}

class _PrinterScreenState extends State<PrinterScreen> {
  PrinterStatus _connectionStatus = PrinterStatus.disconnected;
  List<BluetoothPrinterDevice> _devices = [];
  BluetoothPrinterDevice? _connectedDevice;
  bool _isScanning = false;
  BluetoothScanMode _scanMode = BluetoothScanMode.active;
  late StreamSubscription<PrinterStatus> _statusSubscription;

  @override
  void initState() {
    super.initState();
    _statusSubscription = BluetoothPosPrinter.instance.statusStream.listen((
      status,
    ) {
      setState(() {
        _connectionStatus = status;
        if (status == PrinterStatus.disconnected) {
          _connectedDevice = null;
        }
      });
    });
  }

  @override
  void dispose() {
    _statusSubscription.cancel();
    super.dispose();
  }

  Future<void> _startScan() async {
    final isBluetoothOn = await BluetoothPosPrinter.instance.isBluetoothEnabled();
    if (!isBluetoothOn) {
      _showSnackBar('Bluetooth está desativado. Por favor, ative o Bluetooth.');
      return;
    }

    setState(() {
      _isScanning = true;
      _devices.clear();
    });

    try {
      final devices = await BluetoothPosPrinter.instance.scan(
        mode: _scanMode,
      );
      setState(() {
        _devices = devices;
      });
    } catch (e) {
      _showSnackBar('Erro ao buscar impressoras: $e');
    } finally {
      setState(() {
        _isScanning = false;
      });
    }
  }

  Future<void> _connect(BluetoothPrinterDevice device) async {
    try {
      final success = await BluetoothPosPrinter.instance.connect(
        device.address,
      );
      if (success) {
        setState(() {
          _connectedDevice = device;
        });
      } else {
        _showSnackBar('Falha ao conectar à impressora.');
      }
    } catch (e) {
      _showSnackBar('Erro de conexão: $e');
    }
  }

  Future<void> _disconnect() async {
    try {
      await BluetoothPosPrinter.instance.disconnect();
    } catch (e) {
      _showSnackBar('Erro ao desconectar: $e');
    }
  }

  Future<void> _printTestCoupon() async {
    try {
      final profile = await CapabilityProfile.load();
      final generator = Generator(PaperSize.mm80, profile);
      List<int> bytes = [];

      bytes += generator.text(
        'TESTE DE IMPRESSAO',
        styles: const PosStyles(align: PosAlign.center, bold: true),
      );

      bytes += generator.text('--------------------------------');

      final dateStr = DateFormat('dd/MM/yyyy HH:mm:ss').format(DateTime.now());
      bytes += generator.text(
        'Data: $dateStr',
        styles: const PosStyles(align: PosAlign.left),
      );

      bytes += generator.text('--------------------------------');
      bytes += generator.text(
        'Obrigado por usar o plugin!',
        styles: const PosStyles(align: PosAlign.center),
      );

      bytes += generator.feed(2);
      bytes += generator.cut();

      final success = await BluetoothPosPrinter.instance.printRawBytes(bytes);
      if (!success) {
        _showSnackBar('Falha ao enviar dados para a impressora.');
      } else {
        _showSnackBar('Cupom enviado com sucesso!');
      }
    } catch (e) {
      _showSnackBar('Erro na impressão: $e');
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(
      context,
    ).showSnackBar(SnackBar(content: Text(message)));
  }

  String _getStatusText() {
    switch (_connectionStatus) {
      case PrinterStatus.disconnected:
        return 'Desconectado';
      case PrinterStatus.connecting:
        return 'Conectando...';
      case PrinterStatus.connected:
        return 'Conectado a: ${_connectedDevice?.name ?? 'Impressora'}';
    }
  }

  Color _getStatusColor() {
    switch (_connectionStatus) {
      case PrinterStatus.disconnected:
        return Colors.redAccent;
      case PrinterStatus.connecting:
        return Colors.orangeAccent;
      case PrinterStatus.connected:
        return Colors.green;
    }
  }

  Icon _getDeviceIcon(String type) {
    switch (type) {
      case 'printer':
        return const Icon(Icons.print, color: Colors.blueAccent);
      case 'phone':
        return const Icon(Icons.phone_android, color: Colors.green);
      case 'computer':
        return const Icon(Icons.computer, color: Colors.purple);
      case 'audio':
        return const Icon(Icons.headphones, color: Colors.orange);
      case 'peripheral':
        return const Icon(Icons.mouse, color: Colors.teal);
      default:
        return const Icon(Icons.bluetooth, color: Colors.grey);
    }
  }

  @override
  Widget build(BuildContext context) {
    final isConnected = _connectionStatus == PrinterStatus.connected;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Impressora Térmica Bluetooth'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Column(
        children: [
          // Status Banner
          Container(
            width: double.infinity,
            padding: const EdgeInsets.all(16.0),
            color: _getStatusColor(),
            child: Text(
              _getStatusText(),
              style: const TextStyle(
                color: Colors.white,
                fontWeight: FontWeight.bold,
                fontSize: 16,
              ),
              textAlign: TextAlign.center,
            ),
          ),

          // Modo de Busca
          Card(
            margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
            elevation: 2,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  const Text(
                    'Modo de Busca',
                    style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 12),
                  SegmentedButton<BluetoothScanMode>(
                    segments: const <ButtonSegment<BluetoothScanMode>>[
                      ButtonSegment<BluetoothScanMode>(
                        value: BluetoothScanMode.active,
                        label: Text('Ativos'),
                        icon: Icon(Icons.radar),
                      ),
                      ButtonSegment<BluetoothScanMode>(
                        value: BluetoothScanMode.paired,
                        label: Text('Pareados'),
                        icon: Icon(Icons.link),
                      ),
                      ButtonSegment<BluetoothScanMode>(
                        value: BluetoothScanMode.all,
                        label: Text('Todos'),
                        icon: Icon(Icons.all_inclusive),
                      ),
                    ],
                    selected: <BluetoothScanMode>{_scanMode},
                    onSelectionChanged: _isScanning
                        ? null
                        : (Set<BluetoothScanMode> newSelection) {
                            setState(() {
                              _scanMode = newSelection.first;
                            });
                          },
                  ),
                  const SizedBox(height: 8),
                  Text(
                    _scanMode == BluetoothScanMode.active
                        ? 'Busca apenas novos aparelhos ativos próximos (padrão)'
                        : _scanMode == BluetoothScanMode.paired
                            ? 'Exibe apenas aparelhos já pareados no Android'
                            : 'Exibe pareados e faz busca ativa por novos aparelhos',
                    style: TextStyle(color: Colors.grey.shade600, fontSize: 12),
                    textAlign: TextAlign.center,
                  ),
                ],
              ),
            ),
          ),

          // Scan Control
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: ElevatedButton.icon(
              onPressed: _isScanning ? null : _startScan,
              icon: _isScanning
                  ? const SizedBox(
                      width: 20,
                      height: 20,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    )
                  : const Icon(Icons.search),
              label: const Text('Buscar Impressoras'),
              style: ElevatedButton.styleFrom(
                minimumSize: const Size.fromHeight(50),
              ),
            ),
          ),

          // Device List
          Expanded(
            child: _devices.isEmpty && !_isScanning
                ? const Center(child: Text('Nenhuma impressora encontrada.'))
                : ListView.builder(
                    itemCount: _devices.length,
                    itemBuilder: (context, index) {
                      final device = _devices[index];
                      final isCurrentConnectedDevice =
                          _connectedDevice?.address == device.address;

                      return ListTile(
                        leading: _getDeviceIcon(device.type),
                        title: Text(device.name),
                        subtitle: Text(device.address),
                        trailing:
                            isCurrentConnectedDevice &&
                                _connectionStatus == PrinterStatus.connected
                            ? ElevatedButton(
                                onPressed: _disconnect,
                                style: ElevatedButton.styleFrom(
                                  backgroundColor: Colors.redAccent,
                                  foregroundColor: Colors.white,
                                ),
                                child: const Text('Desconectar'),
                              )
                            : ElevatedButton(
                                onPressed:
                                    _connectionStatus ==
                                        PrinterStatus.connecting
                                    ? null
                                    : () => _connect(device),
                                child: const Text('Conectar'),
                              ),
                      );
                    },
                  ),
          ),

          // Print Panel
          Container(
            padding: const EdgeInsets.all(16.0),
            decoration: BoxDecoration(
              color: Theme.of(context).cardColor,
              boxShadow: [
                BoxShadow(
                  color: Colors.black12,
                  blurRadius: 4,
                  offset: const Offset(0, -2),
                ),
              ],
            ),
            child: SafeArea(
              child: ElevatedButton.icon(
                onPressed: isConnected ? _printTestCoupon : null,
                icon: const Icon(Icons.receipt_long),
                label: const Text(
                  'Imprimir Cupom de Teste',
                  style: TextStyle(fontSize: 16),
                ),
                style: ElevatedButton.styleFrom(
                  minimumSize: const Size.fromHeight(60),
                  backgroundColor: isConnected
                      ? Colors.blueAccent
                      : Colors.grey,
                  foregroundColor: Colors.white,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
150
points
195
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A robust Flutter plugin for communicating with Bluetooth thermal (POS) printers. Supports Bluetooth Classic (SPP) on Android and Bluetooth Low Energy (BLE) on iOS.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on bluetooth_pos_printer

Packages that implement bluetooth_pos_printer