Flutter蓝牙打印插件bluetooth_print_urovo的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter蓝牙打印插件bluetooth_print_urovo的使用

介绍

bluetooth_print_urovo 是一个基于 bluetooth_print 插件的分支,用于在Flutter应用中实现蓝牙热敏打印机的功能。该插件支持iOS和Android平台,可以帮助开发者快速构建蓝牙打印应用。

功能特性

以下是 bluetooth_print_urovo 插件的主要功能:

功能 Android iOS 描述
扫描设备 开始扫描蓝牙低功耗设备。
连接设备 建立与设备的连接。
断开连接 取消活动或待定的连接。
设备状态监听 监听设备状态变化。
打印测试消息 打印设备测试消息。
打印文本 打印自定义文本,支持布局设置。
打印图片 打印图片。
打印二维码 打印二维码,支持调整大小。
打印条形码 打印条形码。

使用步骤

1. 添加依赖

pubspec.yaml 文件中添加 bluetooth_print 依赖:

dependencies:
  flutter:
    sdk: flutter
  bluetooth_print: 
2. 添加权限

为了使用蓝牙功能,需要在项目的 AndroidManifest.xmlInfo.plist 文件中添加相应的权限。

Android
android/app/src/main/AndroidManifest.xml 中添加以下权限:

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

iOS
ios/Runner/Info.plist 中添加以下权限:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要蓝牙权限</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要蓝牙权限</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要位置权限</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要位置权限</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要位置权限</string>
3. 初始化 BluetoothPrint 实例

在 Dart 代码中初始化 BluetoothPrint 实例:

import 'package:bluetooth_print/bluetooth_print.dart';
import 'package:bluetooth_print/bluetooth_print_model.dart';

BluetoothPrint bluetoothPrint = BluetoothPrint.instance;
4. 扫描设备

开始扫描附近的蓝牙设备,并显示扫描结果:

bluetoothPrint.startScan(timeout: Duration(seconds: 4));

StreamBuilder<List<BluetoothDevice>>(
  stream: bluetoothPrint.scanResults,
  initialData: [],
  builder: (c, snapshot) => Column(
    children: snapshot.data!.map((d) => ListTile(
      title: Text(d.name ?? ''),
      subtitle: Text(d.address),
      onTap: () async {
        setState(() {
          _device = d;
        });
      },
      trailing: _device != null && _device.address == d.address
          ? Icon(Icons.check, color: Colors.green)
          : null,
    )).toList(),
  ),
),
5. 连接设备

选择设备后,建立连接:

await bluetoothPrint.connect(_device);
6. 断开连接

断开与设备的连接:

await bluetoothPrint.disconnect();
7. 监听设备状态

监听设备的连接状态变化:

bluetoothPrint.state.listen((state) {
  print('当前设备状态: $state');

  switch (state) {
    case BluetoothPrint.CONNECTED:
      setState(() {
        _connected = true;
      });
      break;
    case BluetoothPrint.DISCONNECTED:
      setState(() {
        _connected = false;
      });
      break;
    default:
      break;
  }
});
8. 打印收据(ESC命令模式)

使用ESC命令模式打印收据:

Map<String, dynamic> config = Map();
List<LineText> list = [];

list.add(LineText(type: LineText.TYPE_TEXT, content: '*************************', weight: 1, align: LineText.ALIGN_CENTER, linefeed: 1));
list.add(LineText(type: LineText.TYPE_TEXT, content: '打印单据头', weight: 1, align: LineText.ALIGN_CENTER, fontZoom: 2, linefeed: 1));
list.add(LineText(linefeed: 1));

list.add(LineText(type: LineText.TYPE_TEXT, content: '物资名称规格型号', align: LineText.ALIGN_LEFT, absolutePos: 0, relativePos: 0, linefeed: 0));
list.add(LineText(type: LineText.TYPE_TEXT, content: '单位', align: LineText.ALIGN_LEFT, absolutePos: 350, relativePos: 0, linefeed: 0));
list.add(LineText(type: LineText.TYPE_TEXT, content: '数量', align: LineText.ALIGN_LEFT, absolutePos: 500, relativePos: 0, linefeed: 1));

