drago_blue_printer 0.0.8 copy "drago_blue_printer: ^0.0.8" to clipboard
drago_blue_printer: ^0.0.8 copied to clipboard

A Flutter plugin for connecting to thermal printer via bluetooth

example/lib/main.dart

import 'dart:io';
import 'package:example/testprint.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:drago_blue_printer/drago_blue_printer.dart';
import 'package:flutter/services.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  DragoBluePrinter bluetooth = DragoBluePrinter.instance;

  List<BluetoothDevice> _devices = [];
  final List<BluetoothDevice> _scannedDevices = [];
  BluetoothDevice? _device;
  bool _connected = false;
  String? pathImage;
  var testPrint = TestPrint();
  bool _isLoading = false;
  bool _isScanning = false;
  StreamSubscription<BluetoothDevice>? _scanSubscription;

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

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

  Future<void> initPlatformState() async {
    if (!mounted) return;
    setState(() => _isLoading = true);

    List<BluetoothDevice> devices = [];

    try {
      devices = await bluetooth.getBondedDevices();
    } catch (e) {
      debugPrint("Error in initPlatformState: $e");
      // Optionally show a snackbar here
      // show("Error loading devices: $e");
    }

    bluetooth.onStateChanged().listen((state) {
      switch (state) {
        case DragoBluePrinter.CONNECTED:
          setState(() {
            _connected = true;
            debugPrint("bluetooth device state: connected");
          });
          break;
        case DragoBluePrinter.DISCONNECTED:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: disconnected");
          });
          break;
        case DragoBluePrinter.DISCONNECT_REQUESTED:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: disconnect requested");
          });
          break;
        case DragoBluePrinter.STATE_TURNING_OFF:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: bluetooth turning off");
          });
          break;
        case DragoBluePrinter.STATE_OFF:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: bluetooth off");
          });
          break;
        case DragoBluePrinter.STATE_ON:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: bluetooth on");
          });
          break;
        case DragoBluePrinter.STATE_TURNING_ON:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: bluetooth turning on");
          });
          break;
        case DragoBluePrinter.ERROR:
          setState(() {
            _connected = false;
            debugPrint("bluetooth device state: error");
          });
          break;
        default:
          debugPrint(state.toString());
          break;
      }
    });

    if (!mounted) return;
    setState(() {
      _devices = devices;
      _isLoading = false;
    });

    // The connection status is now handled by the onStateChanged listener
    // if (isConnected) {
    //   setState(() {
    //     _connected = true;
    //   });
    // }
  }

  void _startScan() {
    setState(() {
      _isScanning = true;
      _scannedDevices.clear();
    });

    _scanSubscription?.cancel();
    _scanSubscription = bluetooth.scan().listen((device) {
      if (!_devices.any((d) => d.address == device.address) &&
          !_scannedDevices.any((d) => d.address == device.address)) {
        setState(() {
          _scannedDevices.add(device);
        });
      }
    });

    // Auto stop after 30 seconds
    Future.delayed(const Duration(seconds: 30), () {
      _stopScan();
    });
  }

  void _stopScan() {
    _scanSubscription?.cancel();
    if (mounted) {
      setState(() {
        _isScanning = false;
      });
    }
  }

  void _pairDevice(BluetoothDevice device) async {
    try {
      await bluetooth.pairDevice(device);
      // Give it a moment to pair, then refresh bonded list
      await Future.delayed(const Duration(seconds: 2));
      initPlatformState();
    } catch (e) {
      debugPrint("Pairing error: $e");
      show("Pairing failed: $e", duration: const Duration(seconds: 5));
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
        appBarTheme: AppBarTheme(
          centerTitle: true,
          elevation: 2,
          backgroundColor: Colors.blue.shade50,
        ),
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Bluetooth Printer'),
          actions: [
            IconButton(
              icon: const Icon(Icons.refresh),
              onPressed: initPlatformState,
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton.extended(
          onPressed: _isScanning ? _stopScan : _startScan,
          icon: Icon(_isScanning ? Icons.stop : Icons.search),
          label: Text(_isScanning ? "Stop Scan" : "Scan Devices"),
        ),
        body: _isLoading
            ? const Center(child: CircularProgressIndicator())
            : SingleChildScrollView(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Container(
                      padding: const EdgeInsets.all(16),
                      width: double.infinity,
                      color: Colors.blue.shade50,
                      child: Text(
                        "Paired Devices",
                        style: TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.bold,
                            color: Colors.blue.shade900),
                      ),
                    ),
                    if (_devices.isEmpty)
                      const Padding(
                        padding: EdgeInsets.all(20),
                        child: Center(
                            child: Text("No paired devices",
                                style: TextStyle(color: Colors.grey))),
                      ),
                    ListView.builder(
                      shrinkWrap: true,
                      physics: const NeverScrollableScrollPhysics(),
                      itemCount: _devices.length,
                      padding: const EdgeInsets.all(12),
                      itemBuilder: (context, index) {
                        return _buildPairedDeviceItem(_devices[index]);
                      },
                    ),

                    if (_isScanning || _scannedDevices.isNotEmpty) ...[
                      Container(
                        padding: const EdgeInsets.all(16),
                        width: double.infinity,
                        color: Colors.orange.shade50,
                        child: Row(
                          children: [
                            Text(
                              "Available Devices",
                              style: TextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.bold,
                                  color: Colors.orange.shade900),
                            ),
                            if (_isScanning) ...[
                              const SizedBox(width: 10),
                              const SizedBox(
                                  width: 12,
                                  height: 12,
                                  child: CircularProgressIndicator(
                                      strokeWidth: 2)),
                            ]
                          ],
                        ),
                      ),
                      ListView.builder(
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemCount: _scannedDevices.length,
                        padding: const EdgeInsets.all(12),
                        itemBuilder: (context, index) {
                          return _buildScannedDeviceItem(
                              _scannedDevices[index]);
                        },
                      ),
                    ],
                    const SizedBox(height: 80), // Fab space
                  ],
                ),
              ),
      ),
    );
  }

  Widget _buildPairedDeviceItem(BluetoothDevice device) {
    final isSelected = _device?.address == device.address;
    final isReallyConnected = isSelected && _connected;

    return Card(
      elevation: isReallyConnected ? 4 : 1,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
        side: isReallyConnected
            ? const BorderSide(color: Colors.green, width: 2)
            : BorderSide.none,
      ),
      margin: const EdgeInsets.only(bottom: 12),
      child: ExpansionTile(
        leading: CircleAvatar(
          backgroundColor:
              isReallyConnected ? Colors.green.shade100 : Colors.blue.shade50,
          child: Icon(
            Icons.print,
            color: isReallyConnected ? Colors.green : Colors.blue,
          ),
        ),
        title: Text(
          device.name ?? "Unknown Device",
          style: const TextStyle(fontWeight: FontWeight.bold),
        ),
        subtitle: Text(device.address ?? ""),
        trailing: isReallyConnected
            ? const Chip(
                label: Text("Connected",
                    style: TextStyle(color: Colors.white, fontSize: 12)),
                backgroundColor: Colors.green,
                side: BorderSide.none,
              )
            : TextButton(
                onPressed: () => _connect(device),
                child: const Text("Connect"),
              ),
        children: [
          if (isReallyConnected)
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  FilledButton.icon(
                    onPressed: () async {
                      if (pathImage != null) {
                        await testPrint.sample(pathImage!);
                      }
                    },
                    icon: const Icon(Icons.receipt_long),
                    label: const Text("Print Test"),
                  ),
                  OutlinedButton.icon(
                    onPressed: _disconnect,
                    icon: const Icon(Icons.link_off),
                    label: const Text("Disconnect"),
                    style:
                        OutlinedButton.styleFrom(foregroundColor: Colors.red),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }

  Widget _buildScannedDeviceItem(BluetoothDevice device) {
    return Card(
      elevation: 1,
      margin: const EdgeInsets.only(bottom: 8),
      child: ListTile(
        leading: const Icon(Icons.bluetooth),
        title: Text(device.name ?? "Unknown"),
        subtitle: Text(device.address ?? ""),
        trailing: OutlinedButton(
          onPressed: () => _pairDevice(device),
          child: const Text("Pair"),
        ),
      ),
    );
  }

  void _connect(BluetoothDevice device) {
    setState(() {
      _device = device;
    });
    bluetooth.isConnected.then((isConnected) {
      if (!(isConnected ?? false)) {
        bluetooth.connect(device).catchError((error) {
          setState(() => _connected = false);
          show("Connection failed: $error",
              duration: const Duration(seconds: 5));
        });
        // Removed setState(() => _connected = true); as it's handled by onStateChanged
      }
    });
  }

  void _disconnect() {
    bluetooth.disconnect();
    setState(() => _connected = false);
  }

  //write to app path
  Future<void> writeToFile(ByteData data, String path) {
    final buffer = data.buffer;
    return File(path).writeAsBytes(
        buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
  }

  Future show(
    String message, {
    Duration duration = const Duration(seconds: 3),
  }) async {
    await Future.delayed(const Duration(milliseconds: 100));
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(
          message,
          style: const TextStyle(
            color: Colors.white,
          ),
        ),
        duration: duration,
      ),
    );
  }
}
9
likes
160
points
193
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for connecting to thermal printer via bluetooth

Repository (GitHub)
View/report issues

Documentation

API reference

License

CPL-1.0 (license)

Dependencies

flutter, permission_handler

More

Packages that depend on drago_blue_printer

Packages that implement drago_blue_printer