bluetooth_thermal_printer

空安全
https://pub.dev/packages/esc_pos_utils_plus
空安全
https://pub.dev/packages/bluetooth_thermal_printer
两个配合, 还是无法打印图片, 结论: 打印机不支持,这个方式.
bluetooth_thermal_printer: ^0.0.6
esc_pos_utils: ^1.0.0

  1. import 'dart:async';
  2. import 'package:bluetooth_thermal_printer/bluetooth_thermal_printer.dart';
  3. import 'package:esc_pos_utils/esc_pos_utils.dart';
  4. import 'package:flutter/material.dart';
  5. void main() {
  6. runApp(MyApp());
  7. }
  8. class MyApp extends StatefulWidget {
  9. @override
  10. _MyAppState createState() => _MyAppState();
  11. }
  12. class _MyAppState extends State<MyApp> {
  13. @override
  14. void initState() {
  15. super.initState();
  16. }
  17. bool connected = false;
  18. List availableBluetoothDevices = [];
  19. Future<void> getBluetooth() async {
  20. final List bluetooths = await BluetoothThermalPrinter.getBluetooths;
  21. print("Print $bluetooths");
  22. setState(() {
  23. availableBluetoothDevices = bluetooths;
  24. });
  25. }
  26. Future<void> setConnect(String mac) async {
  27. final String result = await BluetoothThermalPrinter.connect(mac);
  28. print("state conneected $result");
  29. if (result == "true") {
  30. setState(() {
  31. connected = true;
  32. });
  33. }
  34. }
  35. Future<void> printTicket() async {
  36. String isConnected = await BluetoothThermalPrinter.connectionStatus;
  37. if (isConnected == "true") {
  38. List<int> bytes = await getTicket();
  39. final result = await BluetoothThermalPrinter.writeBytes(bytes);
  40. print("Print $result");
  41. } else {
  42. //Hadnle Not Connected Senario
  43. }
  44. }
  45. Future<void> printGraphics() async {
  46. String isConnected = await BluetoothThermalPrinter.connectionStatus;
  47. if (isConnected == "true") {
  48. List<int> bytes = await getGraphicsTicket();
  49. final result = await BluetoothThermalPrinter.writeBytes(bytes);
  50. print("Print $result");
  51. } else {
  52. //Hadnle Not Connected Senario
  53. }
  54. }
  55. Future<List<int>> getGraphicsTicket() async {
  56. List<int> bytes = [];
  57. CapabilityProfile profile = await CapabilityProfile.load();
  58. final generator = Generator(PaperSize.mm80, profile);
  59. // Print QR Code using native function
  60. bytes += generator.qrcode('example.com');
  61. bytes += generator.hr();
  62. // Print Barcode using native function
  63. final List<int> barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4];
  64. bytes += generator.barcode(Barcode.upcA(barData));
  65. bytes += generator.cut();
  66. return bytes;
  67. }
  68. Future<List<int>> getTicket() async {
  69. List<int> bytes = [];
  70. CapabilityProfile profile = await CapabilityProfile.load();
  71. final generator = Generator(PaperSize.mm80, profile);
  72. // Print image
  73. // final ByteData data = await rootBundle.load('assets/rabbit_black.jpg');
  74. // final Uint8List bytes = data.buffer.asUint8List();
  75. // final Image image = decodeImage(bytes);
  76. // ticket.image(image);
  77. bytes += generator.text(
  78. "////=========================================================================================///",
  79. styles: PosStyles(
  80. align: PosAlign.center,
  81. height: PosTextSize.size2,
  82. width: PosTextSize.size2,
  83. ),
  84. linesAfter: 1);
  85. bytes += generator.text(
  86. "18th Main Road, 2nd Phase, J. P. Nagar, Bengaluru, Karnataka 560078",
  87. styles: PosStyles(align: PosAlign.center));
  88. bytes += generator.text('Tel: +919591708470',
  89. styles: PosStyles(align: PosAlign.center));
  90. bytes += generator.hr();
  91. bytes += generator.row([
  92. PosColumn(
  93. text: 'No',
  94. width: 1,
  95. styles: PosStyles(align: PosAlign.left, bold: true)),
  96. PosColumn(
  97. text: 'Item',
  98. width: 5,
  99. styles: PosStyles(align: PosAlign.left, bold: true)),
  100. PosColumn(
  101. text: 'Price',
  102. width: 2,
  103. styles: PosStyles(align: PosAlign.center, bold: true)),
  104. PosColumn(
  105. text: 'Qty',
  106. width: 2,
  107. styles: PosStyles(align: PosAlign.center, bold: true)),
  108. PosColumn(
  109. text: 'Total',
  110. width: 2,
  111. styles: PosStyles(align: PosAlign.right, bold: true)),
  112. ]);
  113. bytes += generator.row([
  114. PosColumn(text: "1", width: 1),
  115. PosColumn(
  116. text: "Tea",
  117. width: 5,
  118. styles: PosStyles(
  119. align: PosAlign.left,
  120. )),
  121. PosColumn(
  122. text: "10",
  123. width: 2,
  124. styles: PosStyles(
  125. align: PosAlign.center,
  126. )),
  127. PosColumn(text: "1", width: 2, styles: PosStyles(align: PosAlign.center)),
  128. PosColumn(text: "10", width: 2, styles: PosStyles(align: PosAlign.right)),
  129. ]);
  130. bytes += generator.row([
  131. PosColumn(text: "2", width: 1),
  132. PosColumn(
  133. text: "Sada Dosa",
  134. width: 5,
  135. styles: PosStyles(
  136. align: PosAlign.left,
  137. )),
  138. PosColumn(
  139. text: "30",
  140. width: 2,
  141. styles: PosStyles(
  142. align: PosAlign.center,
  143. )),
  144. PosColumn(text: "1", width: 2, styles: PosStyles(align: PosAlign.center)),
  145. PosColumn(text: "30", width: 2, styles: PosStyles(align: PosAlign.right)),
  146. ]);
  147. bytes += generator.row([
  148. PosColumn(text: "3", width: 1),
  149. PosColumn(
  150. text: "Masala Dosa",
  151. width: 5,
  152. styles: PosStyles(
  153. align: PosAlign.left,
  154. )),
  155. PosColumn(
  156. text: "50",
  157. width: 2,
  158. styles: PosStyles(
  159. align: PosAlign.center,
  160. )),
  161. PosColumn(text: "1", width: 2, styles: PosStyles(align: PosAlign.center)),
  162. PosColumn(text: "50", width: 2, styles: PosStyles(align: PosAlign.right)),
  163. ]);
  164. bytes += generator.row([
  165. PosColumn(text: "4", width: 1),
  166. PosColumn(
  167. text: "Rova Dosa",
  168. width: 5,
  169. styles: PosStyles(
  170. align: PosAlign.left,
  171. )),
  172. PosColumn(
  173. text: "70",
  174. width: 2,
  175. styles: PosStyles(
  176. align: PosAlign.center,
  177. )),
  178. PosColumn(text: "1", width: 2, styles: PosStyles(align: PosAlign.center)),
  179. PosColumn(text: "70", width: 2, styles: PosStyles(align: PosAlign.right)),
  180. ]);
  181. bytes += generator.hr();
  182. bytes += generator.row([
  183. PosColumn(
  184. text: 'TOTAL',
  185. width: 6,
  186. styles: PosStyles(
  187. align: PosAlign.left,
  188. height: PosTextSize.size4,
  189. width: PosTextSize.size4,
  190. )),
  191. PosColumn(
  192. text: "160",
  193. width: 6,
  194. styles: PosStyles(
  195. align: PosAlign.right,
  196. height: PosTextSize.size4,
  197. width: PosTextSize.size4,
  198. )),
  199. ]);
  200. bytes += generator.hr(ch: '=', linesAfter: 1);
  201. // ticket.feed(2);
  202. bytes += generator.text('Thank you!',
  203. styles: PosStyles(align: PosAlign.center, bold: true));
  204. bytes += generator.text("26-11-2020 15:22:45",
  205. styles: PosStyles(align: PosAlign.center), linesAfter: 1);
  206. bytes += generator.text(
  207. 'Note: Goods once sold will not be taken back or exchanged.',
  208. styles: PosStyles(align: PosAlign.center, bold: false));
  209. bytes += generator.cut();
  210. return bytes;
  211. }
  212. @override
  213. Widget build(BuildContext context) {
  214. return MaterialApp(
  215. home: Scaffold(
  216. appBar: AppBar(
  217. title: const Text('Bluetooth Thermal Printer Demo'),
  218. ),
  219. body: Container(
  220. padding: EdgeInsets.all(20),
  221. child: Column(
  222. crossAxisAlignment: CrossAxisAlignment.start,
  223. children: [
  224. Text("Search Paired Bluetooth"),
  225. TextButton(
  226. onPressed: () {
  227. this.getBluetooth();
  228. },
  229. child: Text("Search"),
  230. ),
  231. Container(
  232. height: 200,
  233. child: ListView.builder(
  234. itemCount: availableBluetoothDevices.length > 0
  235. ? availableBluetoothDevices.length
  236. : 0,
  237. itemBuilder: (context, index) {
  238. return ListTile(
  239. onTap: () {
  240. String select = availableBluetoothDevices[index];
  241. List list = select.split("#");
  242. // String name = list[0];
  243. String mac = list[1];
  244. this.setConnect(mac);
  245. },
  246. title: Text('${availableBluetoothDevices[index]}'),
  247. subtitle: Text("Click to connect"),
  248. );
  249. },
  250. ),
  251. ),
  252. SizedBox(
  253. height: 30,
  254. ),
  255. TextButton(
  256. onPressed: connected ? this.printGraphics : null,
  257. child: Text("Print"),
  258. ),
  259. TextButton(
  260. onPressed: connected ? this.printTicket : null,
  261. child: Text("Print Ticket"),
  262. ),
  263. ],
  264. ),
  265. ),
  266. ),
  267. );
  268. }
  269. }

esc_pos_bluetooth

非空安全
https://pub.dev/packages/bluetooth_print
测试 无法启动.

网络打印, 不支持蓝牙, pdf 可转图片
https://pub.dev/packages/printing