From a45bbc03c3e98e3273655c120c121ecb6dcf33f9 Mon Sep 17 00:00:00 2001 From: suvaissov Date: Thu, 11 Mar 2021 15:48:58 +0600 Subject: [PATCH] print images --- android/app/build.gradle | 6 +- assets/images/check.png | Bin 0 -> 16206 bytes lib/core/models/setting_model.dart | 6 + lib/core/route_names.dart | 4 + lib/core/router.dart | 12 + lib/main.dart | 6 +- lib/redux/actions/setting_actions.dart | 8 +- lib/redux/constants/setting_const.dart | 3 +- lib/redux/reducers/setting_reducer.dart | 3 +- lib/redux/state/setting_state.dart | 18 +- lib/views/check/image_show_container.dart | 43 ++- lib/views/home/components/popup_menu.dart | 2 +- lib/views/home/home_view.dart | 2 + .../settings/component/setting_item.dart | 37 ++ lib/views/settings/printer/PrinterSelect.dart | 324 ++++++++++++++++++ lib/views/settings/printer/PrinterTest.dart | 139 ++++++++ lib/views/settings/setting_printer_view.dart | 97 ++++++ pubspec.lock | 105 ++++++ pubspec.yaml | 7 +- 19 files changed, 805 insertions(+), 17 deletions(-) create mode 100644 assets/images/check.png create mode 100644 lib/core/models/setting_model.dart create mode 100644 lib/views/settings/component/setting_item.dart create mode 100644 lib/views/settings/printer/PrinterSelect.dart create mode 100644 lib/views/settings/printer/PrinterTest.dart create mode 100644 lib/views/settings/setting_printer_view.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 196e0fe..c91f042 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,7 +34,7 @@ if (keystorePropertiesFile.exists()) { android { - compileSdkVersion 29 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -46,8 +46,8 @@ android { defaultConfig { applicationId "kz.com.aman.kassa" - minSdkVersion 18 - targetSdkVersion 29 + minSdkVersion 21 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/assets/images/check.png b/assets/images/check.png new file mode 100644 index 0000000000000000000000000000000000000000..49b46d4028b62174f694245e0f2df468df84eae5 GIT binary patch literal 16206 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 I&BHMNKQ^VW8vp generateRoute(RouteSettings settings) { routeName: settings.name, viewToShow: ImageShowContainer(data), ); + case SettingsPrinterRoute: + return _getPageRoute( + routeName: settings.name, + viewToShow: SettingPrinterView(), + ); + case SettingsPrinterBTRoute: + return _getPageRoute( + routeName: settings.name, + viewToShow: PrinterSelectView(), + ); default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/main.dart b/lib/main.dart index 385b2c3..1f0d5e6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -44,9 +44,9 @@ class MainApplication extends StatelessWidget { primaryColor: primaryColor, accentColor: yellowColor, scaffoldBackgroundColor: Colors.white, - textTheme: GoogleFonts.latoTextTheme( - Theme.of(context).textTheme, - ) + // textTheme: GoogleFonts.latoTextTheme( + // Theme.of(context).textTheme, + // ) ), debugShowCheckedModeBanner: false, builder: (context, child) => Navigator( diff --git a/lib/redux/actions/setting_actions.dart b/lib/redux/actions/setting_actions.dart index da4953b..3937b17 100644 --- a/lib/redux/actions/setting_actions.dart +++ b/lib/redux/actions/setting_actions.dart @@ -3,7 +3,7 @@ import 'package:aman_kassa_flutter/redux/state/setting_state.dart'; import 'package:meta/meta.dart'; import 'package:redux/redux.dart'; import 'package:redux_thunk/redux_thunk.dart'; - +import 'package:flutter_bluetooth_basic/src/bluetooth_device.dart'; import '../store.dart'; @immutable @@ -23,4 +23,10 @@ ThunkAction changeTradeTypeFromSetting(String tradeType) { return (Store store) async { store.dispatch(SetSettingStateAction(SettingState(tradeType: tradeType ))); }; +} + +ThunkAction selectPrinterFromSetting(BluetoothDevice device) { + return (Store store) async { + store.dispatch(SetSettingStateAction(SettingState(printerBT: device ))); + }; } \ No newline at end of file diff --git a/lib/redux/constants/setting_const.dart b/lib/redux/constants/setting_const.dart index 787527a..6bae39b 100644 --- a/lib/redux/constants/setting_const.dart +++ b/lib/redux/constants/setting_const.dart @@ -3,4 +3,5 @@ const String SettingModeCalc = 'calcMode'; const String SettingTradeTypeGood = 'g'; -const String SettingTradeTypeService = 's'; \ No newline at end of file +const String SettingTradeTypeService = 's'; + diff --git a/lib/redux/reducers/setting_reducer.dart b/lib/redux/reducers/setting_reducer.dart index 5e76b10..9153491 100644 --- a/lib/redux/reducers/setting_reducer.dart +++ b/lib/redux/reducers/setting_reducer.dart @@ -5,6 +5,7 @@ settingReducer(SettingState prevState, SetSettingStateAction action) { final payload = action.settingState; return prevState.copyWith( mode: payload.mode, - tradeType: payload.tradeType + tradeType: payload.tradeType, + printerBT: payload.printerBT ); } diff --git a/lib/redux/state/setting_state.dart b/lib/redux/state/setting_state.dart index dd5bb1a..76f463b 100644 --- a/lib/redux/state/setting_state.dart +++ b/lib/redux/state/setting_state.dart @@ -1,28 +1,35 @@ import 'package:aman_kassa_flutter/redux/constants/setting_const.dart'; import 'package:meta/meta.dart'; +import 'package:flutter_bluetooth_basic/src/bluetooth_device.dart'; + @immutable class SettingState { final String mode; final String tradeType; + final BluetoothDevice printerBT; - SettingState({this.mode, this.tradeType}); + SettingState({this.mode, this.tradeType, this.printerBT}); //read hive factory SettingState.initial(SettingState payload) { return SettingState( mode: payload?.mode ?? SettingModeKassa, - tradeType: payload?.tradeType ?? SettingTradeTypeGood); + tradeType: payload?.tradeType ?? SettingTradeTypeGood, + printerBT: payload?.printerBT ?? null + ); } //write hive SettingState copyWith({ @required mode, @required tradeType, + @required printerBT, }) { return SettingState( mode: mode ?? this.mode, tradeType: tradeType ?? this.tradeType, + printerBT: printerBT ?? this.printerBT ); } @@ -31,11 +38,16 @@ class SettingState { ? SettingState( tradeType: json['tradeType'], mode: json['mode'], + printerBT: json['printerBT']!=null ? BluetoothDevice.fromJson(json['printerBT']) : null ) : null; } dynamic toJson() { - return {"tradeType": tradeType, "mode": mode}; + return { + "tradeType": tradeType, + "mode": mode, + "printerBT": printerBT !=null ? printerBT.toJson() : null + }; } } diff --git a/lib/views/check/image_show_container.dart b/lib/views/check/image_show_container.dart index 93be746..b47ecd5 100644 --- a/lib/views/check/image_show_container.dart +++ b/lib/views/check/image_show_container.dart @@ -5,10 +5,15 @@ import 'package:aman_kassa_flutter/core/models/dialog_models.dart'; import 'package:aman_kassa_flutter/core/route_names.dart'; import 'package:aman_kassa_flutter/core/services/dialog_service.dart'; import 'package:aman_kassa_flutter/core/services/navigator_service.dart'; +import 'package:aman_kassa_flutter/redux/store.dart'; import 'package:aman_kassa_flutter/shared/app_colors.dart'; import 'package:aman_kassa_flutter/shared/ui_helpers.dart'; +import 'package:aman_kassa_flutter/views/settings/printer/PrinterTest.dart'; import 'package:aman_kassa_flutter/widgets/fields/busy_button_icon.dart'; +import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; +import 'package:esc_pos_utils/esc_pos_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_basic/flutter_bluetooth_basic.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -59,9 +64,18 @@ class _MyFloatingActionButtonState extends State { bool showFab = true; DialogService _dialog = locator(); NavigatorService _navigatorService = locator(); + PrinterBluetoothManager printerManager = PrinterBluetoothManager(); + final DialogService _dialogService = locator(); + BluetoothDevice printerBtDevice = Redux.store.state.settingState.printerBT; + + double sheetHeight = 260; @override Widget build(BuildContext context) { + if ( printerBtDevice != null){ + sheetHeight = 340; + } + return showFab ? FloatingActionButton( child: Icon(Icons.share), @@ -79,7 +93,7 @@ class _MyFloatingActionButtonState extends State { color: Colors.grey[300], spreadRadius: 5) ]), - height: 260, + height: sheetHeight, child: Column( children: [ verticalSpaceSmall, @@ -102,9 +116,17 @@ class _MyFloatingActionButtonState extends State { BusyButtonIcon( title: 'Поделиться', onPressed: shareFile, - mainColor: yellowColor, + mainColor: redColor, icon: Icons.share, ), + verticalSpaceSmall, + BusyButtonIcon( + title: 'Печать', + onPressed: printFile, + mainColor: primaryColor, + icon: Icons.print, + ), + ], ))); showFoatingActionButton(false); @@ -125,6 +147,23 @@ class _MyFloatingActionButtonState extends State { } } + void printFile() async { + Navigator.of(context).pop(false); + + printerManager.selectPrinter(PrinterBluetooth(Redux.store.state.settingState.printerBT)); + // TODO Don't forget to choose printer's paper + const PaperSize paper = PaperSize.mm58; + final PosPrintResult res = + await printerManager.printTicket( + await printImageCheck(paper,widget.data.data ), + chunkSizeBytes: 3096, + queueSleepTimeMs: 50 + ); + if(res.value != 1) { + _dialogService.showDialog(description: res.msg); + } + } + void qrGenerate() async { _navigatorService.push(QrViewRoute, arguments: diff --git a/lib/views/home/components/popup_menu.dart b/lib/views/home/components/popup_menu.dart index e588169..fad97bf 100644 --- a/lib/views/home/components/popup_menu.dart +++ b/lib/views/home/components/popup_menu.dart @@ -7,7 +7,7 @@ const List choices = const [ //const Choice(title: 'Помощь', icon: Icons.help, command: 'help'), const Choice( title: 'Информация о ККМ', icon: Icons.info_outline, command: 'infokkm'), - //const Choice(title: 'Язык', icon: Icons.language, command: 'language'), + const Choice(title: 'Принтер', icon: Icons.print, command: 'print'), const Choice(title: 'Выйти', icon: Icons.exit_to_app, command: 'exit') ]; diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart index 72aab32..f99dc09 100644 --- a/lib/views/home/home_view.dart +++ b/lib/views/home/home_view.dart @@ -64,6 +64,8 @@ class _HomeViewState extends State { Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop(); } else if (choice.command == 'infokkm') { _navigatorService.push(InfoKkmViewRoute); + } else if (choice.command == 'print') { + _navigatorService.push(SettingsPrinterRoute); } } diff --git a/lib/views/settings/component/setting_item.dart b/lib/views/settings/component/setting_item.dart new file mode 100644 index 0000000..7f81896 --- /dev/null +++ b/lib/views/settings/component/setting_item.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +class SettingItem extends StatefulWidget { + + final String name; + final String value; + final String title; + final Function onTap; + + SettingItem({Key key, this.name, this.value, this.onTap, this.title }) : super(key: key); + + @override + _SettingItemState createState() => _SettingItemState(); +} + +class _SettingItemState extends State { + @override + Widget build(BuildContext context) { + return Card( + child: ListTile( + title: Text(widget.title), + subtitle: Text.rich( + TextSpan( + text: widget.name, + style: TextStyle(fontWeight: FontWeight.w500), + children: [ + if(widget.value !=null) + TextSpan(text: ' ${widget.value}', style: TextStyle(fontStyle: FontStyle.italic)), + ], + ) + ), + trailing: Icon(Icons.chevron_right), + onTap: widget.onTap, + ), + ); + } +} \ No newline at end of file diff --git a/lib/views/settings/printer/PrinterSelect.dart b/lib/views/settings/printer/PrinterSelect.dart new file mode 100644 index 0000000..d790aa2 --- /dev/null +++ b/lib/views/settings/printer/PrinterSelect.dart @@ -0,0 +1,324 @@ +import 'dart:async'; +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:aman_kassa_flutter/core/logger.dart'; +import 'package:aman_kassa_flutter/redux/actions/setting_actions.dart'; +import 'package:aman_kassa_flutter/redux/store.dart'; + +import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; +import 'package:esc_pos_utils/esc_pos_utils.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart' hide Image; +import 'package:flutter/rendering.dart'; + + +import 'package:flutter_bluetooth_basic/flutter_bluetooth_basic.dart'; +import 'package:intl/intl.dart'; +import 'package:logger/logger.dart'; + +import 'PrinterTest.dart'; + + +class PrinterSelectView extends StatefulWidget { + PrinterSelectView({Key key, this.title}) : super(key: key); + final String title; + + @override + _PrinterSelectViewState createState() => _PrinterSelectViewState(); + +} + +class _PrinterSelectViewState extends State { + PrinterBluetoothManager printerManager = PrinterBluetoothManager(); + List _devices = []; + Logger _logger = getLogger('PrinterSelectView'); + + @override + void initState() { + super.initState(); + + printerManager.scanResults.listen((devices) async { + // print('UI: Devices found ${devices.length}'); + setState(() { + _devices = devices; + }); + }); + _startScanDevices(); + } + + void _startScanDevices() { + setState(() { + _devices = []; + }); + printerManager.startScan(Duration(seconds: 4)); + } + + void _stopScanDevices() { + printerManager.stopScan(); + } + + Future demoReceipt(PaperSize paper) async { + final Ticket ticket = Ticket(paper, ); + + // Print image + // final ByteData data = await rootBundle.load('assets/images/aman_kassa_check.png'); + // final Uint8List bytes = data.buffer.asUint8List(); + // final Im.Image image = Im.decodeImage(bytes); + // Im.Image thumbnail = Im.copyResize(image, width: 270); + // ticket.image(thumbnail, align: PosAlign.center); + + //ticket.imageRaster(image, align: PosAlign.center); + + ticket.text('AMAN-SATU', + styles: PosStyles( + align: PosAlign.center, + height: PosTextSize.size2, + width: PosTextSize.size2, + ), + linesAfter: 1); + + ticket.text('889 Watson Lane', styles: PosStyles(align: PosAlign.center)); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, codeTable: PosCodeTable.westEur), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontA), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontB), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, height: PosTextSize.size1), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size2), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size3), containsChinese: true); + ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size4), containsChinese: true); + ticket.text('Tel: 830-221-1234', styles: PosStyles(align: PosAlign.center)); + ticket.text('Web: www.example.com', + styles: PosStyles(align: PosAlign.center), linesAfter: 1); + + ticket.hr(); + ticket.row([ + PosColumn(text: 'Qty', width: 1), + PosColumn(text: 'Item', width: 7), + PosColumn( + text: 'Price', width: 2, styles: PosStyles(align: PosAlign.right)), + PosColumn( + text: 'Total', width: 2, styles: PosStyles(align: PosAlign.right)), + ]); + + ticket.row([ + PosColumn(text: '2', width: 1), + PosColumn(text: 'ONION RINGS', width: 7), + PosColumn( + text: '0.99', width: 2, styles: PosStyles(align: PosAlign.right)), + PosColumn( + text: '1.98', width: 2, styles: PosStyles(align: PosAlign.right)), + ]); + ticket.row([ + PosColumn(text: '1', width: 1), + PosColumn(text: 'PIZZA', width: 7), + PosColumn( + text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), + PosColumn( + text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), + ]); + ticket.row([ + PosColumn(text: '1', width: 1), + PosColumn(text: 'SPRING ROLLS', width: 7), + PosColumn( + text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), + PosColumn( + text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), + ]); + ticket.row([ + PosColumn(text: '3', width: 1), + PosColumn(text: 'CRUNCHY STICKS', width: 7), + PosColumn( + text: '0.85', width: 2, styles: PosStyles(align: PosAlign.right)), + PosColumn( + text: '2.55', width: 2, styles: PosStyles(align: PosAlign.right)), + ]); + ticket.hr(); + + ticket.row([ + PosColumn( + text: 'TOTAL', + width: 6, + styles: PosStyles( + height: PosTextSize.size2, + width: PosTextSize.size2, + )), + PosColumn( + text: '\$10.97', + width: 6, + styles: PosStyles( + align: PosAlign.right, + height: PosTextSize.size2, + width: PosTextSize.size2, + )), + ]); + + ticket.hr(ch: '=', linesAfter: 1); + + ticket.row([ + PosColumn( + text: 'Cash', + width: 7, + styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), + PosColumn( + text: '\$15.00', + width: 5, + styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), + ]); + ticket.row([ + PosColumn( + text: 'Change', + width: 7, + styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), + PosColumn( + text: '\$4.03', + width: 5, + styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), + ]); + + ticket.feed(2); + ticket.text('Thank you!', + styles: PosStyles(align: PosAlign.center, bold: true)); + + final now = DateTime.now(); + final formatter = DateFormat('MM/dd/yyyy H:m'); + final String timestamp = formatter.format(now); + ticket.text(timestamp, + styles: PosStyles(align: PosAlign.center), linesAfter: 2); + + // Print QR Code from image + // try { + // const String qrData = 'example.com'; + // const double qrSize = 200; + // final uiImg = await QrPainter( + // data: qrData, + // version: QrVersions.auto, + // gapless: false, + // ).toImageData(qrSize); + // final dir = await getTemporaryDirectory(); + // final pathName = '${dir.path}/qr_tmp.png'; + // final qrFile = File(pathName); + // final imgFile = await qrFile.writeAsBytes(uiImg.buffer.asUint8List()); + // final img = decodeImage(imgFile.readAsBytesSync()); + + // ticket.image(img); + // } catch (e) { + // print(e); + // } + + // Print QR Code using native function + // ticket.qrcode('example.com'); + + ticket.feed(2); + ticket.cut(); + return ticket; + } + + + + void _selectPrinter(PrinterBluetooth printer, BuildContext context, ) async { + printerManager.selectPrinter(printer); + _logger.i(printer.name); + _logger.i(printer.address); + + BluetoothDevice device = new BluetoothDevice() + ..address = printer.address + ..name=printer.name + ..type=printer.type; + + await Redux.store.dispatch(selectPrinterFromSetting(device)); + Navigator.of(context).pop(false); + } + + void _testPrint(PrinterBluetooth printer) async { + printerManager.selectPrinter(printer); + + // TODO Don't forget to choose printer's paper + const PaperSize paper = PaperSize.mm58; + + // TEST PRINT + // final PosPrintResult res = + // await printerManager.printTicket(await testTicket(paper), queueSleepTimeMs: 50); + + final PosPrintResult res = + await printerManager.printTicket( + await testTicketImage(paper), + chunkSizeBytes: 1024, + queueSleepTimeMs: 50 + ); + + // DEMO RECEIPT + // final PosPrintResult res = + // await printerManager.printTicket(await demoReceipt(paper) , queueSleepTimeMs: 50); + + } + + final key = GlobalKey(); + + @override + Widget build(BuildContext context) { + return RepaintBoundary( + key: key, + child: Scaffold( + appBar: AppBar( + title: Text('Выберите принтер'), + ), + body: ListView.builder( + itemCount: _devices.length, + itemBuilder: (BuildContext _, int index) { + return InkWell( + onTap: () => _selectPrinter(_devices[index], context), + child: Column( + children: [ + Container( + height: 60, + padding: EdgeInsets.only(left: 10), + alignment: Alignment.centerLeft, + child: Row( + children: [ + Icon(Icons.print), + SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(_devices[index].name ?? ''), + Text(_devices[index].address), + Text( + 'Click to print a test receipt', + style: TextStyle(color: Colors.grey[700]), + ), + ], + ), + ) + ], + ), + ), + Divider(), + ], + ), + ); + }), + floatingActionButton: StreamBuilder( + stream: printerManager.isScanningStream, + initialData: false, + builder: (c, snapshot) { + if (snapshot.data) { + return FloatingActionButton( + child: Icon(Icons.stop), + onPressed: _stopScanDevices, + backgroundColor: Colors.red, + ); + } else { + return FloatingActionButton( + child: Icon(Icons.search), + onPressed: _startScanDevices, + ); + } + }, + ), + ), + ); + } + +} \ No newline at end of file diff --git a/lib/views/settings/printer/PrinterTest.dart b/lib/views/settings/printer/PrinterTest.dart new file mode 100644 index 0000000..f42e0f8 --- /dev/null +++ b/lib/views/settings/printer/PrinterTest.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:esc_pos_utils/esc_pos_utils.dart'; +import 'package:flutter/services.dart'; +import 'package:image/image.dart' as Im; + +Future testTicket(PaperSize paper) async { + final Ticket ticket = Ticket(paper); + + //Uint8List encTxt11 = await CharsetConverter.encode("cp866", "Russian: Привет Мир!"); + //ticket.textEncoded(encTxt11, styles: PosStyles(codeTable: PosCodeTable.pc866_2)); + //ticket.textEncoded(encTxt11); + + // ticket.text('Special 1: àÀ', styles: PosStyles(codeTable: PosCodeTable.westEur)); //А + // ticket.text('Special 1: á'.toUpperCase(), styles: PosStyles(codeTable: PosCodeTable.westEur));// Б + // ticket.text('Special 1: â', styles: PosStyles(codeTable: PosCodeTable.westEur)); //В + // ticket.text('Special 1: ã', styles: PosStyles(codeTable: PosCodeTable.westEur));// Г + // ticket.text('Special 1: äÄ', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Д + // ticket.text('Special 1: å', styles: PosStyles(codeTable: PosCodeTable.westEur));// Е + // ticket.text('Special 1: æÆ', styles: PosStyles(codeTable: PosCodeTable.westEur));// Ж + // ticket.text('Special 1: ç', styles: PosStyles(codeTable: PosCodeTable.westEur));//З + // ticket.text('Special 1: èÈ', styles: PosStyles(codeTable: PosCodeTable.westEur)); // И + // ticket.text('Special 1: éÉ', styles: PosStyles(codeTable: PosCodeTable.westEur)); // Й + // ticket.text('Special 1: ê', styles: PosStyles(codeTable: PosCodeTable.westEur));//К + // ticket.text('Special 1: ëË', styles: PosStyles(codeTable: PosCodeTable.westEur)); // Л + // ticket.text('Special 1: ìÌ', styles: PosStyles(codeTable: PosCodeTable.westEur));// M + // ticket.text('Special 1: íÍ', styles: PosStyles(codeTable: PosCodeTable.westEur)); // Н + // ticket.text('Special 1: î', styles: PosStyles(codeTable: PosCodeTable.westEur));// О + // ticket.text('Special 1: ï', styles: PosStyles(codeTable: PosCodeTable.westEur)); // П + // ticket.text('Special 1: ð', styles: PosStyles(codeTable: PosCodeTable.westEur));// Р + // ticket.text('Special 1: ñ', styles: PosStyles(codeTable: PosCodeTable.westEur));// С + // ticket.text('Special 1: ò', styles: PosStyles(codeTable: PosCodeTable.westEur)); // Т + // ticket.text('Special 1: óÓ', styles: PosStyles(codeTable: PosCodeTable.westEur)); //У + // ticket.text('Special 1: ô', styles: PosStyles(codeTable: PosCodeTable.westEur));// Ф + // ticket.text('Special 1: õÕ', styles: PosStyles(codeTable: PosCodeTable.westEur));// Х + // ticket.text('Special 1: ö', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Ц + // ticket.text('Special 1: ÷', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Ч + // ticket.text('Special 1: ø', styles: PosStyles(codeTable: PosCodeTable.westEur));//Ш + // ticket.text('Special 1: ù', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Щ + // ticket.text('Special 1: ú', styles: PosStyles(codeTable: PosCodeTable.westEur));//Ъ + // ticket.text('Special 1: û', styles: PosStyles(codeTable: PosCodeTable.westEur));//Ы + // ticket.text('Special 1: üÜ', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Ь + // ticket.text('Special 1: ý', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Э + // ticket.text('Special 1: þ', styles: PosStyles(codeTable: PosCodeTable.westEur)); // ю + // ticket.text('Special 1: ÿß', styles: PosStyles(codeTable: PosCodeTable.westEur)); //Я + + // Uint8List encTxt11 = await CharsetConverter.encode("cp866", "Russian: Привет Мир!"); + // //ticket.textEncoded(encTxt11, styles: PosStyles(codeTable: PosCodeTable.pc866_2)); + // ticket.textEncoded(encTxt11); + + ticket.text( + 'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'); + //ticket.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ', styles: PosStyles(codeTable: PosCodeTable.westEur)); + //ticket.text('Special 2: blåbærgrød', styles: PosStyles(codeTable: PosCodeTable.westEur)); + + ticket.text('Bold text', styles: PosStyles(bold: true)); + ticket.text('Reverse text', styles: PosStyles(reverse: true)); + ticket.text('Underlined text', + styles: PosStyles(underline: true), linesAfter: 1); + ticket.text('Align left', styles: PosStyles(align: PosAlign.left)); + ticket.text('Align center', styles: PosStyles(align: PosAlign.center)); + ticket.text('Align right', + styles: PosStyles(align: PosAlign.right), linesAfter: 1); + + ticket.row([ + PosColumn( + text: 'col3', + width: 3, + styles: PosStyles(align: PosAlign.center, underline: true), + ), + PosColumn( + text: 'col6', + width: 6, + styles: PosStyles(align: PosAlign.center, underline: true), + ), + PosColumn( + text: 'col3', + width: 3, + styles: PosStyles(align: PosAlign.center, underline: true), + ), + ]); + + ticket.text('Text size 200%', + styles: PosStyles( + height: PosTextSize.size2, + width: PosTextSize.size2, + )); + + // Print image + //final ByteData data = await rootBundle.load('assets/images/logo.png'); + //final Uint8List bytes = data.buffer.asUint8List(); + // Print image using alternative commands + // ticket.imageRaster(image); + // ticket.imageRaster(image, imageFn: PosImageFn.graphics); + + // Print barcode + final List barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4]; + ticket.barcode(Barcode.upcA(barData)); + + + + ticket.feed(2); + + ticket.cut(); + return ticket; +} + +Future testTicketImage(PaperSize paper) async { + final Ticket ticket = Ticket(paper); + + // Print image + final ByteData byteData = await rootBundle.load('assets/images/check.png'); + final Uint8List bytes = byteData.buffer.asUint8List(); + final Im.Image imagea = Im.decodeImage(bytes); + // Using `ESC *` + //ticket.image(imagea); + // Using `GS v 0` (obsolete) + //ticket.imageRaster(imagea); + // Using `GS ( L` + ticket.imageRaster(imagea, imageFn: PosImageFn.bitImageRaster); + + + + ticket.feed(2); + + ticket.cut(); + return ticket; +} + +Future printImageCheck(PaperSize paper, String base64Src) async { + final Ticket ticket = Ticket(paper); + final Uint8List bytes = base64Decode(base64Src); + final Im.Image image = Im.decodeImage(bytes); + ticket.imageRaster(image, imageFn: PosImageFn.bitImageRaster); + ticket.feed(2); + ticket.cut(); + return ticket; +} \ No newline at end of file diff --git a/lib/views/settings/setting_printer_view.dart b/lib/views/settings/setting_printer_view.dart new file mode 100644 index 0000000..76401ed --- /dev/null +++ b/lib/views/settings/setting_printer_view.dart @@ -0,0 +1,97 @@ +import 'dart:typed_data'; + +import 'package:aman_kassa_flutter/core/locator.dart'; +import 'package:aman_kassa_flutter/core/route_names.dart'; +import 'package:aman_kassa_flutter/core/services/dialog_service.dart'; +import 'package:aman_kassa_flutter/core/services/navigator_service.dart'; +import 'package:aman_kassa_flutter/redux/state/setting_state.dart'; +import 'package:aman_kassa_flutter/redux/store.dart'; +import 'package:aman_kassa_flutter/shared/app_colors.dart'; +import 'package:aman_kassa_flutter/widgets/fields/aman_icon_button_horizontal.dart'; +import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; +import 'package:esc_pos_utils/esc_pos_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:aman_kassa_flutter/views/settings/printer/PrinterTest.dart'; +import 'package:flutter_redux/flutter_redux.dart'; + +import 'component/setting_item.dart'; + +class SettingPrinterView extends StatefulWidget { + @override + _SettingPrinterViewState createState() => _SettingPrinterViewState(); +} + +class _SettingPrinterViewState extends State { + NavigatorService _navigatorService = locator(); + final DialogService _dialogService = locator(); + PrinterBluetoothManager printerManager = PrinterBluetoothManager(); + + + @override + void initState() { + super.initState(); + } + + + void _testPrint() async { + printerManager.selectPrinter(PrinterBluetooth(Redux.store.state.settingState.printerBT)); + // TODO Don't forget to choose printer's paper + const PaperSize paper = PaperSize.mm58; + final PosPrintResult res = + await printerManager.printTicket( + await testTicketImage(paper), + chunkSizeBytes: 3096, + queueSleepTimeMs: 50 + ); + _dialogService.showDialog(description: res.msg); + + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Настройка принтера'), + ), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: StoreConnector( + converter: (store) => store.state.settingState, + builder: (context, vm) { + return Column( + children: [ + SettingItem( + title: 'Принтер', + name: vm.printerBT?.name, + value: vm.printerBT != null + ? 'BT: ${vm.printerBT.address} ' + : 'не выбран', + onTap: () { + _navigatorService.push(SettingsPrinterBTRoute); + }), + Expanded( + child: Padding( + padding: const EdgeInsets.only(bottom: 24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + AmanIconButtonHorizontal( + icon: Icons.local_printshop_outlined, + title: 'Напечатать тестовую страницу', + activeColor: primaryColor, + selected: vm.printerBT != null, + onPressed: () { + _testPrint(); + }, + ), + ], + ), + ), + ) + ], + ); + }), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 861beca..06ba462 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" async: dependency: transitive description: @@ -43,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0-nullsafety.1" + charset_converter: + dependency: "direct main" + description: + name: charset_converter + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" clock: dependency: transitive description: @@ -71,6 +92,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" cupertino_icons: dependency: "direct main" description: @@ -99,6 +127,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.5" + esc_pos_bluetooth: + dependency: "direct main" + description: + name: esc_pos_bluetooth + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.8" + esc_pos_utils: + dependency: "direct main" + description: + name: esc_pos_utils + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.6" esys_flutter_share: dependency: "direct main" description: @@ -139,6 +181,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bluetooth_basic: + dependency: "direct main" + description: + name: flutter_bluetooth_basic + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" flutter_redux: dependency: "direct main" description: @@ -163,6 +212,13 @@ packages: description: flutter source: sdk version: "0.0.0" + gbk_codec: + dependency: transitive + description: + name: gbk_codec + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" get_it: dependency: "direct main" description: @@ -177,6 +233,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + hex: + dependency: transitive + description: + name: hex + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" http: dependency: "direct main" description: @@ -191,6 +261,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.19" intl: dependency: "direct main" description: @@ -198,6 +275,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.16.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" logger: dependency: "direct main" description: @@ -289,6 +373,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" platform: dependency: transitive description: @@ -373,6 +464,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.23.1" shared_preferences: dependency: transitive description: @@ -553,6 +651,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.1" sdks: dart: ">=2.10.2 <2.11.0" flutter: ">=1.22.2 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index fe85302..a719791 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,10 @@ dependencies: qr_flutter: ^3.2.0 mask_text_input_formatter: ^1.2.1 flutter_screenutil: ^2.3.1 + esc_pos_bluetooth: ^0.2.8 + flutter_bluetooth_basic: ^0.1.5 + esc_pos_utils: ^0.3.6 # no edit for esc_pos_bluetooth: ^0.2.8 + charset_converter: ^1.0.3 dev_dependencies: flutter_test: sdk: flutter @@ -40,8 +44,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - assets/images/logo.png - - assets/images/icon_large.png + - assets/images/ - assets/lang/en.json - assets/lang/ru.json - assets/google_fonts/