diff --git a/android/app/build.gradle b/android/app/build.gradle index 013da0a..93cab99 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 29 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -53,7 +53,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "kz.com.aman.satu" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName // multiDexEnabled true diff --git a/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt b/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt index 8ab0f26..83f40dd 100644 --- a/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt +++ b/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt @@ -1,6 +1,12 @@ package kz.com.aman.satu import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + } } diff --git a/android/build.gradle b/android/build.gradle index 3100ad2..c505a86 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146..bc6a58a 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/assets/images/bg_sign.png b/assets/images/bg_sign.png new file mode 100644 index 0000000..4d21f60 Binary files /dev/null and b/assets/images/bg_sign.png differ diff --git a/assets/images/top_bar_bg.png b/assets/images/top_bar_bg.png index 4dfd66a..a89c11f 100644 Binary files a/assets/images/top_bar_bg.png and b/assets/images/top_bar_bg.png differ diff --git a/lib/core/utils/pos_printer.dart b/lib/core/utils/pos_printer.dart new file mode 100644 index 0000000..2e0380f --- /dev/null +++ b/lib/core/utils/pos_printer.dart @@ -0,0 +1,71 @@ +import 'dart:typed_data'; + +import 'package:charset_converter/charset_converter.dart'; +import 'package:esc_pos_utils/esc_pos_utils.dart'; +import 'package:flutter/services.dart'; +import 'dart:io'; +import 'package:image/image.dart'; + + + +Future> getReceipt() async { + final profile = await CapabilityProfile.load(); + final generator = Generator(PaperSize.mm80, profile); + List bytes = []; + generator.setGlobalCodeTable('CP866'); + Uint8List firstCol = await CharsetConverter.encode('cp866', 'Русский'); + + bytes += generator.textEncoded(firstCol, styles: const PosStyles(align: PosAlign.left)); + + bytes += + generator.text('Align center', styles: const PosStyles(align: PosAlign.center)); + bytes += generator.text('Align right', + styles: const PosStyles(align: PosAlign.right), linesAfter: 1); + + bytes += generator.row([ + PosColumn( + text: 'col3', + width: 3, + styles: const PosStyles(align: PosAlign.center, underline: true), + ), + PosColumn( + text: 'col6', + width: 6, + styles: const PosStyles(align: PosAlign.center, underline: true), + ), + PosColumn( + text: 'col3', + width: 3, + styles: const PosStyles(align: PosAlign.center, underline: true), + ), + ]); + + // bytes += generator.text('Text size 200%', + // styles: const PosStyles( + // height: PosTextSize.size2, + // width: PosTextSize.size2, + // )); + // + // // Print barcode + // final List barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4]; + // bytes += generator.barcode(Barcode.upcA(barData)); + + bytes += generator.feed(2); + bytes += generator.cut(); + return bytes; +} + +Future> getReceiptImg() async { + final profile = await CapabilityProfile.load(); + final generator = Generator(PaperSize.mm80, profile); + List bytes = []; + generator.setGlobalCodeTable('CP866'); + final ByteData data = await rootBundle.load('assets/images/aman_kassa_check.png') as ByteData; + final Uint8List imgBytes = data.buffer.asUint8List(); + final Image? image = decodeImage(imgBytes); +// Using `ESC *` + bytes += generator.image(image!); + bytes += generator.feed(2); + bytes += generator.cut(); + return bytes; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index ec1015e..80cfe63 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -18,6 +20,8 @@ void main() async { //initialize locator await LocatorInjector.setupLocator(); + HttpOverrides.global = MyHttpOverrides(); + LicenseRegistry.addLicense(() async* { final license = await rootBundle.loadString('assets/google_fonts/OFL.txt'); yield LicenseEntryWithLineBreaks(['google_fonts'], license); @@ -27,12 +31,9 @@ void main() async { runApp(MainApplication()); } - class MainApplication extends StatelessWidget { - @override Widget build(BuildContext context) { - return StoreProvider( store: Redux.store!, child: ScreenUtilInit( @@ -46,7 +47,7 @@ class MainApplication extends StatelessWidget { // textTheme: GoogleFonts.robotoTextTheme( // Theme.of(context).textTheme, // ) - ), + ), debugShowCheckedModeBanner: false, builder: (context, child) => Navigator( key: locator().dialogNavigationKey, @@ -54,7 +55,8 @@ class MainApplication extends StatelessWidget { builder: (context) => DialogManager(child: child!)), ), navigatorKey: locator().navigatorKey, - home: StartUpView(), // first page + home: StartUpView(), + // first page onGenerateRoute: generateRoute, ), ), @@ -62,3 +64,11 @@ class MainApplication extends StatelessWidget { } } +class MyHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + ..badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + } +} diff --git a/lib/routes/route_names.dart b/lib/routes/route_names.dart index 377d7fd..90a54ec 100644 --- a/lib/routes/route_names.dart +++ b/lib/routes/route_names.dart @@ -9,6 +9,10 @@ const String contragentSelectViewRoute = 'ContragentSelectViewRoute'; const String paymentViewRoute = 'paymentViewRoute'; const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView'; +const String settingPrinterBleBluetoothView = 'SettingPrinterBleBluetoothView'; +const String settingPrinterBlueView = 'settingPrinterBlueView'; + + // Generate the views here diff --git a/lib/routes/router.dart b/lib/routes/router.dart index c8cf06e..04066b2 100644 --- a/lib/routes/router.dart +++ b/lib/routes/router.dart @@ -6,11 +6,12 @@ import 'package:satu/views/dictionaries/category/category_select_view.dart'; import 'package:satu/views/dictionaries/category/category_view.dart'; import 'package:satu/views/dictionaries/goods/goods_edit.dart'; import 'package:satu/views/dictionaries/goods/goods_view.dart'; +import 'package:satu/views/settings/printer_bluetooth/printer_view.dart'; import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart'; import 'package:satu/views/work/views/add_product/add_product_view.dart'; import 'package:satu/views/login/login_view.dart'; import 'package:satu/views/main/main_view.dart'; -import 'package:satu/views/settings/printer_bluetooth/PrinterSelect.dart'; +import 'package:satu/views/settings/printer_bluetooth/printer_select.dart'; import 'package:satu/views/work/views/contragent/select_contragent_view.dart'; import 'package:satu/views/work/views/payment/payment_view.dart'; import 'package:satu/views/work/work_view.dart'; @@ -49,7 +50,7 @@ Route generateRoute(RouteSettings settings) { case settingPrinterBluetoothViewRoute: return _getPageRoute( routeName: settings.name!, - //viewToShow: PrinterSelectView(title: 'Принтер печати чеков',), + viewToShow: const PrinterView(), ); case contragentSelectViewRoute: return _getPageRoute( diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart index 3866dce..79da11a 100644 --- a/lib/views/main/main_view.dart +++ b/lib/views/main/main_view.dart @@ -6,7 +6,7 @@ import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/views/dictionaries/category/category_view.dart'; import 'package:satu/views/dictionaries/goods/goods_view.dart'; -import 'package:satu/views/settings/printer_bluetooth/PrinterSelect.dart'; +import 'package:satu/views/settings/printer_bluetooth/printer_select.dart'; import 'package:satu/views/settings/setting_view.dart'; import 'package:satu/views/work/work_view.dart'; import 'package:satu/widgets/drawer/app_drawer.dart'; diff --git a/lib/views/settings/component/setting_item.dart b/lib/views/settings/component/setting_item.dart index 6680f5d..67c26b3 100644 --- a/lib/views/settings/component/setting_item.dart +++ b/lib/views/settings/component/setting_item.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; class SettingItem extends StatefulWidget { + const SettingItem({Key? key, this.name, this.value, this.onTap}) + : super(key: key); final String? name; final String? value; - final Function? onTap; - - SettingItem({Key? key, this.name, this.value, this.onTap}) : super(key: key); + final Function()? onTap; @override _SettingItemState createState() => _SettingItemState(); @@ -18,10 +18,10 @@ class _SettingItemState extends State { return Card( child: ListTile( title: Text(widget.name ?? ''), - subtitle: widget.value !=null ? Text(widget.value ?? '') : null, - trailing: Icon(Icons.chevron_right), - onTap: () => widget.onTap, + subtitle: widget.value != null ? Text(widget.value ?? '') : null, + trailing: const Icon(Icons.chevron_right), + onTap: widget.onTap, ), ); } -} \ No newline at end of file +} diff --git a/lib/views/settings/printer_bluetooth/PrinterSelect.dart b/lib/views/settings/printer_bluetooth/PrinterSelect.dart deleted file mode 100644 index a7a61e2..0000000 --- a/lib/views/settings/printer_bluetooth/PrinterSelect.dart +++ /dev/null @@ -1,366 +0,0 @@ -// import 'dart:convert'; -// import 'dart:typed_data'; -// -// import 'package:charset_converter/charset_converter.dart'; -// import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; -// import 'package:esc_pos_utils/esc_pos_utils.dart'; -// import 'package:flutter/material.dart' hide Image; -// import 'package:image/image.dart' as Im; -// import 'package:flutter/services.dart'; -// import 'package:intl/intl.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 = []; -// -// @override -// void initState() { -// super.initState(); -// -// printerManager.scanResults.listen((devices) async { -// // print('UI: Devices found ${devices.length}'); -// setState(() { -// _devices = devices; -// }); -// }); -// } -// -// 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; -// } -// -// Future testTicket(PaperSize paper) async { -// final Ticket ticket = Ticket(paper); -// Uint8List encTxt4 = -// await CharsetConverter.encode("cp866", "Russian: Привет Мир!"); -// ticket.textEncoded(encTxt4, -// styles: PosStyles(codeTable: PosCodeTable.pc866_2)); -// -// ticket.text('Kazakh: Сәлем Әлем!', -// styles: PosStyles(codeTable: PosCodeTable.westEur), containsChinese: true); -// -// // Uint8List encTxt1 = -// // await CharsetConverter.encode("utf-8", "Kazakh: Сәлем Әлем!"); -// // ticket.textEncoded(encTxt1, -// // styles: PosStyles(codeTable: PosCodeTable.pc866_2)); -// 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)); -// -// // Print mixed (chinese + latin) text. Only for printers supporting Kanji mode -// ticket.text( -// 'hello ! 中文字 # world @ éphémère &', -// styles: PosStyles(codeTable: PosCodeTable.westEur), -// containsChinese: true, -// ); -// -// ticket.text( -// 'hello ! Мир # world @ éphémère &', -// styles: PosStyles(codeTable: PosCodeTable.westEur), -// containsChinese: true, -// ); -// -// ticket.feed(2); -// -// ticket.cut(); -// return ticket; -// } -// -// 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)); -// -// // DEMO RECEIPT -// final PosPrintResult res = -// await printerManager.printTicket(await testTicket(paper) , queueSleepTimeMs: 50); -// -// } -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text(widget.title), -// ), -// body: ListView.builder( -// itemCount: _devices.length, -// itemBuilder: (BuildContext context, int index) { -// return InkWell( -// onTap: () => _testPrint(_devices[index]), -// 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_bluetooth/printer_select.dart b/lib/views/settings/printer_bluetooth/printer_select.dart new file mode 100644 index 0000000..0e6bdc6 --- /dev/null +++ b/lib/views/settings/printer_bluetooth/printer_select.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + + +class PrinterSelectView extends StatefulWidget { + const PrinterSelectView({Key? key}) : super(key: key); + + @override + _PrinterSelectViewState createState() => _PrinterSelectViewState(); +} + +class _PrinterSelectViewState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container(), + ); + } +} diff --git a/lib/views/settings/printer_bluetooth/printer_view.dart b/lib/views/settings/printer_bluetooth/printer_view.dart new file mode 100644 index 0000000..58a20ed --- /dev/null +++ b/lib/views/settings/printer_bluetooth/printer_view.dart @@ -0,0 +1,207 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:bluetooth_print/bluetooth_print.dart'; +import 'package:bluetooth_print/bluetooth_print_model.dart'; +import 'package:charset_converter/charset_converter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:logger/logger.dart'; +import 'package:satu/core/utils/logger.dart'; +import 'package:satu/core/utils/pos_printer.dart'; + +class PrinterView extends StatefulWidget { + const PrinterView({Key? key}) : super(key: key); + + @override + _PrinterViewState createState() => _PrinterViewState(); +} + +class _PrinterViewState extends State { + final Logger log = getLogger('_PrinterViewState'); + BluetoothPrint? bluetoothPrint; + + bool _connected = false; + BluetoothDevice? _device; + String tips = 'no device connect'; + + @override + void initState() { + bluetoothPrint = BluetoothPrint.instance; + if (WidgetsBinding.instance != null) { + WidgetsBinding.instance!.addPostFrameCallback((_) => initBluetooth()); + } + + super.initState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initBluetooth() async { + bluetoothPrint!.startScan(timeout: Duration(seconds: 4)); + + final bool isConnected = await bluetoothPrint!.isConnected ?? false; + + bluetoothPrint!.state.listen((state) { + print('cur device status: $state'); + + switch (state) { + case BluetoothPrint.CONNECTED: + setState(() { + _connected = true; + tips = 'connect success'; + }); + break; + case BluetoothPrint.DISCONNECTED: + setState(() { + _connected = false; + tips = 'disconnect success'; + }); + 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('BluetoothPrint example app'), + ), + body: RefreshIndicator( + onRefresh: () => + bluetoothPrint!.startScan(timeout: Duration(seconds: 4)), + child: SingleChildScrollView( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: + EdgeInsets.symmetric(vertical: 10, horizontal: 10), + child: Text(tips), + ), + ], + ), + const Divider(), + StreamBuilder>( + stream: bluetoothPrint!.scanResults, + initialData: const [], + 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 + ? const Icon( + Icons.check, + color: Colors.green, + ) + : null, + )) + .toList(), + ), + ), + const Divider(), + Container( + padding: EdgeInsets.fromLTRB(20, 5, 20, 10), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OutlinedButton( + onPressed: _connected + ? null + : () async { + if (_device != null && + _device!.address != null) { + await bluetoothPrint!.connect(_device!); + } else { + setState(() { + tips = 'please select device'; + }); + print('please select device'); + } + }, + child: const Text('connect'), + ), + SizedBox(width: 10.0), + OutlinedButton( + onPressed: _connected + ? () async { + await bluetoothPrint!.disconnect(); + } + : null, + child: const Text('disconnect'), + ), + ], + ), + OutlinedButton( + onPressed: _connected + ? () async { + Map config = Map(); + await bluetoothPrint! + .rawBytes(config, await getReceipt()); + } + : null, + child: const Text('print receipt(esc) - text'), + ), + OutlinedButton( + onPressed: _connected + ? () async { + Map config = Map(); + await bluetoothPrint! + .rawBytes(config, await getReceiptImg()); + } + : null, + child: const Text('print label(esc) img'), + ), + ], + ), + ) + ], + ), + ), + ), + floatingActionButton: StreamBuilder( + stream: bluetoothPrint!.isScanning, + initialData: false, + builder: (c, snapshot) { + if (snapshot.data != null) { + return FloatingActionButton( + onPressed: () => bluetoothPrint!.stopScan(), + backgroundColor: Colors.red, + child: const Icon(Icons.stop), + ); + } else { + return FloatingActionButton( + child: const Icon(Icons.search), + onPressed: () => bluetoothPrint! + .startScan(timeout: const Duration(seconds: 4))); + } + }, + ), + ), + ); + } +} diff --git a/lib/views/settings/setting_view.dart b/lib/views/settings/setting_view.dart index bbeec81..d86a69a 100644 --- a/lib/views/settings/setting_view.dart +++ b/lib/views/settings/setting_view.dart @@ -7,11 +7,11 @@ import 'package:satu/widgets/bar/products_app_bar.dart'; import 'component/setting_item.dart'; class SettingsView extends StatelessWidget { - NavigatorService _navigatorService = locator(); + final NavigatorService _navigatorService = locator(); @override Widget build(BuildContext context) { return Scaffold( - appBar: ProductsAppBar(title: 'Настройки',), + appBar: const ProductsAppBar(title: 'Настройки', drawerShow: true,), body: Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0 ), child: SingleChildScrollView( @@ -20,11 +20,11 @@ class SettingsView extends StatelessWidget { SettingItem( name: 'Принтер', value: 'не выбран', - onTap: () { + onTap: (){ _navigatorService.push(settingPrinterBluetoothViewRoute); } - ), - ], + ) + ] ), ), ), diff --git a/lib/widgets/drawer/app_drawer.dart b/lib/widgets/drawer/app_drawer.dart index 8974c43..1afb456 100644 --- a/lib/widgets/drawer/app_drawer.dart +++ b/lib/widgets/drawer/app_drawer.dart @@ -12,6 +12,7 @@ import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/ui_helpers.dart'; import 'package:satu/views/dictionaries/category/category_view.dart'; import 'package:satu/views/dictionaries/goods/goods_view.dart'; +import 'package:satu/views/settings/setting_view.dart'; import 'package:satu/views/work/work_view.dart'; class AppDrawer extends StatelessWidget { @@ -58,7 +59,12 @@ class AppDrawer extends StatelessWidget { svgFile: 'question', text: 'Справочник', disable: true), _createDrawerSectionTitle(text: 'ПРОЧЕЕ'), _createDrawerItem( - svgFile: 'settings', text: 'Настройки', disable: true), + svgFile: 'settings', + text: 'Настройки', + onTap: () { + Navigator.of(context).pop(); + Redux.store!.dispatch(navigateDrawer(SettingsView)); + }), _createDrawerItem( svgFile: 'global', text: 'Перейти на сайт', disable: true), _createDrawerItem( @@ -106,17 +112,16 @@ class AppDrawer extends StatelessWidget { horizontalSpaceSmall, StoreConnector( converter: (store) => store.state.userState!, - builder: (context, snapshot) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(snapshot.auth?.username ?? '', - style: TextStyle(fontSize: 16.0)), - Text('Продавец', style: TextStyle(fontSize: 12)), - ], - ); - } - ), + builder: (context, snapshot) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(snapshot.auth?.username ?? '', + style: TextStyle(fontSize: 16.0)), + Text('Продавец', style: TextStyle(fontSize: 12)), + ], + ); + }), ], )), ])), diff --git a/pubspec.lock b/pubspec.lock index 266db29..eb9d1d6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,13 +22,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" auto_size_text: dependency: "direct main" description: @@ -36,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0-nullsafety.0" + bluetooth_print: + dependency: "direct main" + description: + path: "../bluetooth_print" + relative: true + source: path + version: "3.0.1" boolean_selector: dependency: transitive description: @@ -56,7 +70,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" charset_converter: dependency: "direct main" description: @@ -120,6 +134,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" + esc_pos_utils: + dependency: "direct main" + description: + name: esc_pos_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" fake_async: dependency: transitive description: @@ -141,11 +162,25 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_reactive_ble: + dependency: "direct main" + description: + name: flutter_reactive_ble + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" flutter_redux: dependency: "direct main" description: @@ -177,6 +212,20 @@ packages: description: flutter source: sdk version: "0.0.0" + functional_data: + dependency: transitive + description: + name: functional_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + gbk_codec: + dependency: transitive + description: + name: gbk_codec + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" get_it: dependency: "direct main" description: @@ -191,6 +240,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.1.0" + hex: + dependency: transitive + description: + name: hex + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" html: dependency: transitive description: @@ -212,6 +268,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" implicitly_animated_reorderable_list: dependency: "direct main" description: @@ -233,13 +296,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" lint: dependency: "direct dev" description: name: lint url: "https://pub.dartlang.org" source: hosted - version: "1.5.3" + version: "1.7.2" + location_permissions: + dependency: "direct main" + description: + name: location_permissions + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" logger: dependency: "direct main" description: @@ -281,7 +358,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" nested: dependency: transitive description: @@ -316,21 +393,21 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.5" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" path_provider_platform_interface: dependency: transitive description: @@ -344,7 +421,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.3" pedantic: dependency: transitive description: @@ -358,7 +435,7 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "8.1.4+2" + version: "8.1.6" permission_handler_platform_interface: dependency: transitive description: @@ -379,7 +456,7 @@ packages: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.2" plugin_platform_interface: dependency: transitive description: @@ -393,7 +470,14 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" + version: "4.2.3" + protobuf: + dependency: transitive + description: + name: protobuf + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" provider: dependency: "direct main" description: @@ -415,6 +499,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + reactive_ble_mobile: + dependency: transitive + description: + name: reactive_ble_mobile + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + reactive_ble_platform_interface: + dependency: transitive + description: + name: reactive_ble_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" redux: dependency: "direct main" description: @@ -450,27 +548,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.1" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.2" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.8" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shared_preferences_platform_interface: dependency: transitive description: @@ -484,14 +589,14 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" sky_engine: dependency: transitive description: flutter @@ -517,7 +622,7 @@ packages: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.1+1" stack_trace: dependency: transitive description: @@ -559,7 +664,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: @@ -594,21 +699,21 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.9" + version: "6.0.10" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" url_launcher_platform_interface: dependency: transitive description: @@ -622,14 +727,14 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.4" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" uuid: dependency: "direct main" description: @@ -650,7 +755,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.5" + version: "2.2.9" xdg_directories: dependency: transitive description: @@ -666,5 +771,5 @@ packages: source: hosted version: "5.1.2" sdks: - dart: ">=2.13.0 <3.0.0" - flutter: ">=2.0.0" + dart: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index c373e5b..bc46b76 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,7 +40,7 @@ dependencies: equatable: ^2.0.3 http: ^0.13.3 sqflite: ^2.0.0+4 - path_provider: ^2.0.3 + path_provider: ^2.0.5 material_design_icons_flutter: 5.0.5955-rc.1 intl: ^0.17.0 device_info: ^2.0.2 @@ -49,19 +49,26 @@ dependencies: qr_flutter: ^4.0.0 mask_text_input_formatter: ^2.0.0 flutter_screenutil: ^5.0.0+2 - shared_preferences: ^2.0.7 + shared_preferences: ^2.0.8 material_floating_search_bar: ^0.3.4 implicitly_animated_reorderable_list: ^0.4.1 uuid: ^3.0.4 charset_converter: ^2.0.0 ai_barcode: ^3.0.1 - permission_handler: ^8.1.4+2 + permission_handler: ^8.1.6 flutter_svg: ^0.22.0 grouped_list: ^4.1.0 + #flutter_reactive_ble + flutter_reactive_ble: ^4.0.1 + location_permissions: ^4.0.0 + bluetooth_print: + path: ../bluetooth_print/ + esc_pos_utils: ^1.1.0 +# flutter_blue: ^0.8.0 dev_dependencies: flutter_test: sdk: flutter - lint: ^1.5.3 + lint: ^1.7.2 # For information on the generic Dart part of this file, see the