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.

NTBP Plugin - Flutter Thermal Bluetooth Label Printer #

Flutter Platform License Version

A comprehensive, production-ready Flutter plugin for thermal Bluetooth label printing with advanced features including gap detection, precise positioning, and support for multiple label formats. Works with any UI implementation - the plugin handles all the complexity behind the scenes.

🎯 Why Choose NTBP Plugin? #

  • 🔄 UI-Agnostic: Works seamlessly with any Flutter UI framework (Material, Cupertino, custom)
  • 🚀 Production Ready: Tested extensively with real thermal printers
  • 📱 Cross-Platform: Full Android and iOS support
  • 🛡️ Robust: Advanced error handling and buffer management
  • 📚 Well-Documented: Comprehensive guides and examples
  • 🔧 Maintainable: Clean architecture and extensible design

🚀 Core Features #

  • Bluetooth Connectivity: Support for both Classic Bluetooth (SPP) and BLE
  • Thermal Printing: TSPL language support for professional label printing
  • Smart Positioning: Automatic content centering based on label dimensions
  • Gap Detection: Intelligent label boundary detection and frame management
  • Multiple Formats: QR codes, barcodes, text, and combined content printing
  • Batch Processing: Sequence printing with proper label separation
  • Buffer Management: Advanced buffer clearing strategies for reliable printing
  • Cross-Platform: Android and iOS support with identical APIs

📱 Supported Printers #

  • TSPL-Compatible Printers: N41, N42, and similar thermal label printers
  • Resolution: 203 DPI (8 dots/mm) and higher
  • Label Sizes: 2" (50mm) to 4.65" (118mm) width
  • Media Types: Die-cut labels, continuous paper, black mark paper

🏗️ Architecture Overview #

┌─────────────────────────────────────────────────────────────┐
│                    Flutter App                             │
│              (Any UI Framework)                            │
├─────────────────────────────────────────────────────────────┤
│                 ntbp_plugin.dart                           │
│              (Public API Interface)                        │
├─────────────────────────────────────────────────────────────┤
│           ntbp_plugin_platform_interface.dart              │
│              (Abstract Platform Interface)                 │
├─────────────────────────────────────────────────────────────┤
│           ntbp_plugin_method_channel.dart                  │
│              (Flutter Method Channel)                     │
├─────────────────────────────────────────────────────────────┤
│                    Native Implementation                    │
│  ┌─────────────────┐    ┌─────────────────┐               │
│  │   Android       │    │      iOS        │               │
│  │   (Kotlin)      │    │   (Swift)       │               │
│  │                 │    │                 │               │
│  │ • Bluetooth     │    │ • CoreBluetooth │               │
│  │ • TSPL Commands │    │ • TSPL Commands │               │
│  │ • Buffer Mgmt   │    │ • Buffer Mgmt   │               │
│  └─────────────────┘    └─────────────────┘               │
└─────────────────────────────────────────────────────────────┘

📋 Table of Contents #

  1. Quick Start
  2. Installation
  3. Basic Usage
  4. Advanced Features
  5. API Reference
  6. Integration Guide
  7. UI Implementation Examples
  8. Troubleshooting
  9. Development Guide
  10. Contributing

🚀 Quick Start #

1. Add Dependency #

dependencies:
  ntbp_plugin: ^1.0.0

2. Basic Implementation (UI-Agnostic) #

import 'package:ntbp_plugin/ntbp_plugin.dart';

class PrinterService {
  final NtbpPlugin _plugin = NtbpPlugin();
  
