totem_alecto 0.0.1 copy "totem_alecto: ^0.0.1" to clipboard
totem_alecto: ^0.0.1 copied to clipboard

Package to interface with Totem Alecto printers

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';

import 'package:totem_alecto/totem_alecto.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Totem Alecto Example',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const PrinterHomePage(),
    );
  }
}

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

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

class _PrinterHomePageState extends State<PrinterHomePage> {
  final _printer = TotemAlecto();

  String _status = 'Desconectado';
  bool _isConnected = false;
  List<UsbDeviceInfo> _devices = [];
  UsbDeviceInfo? _selectedDevice;
  StreamSubscription<PrinterEvent>? _eventSubscription;

  final _textController = TextEditingController(
    text: 'Teste de Impressão\nTotem Alecto Plugin\n\n',
  );

  @override
  void initState() {
    super.initState();
    _init();
  }

  @override
  void dispose() {
    _eventSubscription?.cancel();
    _textController.dispose();
    super.dispose();
  }

  Future<void> _init() async {
    // Ouvir eventos da impressora
    _eventSubscription = _printer.printerEvents.listen((event) {
      setState(() {
        switch (event.type) {
          case PrinterEventType.onOpen:
            _status = 'Conectado';
            _isConnected = true;
            break;
          case PrinterEventType.onOpenFailed:
            _status = 'Falha na conexão';
            _isConnected = false;
            break;
          case PrinterEventType.onClose:
            _status = 'Desconectado';
            _isConnected = false;
            break;
          case PrinterEventType.onPrintComplete:
            final success = event.success == true ? 'Sucesso' : 'Falha';
            _showSnackBar('Impressão: $success (código: ${event.resultCode})');
            break;
          default:
            break;
        }
      });
    });

    // Verificar suporte
    final isSupported = await _printer.isSupported();
    if (!isSupported) {
      setState(() {
        _status = 'USB não suportado';
      });
      return;
    }

    // Listar dispositivos
    await _refreshDevices();
  }

  Future<void> _refreshDevices() async {
    final devices = await _printer.listDevices();
    setState(() {
      _devices = devices;
      if (devices.isNotEmpty && _selectedDevice == null) {
        _selectedDevice = devices.first;
      }
    });
  }

  Future<void> _connectDevice() async {
    if (_selectedDevice == null) {
      _showSnackBar('Selecione um dispositivo');
      return;
    }

    setState(() {
      _status = 'Conectando...';
    });

    // Verificar permissão
    // var hasPermission = await _printer.hasPermission(_selectedDevice!.deviceName);
    // if (!hasPermission) {
    //   _showSnackBar('Solicitando permissão USB...');
    //   final granted = await _printer.requestPermission(_selectedDevice!.deviceName);
    //   if (!granted) {
    //     _showSnackBar('Permissão USB negada');
    //     setState(() {
    //       _status = 'Permissão negada';
    //     });
    //     return;
    //   }
    //   hasPermission = true;
    // }

    // Conectar
    await _printer.connectByIds(4070, 33054);
  }

  Future<void> _connect() async {
    if (_selectedDevice == null) {
      _showSnackBar('Selecione um dispositivo');
      return;
    }

    setState(() {
      _status = 'Conectando...';
    });

    // Verificar permissão
    var hasPermission = await _printer.hasPermission(
      _selectedDevice!.deviceName,
    );
    if (!hasPermission) {
      _showSnackBar('Solicitando permissão USB...');
      final granted = await _printer.requestPermission(
        _selectedDevice!.deviceName,
      );
      if (!granted) {
        _showSnackBar('Permissão USB negada');
        setState(() {
          _status = 'Permissão negada';
        });
        return;
      }
      hasPermission = true;
    }

    // Conectar
    if (hasPermission) {
      await _printer.connect(_selectedDevice!.deviceName);
    }
  }

  Future<void> _disconnect() async {
    await _printer.disconnect();
  }

  Future<void> _printText() async {
    if (!_isConnected) {
      _showSnackBar('Impressora não conectada');
      return;
    }

    final text = _textController.text;
    if (text.isEmpty) {
      _showSnackBar('Digite um texto para imprimir');
      return;
    }

    final result = await _printer.printText(
      text,
      alignment: TextAlignment.center,
      cutPaper: true,
    );

    _showSnackBar(
      result.success ? 'Enviado para impressão' : 'Erro ao imprimir',
    );
  }

