Flutter蓝牙打印插件bluetooth_print_urovo的使用
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.xml
和 Info.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
更多关于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项目中,你可以按照以下步骤使用这个插件:
- 初始化插件:
在你的主文件(例如
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');
}
}
}
-
处理蓝牙权限和扫描: 在实际应用中,你可能需要处理蓝牙权限请求和设备扫描。这通常涉及使用
flutter_bluetooth_serial
或其他相关插件进行设备发现。由于bluetooth_print_urovo
插件的文档可能不包含完整的扫描逻辑,你可能需要结合其他插件来实现这一功能。 -
断开连接: 在打印完成后,别忘了断开与蓝牙打印机的连接以释放资源。
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使用方法和最佳实践。