  Future<void> printLabel() async {
    try {
      // Initialize plugin
      final isAvailable = await _plugin.isBluetoothAvailable();
      if (!isAvailable) {
        throw Exception('Bluetooth not available');
      }
      
      // Check permissions
      final hasPermission = await _plugin.requestBluetoothPermissions();
      if (!hasPermission) {
        throw Exception('Bluetooth permission denied');
      }
      
      // Scan for devices
      final devices = await _plugin.startScan();
      if (devices.isEmpty) {
        throw Exception('No devices found');
      }
      
      // Connect to device
      await _plugin.connectToDevice(devices.first);
      
      // Print single label
      final success = await _plugin.printSingleLabelWithGapDetection(
        qrData: "QR123456",
        textData: "Sample Label",
        width: 75.0,
        height: 50.0,
        unit: 'mm',
        dpi: 203,
        copies: 1,
        textSize: 9,
      );
      
      if (success) {
        print('Label printed successfully!');
      }
    } catch (e) {
      print('Error: $e');
    }
  }
}

3. Advanced Usage - Multiple Labels #

Future<void> printMultipleLabels() async {
  final labelData = [
    {'qrData': 'QR001', 'textData': 'Product A'},
    {'qrData': 'QR002', 'textData': 'Product B'},
    {'qrData': 'QR003', 'textData': 'Product C'},
  ];
  
  final success = await _plugin.printMultipleLabelsWithGapDetection(
    labelDataList: labelData,
    width: 75.0,
    height: 50.0,
    unit: 'mm',
    dpi: 203,
    copiesPerLabel: 1,
    textSize: 9,
  );
  
  if (success) {
    print('All labels printed successfully!');
  }
}

📦 Installation #

Flutter Dependencies #

dependencies:
  flutter:
    sdk: flutter
  ntbp_plugin: ^1.0.0

Android Configuration #

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS Configuration #

Add to ios/Runner/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to thermal printers for label printing.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to connect to thermal printers for label printing.</string>

Create ios/Runner/PrivacyInfo.xcprivacy:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSPrivacyAccessedAPICategoryBluetooth</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>This app uses Bluetooth to connect to thermal printers for label printing functionality.</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

🎨 UI Implementation Examples #

Example 1: Material Design UI #

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

class MaterialPrinterScreen extends StatefulWidget {
  @override
  _MaterialPrinterScreenState createState() => _MaterialPrinterScreenState();
}

class _MaterialPrinterScreenState extends State<MaterialPrinterScreen> {
  final NtbpPlugin _plugin = NtbpPlugin();
  List<Map<String, dynamic>> _devices = [];
  Map<String, dynamic>? _connectedDevice;
  bool _isScanning = false;
  bool _isPrinting = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Thermal Printer')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            // Bluetooth Status
            Card(
              child: ListTile(
                leading: Icon(Icons.bluetooth),
                title: Text('Bluetooth Status'),
                subtitle: Text(_connectedDevice?['name'] ?? 'Not Connected'),
                trailing: _connectedDevice != null
                    ? IconButton(
                        icon: Icon(Icons.disconnect),
                        onPressed: _disconnect,
                      )
                    : null,
              ),
            ),
            
            // Scan Button
            ElevatedButton.icon(
              onPressed: _isScanning ? null : _startScan,
              icon: Icon(_isScanning ? Icons.stop : Icons.search),
              label: Text(_isScanning ? 'Scanning...' : 'Scan for Devices'),
            ),
            
            // Device List
            Expanded(
              child: ListView.builder(
                itemCount: _devices.length,
                itemBuilder: (context, index) {
                  final device = _devices[index];
                  return ListTile(
                    title: Text(device['name'] ?? 'Unknown Device'),
                    subtitle: Text(device['address'] ?? ''),
                    trailing: ElevatedButton(
                      onPressed: () => _connectToDevice(device),
                      child: Text('Connect'),
                    ),
                  );
                },
              ),
            ),
            
            // Print Buttons
            if (_connectedDevice != null) ...[
              ElevatedButton.icon(
                onPressed: _isPrinting ? null : _printSingleLabel,
                icon: Icon(Icons.print),
                label: Text('Print Single Label'),
              ),
              SizedBox(height: 8),
              ElevatedButton.icon(
                onPressed: _isPrinting ? null : _printMultipleLabels,
                icon: Icon(Icons.print_disabled),
                label: Text('Print Multiple Labels'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Future<void> _startScan() async {
    setState(() => _isScanning = true);
    try {
      final devices = await _plugin.startScan();
      setState(() {
        _devices = devices.cast<Map<String, dynamic>>();
        _isScanning = false;
      });
    } catch (e) {
      setState(() => _isScanning = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Scan failed: $e')),
      );
    }
  }

  Future<void> _connectToDevice(Map<String, dynamic> device) async {
    try {
      await _plugin.connectToDevice(device);
      setState(() => _connectedDevice = device);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connected to ${device['name']}')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connection failed: $e')),
      );
    }
  }