list.add(LineText(type: LineText.TYPE_TEXT, content: '混凝土C30', align: LineText.ALIGN_LEFT, absolutePos: 0, relativePos: 0, linefeed: 0));
list.add(LineText(type: LineText.TYPE_TEXT, content: '吨', align: LineText.ALIGN_LEFT, absolutePos: 350, relativePos: 0, linefeed: 0));
list.add(LineText(type: LineText.TYPE_TEXT, content: '12.0', align: LineText.ALIGN_LEFT, absolutePos: 500, relativePos: 0, linefeed: 1));

list.add(LineText(type: LineText.TYPE_TEXT, content: '*************************', weight: 1, align: LineText.ALIGN_CENTER, linefeed: 1));
list.add(LineText(linefeed: 1));

ByteData data = await rootBundle.load("assets/images/bluetooth_print.png");
List<int> imageBytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
String base64Image = base64Encode(imageBytes);
// list.add(LineText(type: LineText.TYPE_IMAGE, content: base64Image, align: LineText.ALIGN_CENTER, linefeed: 1));

await bluetoothPrint.printReceipt(config, list);
9. 打印标签(TSC命令模式)

使用TSC命令模式打印标签:

Map<String, dynamic> config = Map();
config['width'] = 40; // 标签宽度,单位mm
config['height'] = 70; // 标签高度,单位mm
config['gap'] = 2; // 标签间隔,单位mm

List<LineText> list = [];
list.add(LineText(type: LineText.TYPE_TEXT, x: 10, y: 10, content: 'A Title'));
list.add(LineText(type: LineText.TYPE_TEXT, x: 10, y: 40, content: 'this is content'));
list.add(LineText(type: LineText.TYPE_QRCODE, x: 10, y: 70, content: 'qrcode i\n'));
list.add(LineText(type: LineText.TYPE_BARCODE, x: 10, y: 190, content: 'qrcode i\n'));

List<LineText> list1 = [];
ByteData data = await rootBundle.load("assets/images/guide3.png");
List<int> imageBytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
String base64Image = base64Encode(imageBytes);
list1.add(LineText(type: LineText.TYPE_IMAGE, x: 10, y: 10, content: base64Image));

await bluetoothPrint.printLabel(config, list);
await bluetoothPrint.printLabel(config, list1);
10. 打印自测试页

打印设备的自测试页:

await bluetoothPrint.printTest();

完整示例代码

