ntbp_plugin 1.0.0 copy "ntbp_plugin: ^1.0.0" to clipboard
ntbp_plugin: ^1.0.0 copied to clipboard

A comprehensive Flutter plugin for thermal Bluetooth label printing with advanced features including gap detection, precise positioning, and support for multiple label formats.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:ntbp_plugin/ntbp_plugin.dart';

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

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

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

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

  @override
  State<NtbpExamplePage> createState() => _NtbpExamplePageState();
}

class _NtbpExamplePageState extends State<NtbpExamplePage> {
  final NtbpPlugin _ntbpPlugin = NtbpPlugin();
  bool _isInitialized = false;
  bool _isDiscovering = false;
  List<BluetoothDevice> _availableDevices = [];
  BluetoothDevice? _connectedDevice;
  Map<String, String> _devicePairingStatus = {};
  
  // Label configuration
  final TextEditingController _qrDataController = TextEditingController();
  final TextEditingController _textDataController = TextEditingController();
  final TextEditingController _widthController = TextEditingController();
  final TextEditingController _heightController = TextEditingController();
  final TextEditingController _copiesController = TextEditingController();
  final TextEditingController _textSizeController = TextEditingController();
  
  // Multiple labels data
  final List<Map<String, String>> _multipleLabelsData = [];
  final TextEditingController _multipleLabelsController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _initializePlugin();
    _setupDefaultValues();
  }

  void _setupDefaultValues() {
    _widthController.text = '75.0';
    _heightController.text = '50.0';
    _copiesController.text = '1';
    _textSizeController.text = '9';
    _qrDataController.text = 'TEST_QR_001';
    _textDataController.text = 'Sample Label Text';
  }

  Future<void> _initializePlugin() async {
    try {
      // Check if Bluetooth is available
      final isAvailable = await _ntbpPlugin.isBluetoothAvailable();
      setState(() {
        _isInitialized = isAvailable;
      });
      if (isAvailable) {
        _showMessage('✅ NTBP Plugin initialized successfully');
      } else {
        _showMessage('❌ Bluetooth not available');
      }
    } catch (e) {
      _showMessage('❌ Failed to initialize plugin: $e');
    }
  }

  Future<void> _checkPermissions() async {
    try {
      final granted = await _ntbpPlugin.requestBluetoothPermissions();
      if (granted) {
        _showMessage('✅ Bluetooth permissions granted');
      } else {
        _showMessage('❌ Bluetooth permissions denied');
      }
    } catch (e) {
      _showMessage('❌ Error requesting permissions: $e');
    }
  }

  Future<void> _startDiscovery() async {
    try {
      setState(() {
        _isDiscovering = true;
      });
      
      await _ntbpPlugin.startScan();
      
      // Wait for discovery to complete
      await Future.delayed(const Duration(seconds: 10));
      
      final devices = await _ntbpPlugin.getDiscoveredDevices();
      
      setState(() {
        _availableDevices = devices;
        _isDiscovering = false;
      });
      
      // Check pairing status for each device
      for (final device in devices) {
        if (device.address != null) {
          final status = await _ntbpPlugin.checkDevicePairingStatus(device.address!);
          setState(() {
            _devicePairingStatus[device.address!] = status;
          });
        }
      }
      
      _showMessage('🔍 Device discovery completed. Found ${devices.length} device(s)');
    } catch (e) {
      _showMessage('❌ Error during discovery: $e');
      setState(() {
        _isDiscovering = false;
      });
    }
  }

  Future<void> _stopDiscovery() async {
    try {
      await _ntbpPlugin.stopScan();
      setState(() {
        _isDiscovering = false;
      });
      _showMessage('⏹️ Discovery stopped');
    } catch (e) {
      _showMessage('❌ Error stopping discovery: $e');
    }
  }

  Future<void> _connectToDevice(BluetoothDevice device) async {
    try {
      _showMessage('🔌 Connecting to ${device.name ?? 'Unknown Device'}...');
      
      final success = await _ntbpPlugin.connectToDevice(device);
      
      if (success) {
        setState(() {
          _connectedDevice = device;
        });
        _showMessage('✅ Connected to ${device.name ?? 'Unknown Device'}');
      } else {
        _showMessage('❌ Failed to connect to ${device.name ?? 'Unknown Device'}');
      }
    } catch (e) {
      _showMessage('❌ Connection error: $e');
    }
  }

  Future<void> _disconnect() async {
    try {
      final success = await _ntbpPlugin.disconnect();
      if (success) {
        setState(() {
          _connectedDevice = null;
        });
        _showMessage('🔌 Disconnected from device');
      } else {
        _showMessage('❌ Failed to disconnect');
      }
    } catch (e) {
      _showMessage('❌ Error disconnecting: $e');
    }
  }

  Future<void> _printSingleLabel() async {
    try {
      if (_connectedDevice == null) {
        _showMessage('❌ Please connect to a printer first');
        return;
      }

      final width = double.tryParse(_widthController.text) ?? 75.0;
      final height = double.tryParse(_heightController.text) ?? 50.0;
      final copies = int.tryParse(_copiesController.text) ?? 1;
      final textSize = int.tryParse(_textSizeController.text) ?? 9;
      final qrData = _qrDataController.text.isNotEmpty ? _qrDataController.text : 'TEST_QR_001';
      final textData = _textDataController.text.isNotEmpty ? _textDataController.text : 'Sample Text';

      _showMessage('🏷️ Printing single label: ${width}x${height}mm, $copies copies, text size: $textSize...');
      
      final success = await NtbpPlugin.printSingleLabelWithGapDetection(
        qrData: qrData,
        textData: textData,
        width: width,
        height: height,
        unit: 'mm',
        dpi: 203,
        copies: copies,
        textSize: textSize,
      );

      if (success) {
        _showMessage('✅ Single label printed successfully!');
      } else {
        _showMessage('❌ Failed to print single label');
      }
    } catch (e) {
      _showMessage('❌ Error printing single label: $e');
    }
  }

  Future<void> _printMultipleLabels() async {
    try {
      if (_connectedDevice == null) {
        _showMessage('❌ Please connect to a printer first');
        return;
      }

      if (_multipleLabelsData.isEmpty) {
        _showMessage('❌ Please add some label data first');
        return;
      }

      final width = double.tryParse(_widthController.text) ?? 75.0;
      final height = double.tryParse(_heightController.text) ?? 50.0;
      final copiesPerLabel = int.tryParse(_copiesController.text) ?? 1;
      final textSize = int.tryParse(_textSizeController.text) ?? 9;

      _showMessage('🏷️ Printing ${_multipleLabelsData.length} labels: ${width}x${height}mm, $copiesPerLabel copies each, text size: $textSize...');
      
      final success = await NtbpPlugin.printMultipleLabelsWithGapDetection(
        labelDataList: _multipleLabelsData.map((data) => {
          'qrData': data['qrData'] ?? 'QR_${DateTime.now().millisecondsSinceEpoch}',
          'textData': data['textData'] ?? 'Text_${DateTime.now().millisecondsSinceEpoch}',
        }).toList(),
        width: width,
        height: height,
        unit: 'mm',
        dpi: 203,
        copiesPerLabel: copiesPerLabel,
        textSize: textSize,
      );

      if (success) {
        _showMessage('✅ Multiple labels printed successfully!');
      } else {
        _showMessage('❌ Failed to print multiple labels');
      }
    } catch (e) {
      _showMessage('❌ Error printing multiple labels: $e');
    }
  }

  void _addLabelData() {
    final qrData = _qrDataController.text.isNotEmpty ? _qrDataController.text : 'QR_${_multipleLabelsData.length + 1}';
    final textData = _textDataController.text.isNotEmpty ? _textDataController.text : 'Text_${_multipleLabelsData.length + 1}';

    setState(() {
      _multipleLabelsData.add({
        'qrData': qrData,
        'textData': textData,
      });
    });
    
    _showMessage('➕ Added label data: $qrData - $textData');
  }

  void _clearLabelData() {
    setState(() {
      _multipleLabelsData.clear();
    });
    _showMessage('🗑️ Cleared all label data');
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
        title: const Text('NTBP Plugin - Label Printing'),
        backgroundColor: Colors.blue[700],
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Status Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '📱 Status',
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.bold,
                        color: Colors.blue[700],
                      ),
                    ),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _checkPermissions,
                            icon: const Icon(Icons.security),
                            label: const Text('Check Permissions'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.orange,
                              foregroundColor: Colors.white,
                            ),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _isInitialized ? _startDiscovery : null,
                            icon: const Icon(Icons.search),
                            label: Text(_isDiscovering ? 'Discovering...' : 'Start Discovery'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.green,
                              foregroundColor: Colors.white,
                            ),
                          ),
                        ),
                        const SizedBox(width: 8),
                        ElevatedButton.icon(
                          onPressed: _isDiscovering ? _stopDiscovery : null,
                          icon: const Icon(Icons.stop),
                          label: const Text('Stop'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.red,
                            foregroundColor: Colors.white,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Device List
            if (_availableDevices.isNotEmpty) ...[
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        '🔍 Available Devices (${_availableDevices.length})',
                        style: Theme.of(context).textTheme.titleMedium?.copyWith(
                          fontWeight: FontWeight.bold,
                          color: Colors.green[700],
                        ),
                      ),
                      const SizedBox(height: 12),
                      ...(_availableDevices.map((device) {
                        final pairingStatus = _devicePairingStatus[device.address] ?? 'UNKNOWN';
                        final needsPairing = pairingStatus == 'NOT_PAIRED';
                        final isConnected = _connectedDevice?.address == device.address;
                        
                        return Card(
                          margin: const EdgeInsets.only(bottom: 8),
                          color: isConnected ? Colors.blue[50] : null,
                          child: ListTile(
                            title: Text(device.name ?? 'Unknown Device'),
                            subtitle: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(device.address ?? ''),
                                Text(
                                  'Status: $pairingStatus',
                                  style: TextStyle(
                                    color: needsPairing ? Colors.orange : Colors.green,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                                if (isConnected)
                                  Text(
                                    '✅ CONNECTED',
                                    style: TextStyle(
                                      color: Colors.blue[700],
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                              ],
                            ),
                            trailing: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                if (isConnected) ...[
                                  ElevatedButton(
                                    onPressed: _disconnect,
                                    child: const Text('Disconnect'),
                                    style: ElevatedButton.styleFrom(
                                      backgroundColor: Colors.red,
                                      foregroundColor: Colors.white,
                                      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                                    ),
                                  ),
                                ] else ...[
                                  ElevatedButton(
                                    onPressed: needsPairing ? null : () => _connectToDevice(device),
                                    child: Text(needsPairing ? 'Pair First' : 'Connect'),
                                    style: ElevatedButton.styleFrom(
                                      backgroundColor: needsPairing ? Colors.grey : Colors.blue,
                                      foregroundColor: Colors.white,
                                      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                                    ),
                                  ),
                                ],
                              ],
                            ),
                          ),
                        );
                      })),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
            ],

            // Label Configuration Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '⚙️ Label Configuration',
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.bold,
                        color: Colors.purple[700],
                      ),
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: _widthController,
                            decoration: const InputDecoration(
                              labelText: 'Width (mm)',
                              border: OutlineInputBorder(),
                            ),
                            keyboardType: TextInputType.number,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: TextField(
                            controller: _heightController,
                            decoration: const InputDecoration(
                              labelText: 'Height (mm)',
                              border: OutlineInputBorder(),
                            ),
                            keyboardType: TextInputType.number,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: TextField(
                            controller: _copiesController,
                            decoration: const InputDecoration(
                              labelText: 'Copies',
                              border: OutlineInputBorder(),
                            ),
                            keyboardType: TextInputType.number,
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: _textSizeController,
                            decoration: const InputDecoration(
                              labelText: 'Text Size (1-10)',
                              border: OutlineInputBorder(),
                              helperText: 'Larger numbers = bigger text',
                            ),
                            keyboardType: TextInputType.number,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: Container(), // Spacer
                        ),
                      ],
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: _qrDataController,
                            decoration: const InputDecoration(
                              labelText: 'QR Code Data',
                              border: OutlineInputBorder(),
                            ),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: TextField(
                            controller: _textDataController,
                            decoration: const InputDecoration(
                              labelText: 'Text Data',
                              border: OutlineInputBorder(),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Single Label Printing
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '🏷️ Single Label Printing',
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.bold,
                        color: Colors.blue[700],
                      ),
                    ),
                    const SizedBox(height: 12),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton.icon(
                        onPressed: _connectedDevice != null ? _printSingleLabel : null,
                        icon: const Icon(Icons.print),
                        label: const Text('Print Single Label'),
                        style: ElevatedButton.styleFrom(
                          backgroundColor: Colors.blue,
                          foregroundColor: Colors.white,
                          padding: const EdgeInsets.symmetric(vertical: 16),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Multiple Labels Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '📋 Multiple Labels Management',
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.bold,
                        color: Colors.green[700],
                      ),
                    ),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _addLabelData,
                            icon: const Icon(Icons.add),
                            label: const Text('Add Label Data'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.green,
                              foregroundColor: Colors.white,
                            ),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _multipleLabelsData.isNotEmpty ? _clearLabelData : null,
                            icon: const Icon(Icons.clear),
                            label: const Text('Clear All'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.red,
                              foregroundColor: Colors.white,
                            ),
                          ),
                        ),
                      ],
                    ),
                    if (_multipleLabelsData.isNotEmpty) ...[
                      const SizedBox(height: 12),
                      Text(
                        'Label Data (${_multipleLabelsData.length} items):',
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 8),
                      ...(_multipleLabelsData.asMap().entries.map((entry) {
                        final index = entry.key;
                        final data = entry.value;
                        return Card(
                          margin: const EdgeInsets.only(bottom: 4),
                          child: ListTile(
                            dense: true,
                            title: Text('${index + 1}. ${data['qrData']}'),
                            subtitle: Text(data['textData'] ?? ''),
                            trailing: IconButton(
                              icon: const Icon(Icons.delete, color: Colors.red),
                              onPressed: () {
                                setState(() {
                                  _multipleLabelsData.removeAt(index);
                                });
                                _showMessage('🗑️ Removed label ${index + 1}');
                              },
                            ),
                          ),
                        );
                      })),
                      const SizedBox(height: 12),
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton.icon(
                          onPressed: _connectedDevice != null ? _printMultipleLabels : null,
                          icon: const Icon(Icons.print),
                          label: const Text('Print Multiple Labels'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.green,
                            foregroundColor: Colors.white,
                            padding: const EdgeInsets.symmetric(vertical: 16),
                          ),
                        ),
                      ),
                    ],
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Connection Status
            if (_connectedDevice != null)
              Card(
                color: Colors.green[50],
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Row(
                    children: [
                      Icon(Icons.bluetooth_connected, color: Colors.green[700]),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              '✅ Connected',
                              style: TextStyle(
                                fontWeight: FontWeight.bold,
                                color: Colors.green[700],
                              ),
                            ),
                            Text(_connectedDevice?.name ?? 'Unknown Device'),
                            Text(_connectedDevice?.address ?? ''),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _qrDataController.dispose();
    _textDataController.dispose();
    _widthController.dispose();
    _heightController.dispose();
    _copiesController.dispose();
    _textSizeController.dispose();
    _multipleLabelsController.dispose();
    super.dispose();
  }
}
1
likes
150
points
1
downloads

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter plugin for thermal Bluetooth label printing with advanced features including gap detection, precise positioning, and support for multiple label formats.

Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter, permission_handler, plugin_platform_interface

More

Packages that depend on ntbp_plugin

Packages that implement ntbp_plugin