  Future<void> _printImage() async {
    if (!_isConnected) {
      _showSnackBar('Impressora não conectada');
      return;
    }

    // Carregar a imagem do ativo
    final byteData = await rootBundle.load('assets/images/epoc_logo.png');
    final bytes = byteData.buffer.asUint8List();

    final result = await _printer.printPicture(
      bytes,
      alignment: TextAlignment.center,
      cutPaper: false,
    );

    _showSnackBar(
      result.success
          ? 'Imagem enviada para impressão'
          : 'Erro ao imprimir imagem',
    );
  }

  Future<void> _printFormatted() async {
    if (!_isConnected) {
      _showSnackBar('Impressora não conectada');
      return;
    }

    // Exemplo de texto formatado
    await _printer.printTextFormatted(
      '=== TOTEM ALECTO ===\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true, doubleHeight: true),
      cutPaper: false,
    );

    await _printer.printText(
      '\nData: ${DateTime.now()}\n',
      alignment: TextAlignment.left,
      cutPaper: false,
    );

    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );
    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );
    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );
    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );
    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );
    await _printer.printTextFormatted(
      '\n*** FIM DO TESTE ***\n\n\n',
      alignment: TextAlignment.center,
      fontAttributes: const FontAttributes(bold: true),
      cutPaper: false,
    );

    _showSnackBar('Impressão formatada enviada');
  }

  Future<void> _openDrawer() async {
    if (!_isConnected) {
      _showSnackBar('Impressora não conectada');
      return;
    }

    await _printer.openCashDrawer();
    _showSnackBar('Gaveta aberta');
  }

  Future<void> _beep() async {
    if (!_isConnected) {
      _showSnackBar('Impressora não conectada');
      return;
    }

    await _printer.beep(times: 3, duration: 100);
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message), duration: const Duration(seconds: 2)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Totem Alecto'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _refreshDevices,
            tooltip: 'Atualizar dispositivos',
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Status Card
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    Icon(
                      _isConnected ? Icons.print : Icons.print_disabled,
                      size: 48,
                      color: _isConnected ? Colors.green : Colors.grey,
                    ),
                    const SizedBox(height: 8),
                    Text(
                      _status,
                      style: Theme.of(context).textTheme.titleLarge,
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // Device Selection
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Dispositivos USB',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    const SizedBox(height: 8),
                    if (_devices.isEmpty)
                      const Text('Nenhum dispositivo encontrado')
                    else
                      DropdownButton<UsbDeviceInfo>(
                        isExpanded: true,
                        value: _selectedDevice,
                        items: _devices.map((device) {
                          return DropdownMenuItem(
                            value: device,
                            child: Text(device.displayName),
                          );
                        }).toList(),
                        onChanged: (device) {
                          setState(() {
                            _selectedDevice = device;
                          });
                        },
                      ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isConnected ? null : _connect,
                            icon: const Icon(Icons.usb),
                            label: const Text('Conectar'),
                          ),
                        ),

                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isConnected ? null : _connectDevice,
                            icon: const Icon(Icons.usb),
                            label: const Text('Conectar Via Device'),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isConnected ? _disconnect : null,
                            icon: const Icon(Icons.usb_off),
                            label: const Text('Desconectar'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.red,
                              foregroundColor: Colors.white,
                            ),
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // Print Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Impressão',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    const SizedBox(height: 8),
                    TextField(
                      controller: _textController,
                      maxLines: 4,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'Digite o texto para imprimir...',
                      ),
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isConnected ? _printText : null,
                            icon: const Icon(Icons.print),
                            label: const Text('Imprimir'),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isConnected ? _printFormatted : null,
                            icon: const Icon(Icons.text_format),
                            label: const Text('Formatado'),
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            const SizedBox(width: 8),

            Image.asset('assets/images/epoc_logo.png', height: 48),
            SizedBox(
              width: 200,
              child: ElevatedButton.icon(
                onPressed: _isConnected ? _printImage : null,
                icon: const Icon(Icons.text_format),
                label: const Text('Imprimir imagem'),
              ),
            ),
            // Actions Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Ações',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                    const SizedBox(height: 8),
                    Wrap(
                      spacing: 8,
                      runSpacing: 8,
                      children: [
                        ElevatedButton.icon(
                          onPressed: _isConnected ? _openDrawer : null,
                          icon: const Icon(Icons.inventory_2),
                          label: const Text('Gaveta'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isConnected ? _beep : null,
                          icon: const Icon(Icons.volume_up),
                          label: const Text('Beep'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isConnected
                              ? () => _printer.feedPaper(lines: 3)
                              : null,
                          icon: const Icon(Icons.arrow_downward),
                          label: const Text('Avançar'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isConnected
                              ? () => _printer.cutPaper()
                              : null,
                          icon: const Icon(Icons.content_cut),
                          label: const Text('Cortar'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}