以下是一个完整的示例代码,展示了如何使用 bluetooth_print_urovo 插件进行蓝牙设备扫描、连接、打印等功能:

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  BluetoothPrint bluetoothPrint = BluetoothPrint.instance;

  bool _connected = false;
  BluetoothDevice? _device;
  String tips = '未连接设备';

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

    WidgetsBinding.instance.addPostFrameCallback((_) => initBluetooth());
  }

  // 初始化蓝牙
  Future<void> initBluetooth() async {
    bluetoothPrint.startScan(timeout: Duration(seconds: 4));

    bool isConnected = await bluetoothPrint.isConnected ?? false;

    bluetoothPrint.state.listen((state) {
      print('当前设备状态: $state');

      switch (state) {
        case BluetoothPrint.CONNECTED:
          setState(() {
            _connected = true;
            tips = '连接成功';
          });
          break;
        case BluetoothPrint.DISCONNECTED:
          setState(() {
            _connected = false;
            tips = '断开连接';
          });
          break;
        default:
          break;
      }
    });

    if (!mounted) return;

    if (isConnected) {
      setState(() {
        _connected = true;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('蓝牙打印示例'),
        ),
        body: RefreshIndicator(
          onRefresh: () =>
              bluetoothPrint.startScan(timeout: Duration(seconds: 4)),
          child: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
                      child: Text(tips),
                    ),
                  ],
                ),
                Divider(),
                StreamBuilder<List<BluetoothDevice>>(
                  stream: bluetoothPrint.scanResults,
                  initialData: [],
                  builder: (c, snapshot) => Column(
                    children: snapshot.data!.map((d) => ListTile(
                      title: Text(d.name ?? ''),
                      subtitle: Text(d.address ?? ''),
                      onTap: () async {
                        setState(() {
                          _device = d;
                        });
                      },
                      trailing: _device != null && _device!.address == d.address
                          ? Icon(Icons.check, color: Colors.green)
                          : null,
                    )).toList(),
                  ),
                ),
                Divider(),
                Container(
                  padding: EdgeInsets.fromLTRB(20, 5, 20, 10),
                  child: Column(
                    children: <Widget>[
                      Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          OutlinedButton(
                            child: Text('连接'),
                            onPressed: _connected
                                ? null
                                : () async {
                                    if (_device != null &&
                                        _device!.address != null) {
                                      setState(() {
                                        tips = '正在连接...';
                                      });
                                      await bluetoothPrint.connect(_device!);
                                    } else {
                                      setState(() {
                                        tips = '请选择设备';
                                      });
                                      print('请选择设备');
                                    }
                                  },
                          ),
                          SizedBox(width: 10.0),
                          OutlinedButton(
                            child: Text('断开连接'),
                            onPressed: _connected
                                ? () async {
                                    setState(() {
                                      tips = '正在断开连接...';
                                    });
                                    await bluetoothPrint.disconnect();
                                  }
                                : null,
                          ),
                        ],
                      ),
                      Divider(),
                      OutlinedButton(
                        child: Text('打印收据(ESC)'),
                        onPressed: _connected
                            ? () async {
                                Map<String, dynamic> config = Map();
                                List<LineText> list = [];

                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '*************************',
                                    weight: 1,
                                    align: LineText.ALIGN_CENTER,
                                    linefeed: 1));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '打印单据头',
                                    weight: 1,
                                    align: LineText.ALIGN_CENTER,
                                    fontZoom: 2,
                                    linefeed: 1));
                                list.add(LineText(linefeed: 1));

                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '物资名称规格型号',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 0,
                                    relativePos: 0,
                                    linefeed: 0));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '单位',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 350,
                                    relativePos: 0,
                                    linefeed: 0));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '数量',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 500,
                                    relativePos: 0,
                                    linefeed: 1));

                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '混凝土C30',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 0,
                                    relativePos: 0,
                                    linefeed: 0));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '吨',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 350,
                                    relativePos: 0,
                                    linefeed: 0));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '12.0',
                                    align: LineText.ALIGN_LEFT,
                                    absolutePos: 500,
                                    relativePos: 0,
                                    linefeed: 1));

                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    content: '*************************',
                                    weight: 1,
                                    align: LineText.ALIGN_CENTER,
                                    linefeed: 1));
                                list.add(LineText(linefeed: 1));

                                ByteData data =
                                    await rootBundle.load("assets/images/bluetooth_print.png");
                                List<int> imageBytes = data.buffer.asUint8List(
                                    data.offsetInBytes, data.lengthInBytes);
                                String base64Image = base64Encode(imageBytes);
                                // list.add(LineText(
                                //     type: LineText.TYPE_IMAGE,
                                //     content: base64Image,
                                //     align: LineText.ALIGN_CENTER,
                                //     linefeed: 1));

                                await bluetoothPrint.printReceipt(config, list);
                              }
                            : null,
                      ),
                      OutlinedButton(
                        child: Text('打印标签(TSC)'),
                        onPressed: _connected
                            ? () async {
                                Map<String, dynamic> config = Map();
                                config['width'] = 40; // 标签宽度,单位mm
                                config['height'] = 70; // 标签高度,单位mm
                                config['gap'] = 2; // 标签间隔,单位mm

                                List<LineText> list = [];
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    x: 10,
                                    y: 10,
                                    content: 'A Title'));
                                list.add(LineText(
                                    type: LineText.TYPE_TEXT,
                                    x: 10,
                                    y: 40,
                                    content: 'this is content'));
                                list.add(LineText(
                                    type: LineText.TYPE_QRCODE,
                                    x: 10,
                                    y: 70,
                                    content: 'qrcode i\n'));
                                list.add(LineText(
                                    type: LineText.TYPE_BARCODE,
                                    x: 10,
                                    y: 190,
                                    content: 'qrcode i\n'));

                                List<LineText> list1 = [];
                                ByteData data =
                                    await rootBundle.load("assets/images/guide3.png");
                                List<int> imageBytes = data.buffer.asUint8List(
                                    data.offsetInBytes, data.lengthInBytes);
                                String base64Image = base64Encode(imageBytes);
                                list1.add(LineText(
                                    type: LineText.TYPE_IMAGE,
                                    x: 10,
                                    y: 10,
                                    content: base64Image));

                                await bluetoothPrint.printLabel(config, list);
                                await bluetoothPrint.printLabel(config, list1);
                              }
                            : null,
                      ),
                      OutlinedButton(
                        child: Text('打印自测试页'),
                        onPressed: _connected
                            ? () async {
                                await bluetoothPrint.printTest();
                              }
                            : null,
                      )
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
        floatingActionButton: StreamBuilder<bool>(
          stream: bluetoothPrint.isScanning,
          initialData: false,
          builder: (c, snapshot) {
            if (snapshot.data == true) {
              return FloatingActionButton(
                child: Icon(Icons.stop),
                onPressed: () => bluetoothPrint.stopScan(),
                backgroundColor: Colors.red,
              );
            } else {
              return FloatingActionButton(
                  child: Icon(Icons.search),
                  onPressed: () =>
                      bluetoothPrint.startScan(timeout: Duration(seconds: 4)));
            }
          },
        ),
      ),
    );
  }
}