  Future<void> _disconnect() async {
    try {
      await _plugin.disconnect();
      setState(() => _connectedDevice = null);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Disconnected')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Disconnect failed: $e')),
      );
    }
  }

  Future<void> _printSingleLabel() async {
    setState(() => _isPrinting = true);
    try {
      final success = await _plugin.printSingleLabelWithGapDetection(
        qrData: "QR_${DateTime.now().millisecondsSinceEpoch}",
        textData: "Sample Label",
        width: 75.0,
        height: 50.0,
        unit: 'mm',
        dpi: 203,
        copies: 1,
        textSize: 9,
      );
      
      if (success) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Label printed successfully!')),
        );
      }
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Print failed: $e')),
      );
    } finally {
      setState(() => _isPrinting = false);
    }
  }

  Future<void> _printMultipleLabels() async {
    setState(() => _isPrinting = true);
    try {
      final labelData = [
        {'qrData': 'QR001', 'textData': 'Product A'},
        {'qrData': 'QR002', 'textData': 'Product B'},
        {'qrData': 'QR003', 'textData': 'Product C'},
      ];
      
      final success = await _plugin.printMultipleLabelsWithGapDetection(
        labelDataList: labelData,
        width: 75.0,
        height: 50.0,
        unit: 'mm',
        dpi: 203,
        copiesPerLabel: 1,
        textSize: 9,
      );
      
      if (success) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('All labels printed successfully!')),
        );
      }
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Print failed: $e')),
      );
    } finally {
      setState(() => _isPrinting = false);
    }
  }
}

Example 2: Cupertino (iOS Style) UI #

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

class CupertinoPrinterScreen extends StatefulWidget {
  @override
  _CupertinoPrinterScreenState createState() => _CupertinoPrinterScreenState();
}

class _CupertinoPrinterScreenState extends State<CupertinoPrinterScreen> {
  final NtbpPlugin _plugin = NtbpPlugin();
  List<Map<String, dynamic>> _devices = [];
  Map<String, dynamic>? _connectedDevice;

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text('Thermal Printer'),
      ),
      child: SafeArea(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              // Status Card
              Container(
                padding: EdgeInsets.all(16.0),
                decoration: BoxDecoration(
                  color: CupertinoColors.systemGrey6,
                  borderRadius: BorderRadius.circular(8.0),
                ),
                child: Row(
                  children: [
                    Icon(CupertinoIcons.bluetooth),
                    SizedBox(width: 8.0),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            'Bluetooth Status',
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          Text(_connectedDevice?['name'] ?? 'Not Connected'),
                        ],
                      ),
                    ),
                    if (_connectedDevice != null)
                      CupertinoButton(
                        padding: EdgeInsets.zero,
                        onPressed: _disconnect,
                        child: Icon(CupertinoIcons.clear),
                      ),
                  ],
                ),
              ),
              
              SizedBox(height: 16.0),
              
              // Scan Button
              SizedBox(
                width: double.infinity,
                child: CupertinoButton.filled(
                  onPressed: _startScan,
                  child: Text('Scan for Devices'),
                ),
              ),
              
              SizedBox(height: 16.0),
              
              // Device List
              Expanded(
                child: CupertinoScrollbar(
                  child: ListView.builder(
                    itemCount: _devices.length,
                    itemBuilder: (context, index) {
                      final device = _devices[index];
                      return Container(
                        margin: EdgeInsets.only(bottom: 8.0),
                        decoration: BoxDecoration(
                          color: CupertinoColors.systemGrey6,
                          borderRadius: BorderRadius.circular(8.0),
                        ),
                        child: ListTile(
                          title: Text(device['name'] ?? 'Unknown Device'),
                          subtitle: Text(device['address'] ?? ''),
                          trailing: CupertinoButton(
                            padding: EdgeInsets.symmetric(horizontal: 16.0),
                            onPressed: () => _connectToDevice(device),
                            child: Text('Connect'),
                          ),
                        ),
                      );
                    },
                  ),
                ),
              ),
              
              // Print Buttons
              if (_connectedDevice != null) ...[
                SizedBox(height: 16.0),
                SizedBox(
                  width: double.infinity,
                  child: CupertinoButton.filled(
                    onPressed: _printSingleLabel,
                    child: Text('Print Single Label'),
                  ),
                ),
                SizedBox(height: 8.0),
                SizedBox(
                  width: double.infinity,
                  child: CupertinoButton.filled(
                    onPressed: _printMultipleLabels,
                    child: Text('Print Multiple Labels'),
                  ),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }

  // Implementation methods similar to Material example...
  Future<void> _startScan() async { /* ... */ }
  Future<void> _connectToDevice(Map<String, dynamic> device) async { /* ... */ }
  Future<void> _disconnect() async { /* ... */ }
  Future<void> _printSingleLabel() async { /* ... */ }
  Future<void> _printMultipleLabels() async { /* ... */ }
}

Example 3: Custom UI (No Framework Dependencies) #

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

class CustomPrinterScreen extends StatefulWidget {
  @override
  _CustomPrinterScreenState createState() => _CustomPrinterScreenState();
}

class _CustomPrinterScreenState extends State<CustomPrinterScreen> {
  final NtbpPlugin _plugin = NtbpPlugin();
  List<Map<String, dynamic>> _devices = [];
  Map<String, dynamic>? _connectedDevice;
  bool _isScanning = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [Colors.blue[900]!, Colors.blue[600]!],
          ),
        ),
        child: SafeArea(
          child: Column(
            children: [
              // Custom Header
              Container(
                padding: EdgeInsets.all(20.0),
                child: Row(
                  children: [
                    Container(
                      padding: EdgeInsets.all(12.0),
                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.2),
                        borderRadius: BorderRadius.circular(12.0),
                      ),
                      child: Icon(
                        Icons.print,
                        color: Colors.white,
                        size: 28.0,
                      ),
                    ),
                    SizedBox(width: 16.0),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            'Thermal Printer',
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 24.0,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          Text(
                            _connectedDevice?['name'] ?? 'Not Connected',
                            style: TextStyle(
                              color: Colors.white.withOpacity(0.8),
                              fontSize: 16.0,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              
              // Main Content
              Expanded(
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(30.0),
                      topRight: Radius.circular(30.0),
                    ),
                  ),
                  child: Padding(
                    padding: EdgeInsets.all(20.0),
                    child: Column(
                      children: [
                        // Scan Button
                        Container(
                          width: double.infinity,
                          height: 56.0,
                          decoration: BoxDecoration(
                            gradient: LinearGradient(
                              colors: [Colors.orange[400]!, Colors.orange[600]!],
                            ),
                            borderRadius: BorderRadius.circular(28.0),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.orange.withOpacity(0.3),
                                blurRadius: 8.0,
                                offset: Offset(0, 4),
                              ),
                            ],
                          ),
                          child: Material(
                            color: Colors.transparent,
                            child: InkWell(
                              borderRadius: BorderRadius.circular(28.0),
                              onTap: _isScanning ? null : _startScan,
                              child: Center(
                                child: Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: [
                                    Icon(
                                      _isScanning ? Icons.stop : Icons.search,
                                      color: Colors.white,
                                      size: 24.0,
                                    ),
                                    SizedBox(width: 8.0),
                                    Text(
                                      _isScanning ? 'Scanning...' : 'Scan for Devices',
                                      style: TextStyle(
                                        color: Colors.white,
                                        fontSize: 18.0,
                                        fontWeight: FontWeight.bold,
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                        ),
                        
                        SizedBox(height: 24.0),
                        
                        // Device List
                        Expanded(
                          child: _devices.isEmpty
                              ? Center(
                                  child: Column(
                                    mainAxisAlignment: MainAxisAlignment.center,
                                    children: [
                                      Icon(
                                        Icons.bluetooth_searching,
                                        size: 64.0,
                                        color: Colors.grey[400],
                                      ),
                                      SizedBox(height: 16.0),
                                      Text(
                                        'No devices found',
                                        style: TextStyle(
                                          fontSize: 18.0,
                                          color: Colors.grey[600],
                                        ),
                                      ),
                                      Text(
                                        'Press scan to discover devices',
                                        style: TextStyle(
                                          fontSize: 14.0,
                                          color: Colors.grey[500],
                                        ),
                                      ),
                                    ],
                                  ),
                                )
                              : ListView.builder(
                                  itemCount: _devices.length,
                                  itemBuilder: (context, index) {
                                    final device = _devices[index];
                                    return Container(
                                      margin: EdgeInsets.only(bottom: 12.0),
                                      decoration: BoxDecoration(
                                        color: Colors.grey[50],
                                        borderRadius: BorderRadius.circular(16.0),
                                        border: Border.all(
                                          color: Colors.grey[200]!,
                                          width: 1.0,
                                        ),
                                      ),
                                      child: ListTile(
                                        contentPadding: EdgeInsets.symmetric(
                                          horizontal: 20.0,
                                          vertical: 8.0,
                                        ),
                                        leading: Container(
                                          padding: EdgeInsets.all(8.0),
                                          decoration: BoxDecoration(
                                            color: Colors.blue[100],
                                            borderRadius: BorderRadius.circular(8.0),
                                          ),
                                          child: Icon(
                                            Icons.bluetooth,
                                            color: Colors.blue[600],
                                            size: 20.0,
                                          ),
                                        ),
                                        title: Text(
                                          device['name'] ?? 'Unknown Device',
                                          style: TextStyle(
                                            fontWeight: FontWeight.w600,
                                          ),
                                        ),
                                        subtitle: Text(
                                          device['address'] ?? '',
                                          style: TextStyle(
                                            fontSize: 12.0,
                                            color: Colors.grey[600],
                                          ),
                                        ),
                                        trailing: Container(
                                          height: 40.0,
                                          child: ElevatedButton(
                                            onPressed: () => _connectToDevice(device),
                                            style: ElevatedButton.styleFrom(
                                              backgroundColor: Colors.blue[600],
                                              foregroundColor: Colors.white,
                                              shape: RoundedRectangleBorder(
                                                borderRadius: BorderRadius.circular(20.0),
                                              ),
                                            ),
                                            child: Text('Connect'),
                                          ),
                                        ),
                                      ),
                                    );
                                  },
                                ),
                        ),
                        
                        // Print Buttons
                        if (_connectedDevice != null) ...[
                          SizedBox(height: 24.0),
                          Row(
                            children: [
                              Expanded(
                                child: Container(
                                  height: 56.0,
                                  decoration: BoxDecoration(
                                    gradient: LinearGradient(
                                      colors: [Colors.green[400]!, Colors.green[600]!],
                                    ),
                                    borderRadius: BorderRadius.circular(28.0),
                                    boxShadow: [
                                      BoxShadow(
                                        color: Colors.green.withOpacity(0.3),
                                        blurRadius: 8.0,
                                        offset: Offset(0, 4),
                                      ),
                                    ],
                                  ),
                                  child: Material(
                                    color: Colors.transparent,
                                    child: InkWell(
                                      borderRadius: BorderRadius.circular(28.0),
                                      onTap: _printSingleLabel,
                                      child: Center(
                                        child: Row(
                                          mainAxisAlignment: MainAxisAlignment.center,
                                          children: [
                                            Icon(
                                              Icons.print,
                                              color: Colors.white,
                                              size: 24.0,
                                            ),
                                            SizedBox(width: 8.0),
                                            Text(
                                              'Single Label',
                                              style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 16.0,
                                                fontWeight: FontWeight.bold,
                                              ),
                                            ),
                                          ],
                                        ),
                                      ),
                                    ),
                                  ),
                                ),
                              ),
                              SizedBox(width: 16.0),
                              Expanded(
                                child: Container(
                                  height: 56.0,
                                  decoration: BoxDecoration(
                                    gradient: LinearGradient(
                                      colors: [Colors.purple[400]!, Colors.purple[600]!],
                                    ),
                                    borderRadius: BorderRadius.circular(28.0),
                                    boxShadow: [
                                      BoxShadow(
                                        color: Colors.purple.withOpacity(0.3),
                                        blurRadius: 8.0,
                                        offset: Offset(0, 4),
                                      ),
                                    ],
                                  ),
                                  child: Material(
                                    color: Colors.transparent,
                                    child: InkWell(
                                      borderRadius: BorderRadius.circular(28.0),
                                      onTap: _printMultipleLabels,
                                      child: Center(
                                        child: Row(
                                          mainAxisAlignment: MainAxisAlignment.center,
                                          children: [
                                            Icon(
                                              Icons.print_disabled,
                                              color: Colors.white,
                                              size: 24.0,
                                            ),
                                            SizedBox(width: 8.0),
                                            Text(
                                              'Multiple Labels',
                                              style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 16.0,
                                                fontWeight: FontWeight.bold,
                                              ),
                                            ),
                                          ],
                                        ),
                                      ),
                                    ),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ],
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // Implementation methods...
  Future<void> _startScan() async { /* ... */ }
  Future<void> _connectToDevice(Map<String, dynamic> device) async { /* ... */ }
  Future<void> _printSingleLabel() async { /* ... */ }
  Future<void> _printMultipleLabels() async { /* ... */ }
}

🔧 Advanced Features #

Buffer Management #

The plugin includes advanced buffer clearing strategies to prevent content bleeding:

// Clear buffer before critical operations
await _plugin.clearBuffer();

// Get printer status
final status = await _plugin.getPrinterStatus();

// Feed paper
await _plugin.feedPaper();

Custom Label Configuration #

// Get paper configuration for custom calculations
final config = await _plugin.getLabelPaperConfig(
  width: 75.0,
  height: 50.0,
  unit: 'mm',
  dpi: 203,
);

print('Available width: ${config['availableWidth']} dots');
print('QR size: ${config['qrSize']} dots');

Device Pairing with PIN #

// Request device pairing with PIN code
await _plugin.requestDevicePairingWithPin(
  device: device,
  pinCode: '1234',
);

// Get stored PIN code
final pinCode = await _plugin.getStoredPinCode(device);

// Remove stored PIN code
await _plugin.removeStoredPinCode(device);

📚 API Reference #

Bluetooth Management #

Method Description Returns
isBluetoothAvailable() Check if Bluetooth is available Future<bool>
requestBluetoothPermissions() Request Bluetooth permissions Future<bool>
startScan() Scan for Bluetooth devices Future<List<Map<String, dynamic>>>
connectToDevice(device) Connect to selected device Future<void>
disconnect() Disconnect from device Future<bool>
getConnectionStatus() Get current connection status Future<String>
getDetailedConnectionStatus() Get detailed connection info Future<Map<String, dynamic>>

Printing Methods #

Method Description Returns
printSingleLabelWithGapDetection(...) Print single label with gap detection Future<bool>
printMultipleLabelsWithGapDetection(...) Print multiple labels with gap detection Future<bool>
printProfessionalLabel(...) Professional label printing Future<bool>
printLabelSequence(...) Sequence label printing Future<bool>
printCustomLabel(...) Custom label with specific sizes Future<bool>
printSmartLabel(...) Smart label printing Future<bool>
printSmartSequence(...) Smart sequence printing Future<bool>

Buffer Management #

Method Description Returns
clearBuffer() Clear printer buffer Future<bool>
getPrinterStatus() Get printer status Future<String>
feedPaper() Feed paper Future<bool>

Device Pairing #

Method Description Returns
requestDevicePairing(device) Request device pairing Future<bool>
requestDevicePairingWithPin(device, pinCode) Pair with PIN code Future<bool>
getStoredPinCode(device) Get stored PIN code Future<String?>
removeStoredPinCode(device) Remove stored PIN code Future<bool>
checkDevicePairingStatus(device) Check pairing status Future<bool>

🚨 Troubleshooting #

Common Issues & Solutions #

1. Bluetooth Not Available

// Check Bluetooth availability
final isAvailable = await _plugin.isBluetoothAvailable();
if (!isAvailable) {
  // Handle Bluetooth not available
  print('Bluetooth is not available on this device');
}

2. Permission Denied

// Request permissions
final hasPermission = await _plugin.requestBluetoothPermissions();
if (!hasPermission) {
  // Guide user to settings
  print('Please enable Bluetooth permissions in device settings');
}

3. No Devices Found

// Ensure Bluetooth is on and scan again
try {
  final devices = await _plugin.startScan();
  if (devices.isEmpty) {
    print('No devices found. Ensure printer is powered on and in pairing mode.');
  }
} catch (e) {
  print('Scan failed: $e');
}

4. Connection Failed

// Check connection status
final status = await _plugin.getConnectionStatus();
if (status != 'CONNECTED') {
  // Try reconnecting
  await _plugin.disconnect();
  await Future.delayed(Duration(seconds: 2));
  await _plugin.connectToDevice(device);
}

5. Printing Issues

// Clear buffer before printing
await _plugin.clearBuffer();

// Check printer status
final printerStatus = await _plugin.getPrinterStatus();
if (printerStatus != 'READY') {
  print('Printer not ready: $printerStatus');
  return;
}

Debug Information #

Enable detailed logging for troubleshooting:

// Check detailed connection status
final details = await _plugin.getDetailedConnectionStatus();
print('Connection details: $details');

// Monitor connection state changes
// The plugin automatically logs all operations

🛠️ Development Guide #

Building from Source #

# Clone the repository
git clone https://github.com/yourusername/ntbp_plugin.git
cd ntbp_plugin

# Get dependencies
flutter pub get

# Run tests
flutter test

# Build example app
cd example
flutter build apk --debug

Project Structure #

ntbp_plugin/
├── lib/
│   ├── ntbp_plugin.dart              # Main plugin interface
│   ├── ntbp_plugin_platform_interface.dart  # Abstract interface
│   └── ntbp_plugin_method_channel.dart      # Method channel implementation
├── android/
│   └── src/main/kotlin/
│       └── com/example/ntbp_plugin/
│           └── NtbpPlugin.kt         # Android implementation
├── ios/
│   └── Classes/
│       └── NtbpPlugin.swift          # iOS implementation
├── example/
│   └── lib/
│       └── main.dart                 # Example app
└── test/
    └── ntbp_plugin_test.dart         # Unit tests

Adding New Features #

  1. Update Platform Interface: Add abstract methods to ntbp_plugin_platform_interface.dart
  2. Implement on Android: Add Kotlin implementation in NtbpPlugin.kt
  3. Implement on iOS: Add Swift implementation in NtbpPlugin.swift
  4. Update Method Channel: Add method handling in ntbp_plugin_method_channel.dart
  5. Add Tests: Create unit tests for new functionality
  6. Update Documentation: Document new features and usage

🤝 Contributing #

We welcome contributions! Please see our Contributing Guide for details.

Development Setup #

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

Code Style #

  • Follow Flutter/Dart conventions
  • Use meaningful variable names
  • Add comprehensive comments
  • Include error handling
  • Write unit tests

📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments #

  • Flutter team for the excellent framework
  • TSPL command reference documentation
  • Bluetooth development community
  • All contributors and testers

📞 Support #


Made with ❤️ for the Flutter community

This plugin is designed to work seamlessly with any UI implementation - whether you're using Material Design, Cupertino, or custom widgets, the thermal printing functionality remains consistent and reliable.

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