故障排除

  • iOS导入第三方库问题:如果遇到CocoaPods相关的问题,请确保在 .podspec 文件中正确配置了第三方库。
  • CBCentralManager状态恢复错误:如果出现“State restoration of CBCentralManager is only allowed for applications that have specified the ‘bluetooth-central’ background mode”错误,请在 Info.plist 中添加以下内容:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>允许应用使用蓝牙</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>允许应用使用蓝牙</string>
<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <string>bluetooth-peripheral</string>
</array>

更多关于Flutter蓝牙打印插件bluetooth_print_urovo的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter蓝牙打印插件bluetooth_print_urovo的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用bluetooth_print_urovo插件的一个简单示例。这个插件通常用于与Urovo品牌的蓝牙打印机进行通信和打印。

首先,确保你已经在pubspec.yaml文件中添加了bluetooth_print_urovo依赖:

dependencies:
  flutter:
    sdk: flutter
  bluetooth_print_urovo: ^最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤使用这个插件:

  1. 初始化插件: 在你的主文件(例如main.dart)中,导入插件并初始化。
import 'package:flutter/material.dart';
import 'package:bluetooth_print_urovo/bluetooth_print_urovo.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bluetooth Print Example'),
        ),
        body: BluetoothPrintExample(),
      ),
    );
  }
}

class BluetoothPrintExample extends StatefulWidget {
  @override
  _BluetoothPrintExampleState createState() => _BluetoothPrintExampleState();
}

class _BluetoothPrintExampleState extends State<BluetoothPrintExample> {
  late BluetoothPrintUrovo bluetoothPrint;

  @override
  void initState() {
    super.initState();
    bluetoothPrint = BluetoothPrintUrovo();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          TextButton(
            onPressed: () async {
              await scanAndConnect();
            },
            child: Text('Scan and Connect to Bluetooth Printer'),
          ),
          TextButton(
            onPressed: () async {
              await printText('Hello, Urovo Bluetooth Printer!');
            },
            child: Text('Print Text'),
          ),
        ],
      ),
    );
  }

  Future<void> scanAndConnect() async {
    // 这里你需要实现扫描蓝牙设备并连接到打印机的逻辑
    // 假设你已经有了设备地址,可以直接连接
    String deviceAddress = 'XX:XX:XX:XX:XX:XX'; // 替换为实际的设备地址
    bool isConnected = await bluetoothPrint.connect(deviceAddress);
    if (isConnected) {
      print('Connected to Bluetooth Printer');
    } else {
      print('Failed to connect to Bluetooth Printer');
    }
  }

  Future<void> printText(String text) async {
    // 打印文本到蓝牙打印机
    bool isPrinted = await bluetoothPrint.printText(text);
    if (isPrinted) {
      print('Text printed successfully');
    } else {
      print('Failed to print text');
    }
  }
}
  1. 处理蓝牙权限和扫描: 在实际应用中,你可能需要处理蓝牙权限请求和设备扫描。这通常涉及使用flutter_bluetooth_serial或其他相关插件进行设备发现。由于bluetooth_print_urovo插件的文档可能不包含完整的扫描逻辑,你可能需要结合其他插件来实现这一功能。

  2. 断开连接: 在打印完成后,别忘了断开与蓝牙打印机的连接以释放资源。

Future<void> disconnect() async {
  bool isDisconnected = await bluetoothPrint.disconnect();
  if (isDisconnected) {
    print('Disconnected from Bluetooth Printer');
  } else {
    print('Failed to disconnect from Bluetooth Printer');
  }
}

请注意,这只是一个基本的示例,实际使用中可能需要根据具体需求进行调整,比如错误处理、UI优化、权限处理等。此外,务必参考bluetooth_print_urovo插件的官方文档和示例代码,以获取最新的API使用方法和最佳实践。

回到顶部