├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── codingdevs │ │ └── thermal_printer │ │ ├── ThermalPrinterPlugin.kt │ │ ├── adapter │ │ └── USBPrinterAdapter.kt │ │ ├── bluetooth │ │ ├── BluetoothBleConnection.kt │ │ ├── BluetoothConnection.kt │ │ ├── BluetoothConstants.kt │ │ ├── BluetoothService.kt │ │ ├── IBluetoothConnection.kt │ │ └── SampleGattAttributes.kt │ │ ├── models │ │ └── LocalBluetoothDevice.kt │ │ └── usb │ │ ├── USBPrinterService.kt │ │ └── UsbReceiver.kt │ └── res │ ├── values-es │ └── strings.xml │ └── values │ └── strings.xml ├── assets └── resources │ └── capabilities.json ├── btn-sm-paypal-payment.png ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── codingdevs │ │ │ │ │ └── thermal_printer_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ └── ic_launcher.png ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── image_utils.dart │ └── main.dart ├── pubspec.yaml ├── test │ └── widget_test.dart └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── BLEConnecter.h │ ├── Connecter.h │ ├── ConnecterBlock.h │ ├── ConnecterManager.h │ ├── ConnecterManager.m │ ├── EthernetConnecter.h │ ├── SwiftThermalPrinterPlugin.swift │ ├── ThermalPrinter.h │ └── ThermalPrinterPlugin.m ├── libGSDK.a └── thermal_printer.podspec ├── lib ├── discovery.dart ├── esc_pos_utils_platform │ ├── esc_pos_utils_platform.dart │ └── src │ │ ├── barcode.dart │ │ ├── capability_profile.dart │ │ ├── commands.dart │ │ ├── enums.dart │ │ ├── generator.dart │ │ ├── pos_column.dart │ │ ├── pos_styles.dart │ │ └── qrcode.dart ├── printer.dart ├── resources │ └── capabilities.json ├── src │ ├── connectors │ │ ├── bluetooth.dart │ │ ├── tcp.dart │ │ └── usb.dart │ ├── models │ │ └── printer_device.dart │ ├── printer_manager.dart │ ├── printers │ │ ├── escpos.dart │ │ ├── star.dart │ │ └── tspl.dart │ └── utils.dart └── thermal_printer.dart ├── pubspec.yaml ├── test └── thermal_printer_test.dart └── windows ├── .gitignore ├── CMakeLists.txt ├── include ├── printer.cpp ├── printer.h ├── thermal_printer │ └── thermal_printer_plugin.h └── utils.hpp └── thermal_printer_plugin.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # Avoid committing generated Javascript files: 15 | *.dart.js 16 | *.info.json # Produced by the --dump-info flag. 17 | *.js # When generated by dart2js. Don't specify *.js if your 18 | # project includes source files written in JavaScript. 19 | *.js_ 20 | *.js.deps 21 | *.js.map 22 | 23 | # Miscellaneous 24 | *.class 25 | *.log 26 | *.pyc 27 | *.swp 28 | .DS_Store 29 | .atom/ 30 | .buildlog/ 31 | .history 32 | .svn/ 33 | 34 | config.env 35 | pubspec.lock 36 | 37 | # IntelliJ related 38 | *.iml 39 | *.ipr 40 | *.iws 41 | .idea/ 42 | 43 | # The .vscode folder contains launch configuration and tasks you configure in 44 | # VS Code which you may wish to be included in version control, so this line 45 | # is commented out by default. 46 | #.vscode/ 47 | 48 | # Flutter/Dart/Pub related 49 | **/doc/api/ 50 | **/ios/Flutter/.last_build_id 51 | .dart_tool/ 52 | .flutter-plugins 53 | .flutter-plugins-dependencies 54 | .packages 55 | .pub-cache/ 56 | .pub/ 57 | /build/ 58 | 59 | # Web related 60 | lib/generated_plugin_registrant.dart 61 | 62 | # Symbolication related 63 | app.*.symbols 64 | 65 | # Obfuscation related 66 | app.*.map.json 67 | 68 | # Exceptions to above rules. 69 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 70 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 8 | channel: stable 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 17 | base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 18 | - platform: android 19 | create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 20 | base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 21 | - platform: ios 22 | create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 23 | base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 24 | - platform: windows 25 | create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 26 | base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 27 | 28 | # User provided section 29 | 30 | # List of Local paths (relative to this file) that should be 31 | # ignored by the migrate tool. 32 | # 33 | # Files that are not part of the templates will be ignored by default. 34 | unmanaged_files: 35 | - 'lib/main.dart' 36 | - 'ios/Runner.xcodeproj/project.pbxproj' 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.5 2 | 3 | * fixed windows build 4 | 5 | ## 1.0.4 6 | 7 | * fixed ios build 8 | 9 | ## 1.0.3 10 | 11 | * fixed ios build error 12 | 13 | ## 1.0.2 14 | 15 | * Bumped dart_ping to dart_ping: ^9.0.1 16 | 17 | ## 1.0.1 18 | 19 | * Added Platforms 20 | 21 | ## 1.0.0 22 | 23 | * Initial Release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gustavo Morales 4 | Copyright (c) 2023 Coding Devs 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thermal_printer 2 | 3 | [![Pub Version](https://img.shields.io/badge/pub-v1.0.5-green)](https://pub.dev/packages/thermal_printer) 4 | 5 | A library to discover printers, and send printer commands. 6 | 7 | This library allows to print esc commands to printers in different platforms such as android, ios, windows and different interfaces as Bluetooth and BLE, USB and Wifi/Ethernet 8 | 9 | Inspired by [flutter_pos_printer](https://github.com/feedmepos/flutter_printer/tree/master/packages/flutter_pos_printer). 10 | 11 | 12 | ## Main Features 13 | * Android, iOS and Windows support 14 | * Scan for bluetooth devices 15 | * Send raw `List bytes` data to a device, review this library to generate ESC/POS commands [flutter_esc_pos_utils](https://pub.dev/packages/flutter_esc_pos_utils). 16 | 17 | ## Features 18 | 19 | | | Android | iOS | Windows | Description | 20 | | :--------------- | :----------------: | :------------------: | :----------------: | :-------------------------------- | 21 | | USB interface | :white_check_mark: | :white_square_button: | :white_check_mark: | Allows connection with usb devices. | 22 | | Bluetooth classic interface | :white_check_mark: | :white_square_button: | :white_square_button: | Allows connection with classic bt devices. | 23 | | Bluetooth low energy (BLE) interface | :white_check_mark: | :white_check_mark: | :white_square_button: | Allows connection with bt BLE devices. | 24 | | Net (ethernet/wifi) interface | :white_check_mark: | :white_check_mark: | :white_check_mark: | Allows connection with network devices. | 25 | | scan | :white_check_mark: | :white_check_mark: | :white_check_mark: | Starts a scan for only Bluetooth devices or network devices(Android/iOS). | 26 | | connect | :white_check_mark: | :white_check_mark: | :white_check_mark: | Establishes a connection to the device. | 27 | | disconnect | :white_check_mark: | :white_check_mark: | :white_check_mark: | Cancels an active or pending connection to the device. | 28 | | state | :white_check_mark: | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Device. | 29 | | print | :white_check_mark: | :white_check_mark: | :white_check_mark: | print bytes. | 30 | 31 | ## Getting Started 32 | 33 | For a full example please check /example folder. Here are only the most important parts of the code to illustrate how to use the library. 34 | 35 | Generate bytes to print through [flutter_esc_pos_utils](https://pub.dev/packages/flutter_esc_pos_utils). 36 | 37 | ```dart 38 | import 'package:esc_pos_utils/esc_pos_utils.dart'; 39 | 40 | final profile = await CapabilityProfile.load(); 41 | final generator = Generator(PaperSize.mm58, profile); 42 | List bytes = []; 43 | 44 | bytes += generator.text('Test Print', styles: const PosStyles(align: PosAlign.center)); 45 | bytes += generator.text('Product 1'); 46 | bytes += generator.text('Product 2'); 47 | ``` 48 | 49 | ## Android 50 | Allow to connect bluetooth (classic and BLE), USB and network devices 51 | 52 | ### Change the minSdkVersion for Android 53 | 54 | thermal_printer is compatible only from version 21 of Android SDK so you should change this in android/app/build.gradle: 55 | 56 | In build.gradle set 57 | ``` 58 | defaultConfig { 59 | ... 60 | minSdkVersion 19 61 | targetSdkVersion 33 62 | ... 63 | ``` 64 | 65 | select type of device `PrinterType` ( bluetooth, usb, network) 66 | 67 | if select bluetooth you can send optional params 68 | 69 | - isBle -> allow to connect with bluetooth that supports this technology 70 | - autoconnect -> allow to reconnect when state of device is None 71 | 72 | USB: you can enable the native broadcast receiver to notify connected usb devices 73 | put the following code in AndroidManifest 74 | ``` 75 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ``` 86 | 87 | ## iOS 88 | Allow to connect bluetooth (BLE) and network devices 89 | 90 | ## Windows 91 | Allow to connect USB and network devices 92 | To network devices is necessary to set ipAddress 93 | 94 | 95 | ## How to use it 96 | ### init a PrinterManager instance 97 | 98 | ```dart 99 | import 'package:thermal_printer/thermal_printer.dart'; 100 | 101 | var printerManager = PrinterManager.instance; 102 | 103 | ``` 104 | 105 | ### scan 106 | 107 | ```dart 108 | var devices = []; 109 | _scan(PrinterType type, {bool isBle = false}) { 110 | // Find printers 111 | PrinterManager.instance.discovery(type: type, isBle: isBle).listen((device) { 112 | devices.add(device); 113 | }); 114 | } 115 | ``` 116 | 117 | ### connect 118 | 119 | ```dart 120 | _connectDevice(PrinterDevice selectedPrinter, PrinterType type, {bool reconnect = false, bool isBle = false, String? ipAddress = null}) async { 121 | switch (type) { 122 | // only windows and android 123 | case PrinterType.usb: 124 | await PrinterManager.instance.connect( 125 | type: type, 126 | model: UsbPrinterInput(name: selectedPrinter.name, productId: selectedPrinter.productId, vendorId: selectedPrinter.vendorId)); 127 | break; 128 | // only iOS and android 129 | case PrinterType.bluetooth: 130 | await PrinterManager.instance.connect( 131 | type: type, 132 | model: BluetoothPrinterInput( 133 | name: selectedPrinter.name, 134 | address: selectedPrinter.address!, 135 | isBle: isBle, 136 | autoConnect: reconnect)); 137 | break; 138 | case PrinterType.network: 139 | await PrinterManager.instance.connect(type: type, model: TcpPrinterInput(ipAddress: ipAddress ?? selectedPrinter.address!)); 140 | break; 141 | default: 142 | } 143 | } 144 | ``` 145 | ### disconnect 146 | 147 | ```dart 148 | _disconnectDevice(PrinterType type) async { 149 | await PrinterManager.instance.disconnect(type: type); 150 | } 151 | ``` 152 | 153 | ### listen bluetooth state 154 | ```dart 155 | PrinterManager.instance.stateBluetooth.listen((status) { 156 | log(' ----------------- status bt $status ------------------ '); 157 | }); 158 | ``` 159 | 160 | ### send bytes to print 161 | ```dart 162 | _sendBytesToPrint(List bytes, PrinterType type) async { 163 | PrinterManager.instance.send(type: type, bytes: bytes); 164 | } 165 | 166 | ``` 167 | 168 | ## Troubleshooting 169 | 170 | error:'State restoration of CBCentralManager is only allowed for applications that have specified the "bluetooth-central" background mode' 171 | info.plist add: 172 | 173 | ``` 174 | NSBluetoothAlwaysUsageDescription 175 | Allow App use bluetooth? 176 | NSBluetoothPeripheralUsageDescription 177 | Allow App use bluetooth? 178 | UIBackgroundModes 179 | 180 | bluetooth-central 181 | bluetooth-peripheral 182 | 183 | ``` 184 | 185 | 186 | ## Credits 187 | - https://github.com/andrey-ushakov/esc_pos_utils 188 | - https://github.com/bailabs/esc-pos-printer-flutter 189 | - https://github.com/feedmepos/flutter_printer/tree/master/packages/flutter_pos_printer 190 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.codingdevs.thermal_printer' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.9.10' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.4.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 33 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | minSdkVersion 19 45 | targetSdkVersion 33 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 51 | } 52 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx6608M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'thermal_printer' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/codingdevs/thermal_printer/bluetooth/BluetoothConstants.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer.bluetooth 2 | 3 | object BluetoothConstants { 4 | // Constants that indicate the current connection state 5 | const val STATE_NONE = 0 // we're doing nothing 6 | const val STATE_CONNECTING = 2 // now initiating an outgoing connection 7 | const val STATE_CONNECTED = 3 // now connected to a remote device 8 | const val STATE_FAILED = 4 // we're doing nothing 9 | 10 | // Message types sent from the BluetoothChatService Handler 11 | const val MESSAGE_STATE_CHANGE = 1 12 | const val MESSAGE_READ = 2 13 | const val MESSAGE_WRITE = 3 14 | const val MESSAGE_DEVICE_NAME = 4 15 | const val MESSAGE_TOAST = 5 16 | const val MESSAGE_REGISTER_CLIENT = 6 17 | const val MESSAGE_GET_AVAILABLE_DATA = 7 18 | const val MESSAGE_EVENT = 8 19 | const val MESSAGE_SEND_BT_CMD = 9 20 | const val MESSAGE_FREQUENTLY = 10 21 | const val MESSAGE_START_SCANNING = 11 22 | const val MESSAGE_STOP_SCANNING = 12 23 | 24 | const val BLUETOOTH_REGEX = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" 25 | const val DEVICE_NAME = "device_name" 26 | 27 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/codingdevs/thermal_printer/bluetooth/IBluetoothConnection.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer.bluetooth 2 | import io.flutter.plugin.common.MethodChannel.Result 3 | 4 | interface IBluetoothConnection { 5 | fun connect(address: String, result: Result) 6 | fun stop() 7 | fun write(out: ByteArray?) 8 | var state: Int 9 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/codingdevs/thermal_printer/bluetooth/SampleGattAttributes.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer.bluetooth 2 | 3 | import java.util.* 4 | import kotlin.collections.HashMap 5 | 6 | class SampleGattAttributes { 7 | // Sample Characteristics. 8 | init { 9 | attributes["0000180d-0000-1000-8000-00805f9b34fb"] = "Heart Rate Service" 10 | attributes["0000180a-0000-1000-8000-00805f9b34fb"] = "Device Information Service" 11 | attributes[HEART_RATE_MEASUREMENT] = "Heart Rate Measurement" 12 | attributes["00002a29-0000-1000-8000-00805f9b34fb"] = "Manufacturer Name String" 13 | } 14 | 15 | 16 | companion object { 17 | const val HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb" 18 | const val CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb" 19 | // Unique UUID for this application 20 | val SPP_UUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb") 21 | private val attributes: HashMap = HashMap() 22 | 23 | fun lookup(uuid: String, defaultName: String?): String? { 24 | val name = attributes[uuid] 25 | return name ?: defaultName 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/codingdevs/thermal_printer/models/LocalBluetoothDevice.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer.models 2 | 3 | data class LocalBluetoothDevice( 4 | 5 | val name: String, 6 | val address: String? 7 | ) 8 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/codingdevs/thermal_printer/usb/UsbReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer.usb 2 | 3 | import android.app.PendingIntent 4 | import android.content.BroadcastReceiver 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.hardware.usb.UsbDevice 8 | import android.hardware.usb.UsbManager 9 | import android.util.Log 10 | 11 | class UsbReceiver : BroadcastReceiver() { 12 | override fun onReceive(context: Context?, intent: Intent?) { 13 | Log.d("UsbReceiver", "Inside USB Broadcast action ${intent!!.action}") 14 | 15 | val action = intent.action 16 | if (UsbManager.ACTION_USB_DEVICE_ATTACHED == action) { 17 | 18 | val usbDevice: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) 19 | 20 | val mPermissionIndent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { 21 | PendingIntent.getBroadcast(context, 0, Intent("com.flutter_pos_printer.USB_PERMISSION"), PendingIntent.FLAG_MUTABLE) 22 | } else { 23 | PendingIntent.getBroadcast(context, 0, Intent("com.flutter_pos_printer.USB_PERMISSION"), 0) 24 | } 25 | val mUSBManager = context?.getSystemService(Context.USB_SERVICE) as UsbManager? 26 | mUSBManager?.requestPermission(usbDevice, mPermissionIndent) 27 | 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /android/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | No se puede establecer conexión Bluetooth 4 | Se ha perdido conexión Bluetooth 5 | Debe aceptar todos los permisos para que la aplicación funcione 6 | No se pudieron enviar datos al otro dispositivo 7 | El usuario rechazó dar acceso al dispositivo USB 8 | El dispositivo USB ha sido apagado 9 | El administrador USB no se ha inicializado para obtener la lista de dispositivos 10 | Dispositivo conectado 11 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cannot establish Bluetooth connection 4 | Bluetooth connection lost 5 | Must accept all permissions in order to the app works 6 | "Couldn't send data to the other device" 7 | User refused to give USB device permission 8 | USB device has been turned off 9 | USB Manager is not initialized while trying to get devices list 10 | Connected device 11 | -------------------------------------------------------------------------------- /assets/resources/capabilities.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "default": { 4 | "codePages": { 5 | "0": "CP437", 6 | "1": "CP932", 7 | "2": "CP850", 8 | "3": "CP860", 9 | "4": "CP863", 10 | "5": "CP865", 11 | "6": "Unknown", 12 | "7": "Unknown", 13 | "8": "Unknown", 14 | "11": "CP851", 15 | "12": "CP853", 16 | "13": "CP857", 17 | "14": "CP737", 18 | "15": "ISO_8859-7", 19 | "16": "CP1252", 20 | "17": "CP866", 21 | "18": "CP852", 22 | "19": "CP858", 23 | "20": "Unknown", 24 | "21": "CP874", 25 | "22": "Unknown", 26 | "23": "Unknown", 27 | "24": "Unknown", 28 | "25": "Unknown", 29 | "26": "Unknown", 30 | "30": "TCVN-3-1", 31 | "31": "TCVN-3-2", 32 | "32": "CP720", 33 | "33": "CP775", 34 | "34": "CP855", 35 | "35": "CP861", 36 | "36": "CP862", 37 | "37": "CP864", 38 | "38": "CP869", 39 | "39": "ISO_8859-2", 40 | "40": "ISO_8859-15", 41 | "41": "CP1098", 42 | "42": "CP774", 43 | "43": "CP772", 44 | "44": "CP1125", 45 | "45": "CP1250", 46 | "46": "CP1251", 47 | "47": "CP1253", 48 | "48": "CP1254", 49 | "49": "CP1255", 50 | "50": "CP1256", 51 | "51": "CP1257", 52 | "52": "CP1258", 53 | "53": "RK1048", 54 | "66": "Unknown", 55 | "67": "Unknown", 56 | "68": "Unknown", 57 | "69": "Unknown", 58 | "70": "Unknown", 59 | "71": "Unknown", 60 | "72": "Unknown", 61 | "73": "Unknown", 62 | "74": "Unknown", 63 | "75": "Unknown", 64 | "82": "Unknown", 65 | "254": "Unknown", 66 | "255": "Unknown" 67 | }, 68 | "vendor": "Generic", 69 | "model": "Default", 70 | "description": "Default ESC/POS profile" 71 | }, 72 | 73 | "XP-N160I": { 74 | "codePages": { 75 | "0": "CP437", 76 | "1": "CP932", 77 | "2": "CP850", 78 | "3": "CP860", 79 | "4": "CP863", 80 | "5": "CP865", 81 | "6": "CP1252", 82 | "7": "CP737", 83 | "8": "CP862", 84 | "9": "Unknown", 85 | "10": "Unknown", 86 | "16": "CP1252", 87 | "17": "CP866", 88 | "18": "CP852", 89 | "19": "CP858", 90 | "20": "Unknown", 91 | "21": "Unknown", 92 | "22": "Unknown", 93 | "23": "Unknown", 94 | "24": "CP747", 95 | "25": "CP1257", 96 | "27": "CP1258", 97 | "28": "CP864", 98 | "29": "CP1001", 99 | "30": "Unknown", 100 | "31": "Unknown", 101 | "32": "CP1255", 102 | "50": "CP437", 103 | "51": "CP932", 104 | "52": "CP437", 105 | "53": "CP858", 106 | "54": "CP858", 107 | "55": "CP860", 108 | "56": "CP861", 109 | "57": "CP863", 110 | "58": "CP865", 111 | "59": "CP866", 112 | "60": "CP855", 113 | "61": "CP857", 114 | "62": "CP862", 115 | "63": "CP864", 116 | "64": "CP737", 117 | "65": "CP851", 118 | "66": "CP869", 119 | "67": "CP928", 120 | "68": "CP772", 121 | "69": "CP774", 122 | "70": "CP874", 123 | "71": "CP1252", 124 | "72": "CP1250", 125 | "73": "CP1251", 126 | "74": "CP3840", 127 | "75": "CP3841", 128 | "76": "CP3843", 129 | "77": "CP3844", 130 | "78": "CP3845", 131 | "79": "CP3846", 132 | "80": "CP3847", 133 | "81": "CP3848", 134 | "82": "CP1001", 135 | "83": "CP2001", 136 | "84": "CP3001", 137 | "85": "CP3002", 138 | "86": "CP3011", 139 | "87": "CP3012", 140 | "88": "CP3021", 141 | "89": "CP3041" 142 | }, 143 | "vendor": "Xprinter", 144 | "model": "XP-N160I", 145 | "description": "" 146 | }, 147 | 148 | "RP80USE": { 149 | "codePages": { 150 | "0": "CP437", 151 | "1": "CP932", 152 | "2": "CP850", 153 | "3": "CP860", 154 | "4": "CP863", 155 | "5": "CP865", 156 | "6": "CP1251", 157 | "7": "CP866", 158 | "8": "Unknown", 159 | "9": "Unknown", 160 | "10": "Unknown", 161 | "15": "CP862", 162 | "16": "CP1252", 163 | "17": "CP1253", 164 | "18": "CP852", 165 | "19": "CP858", 166 | "20": "Unknown", 167 | "21": "Unknown", 168 | "22": "CP864", 169 | "23": "ISO_8859-1", 170 | "24": "CP737", 171 | "25": "CP1257", 172 | "26": "Unknown", 173 | "27": "CP720", 174 | "28": "CP855", 175 | "29": "CP857", 176 | "30": "CP1250", 177 | "31": "CP775", 178 | "32": "CP1254", 179 | "34": "CP1256", 180 | "35": "CP1258", 181 | "36": "ISO_8859-2", 182 | "37": "ISO_8859-3", 183 | "38": "ISO_8859-4", 184 | "39": "ISO_8859-5", 185 | "40": "ISO_8859-6", 186 | "41": "ISO_8859-7", 187 | "42": "ISO_8859-8", 188 | "43": "ISO_8859-9", 189 | "44": "ISO_8859-15", 190 | "45": "Unknown", 191 | "46": "CP856", 192 | "47": "CP874" 193 | }, 194 | "vendor": "Rongta", 195 | "model": "RP80USE", 196 | "description": "" 197 | }, 198 | 199 | "TP806L": { 200 | "codePages": { 201 | "0": "PC437", 202 | "1": "Katakana", 203 | "2": "PC850", 204 | "3": "PC860", 205 | "4": "PC863", 206 | "5": "PC865", 207 | "13": "PC857", 208 | "14": "PC737", 209 | "15": "ISO8859-7", 210 | "16": "WPC1252", 211 | "17": "PC866", 212 | "18": "PC852", 213 | "19": "PC858", 214 | "20": "KU42", 215 | "32": "PC720", 216 | "37": "PC864", 217 | "50": "WPC1256", 218 | "63": "ISO-8859-6" 219 | }, 220 | "vendor": "HPRT", 221 | "model": "TP806L", 222 | "description": "" 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /btn-sm-paypal-payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/btn-sm-paypal-payment.png -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5464c5bac742001448fe4fc0597be939379f88ea 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_pos_printer_example 2 | 3 | Demonstrates how to use the thermal_printer plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.codingdevs.thermal_printer_example" 47 | minSdkVersion 19 48 | targetSdkVersion 33 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 16 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/codingdevs/thermal_printer_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codingdevs.thermal_printer_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.4.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx6608M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/assets/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/assets/ic_launcher.png -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - thermal_printer (0.0.1): 4 | - Flutter 5 | - flutter_star_prnt (0.0.1): 6 | - Flutter 7 | - network_info_plus (0.0.1): 8 | - Flutter 9 | 10 | DEPENDENCIES: 11 | - Flutter (from `Flutter`) 12 | - thermal_printer (from `.symlinks/plugins/thermal_printer/ios`) 13 | - flutter_star_prnt (from `.symlinks/plugins/flutter_star_prnt/ios`) 14 | - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) 15 | 16 | EXTERNAL SOURCES: 17 | Flutter: 18 | :path: Flutter 19 | thermal_printer: 20 | :path: ".symlinks/plugins/thermal_printer/ios" 21 | flutter_star_prnt: 22 | :path: ".symlinks/plugins/flutter_star_prnt/ios" 23 | network_info_plus: 24 | :path: ".symlinks/plugins/network_info_plus/ios" 25 | 26 | SPEC CHECKSUMS: 27 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 28 | thermal_printer: 72176df8340825c736b5768fce1591e8884e456e 29 | flutter_star_prnt: c4ea89b6b9ccb708624f5830c9d34b7ee1165999 30 | network_info_plus: b78876159360f5580608c2cea620d6ceffabd0ad 31 | 32 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 33 | 34 | COCOAPODS: 1.11.3 35 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Pos Printer 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_pos_printer_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/image_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:image/image.dart' as img; 2 | 3 | /// Draw the image [src] onto the image [dst]. 4 | /// 5 | /// In other words, drawImage will take an rectangular area from src of 6 | /// width [src_w] and height [src_h] at position ([src_x],[src_y]) and place it 7 | /// in a rectangular area of [dst] of width [dst_w] and height [dst_h] at 8 | /// position ([dst_x],[dst_y]). 9 | /// 10 | /// If the source and destination coordinates and width and heights differ, 11 | /// appropriate stretching or shrinking of the image fragment will be performed. 12 | /// The coordinates refer to the upper left corner. This function can be used to 13 | /// copy regions within the same image (if [dst] is the same as [src]) 14 | /// but if the regions overlap the results will be unpredictable. 15 | img.Image drawImage(img.Image dst, img.Image src, 16 | {int? dstX, int? dstY, int? dstW, int? dstH, int? srcX, int? srcY, int? srcW, int? srcH, bool blend = true}) { 17 | dstX ??= 0; 18 | dstY ??= 0; 19 | srcX ??= 0; 20 | srcY ??= 0; 21 | srcW ??= src.width; 22 | srcH ??= src.height; 23 | dstW ??= (dst.width < src.width) ? dstW = dst.width : src.width; 24 | dstH ??= (dst.height < src.height) ? dst.height : src.height; 25 | 26 | if (blend) { 27 | for (var y = 0; y < dstH; ++y) { 28 | for (var x = 0; x < dstW; ++x) { 29 | final stepX = (x * (srcW / dstW)).toInt(); 30 | final stepY = (y * (srcH / dstH)).toInt(); 31 | final srcPixel = src.getPixel(srcX + stepX, srcY + stepY); 32 | img.drawPixel(dst, dstX + x, dstY + y, srcPixel); 33 | } 34 | } 35 | } else { 36 | for (var y = 0; y < dstH; ++y) { 37 | for (var x = 0; x < dstW; ++x) { 38 | final stepX = (x * (srcW / dstW)).toInt(); 39 | final stepY = (y * (srcH / dstH)).toInt(); 40 | final srcPixel = src.getPixel(srcX + stepX, srcY + stepY); 41 | dst.setPixel(dstX + x, dstY + y, srcPixel); 42 | } 43 | } 44 | } 45 | 46 | return dst; 47 | } 48 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: thermal_printer_example 2 | description: Demonstrates how to use the thermal_printer plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: '>=2.18.4 <3.3.10' 10 | 11 | # Dependencies specify other packages that your package needs in order to work. 12 | # To automatically upgrade your package dependencies to the latest versions 13 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 14 | # dependencies can be manually updated by changing the version numbers below to 15 | # the latest version available on pub.dev. To see which dependencies have newer 16 | # versions available, run `flutter pub outdated`. 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | 21 | thermal_printer: 22 | # When depending on this package from a real application you should use: 23 | # thermal_printer: ^x.y.z 24 | # See https://dart.dev/tools/pub/dependencies#version-constraints 25 | # The example app is bundled with the plugin so we use a path dependency on 26 | # the parent directory to use the current plugin's version. 27 | path: ../ 28 | 29 | # The following adds the Cupertino Icons font to your application. 30 | # Use with the CupertinoIcons class for iOS style icons. 31 | cupertino_icons: ^1.0.2 32 | dart_ping_ios: ^4.0.2 33 | 34 | dev_dependencies: 35 | flutter_test: 36 | sdk: flutter 37 | 38 | # The "flutter_lints" package below contains a set of recommended lints to 39 | # encourage good coding practices. The lint set provided by the package is 40 | # activated in the `analysis_options.yaml` file located at the root of your 41 | # package. See that file for information about deactivating specific lint 42 | # rules and activating additional ones. 43 | flutter_lints: ^1.0.0 44 | 45 | # For information on the generic Dart part of this file, see the 46 | # following page: https://dart.dev/tools/pub/pubspec 47 | 48 | # The following section is specific to Flutter. 49 | flutter: 50 | 51 | # The following line ensures that the Material Icons font is 52 | # included with your application, so that you can use the icons in 53 | # the material Icons class. 54 | uses-material-design: true 55 | 56 | # To add assets to your application, add an assets section, like this: 57 | assets: 58 | - assets/ic_launcher.png 59 | # - images/a_dot_ham.jpeg 60 | 61 | # An image asset can refer to one or more resolution-specific "variants", see 62 | # https://flutter.dev/assets-and-images/#resolution-aware. 63 | 64 | # For details regarding adding assets from package dependencies, see 65 | # https://flutter.dev/assets-and-images/#from-packages 66 | 67 | # To add custom fonts to your application, add a fonts section here, 68 | # in this "flutter" section. Each entry in this list should have a 69 | # "family" key with the font family name, and a "fonts" key with a 70 | # list giving the asset and other descriptors for the font. For 71 | # example: 72 | # fonts: 73 | # - family: Schyler 74 | # fonts: 75 | # - asset: fonts/Schyler-Regular.ttf 76 | # - asset: fonts/Schyler-Italic.ttf 77 | # style: italic 78 | # - family: Trajan Pro 79 | # fonts: 80 | # - asset: fonts/TrajanPro.ttf 81 | # - asset: fonts/TrajanPro_Bold.ttf 82 | # weight: 700 83 | # 84 | # For details regarding fonts from package dependencies, 85 | # see https://flutter.dev/custom-fonts/#from-packages 86 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:thermal_printer_example/main.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Verify Platform version', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const MyApp()); 16 | 17 | // Verify that platform version is retrieved. 18 | expect( 19 | find.byWidgetPredicate( 20 | (Widget widget) => widget is Text && widget.data!.startsWith('Running on:'), 21 | ), 22 | findsOneWidget, 23 | ); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(flutter_pos_printer_example LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "flutter_pos_printer_example") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # Set fallback configurations for older versions of the flutter tool. 13 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 14 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 15 | endif() 16 | 17 | # === Flutter Library === 18 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 19 | 20 | # Published to parent scope for install step. 21 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 22 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 23 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 24 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 25 | 26 | list(APPEND FLUTTER_LIBRARY_HEADERS 27 | "flutter_export.h" 28 | "flutter_windows.h" 29 | "flutter_messenger.h" 30 | "flutter_plugin_registrar.h" 31 | "flutter_texture_registrar.h" 32 | ) 33 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 34 | add_library(flutter INTERFACE) 35 | target_include_directories(flutter INTERFACE 36 | "${EPHEMERAL_DIR}" 37 | ) 38 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 39 | add_dependencies(flutter flutter_assemble) 40 | 41 | # === Wrapper === 42 | list(APPEND CPP_WRAPPER_SOURCES_CORE 43 | "core_implementations.cc" 44 | "standard_codec.cc" 45 | ) 46 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 47 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 48 | "plugin_registrar.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 51 | list(APPEND CPP_WRAPPER_SOURCES_APP 52 | "flutter_engine.cc" 53 | "flutter_view_controller.cc" 54 | ) 55 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 56 | 57 | # Wrapper sources needed for a plugin. 58 | add_library(flutter_wrapper_plugin STATIC 59 | ${CPP_WRAPPER_SOURCES_CORE} 60 | ${CPP_WRAPPER_SOURCES_PLUGIN} 61 | ) 62 | apply_standard_settings(flutter_wrapper_plugin) 63 | set_target_properties(flutter_wrapper_plugin PROPERTIES 64 | POSITION_INDEPENDENT_CODE ON) 65 | set_target_properties(flutter_wrapper_plugin PROPERTIES 66 | CXX_VISIBILITY_PRESET hidden) 67 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 68 | target_include_directories(flutter_wrapper_plugin PUBLIC 69 | "${WRAPPER_ROOT}/include" 70 | ) 71 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 72 | 73 | # Wrapper sources needed for the runner. 74 | add_library(flutter_wrapper_app STATIC 75 | ${CPP_WRAPPER_SOURCES_CORE} 76 | ${CPP_WRAPPER_SOURCES_APP} 77 | ) 78 | apply_standard_settings(flutter_wrapper_app) 79 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 80 | target_include_directories(flutter_wrapper_app PUBLIC 81 | "${WRAPPER_ROOT}/include" 82 | ) 83 | add_dependencies(flutter_wrapper_app flutter_assemble) 84 | 85 | # === Flutter tool backend === 86 | # _phony_ is a non-existent file to force this command to run every time, 87 | # since currently there's no way to get a full input/output list from the 88 | # flutter tool. 89 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 90 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 91 | add_custom_command( 92 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 93 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 94 | ${CPP_WRAPPER_SOURCES_APP} 95 | ${PHONY_OUTPUT} 96 | COMMAND ${CMAKE_COMMAND} -E env 97 | ${FLUTTER_TOOL_ENVIRONMENT} 98 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 99 | ${FLUTTER_TARGET_PLATFORM} $ 100 | VERBATIM 101 | ) 102 | add_custom_target(flutter_assemble DEPENDS 103 | "${FLUTTER_LIBRARY}" 104 | ${FLUTTER_LIBRARY_HEADERS} 105 | ${CPP_WRAPPER_SOURCES_CORE} 106 | ${CPP_WRAPPER_SOURCES_PLUGIN} 107 | ${CPP_WRAPPER_SOURCES_APP} 108 | ) 109 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | 11 | void RegisterPlugins(flutter::PluginRegistry* registry) { 12 | ThermalPrinterPluginRegisterWithRegistrar( 13 | registry->GetRegistrarForPlugin("ThermalPrinterPlugin")); 14 | } 15 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | thermal_printer 7 | ) 8 | 9 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 10 | ) 11 | 12 | set(PLUGIN_BUNDLED_LIBRARIES) 13 | 14 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 19 | endforeach(plugin) 20 | 21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 24 | endforeach(ffi_plugin) 25 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.codingdevs" "\0" 93 | VALUE "FileDescription", "flutter_pos_printer_example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "flutter_pos_printer_example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2022 com.codingdevs. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "flutter_pos_printer_example.exe" "\0" 98 | VALUE "ProductName", "flutter_pos_printer_example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"flutter_pos_printer_example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/BLEConnecter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Connecter.h 3 | // GSDK 4 | // 5 | 6 | #import "Connecter.h" 7 | #import 8 | 9 | @interface BLEConnecter :Connecter 10 | 11 | @property(nonatomic,strong)CBCharacteristic *airPatchChar; 12 | @property(nonatomic,strong)CBCharacteristic *transparentDataWriteChar; 13 | @property(nonatomic,strong)CBCharacteristic *transparentDataReadOrNotifyChar; 14 | @property(nonatomic,strong)CBCharacteristic *connectionParameterChar; 15 | 16 | @property(nonatomic,strong)CBUUID *transServiceUUID; 17 | @property(nonatomic,strong)CBUUID *transTxUUID; 18 | @property(nonatomic,strong)CBUUID *transRxUUID; 19 | @property(nonatomic,strong)CBUUID *disUUID1; 20 | @property(nonatomic,strong)CBUUID *disUUID2; 21 | @property(nonatomic,strong)NSArray *serviceUUID; 22 | 23 | @property(nonatomic,copy)DiscoverDevice discover; 24 | @property(nonatomic,copy)UpdateState updateState; 25 | @property(nonatomic,copy)WriteProgress writeProgress; 26 | 27 | /**数据包大小,默认130个字节*/ 28 | @property(nonatomic,assign)NSUInteger datagramSize; 29 | 30 | @property(nonatomic,strong)CBPeripheral *connPeripheral; 31 | 32 | //+(instancetype)sharedInstance; 33 | 34 | /** 35 | * 方法说明:设置特定的Service UUID,以及Service对应的具有读、写特征值 36 | * @param serviceUUID 蓝牙模块的service uuid 37 | * @param txUUID 具有写入权限特征值 38 | * @param rxUUID 具有读取权限特征值 39 | */ 40 | - (void)configureTransparentServiceUUID: (NSString *)serviceUUID txUUID:(NSString *)txUUID rxUUID:(NSString *)rxUUID; 41 | 42 | /** 43 | * 方法说明:扫描外设 44 | * @param serviceUUIDs 需要连接的外设UUID 45 | * @param options 其它可选操作 46 | * @param discover 发现设备 47 | * peripheral 发现的外设 48 | * advertisementData 49 | * RSSI 外设信号强度 50 | */ 51 | -(void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs options:(nullable NSDictionary *)options discover:(void(^_Nullable)(CBPeripheral *_Nullable peripheral,NSDictionary *_Nullable advertisementData,NSNumber *_Nullable RSSI))discover; 52 | 53 | /** 54 | * 方法说明:停止扫描蓝牙外设 55 | */ 56 | -(void)stopScan; 57 | 58 | /** 59 | * 方法说明:更新蓝牙状态 60 | * @param state 更新蓝牙状态 61 | */ 62 | -(void)didUpdateState:(void(^_Nullable)(NSInteger state))state; 63 | 64 | /** 65 | * 方法说明:连接外设 66 | * @param peripheral 需要连接的外设 67 | * @param options 其它可选操作 68 | * @param timeout 连接超时 69 | * @param connectState 连接状态 70 | */ 71 | -(void)connectPeripheral:(CBPeripheral *_Nullable)peripheral options:(nullable NSDictionary *)options timeout:(NSUInteger)timeout connectBlack:(void(^_Nullable)(ConnectState state)) connectState; 72 | 73 | /** 74 | * 方法说明:连接外设 75 | * @param peripheral 需要连接的外设 76 | * @param options 其它可选操作 77 | */ 78 | -(void)connectPeripheral:(CBPeripheral * _Nullable)peripheral options:(nullable NSDictionary *)options; 79 | 80 | /** 81 | * 方法说明:断开连接 82 | * @param peripheral 需要断开连接的外设 83 | */ 84 | -(void)closePeripheral:(nonnull CBPeripheral *)peripheral; 85 | 86 | /** 87 | * 方法说明: 往蓝牙模块中写入数据 // Method description: write data to Bluetooth module 88 | * @param data 往蓝牙模块中写入的数据 // Data written to the Bluetooth module 89 | * @param progress 写入数据的进度 // Progress of writing data 90 | * @param callBack 读取蓝牙模块返回数据 // Read Bluetooth module data 91 | */ 92 | -(void)write:(NSData *_Nullable)data progress:(void(^_Nullable)(NSUInteger total,NSUInteger progress))progress receCallBack:(void (^_Nullable)(NSData *_Nullable))callBack; 93 | 94 | /** 95 | * 方法说明: 往蓝牙模块中写入数据 // Method description: write data to Bluetooth module 96 | * @param characteristic 特征值 97 | * @param data 往蓝牙模块中写入的数据 // Data written to the Bluetooth module 98 | * @param type 写入方式CBCharacteristicWriteWithResponse写入方式是带流控写入方式。CBCharacteristicWriteWithoutResponse不带流控写入方式

@see CBCharacteristicWriteType

99 | * Writing method CBCharacteristicWriteWithResponse The writing method is a writing method with flow control. CBCharacteristicWriteWithoutResponseWithout flow control write method

@see CBCharacteristicWriteType

100 | */ 101 | -(void)writeValue:(NSData *)data forCharacteristic:(nonnull CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type; 102 | @end 103 | -------------------------------------------------------------------------------- /ios/Classes/Connecter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Connecter.h 3 | // GSDK 4 | // 5 | #import 6 | #import "ConnecterBlock.h" 7 | 8 | @interface Connecter:NSObject 9 | 10 | //读取数据 11 | @property(nonatomic,copy)ReadData readData; 12 | //连接状态 13 | @property(nonatomic,copy)ConnectDeviceState state; 14 | 15 | /** 16 | * 方法说明: 连接 // Method description: connect 17 | */ 18 | -(void)connect; 19 | 20 | /** 21 | * 方法说明: 连接到指定设备 // Method description: connect to the specified device 22 | * @param connectState 连接状态 23 | */ 24 | -(void)connect:(void(^)(ConnectState state))connectState; 25 | 26 | /** 27 | * 方法说明: 关闭连接 28 | */ 29 | -(void)close; 30 | 31 | /** 32 | * 发送数据 // send data 33 | * 向输出流中写入数据 // Write data to the output stream 34 | */ 35 | -(void)write:(NSData *)data receCallBack:(void(^)(NSData *data))callBack; 36 | -(void)write:(NSData *)data; 37 | 38 | /** 39 | * 读取数据 40 | * @parma data 读取到的数据 41 | */ 42 | -(void)read:(void(^)(NSData *data))data; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /ios/Classes/ConnecterBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConnecterBlock.h 3 | // GSDK 4 | // 5 | 6 | #ifndef ConnecterBlock_h 7 | #define ConnecterBlock_h 8 | #import 9 | 10 | /** 11 | * @enum ConnectState 12 | * @discussion 连接状态 13 | * @constant CONNECT_STATE_DISCONNECT ConnectDeviceState返回state为该状态是表示已断开连接 14 | * @constant CONNECT_STATE_CONNECTING ConnectDeviceState返回state为该状态是表示正在连接中 15 | * @constant CONNECT_STATE_CONNECTED ConnectDeviceState返回state为该状态是表示连接成功 16 | * @constant CONNECT_STATE_TIMEOUT ConnectDeviceState返回state为该状态是表示连接超时 17 | * @constant CONNECT_STATE_FAILT ConnectDeviceState返回state为该状态是表示连接失败 18 | */ 19 | typedef enum : NSUInteger { 20 | NOT_FOUND_DEVICE,//未找到设备 21 | CONNECT_STATE_DISCONNECT,//断开连接 22 | CONNECT_STATE_CONNECTING,//连接中 23 | CONNECT_STATE_CONNECTED,//连接上 24 | CONNECT_STATE_TIMEOUT,//连接超时 25 | CONNECT_STATE_FAILT//连接失败 26 | }ConnectState; 27 | 28 | /**发现设备 Discover devices*/ 29 | typedef void(^DiscoverDevice)(CBPeripheral *_Nullable peripheral,NSDictionary * _Nullable advertisementData,NSNumber * _Nullable RSSI); 30 | /**蓝牙状态更新 Bluetooth status update*/ 31 | typedef void(^UpdateState)(NSInteger state); 32 | /**连接状态 Connection Status*/ 33 | typedef void(^ConnectDeviceState)(ConnectState state); 34 | /**读取数据 Read data*/ 35 | typedef void(^ReadData)(NSData * _Nullable data); 36 | /**发送数据进度 只适用于蓝牙发送数据 Sending data progress ** Only for Bluetooth sending data*/ 37 | typedef void(^WriteProgress)(NSUInteger total,NSUInteger progress); 38 | typedef void (^Error)(id error); 39 | 40 | #endif /* ConnecterBlock_h */ 41 | -------------------------------------------------------------------------------- /ios/Classes/ConnecterManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConnecterManager.h 3 | // GSDK 4 | // 5 | 6 | #import 7 | #import "BLEConnecter.h" 8 | #import "EthernetConnecter.h" 9 | #import "Connecter.h" 10 | 11 | /** 12 | * @enum ConnectMethod 13 | * @discussion 连接方式 14 | * @constant BLUETOOTH 蓝牙连接 15 | * @constant ETHERNET 网口连接(wifi连接) 16 | */ 17 | typedef enum : NSUInteger{ 18 | BLUETOOTH, 19 | ETHERNET 20 | }ConnectMethod; 21 | 22 | #define Manager [ConnecterManager sharedInstance] 23 | 24 | @interface ConnecterManager : NSObject 25 | @property(nonatomic,strong)BLEConnecter *bleConnecter; 26 | @property(nonatomic,strong)Connecter *connecter; 27 | 28 | +(instancetype)sharedInstance; 29 | 30 | /** 31 | * 方法说明:关闭连接 32 | */ 33 | -(void)close; 34 | 35 | /** 36 | * 方法说明: 向输出流中写入数据(只适用于蓝牙) // Method description: write data to the output stream (only for Bluetooth) 37 | * @param data 需要写入的数据 38 | * @param progress 写入数据进度 39 | * @param callBack 读取输入流中的数据 40 | */ 41 | -(void)write:(NSData *_Nullable)data progress:(void(^_Nullable)(NSUInteger total,NSUInteger progress))progress receCallBack:(void (^_Nullable)(NSData *_Nullable))callBack; 42 | 43 | /** 44 | * 方法说明:向输出流中写入数据 // Method description: writing data to the output stream 45 | * @param callBack 读取数据接口 46 | */ 47 | -(void)write:(NSData *)data receCallBack:(void (^)(NSData *))callBack; 48 | 49 | /** 50 | * 方法说明:向输出流中写入数据 // Method description: writing data to the output stream 51 | * @param data 需要写入的数据 52 | */ 53 | -(void)write:(NSData *)data; 54 | 55 | /** 56 | * 方法说明:停止扫描 57 | */ 58 | -(void)stopScan; 59 | 60 | /** 61 | * 方法说明:更新蓝牙状态 62 | * @param state 蓝牙状态 63 | */ 64 | -(void)didUpdateState:(void(^)(NSInteger state))state; 65 | 66 | /** 67 | * 方法说明:连接外设 68 | * @param peripheral 需连接的外设 69 | * @param options 其它可选操作 70 | * @param timeout 连接时间 71 | * @param connectState 连接状态 72 | */ 73 | -(void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary *)options timeout:(NSUInteger)timeout connectBlack:(void(^_Nullable)(ConnectState state)) connectState; 74 | 75 | /** 76 | * 方法说明:连接外设 77 | * @param peripheral 需连接的外设 78 | * @param options 其它可选操作 79 | */ 80 | -(void)connectPeripheral:(CBPeripheral * _Nullable)peripheral options:(nullable NSDictionary *)options; 81 | 82 | /** 83 | * 方法说明:扫描外设 84 | * @param serviceUUIDs 需要发现外设的UUID,设置为nil则发现周围所有外设 85 | * @param options 其它可选操作 86 | * @param discover 发现的设备 87 | */ 88 | -(void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs options:(nullable NSDictionary *)options discover:(void(^_Nullable)(CBPeripheral *_Nullable peripheral,NSDictionary *_Nullable advertisementData,NSNumber *_Nullable RSSI))discover; 89 | 90 | 91 | 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /ios/Classes/ConnecterManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConnecterManager.m 3 | // GSDK 4 | // 5 | // 6 | 7 | #import "ConnecterManager.h" 8 | 9 | @interface ConnecterManager(){ 10 | ConnectMethod currentConnMethod; 11 | } 12 | @end 13 | 14 | @implementation ConnecterManager 15 | 16 | static ConnecterManager *manager; 17 | static dispatch_once_t once; 18 | 19 | +(instancetype)sharedInstance { 20 | dispatch_once(&once, ^{ 21 | manager = [[ConnecterManager alloc]init]; 22 | }); 23 | return manager; 24 | } 25 | 26 | /** 27 | * 方法说明:扫描外设 28 | * @param serviceUUIDs 需要发现外设的UUID,设置为nil则发现周围所有外设 29 | * @param options 其它可选操作 30 | * @param discover 发现的设备 31 | */ 32 | -(void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs options:(nullable NSDictionary *)options discover:(void(^_Nullable)(CBPeripheral *_Nullable peripheral,NSDictionary *_Nullable advertisementData,NSNumber *_Nullable RSSI))discover{ 33 | [_bleConnecter scanForPeripheralsWithServices:serviceUUIDs options:options discover:discover]; 34 | } 35 | 36 | /** 37 | * 方法说明:更新蓝牙状态 38 | * @param state 蓝牙状态 39 | */ 40 | -(void)didUpdateState:(void(^)(NSInteger state))state { 41 | if (_bleConnecter == nil) { 42 | currentConnMethod = BLUETOOTH; 43 | [self initConnecter:currentConnMethod]; 44 | } 45 | [_bleConnecter didUpdateState:state]; 46 | } 47 | 48 | -(void)initConnecter:(ConnectMethod)connectMethod { 49 | switch (connectMethod) { 50 | case BLUETOOTH: 51 | _bleConnecter = [BLEConnecter new]; 52 | _connecter = _bleConnecter; 53 | break; 54 | default: 55 | break; 56 | } 57 | } 58 | 59 | /** 60 | * 方法说明:停止扫描 61 | */ 62 | -(void)stopScan { 63 | [_bleConnecter stopScan]; 64 | } 65 | 66 | /** 67 | * 连接 68 | */ 69 | -(void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary *)options timeout:(NSUInteger)timeout connectBlack:(void(^_Nullable)(ConnectState state)) connectState{ 70 | [_bleConnecter connectPeripheral:peripheral options:options timeout:timeout connectBlack:connectState]; 71 | } 72 | 73 | -(void)connectPeripheral:(CBPeripheral * _Nullable)peripheral options:(nullable NSDictionary *)options { 74 | [_bleConnecter connectPeripheral:peripheral options:options]; 75 | } 76 | 77 | -(void)write:(NSData *_Nullable)data progress:(void(^_Nullable)(NSUInteger total,NSUInteger progress))progress receCallBack:(void (^_Nullable)(NSData *_Nullable))callBack { 78 | [_bleConnecter write:data progress:progress receCallBack:callBack]; 79 | } 80 | 81 | -(void)write:(NSData *)data receCallBack:(void (^)(NSData *))callBack { 82 | #ifdef DEBUG 83 | NSLog(@"[ConnecterManager] write:receCallBack:"); 84 | #endif 85 | _bleConnecter.writeProgress = nil; 86 | [_connecter write:data receCallBack:callBack]; 87 | } 88 | 89 | -(void)write:(NSData *)data { 90 | #ifdef DEBUG 91 | NSLog(@"[ConnecterManager] write:"); 92 | #endif 93 | _bleConnecter.writeProgress = nil; 94 | [_connecter write:data]; 95 | } 96 | 97 | -(void)close { 98 | if (_connecter) { 99 | [_connecter close]; 100 | } 101 | switch (currentConnMethod) { 102 | case BLUETOOTH: 103 | _bleConnecter = nil; 104 | break; 105 | } 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /ios/Classes/EthernetConnecter.h: -------------------------------------------------------------------------------- 1 | // 2 | // EthernetConnecter.h 3 | // GSDK 4 | // 5 | 6 | #import "Connecter.h" 7 | 8 | @interface EthernetConnecter :Connecter 9 | /**连接设备的ip地址*/ 10 | @property(nonatomic,strong)NSString *ip; 11 | /**连接设备的端口号*/ 12 | @property(nonatomic,assign)int port; 13 | 14 | //+(instancetype)sharedInstance; 15 | 16 | /** 17 | * 方法说明: 连接设备 18 | * @param ip 连接设备的ip地址 19 | * @param port 连接设备的端口号 20 | * @param connectState 连接状态 @see ConnectState 21 | * @param callback 输入流数据回调 22 | */ 23 | -(void)connectIP:(NSString *)ip port:(int)port connectState:(void (^)(ConnectState state))connectState callback:(void(^)(NSData *data))callback; 24 | 25 | -(void)connectIP:(NSString *)ip port:(int)port connectState:(void (^)(ConnectState))connectState; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ios/Classes/SwiftThermalPrinterPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftThermalPrinterPlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let channel = FlutterMethodChannel(name: "thermal_printer", binaryMessenger: registrar.messenger()) 7 | let instance = SwiftThermalPrinterPlugin() 8 | registrar.addMethodCallDelegate(instance, channel: channel) 9 | } 10 | 11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 12 | result("iOS " + UIDevice.current.systemVersion) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/Classes/ThermalPrinter.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "ConnecterManager.h" 4 | 5 | #define NAMESPACE @"thermal_printer" 6 | 7 | @interface ThermalPrinterPlugin : NSObject 8 | @property(nonatomic,copy)ConnectDeviceState state; 9 | @end 10 | 11 | @interface BluetoothPrintStreamHandler : NSObject 12 | @property FlutterEventSink sink; 13 | @end -------------------------------------------------------------------------------- /ios/Classes/ThermalPrinterPlugin.m: -------------------------------------------------------------------------------- 1 | #import "ThermalPrinter.h" 2 | #import "ConnecterManager.h" 3 | 4 | @interface ThermalPrinterPlugin () 5 | @property(nonatomic, retain) NSObject *registrar; 6 | @property(nonatomic, retain) FlutterMethodChannel *channel; 7 | @property(nonatomic, retain) BluetoothPrintStreamHandler *stateStreamHandler; 8 | @property(nonatomic) NSMutableDictionary *scannedPeripherals; 9 | @end 10 | 11 | @implementation ThermalPrinterPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | FlutterMethodChannel* channel = [FlutterMethodChannel 14 | methodChannelWithName:NAMESPACE @"/methods" 15 | binaryMessenger:[registrar messenger]]; 16 | FlutterEventChannel* stateChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/state" binaryMessenger:[registrar messenger]]; 17 | ThermalPrinterPlugin* instance = [[ThermalPrinterPlugin alloc] init]; 18 | 19 | instance.channel = channel; 20 | instance.scannedPeripherals = [NSMutableDictionary new]; 21 | 22 | // STATE 23 | BluetoothPrintStreamHandler* stateStreamHandler = [[BluetoothPrintStreamHandler alloc] init]; 24 | [stateChannel setStreamHandler:stateStreamHandler]; 25 | instance.stateStreamHandler = stateStreamHandler; 26 | 27 | [registrar addMethodCallDelegate:instance channel:channel]; 28 | } 29 | 30 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 31 | NSLog(@"call method -> %@", call.method); 32 | 33 | if ([@"state" isEqualToString:call.method]) { 34 | result(nil); 35 | } else if([@"isAvailable" isEqualToString:call.method]) { 36 | 37 | result(@(YES)); 38 | } else if([@"isConnected" isEqualToString:call.method]) { 39 | 40 | result(@(NO)); 41 | } else if([@"isOn" isEqualToString:call.method]) { 42 | result(@(YES)); 43 | }else if([@"startScan" isEqualToString:call.method]) { 44 | NSLog(@"getDevices method -> %@", call.method); 45 | [self.scannedPeripherals removeAllObjects]; 46 | 47 | if (Manager.bleConnecter == nil) { 48 | [Manager didUpdateState:^(NSInteger state) { 49 | switch (state) { 50 | case CBCentralManagerStateUnsupported: 51 | NSLog(@"The platform/hardware doesn't support Bluetooth Low Energy."); 52 | break; 53 | case CBCentralManagerStateUnauthorized: 54 | NSLog(@"The app is not authorized to use Bluetooth Low Energy."); 55 | break; 56 | case CBCentralManagerStatePoweredOff: 57 | NSLog(@"Bluetooth is currently powered off."); 58 | break; 59 | case CBCentralManagerStatePoweredOn: 60 | [self startScan]; 61 | NSLog(@"Bluetooth power on"); 62 | break; 63 | case CBCentralManagerStateUnknown: 64 | default: 65 | break; 66 | } 67 | }]; 68 | } else { 69 | [self startScan]; 70 | } 71 | 72 | result(nil); 73 | } else if([@"stopScan" isEqualToString:call.method]) { 74 | [Manager stopScan]; 75 | result(nil); 76 | } else if([@"connect" isEqualToString:call.method]) { 77 | NSDictionary *device = [call arguments]; 78 | @try { 79 | NSLog(@"connect device begin -> %@", [device objectForKey:@"name"]); 80 | CBPeripheral *peripheral = [_scannedPeripherals objectForKey:[device objectForKey:@"address"]]; 81 | 82 | self.state = ^(ConnectState state) { 83 | [self updateConnectState:state]; 84 | }; 85 | [Manager connectPeripheral:peripheral options:nil timeout:2 connectBlack: self.state]; 86 | 87 | result(nil); 88 | } @catch(FlutterError *e) { 89 | result(e); 90 | } 91 | } else if([@"disconnect" isEqualToString:call.method]) { 92 | @try { 93 | [Manager close]; 94 | result(nil); 95 | } @catch(FlutterError *e) { 96 | result(e); 97 | } 98 | } else if([@"writeData" isEqualToString:call.method]) { 99 | @try { 100 | NSDictionary *args = [call arguments]; 101 | 102 | NSMutableArray *bytes = [args objectForKey:@"bytes"]; 103 | 104 | NSNumber* lenBuf = [args objectForKey:@"length"]; 105 | int len = [lenBuf intValue]; 106 | char cArray[len]; 107 | 108 | for (int i = 0; i < len; ++i) { 109 | // NSLog(@"** ind_%d (d): %@, %d", i, bytes[i], [bytes[i] charValue]); 110 | cArray[i] = [bytes[i] charValue]; 111 | } 112 | NSData *data2 = [NSData dataWithBytes:cArray length:sizeof(cArray)]; 113 | // NSLog(@"bytes in hex: %@", [data2 description]); 114 | [Manager write:data2]; 115 | result(nil); 116 | } @catch(FlutterError *e) { 117 | result(e); 118 | } 119 | } 120 | } 121 | 122 | -(void)startScan { 123 | [Manager scanForPeripheralsWithServices:nil options:nil discover:^(CBPeripheral * _Nullable peripheral, NSDictionary * _Nullable advertisementData, NSNumber * _Nullable RSSI) { 124 | if (peripheral.name != nil) { 125 | 126 | NSLog(@"find device -> %@", peripheral.name); 127 | [self.scannedPeripherals setObject:peripheral forKey:[[peripheral identifier] UUIDString]]; 128 | 129 | NSDictionary *device = [NSDictionary dictionaryWithObjectsAndKeys:peripheral.identifier.UUIDString,@"address",peripheral.name,@"name",nil,@"type",nil]; 130 | [_channel invokeMethod:@"ScanResult" arguments:device]; 131 | } 132 | }]; 133 | 134 | } 135 | 136 | -(void)updateConnectState:(ConnectState)state { 137 | dispatch_async(dispatch_get_main_queue(), ^{ 138 | NSNumber *ret = @0; 139 | switch (state) { 140 | case CONNECT_STATE_CONNECTING: 141 | NSLog(@"status -> %@", @"Connecting ..."); 142 | ret = @1; 143 | break; 144 | case CONNECT_STATE_CONNECTED: 145 | NSLog(@"status -> %@", @"Connection success"); 146 | ret = @2; 147 | break; 148 | case CONNECT_STATE_FAILT: 149 | NSLog(@"status -> %@", @"Connection failed"); 150 | ret = @0; 151 | break; 152 | case CONNECT_STATE_DISCONNECT: 153 | NSLog(@"status -> %@", @"Disconnected"); 154 | ret = @0; 155 | break; 156 | default: 157 | NSLog(@"status -> %@", @"Connection timed out"); 158 | ret = @0; 159 | break; 160 | } 161 | 162 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:ret,@"id",nil]; 163 | if(_stateStreamHandler.sink != nil) { 164 | self.stateStreamHandler.sink([dict objectForKey:@"id"]); 165 | } 166 | }); 167 | } 168 | 169 | @end 170 | 171 | @implementation BluetoothPrintStreamHandler 172 | 173 | - (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { 174 | self.sink = eventSink; 175 | return nil; 176 | } 177 | 178 | - (FlutterError*)onCancelWithArguments:(id)arguments { 179 | self.sink = nil; 180 | return nil; 181 | } 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /ios/libGSDK.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingDevs/thermal_printer/328a8174b4ae0d67ece77d183ff1f15e4cc4eaa1/ios/libGSDK.a -------------------------------------------------------------------------------- /ios/thermal_printer.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint thermal_printer.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'thermal_printer' 7 | s.version = '1.0.0' 8 | s.summary = 'A new Flutter plugin project.' 9 | s.description = <<-DESC 10 | A new Flutter plugin project. 11 | DESC 12 | s.homepage = 'http://codingdevs.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Coding Devs' => 'contact@codingdevs.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.public_header_files = 'Classes/**/*.h' 18 | s.static_framework = true 19 | s.dependency 'Flutter' 20 | s.platform = :ios, '9.0' 21 | 22 | # Import all * .a libraries in the Classes folder 23 | s.frameworks = ["SystemConfiguration", "CoreTelephony","WebKit"] 24 | s.vendored_libraries = '**/*.a' 25 | 26 | # Flutter.framework does not contain a i386 slice. 27 | # s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 28 | # s.swift_version = '5.0' 29 | end 30 | -------------------------------------------------------------------------------- /lib/discovery.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter_star_prnt/flutter_star_prnt.dart'; 2 | 3 | import 'thermal_printer.dart'; 4 | 5 | class PrinterDiscovered { 6 | String name; 7 | T detail; 8 | PrinterDiscovered({ 9 | required this.name, 10 | required this.detail, 11 | }); 12 | } 13 | 14 | typedef DiscoverResult = Future>>; 15 | // typedef StarPrinterInfo = PortInfo; 16 | 17 | // DiscoverResult discoverStarPrinter() async { 18 | // if (Platform.isAndroid || Platform.isIOS) { 19 | // return (await StarPrnt.portDiscovery(StarPortType.All)) 20 | // .map((e) => PrinterDiscovered( 21 | // name: e.modelName ?? 'Star Printer', 22 | // detail: e, 23 | // )) 24 | // .toList(); 25 | // } 26 | // return []; 27 | // } 28 | 29 | Future> discoverPrinters( 30 | {List modes = const [ 31 | // discoverStarPrinter, 32 | UsbPrinterConnector.discoverPrinters, 33 | BluetoothPrinterConnector.discoverPrinters, 34 | TcpPrinterConnector.discoverPrinters 35 | ]}) async { 36 | List result = []; 37 | await Future.wait(modes.map((m) async { 38 | result.addAll(await m()); 39 | })); 40 | return result; 41 | } 42 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/esc_pos_utils_platform.dart: -------------------------------------------------------------------------------- 1 | // You have generated a new plugin project without specifying the `--platforms` 2 | // flag. A plugin project with no platform support was generated. To add a 3 | // platform, run `flutter create -t plugin --platforms .` under the 4 | // same directory. You can also find a detailed instruction on how to add 5 | // platforms in the `pubspec.yaml` at 6 | // https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. 7 | library esc_pos_utils_platform; 8 | 9 | export './src/barcode.dart'; 10 | export './src/capability_profile.dart'; 11 | export './src/enums.dart'; 12 | export './src/pos_column.dart'; 13 | export './src/pos_styles.dart'; 14 | export './src/qrcode.dart'; 15 | export './src/generator.dart'; 16 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/capability_profile.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | import 'dart:convert' show json; 10 | import 'package:flutter/services.dart' show rootBundle; 11 | 12 | class CodePage { 13 | CodePage(this.id, this.name); 14 | int id; 15 | String name; 16 | } 17 | 18 | class CapabilityProfile { 19 | CapabilityProfile._internal(this.name, this.codePages); 20 | 21 | /// Public factory 22 | static Future load({String name = 'default'}) async { 23 | final content = await rootBundle.loadString('packages/thermal_printer/resources/capabilities.json'); 24 | Map capabilities = json.decode(content); 25 | 26 | var profile = capabilities['profiles'][name]; 27 | 28 | if (profile == null) { 29 | throw Exception("The CapabilityProfile '$name' does not exist"); 30 | } 31 | 32 | List list = []; 33 | profile['codePages'].forEach((k, v) { 34 | list.add(CodePage(int.parse(k), v)); 35 | }); 36 | 37 | // Call the private constructor 38 | return CapabilityProfile._internal(name, list); 39 | } 40 | 41 | String name; 42 | List codePages; 43 | 44 | int getCodePageId(String? codePage) { 45 | if (codePages.isEmpty) { 46 | throw Exception("The CapabilityProfile isn't initialized"); 47 | } 48 | 49 | return codePages 50 | .firstWhere((cp) => cp.name == codePage, orElse: () => throw Exception("Code Page '$codePage' isn't defined for this profile")) 51 | .id; 52 | } 53 | 54 | static Future> getAvailableProfiles() async { 55 | final content = await rootBundle.loadString('packages/thermal_printer/resources/capabilities.json'); 56 | Map capabilities = json.decode(content); 57 | 58 | var profiles = capabilities['profiles']; 59 | 60 | List res = []; 61 | 62 | profiles.forEach((k, v) { 63 | res.add({ 64 | 'key': k, 65 | 'vendor': v['vendor'] is String ? v['vendor'] : '', 66 | 'model': v['model'] is String ? v['model'] : '', 67 | 'description': v['description'] is String ? v['description'] : '', 68 | }); 69 | }); 70 | 71 | return res; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/commands.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | const esc = '\x1B'; 10 | const gs = '\x1D'; 11 | const fs = '\x1C'; 12 | 13 | // Miscellaneous 14 | const cInit = '$esc@'; // Initialize printer 15 | const cBeep = '${esc}B'; // Beeper [count] [duration] 16 | 17 | // Mech. Control 18 | const cCutFull = '${gs}V0'; // Full cut 19 | const cCutPart = '${gs}V1'; // Partial cut 20 | 21 | // Character 22 | const cReverseOn = '${gs}B\x01'; // Turn white/black reverse print mode on 23 | const cReverseOff = '${gs}B\x00'; // Turn white/black reverse print mode off 24 | const cSizeGSn = '$gs!'; // Select character size [N] 25 | const cSizeESCn = '$esc!'; // Select character size [N] 26 | const cUnderlineOff = '$esc-\x00'; // Turns off underline mode 27 | const cUnderline1dot = '$esc-\x01'; // Turns on underline mode (1-dot thick) 28 | const cUnderline2dots = '$esc-\x02'; // Turns on underline mode (2-dots thick) 29 | const cBoldOn = '${esc}E\x01'; // Turn emphasized mode on 30 | const cBoldOff = '${esc}E\x00'; // Turn emphasized mode off 31 | const cFontA = '${esc}M\x00'; // Font A 32 | const cFontB = '${esc}M\x01'; // Font B 33 | const cTurn90On = '${esc}V\x01'; // Turn 90° clockwise rotation mode on 34 | const cTurn90Off = '${esc}V\x00'; // Turn 90° clockwise rotation mode off 35 | const cCodeTable = '${esc}t'; // Select character code table [N] 36 | const cKanjiOn = '$fs&'; // Select Kanji character mode 37 | const cKanjiOff = '$fs.'; // Cancel Kanji character mode 38 | 39 | // Print Position 40 | const cAlignLeft = '${esc}a0'; // Left justification 41 | const cAlignCenter = '${esc}a1'; // Centered 42 | const cAlignRight = '${esc}a2'; // Right justification 43 | const cPos = '$esc\$'; // Set absolute print position [nL] [nH] 44 | 45 | // Print 46 | const cFeedN = '${esc}d'; // Print and feed n lines [N] 47 | const cReverseFeedN = '${esc}e'; // Print and reverse feed n lines [N] 48 | 49 | // Bit Image 50 | const cRasterImg = '$gs(L'; // Print image - raster bit format (graphics) 51 | const cRasterImg2 = 52 | '${gs}v0'; // Print image - raster bit format (bitImageRaster) [obsolete] 53 | const cBitImg = '$esc*'; // Print image - column format 54 | 55 | // Barcode 56 | const cBarcodeSelectPos = 57 | '${gs}H'; // Select print position of HRI characters [N] 58 | const cBarcodeSelectFont = '${gs}f'; // Select font for HRI characters [N] 59 | const cBarcodeSetH = '${gs}h'; // Set barcode height [N] 60 | const cBarcodeSetW = '${gs}w'; // Set barcode width [N] 61 | const cBarcodePrint = '${gs}k'; // Print barcode 62 | 63 | // Cash Drawer Open 64 | const cCashDrawerPin2 = '${esc}p030'; 65 | const cCashDrawerPin5 = '${esc}p130'; 66 | 67 | // QR Code 68 | const cQrHeader = '$gs(k'; 69 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/enums.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | enum PosAlign { left, center, right } 10 | enum PosCutMode { full, partial } 11 | enum PosFontType { fontA, fontB } 12 | enum PosDrawer { pin2, pin5 } 13 | 14 | /// Choose image printing function 15 | /// bitImageRaster: GS v 0 (obsolete) 16 | /// graphics: GS ( L 17 | enum PosImageFn { bitImageRaster, graphics } 18 | 19 | class PosTextSize { 20 | const PosTextSize._internal(this.value); 21 | final int value; 22 | static const size1 = PosTextSize._internal(1); 23 | static const size2 = PosTextSize._internal(2); 24 | static const size3 = PosTextSize._internal(3); 25 | static const size4 = PosTextSize._internal(4); 26 | static const size5 = PosTextSize._internal(5); 27 | static const size6 = PosTextSize._internal(6); 28 | static const size7 = PosTextSize._internal(7); 29 | static const size8 = PosTextSize._internal(8); 30 | 31 | static int decSize(PosTextSize height, PosTextSize width) => 32 | 16 * (width.value - 1) + (height.value - 1); 33 | } 34 | 35 | class PaperSize { 36 | const PaperSize._internal(this.value); 37 | final int value; 38 | static const mm58 = PaperSize._internal(1); 39 | static const mm80 = PaperSize._internal(2); 40 | 41 | int get width => value == PaperSize.mm58.value ? 372 : 558; 42 | } 43 | 44 | class PosBeepDuration { 45 | const PosBeepDuration._internal(this.value); 46 | final int value; 47 | static const beep50ms = PosBeepDuration._internal(1); 48 | static const beep100ms = PosBeepDuration._internal(2); 49 | static const beep150ms = PosBeepDuration._internal(3); 50 | static const beep200ms = PosBeepDuration._internal(4); 51 | static const beep250ms = PosBeepDuration._internal(5); 52 | static const beep300ms = PosBeepDuration._internal(6); 53 | static const beep350ms = PosBeepDuration._internal(7); 54 | static const beep400ms = PosBeepDuration._internal(8); 55 | static const beep450ms = PosBeepDuration._internal(9); 56 | } 57 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/pos_column.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | import 'dart:typed_data' show Uint8List; 10 | 11 | import 'pos_styles.dart'; 12 | 13 | /// Column contains text, styles and width (an integer in 1..12 range) 14 | /// [containsChinese] not used if the text passed as textEncoded 15 | class PosColumn { 16 | PosColumn({ 17 | this.text = '', 18 | this.textEncoded, 19 | this.containsChinese = false, 20 | this.width = 2, 21 | this.styles = const PosStyles(), 22 | }) { 23 | if (width < 1 || width > 12) { 24 | throw Exception('Column width must be between 1..12'); 25 | } 26 | if (text.length > 0 && textEncoded != null && textEncoded!.length > 0) { 27 | throw Exception('Only one parameter - text or textEncoded - should be passed'); 28 | } 29 | } 30 | 31 | String text; 32 | Uint8List? textEncoded; 33 | bool containsChinese; 34 | int width; 35 | PosStyles styles; 36 | } 37 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/pos_styles.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | import 'enums.dart'; 10 | 11 | /// Text styles 12 | class PosStyles { 13 | const PosStyles({ 14 | this.bold = false, 15 | this.reverse = false, 16 | this.underline = false, 17 | this.turn90 = false, 18 | this.align = PosAlign.left, 19 | this.height = PosTextSize.size1, 20 | this.width = PosTextSize.size1, 21 | this.fontType, 22 | this.codeTable, 23 | }); 24 | 25 | // Init all fields with default values 26 | const PosStyles.defaults({ 27 | this.bold = false, 28 | this.reverse = false, 29 | this.underline = false, 30 | this.turn90 = false, 31 | this.align = PosAlign.left, 32 | this.height = PosTextSize.size1, 33 | this.width = PosTextSize.size1, 34 | this.fontType = PosFontType.fontA, 35 | this.codeTable = "CP437", 36 | }); 37 | 38 | final bool bold; 39 | final bool reverse; 40 | final bool underline; 41 | final bool turn90; 42 | final PosAlign align; 43 | final PosTextSize height; 44 | final PosTextSize width; 45 | final PosFontType? fontType; 46 | final String? codeTable; 47 | 48 | PosStyles copyWith({ 49 | bool? bold, 50 | bool? reverse, 51 | bool? underline, 52 | bool? turn90, 53 | PosAlign? align, 54 | PosTextSize? height, 55 | PosTextSize? width, 56 | PosFontType? fontType, 57 | String? codeTable, 58 | }) { 59 | return PosStyles( 60 | bold: bold ?? this.bold, 61 | reverse: reverse ?? this.reverse, 62 | underline: underline ?? this.underline, 63 | turn90: turn90 ?? this.turn90, 64 | align: align ?? this.align, 65 | height: height ?? this.height, 66 | width: width ?? this.width, 67 | fontType: fontType ?? this.fontType, 68 | codeTable: codeTable ?? this.codeTable, 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/esc_pos_utils_platform/src/qrcode.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * esc_pos_utils 3 | * Created by Andrey U. 4 | * 5 | * Copyright (c) 2019-2020. All rights reserved. 6 | * See LICENSE for distribution and usage details. 7 | */ 8 | 9 | import 'dart:convert'; 10 | 11 | import 'commands.dart'; 12 | 13 | class QRSize { 14 | const QRSize(this.value); 15 | final int value; 16 | 17 | static const Size1 = QRSize(0x01); 18 | static const Size2 = QRSize(0x02); 19 | static const Size3 = QRSize(0x03); 20 | static const Size4 = QRSize(0x04); 21 | static const Size5 = QRSize(0x05); 22 | static const Size6 = QRSize(0x06); 23 | static const Size7 = QRSize(0x07); 24 | static const Size8 = QRSize(0x08); 25 | } 26 | 27 | /// QR Correction level 28 | class QRCorrection { 29 | const QRCorrection._internal(this.value); 30 | final int value; 31 | 32 | /// Level L: Recovery Capacity 7% 33 | static const L = QRCorrection._internal(48); 34 | 35 | /// Level M: Recovery Capacity 15% 36 | static const M = QRCorrection._internal(49); 37 | 38 | /// Level Q: Recovery Capacity 25% 39 | static const Q = QRCorrection._internal(50); 40 | 41 | /// Level H: Recovery Capacity 30% 42 | static const H = QRCorrection._internal(51); 43 | } 44 | 45 | class QRCode { 46 | List bytes = []; 47 | 48 | QRCode(String text, QRSize size, QRCorrection level) { 49 | // FN 167. QR Code: Set the size of module 50 | // pL pH cn fn n 51 | bytes += cQrHeader.codeUnits + [0x03, 0x00, 0x31, 0x43] + [size.value]; 52 | 53 | // FN 169. QR Code: Select the error correction level 54 | // pL pH cn fn n 55 | bytes += cQrHeader.codeUnits + [0x03, 0x00, 0x31, 0x45] + [level.value]; 56 | 57 | // FN 180. QR Code: Store the data in the symbol storage area 58 | List textBytes = latin1.encode(text); 59 | // pL pH cn fn m 60 | bytes += cQrHeader.codeUnits + [textBytes.length + 3, 0x00, 0x31, 0x50, 0x30]; 61 | bytes += textBytes; 62 | 63 | // FN 182. QR Code: Transmit the size information of the symbol data in the symbol storage area 64 | // pL pH cn fn m 65 | bytes += cQrHeader.codeUnits + [0x03, 0x00, 0x31, 0x52, 0x30]; 66 | 67 | // FN 181. QR Code: Print the symbol data in the symbol storage area 68 | // pL pH cn fn m 69 | bytes += cQrHeader.codeUnits + [0x03, 0x00, 0x31, 0x51, 0x30]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/printer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | final flutterPrinterChannel = const MethodChannel('com.codingdevs.thermal_printer'); 4 | final flutterPrinterEventChannelBT = const EventChannel('com.codingdevs.thermal_printer/bt_state'); 5 | final flutterPrinterEventChannelUSB = const EventChannel('com.codingdevs.thermal_printer/usb_state'); 6 | final iosChannel = const MethodChannel('thermal_printer/methods'); 7 | final iosStateChannel = const EventChannel('thermal_printer/state'); 8 | 9 | enum BTStatus { none, connecting, connected, scanning, stopScanning } 10 | 11 | enum USBStatus { none, connecting, connected } 12 | 13 | enum TCPStatus { none, connected } 14 | 15 | abstract class Printer { 16 | Future image(Uint8List image, {int threshold = 150}); 17 | Future beep(); 18 | Future pulseDrawer(); 19 | Future setIp(String ipAddress); 20 | Future selfTest(); 21 | } 22 | 23 | abstract class BasePrinterInput {} 24 | 25 | // 26 | abstract class PrinterConnector { 27 | Future send(List bytes); 28 | Future connect(T model); 29 | Future disconnect({int? delayMs}); 30 | } 31 | 32 | abstract class GenericPrinter extends Printer { 33 | PrinterConnector connector; 34 | T model; 35 | GenericPrinter(this.connector, this.model) : super(); 36 | 37 | List encodeSetIP(String ip) { 38 | List buffer = [0x1f, 0x1b, 0x1f, 0x91, 0x00, 0x49, 0x50]; 39 | final List splittedIp = ip.split('.'); 40 | return buffer..addAll(splittedIp.map((e) => int.parse(e)).toList()); 41 | } 42 | 43 | Future sendToConnector(List Function() fn, {int? delayMs}) async { 44 | await connector.connect(model); 45 | final resp = await connector.send(fn()); 46 | if (delayMs != null) { 47 | await Future.delayed(Duration(milliseconds: delayMs)); 48 | } 49 | return resp; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/resources/capabilities.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "default": { 4 | "codePages": { 5 | "0": "CP437", 6 | "1": "CP932", 7 | "2": "CP850", 8 | "3": "CP860", 9 | "4": "CP863", 10 | "5": "CP865", 11 | "6": "Unknown", 12 | "7": "Unknown", 13 | "8": "Unknown", 14 | "11": "CP851", 15 | "12": "CP853", 16 | "13": "CP857", 17 | "14": "CP737", 18 | "15": "ISO_8859-7", 19 | "16": "CP1252", 20 | "17": "CP866", 21 | "18": "CP852", 22 | "19": "CP858", 23 | "20": "Unknown", 24 | "21": "CP874", 25 | "22": "Unknown", 26 | "23": "Unknown", 27 | "24": "Unknown", 28 | "25": "Unknown", 29 | "26": "Unknown", 30 | "30": "TCVN-3-1", 31 | "31": "TCVN-3-2", 32 | "32": "CP720", 33 | "33": "CP775", 34 | "34": "CP855", 35 | "35": "CP861", 36 | "36": "CP862", 37 | "37": "CP864", 38 | "38": "CP869", 39 | "39": "ISO_8859-2", 40 | "40": "ISO_8859-15", 41 | "41": "CP1098", 42 | "42": "CP774", 43 | "43": "CP772", 44 | "44": "CP1125", 45 | "45": "CP1250", 46 | "46": "CP1251", 47 | "47": "CP1253", 48 | "48": "CP1254", 49 | "49": "CP1255", 50 | "50": "CP1256", 51 | "51": "CP1257", 52 | "52": "CP1258", 53 | "53": "RK1048", 54 | "66": "Unknown", 55 | "67": "Unknown", 56 | "68": "Unknown", 57 | "69": "Unknown", 58 | "70": "Unknown", 59 | "71": "Unknown", 60 | "72": "Unknown", 61 | "73": "Unknown", 62 | "74": "Unknown", 63 | "75": "Unknown", 64 | "82": "Unknown", 65 | "254": "Unknown", 66 | "255": "Unknown" 67 | }, 68 | "vendor": "Generic", 69 | "model": "Default", 70 | "description": "Default ESC/POS profile" 71 | }, 72 | 73 | "XP-N160I": { 74 | "codePages": { 75 | "0": "CP437", 76 | "1": "CP932", 77 | "2": "CP850", 78 | "3": "CP860", 79 | "4": "CP863", 80 | "5": "CP865", 81 | "6": "CP1252", 82 | "7": "CP737", 83 | "8": "CP862", 84 | "9": "Unknown", 85 | "10": "Unknown", 86 | "16": "CP1252", 87 | "17": "CP866", 88 | "18": "CP852", 89 | "19": "CP858", 90 | "20": "Unknown", 91 | "21": "Unknown", 92 | "22": "Unknown", 93 | "23": "Unknown", 94 | "24": "CP747", 95 | "25": "CP1257", 96 | "27": "CP1258", 97 | "28": "CP864", 98 | "29": "CP1001", 99 | "30": "Unknown", 100 | "31": "Unknown", 101 | "32": "CP1255", 102 | "50": "CP437", 103 | "51": "CP932", 104 | "52": "CP437", 105 | "53": "CP858", 106 | "54": "CP858", 107 | "55": "CP860", 108 | "56": "CP861", 109 | "57": "CP863", 110 | "58": "CP865", 111 | "59": "CP866", 112 | "60": "CP855", 113 | "61": "CP857", 114 | "62": "CP862", 115 | "63": "CP864", 116 | "64": "CP737", 117 | "65": "CP851", 118 | "66": "CP869", 119 | "67": "CP928", 120 | "68": "CP772", 121 | "69": "CP774", 122 | "70": "CP874", 123 | "71": "CP1252", 124 | "72": "CP1250", 125 | "73": "CP1251", 126 | "74": "CP3840", 127 | "75": "CP3841", 128 | "76": "CP3843", 129 | "77": "CP3844", 130 | "78": "CP3845", 131 | "79": "CP3846", 132 | "80": "CP3847", 133 | "81": "CP3848", 134 | "82": "CP1001", 135 | "83": "CP2001", 136 | "84": "CP3001", 137 | "85": "CP3002", 138 | "86": "CP3011", 139 | "87": "CP3012", 140 | "88": "CP3021", 141 | "89": "CP3041" 142 | }, 143 | "vendor": "Xprinter", 144 | "model": "XP-N160I", 145 | "description": "" 146 | }, 147 | 148 | "RP80USE": { 149 | "codePages": { 150 | "0": "CP437", 151 | "1": "CP932", 152 | "2": "CP850", 153 | "3": "CP860", 154 | "4": "CP863", 155 | "5": "CP865", 156 | "6": "CP1251", 157 | "7": "CP866", 158 | "8": "Unknown", 159 | "9": "Unknown", 160 | "10": "Unknown", 161 | "15": "CP862", 162 | "16": "CP1252", 163 | "17": "CP1253", 164 | "18": "CP852", 165 | "19": "CP858", 166 | "20": "Unknown", 167 | "21": "Unknown", 168 | "22": "CP864", 169 | "23": "ISO_8859-1", 170 | "24": "CP737", 171 | "25": "CP1257", 172 | "26": "Unknown", 173 | "27": "CP720", 174 | "28": "CP855", 175 | "29": "CP857", 176 | "30": "CP1250", 177 | "31": "CP775", 178 | "32": "CP1254", 179 | "34": "CP1256", 180 | "35": "CP1258", 181 | "36": "ISO_8859-2", 182 | "37": "ISO_8859-3", 183 | "38": "ISO_8859-4", 184 | "39": "ISO_8859-5", 185 | "40": "ISO_8859-6", 186 | "41": "ISO_8859-7", 187 | "42": "ISO_8859-8", 188 | "43": "ISO_8859-9", 189 | "44": "ISO_8859-15", 190 | "45": "Unknown", 191 | "46": "CP856", 192 | "47": "CP874" 193 | }, 194 | "vendor": "Rongta", 195 | "model": "RP80USE", 196 | "description": "" 197 | }, 198 | 199 | "TP806L": { 200 | "codePages": { 201 | "0": "PC437", 202 | "1": "Katakana", 203 | "2": "PC850", 204 | "3": "PC860", 205 | "4": "PC863", 206 | "5": "PC865", 207 | "13": "PC857", 208 | "14": "PC737", 209 | "15": "ISO8859-7", 210 | "16": "WPC1252", 211 | "17": "PC866", 212 | "18": "PC852", 213 | "19": "PC858", 214 | "20": "KU42", 215 | "32": "PC720", 216 | "37": "PC864", 217 | "50": "WPC1256", 218 | "63": "ISO-8859-6" 219 | }, 220 | "vendor": "HPRT", 221 | "model": "TP806L", 222 | "description": "" 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/src/connectors/tcp.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | import 'package:dart_ping/dart_ping.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:thermal_printer/src/models/printer_device.dart'; 7 | import 'package:network_info_plus/network_info_plus.dart'; 8 | import 'package:thermal_printer/discovery.dart'; 9 | import 'package:thermal_printer/printer.dart'; 10 | import 'package:ping_discover_network_forked/ping_discover_network_forked.dart'; 11 | 12 | class TcpPrinterInput extends BasePrinterInput { 13 | final String ipAddress; 14 | final int port; 15 | final Duration timeout; 16 | TcpPrinterInput({ 17 | required this.ipAddress, 18 | this.port = 9100, 19 | this.timeout = const Duration(seconds: 5), 20 | }); 21 | } 22 | 23 | class TcpPrinterInfo { 24 | String address; 25 | TcpPrinterInfo({ 26 | required this.address, 27 | }); 28 | } 29 | 30 | class TcpPrinterConnector implements PrinterConnector { 31 | TcpPrinterConnector._(); 32 | static TcpPrinterConnector _instance = TcpPrinterConnector._(); 33 | 34 | static TcpPrinterConnector get instance => _instance; 35 | 36 | TcpPrinterConnector(); 37 | Socket? _socket; 38 | TCPStatus status = TCPStatus.none; 39 | 40 | Stream get _statusStream => _statusStreamController.stream; 41 | final StreamController _statusStreamController = StreamController.broadcast(); 42 | 43 | static Future>> discoverPrinters({String? ipAddress, int? port, Duration? timeOut}) async { 44 | final List> result = []; 45 | final defaultPort = port ?? 9100; 46 | 47 | String? deviceIp; 48 | if (Platform.isAndroid || Platform.isIOS) { 49 | deviceIp = await NetworkInfo().getWifiIP(); 50 | } else if (ipAddress != null) deviceIp = ipAddress; 51 | if (deviceIp == null) return result; 52 | 53 | final String subnet = deviceIp.substring(0, deviceIp.lastIndexOf('.')); 54 | // final List ips = List.generate(255, (index) => '$subnet.$index'); 55 | 56 | final stream = NetworkAnalyzer.discover2( 57 | subnet, 58 | defaultPort, 59 | timeout: timeOut ?? Duration(milliseconds: 4000), 60 | ); 61 | 62 | await for (var addr in stream) { 63 | if (addr.exists) { 64 | result.add(PrinterDiscovered(name: "${addr.ip}:$defaultPort", detail: TcpPrinterInfo(address: addr.ip))); 65 | } 66 | } 67 | 68 | return result; 69 | } 70 | 71 | /// Starts a scan for network printers. 72 | Stream discovery({TcpPrinterInput? model}) async* { 73 | final defaultPort = model?.port ?? 9100; 74 | 75 | String? deviceIp; 76 | if (Platform.isAndroid || Platform.isIOS) { 77 | deviceIp = await NetworkInfo().getWifiIP(); 78 | } else if (model?.ipAddress != null) { 79 | deviceIp = model!.ipAddress; 80 | } else { 81 | return; 82 | // throw Exception('No IP address provided'); 83 | } 84 | 85 | final String subnet = deviceIp!.substring(0, deviceIp.lastIndexOf('.')); 86 | // final List ips = List.generate(255, (index) => '$subnet.$index'); 87 | 88 | final stream = NetworkAnalyzer.discover2(subnet, defaultPort); 89 | 90 | await for (var data in stream.map((message) => message)) { 91 | if (data.exists) { 92 | yield PrinterDevice(name: "${data.ip}:$defaultPort", address: data.ip); 93 | } 94 | } 95 | } 96 | 97 | @override 98 | Future send(List bytes) async { 99 | try { 100 | // final _socket = await Socket.connect(_host, _port, timeout: _timeout); 101 | _socket?.add(Uint8List.fromList(bytes)); 102 | await Future.delayed(Duration(seconds: 1)); 103 | // await _socket?.flush(); 104 | // _socket?.destroy(); 105 | return true; 106 | } catch (e) { 107 | _socket?.destroy(); 108 | return false; 109 | } 110 | } 111 | 112 | @override 113 | Future connect(TcpPrinterInput model) async { 114 | try { 115 | if (status == TCPStatus.none) { 116 | _socket = await Socket.connect(model.ipAddress, model.port, timeout: model.timeout); 117 | status = TCPStatus.connected; 118 | debugPrint('socket connected'); //if opened you will get it here 119 | _statusStreamController.add(status); 120 | 121 | // Create ping object with desired args 122 | final ping = Ping('${model.ipAddress}', interval: 3, timeout: 7); 123 | 124 | // Begin ping process and listen for output 125 | ping.stream.listen((PingData data) { 126 | if (data.error != null) { 127 | debugPrint(' ----- ping error ${data.error}'); 128 | _socket?.destroy(); 129 | status = TCPStatus.none; 130 | _statusStreamController.add(status); 131 | } 132 | }); 133 | listenSocket(ping); 134 | } 135 | return true; 136 | } catch (e) { 137 | _socket?.destroy(); 138 | status = TCPStatus.none; 139 | _statusStreamController.add(status); 140 | return false; 141 | } 142 | } 143 | 144 | /// [delayMs]: milliseconds to wait after destroying the socket 145 | @override 146 | Future disconnect({int? delayMs}) async { 147 | try { 148 | // await _socket?.flush(); 149 | _socket?.destroy(); 150 | 151 | if (delayMs != null) { 152 | await Future.delayed(Duration(milliseconds: delayMs), () => null); 153 | } 154 | return true; 155 | } catch (e) { 156 | _socket?.destroy(); 157 | status = TCPStatus.none; 158 | _statusStreamController.add(status); 159 | return false; 160 | } 161 | } 162 | 163 | /// Gets the current state of the TCP module 164 | Stream get currentStatus async* { 165 | yield* _statusStream.cast(); 166 | } 167 | 168 | void listenSocket(Ping ping) { 169 | _socket?.listen( 170 | (dynamic message) { 171 | debugPrint('message $message'); 172 | }, 173 | onDone: () { 174 | status = TCPStatus.none; 175 | debugPrint('socket closed'); //if closed you will get it here 176 | _socket?.destroy(); 177 | ping.stop(); 178 | _statusStreamController.add(status); 179 | }, 180 | onError: (error) { 181 | status = TCPStatus.none; 182 | debugPrint('socket error $error'); 183 | _socket?.destroy(); 184 | ping.stop(); 185 | _statusStreamController.add(status); 186 | }, 187 | ); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/src/connectors/usb.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:thermal_printer/discovery.dart'; 6 | import 'package:thermal_printer/thermal_printer.dart'; 7 | 8 | class UsbPrinterInput extends BasePrinterInput { 9 | final String? name; 10 | final String? vendorId; 11 | final String? productId; 12 | UsbPrinterInput({ 13 | this.name, 14 | this.vendorId, 15 | this.productId, 16 | }); 17 | } 18 | 19 | class UsbPrinterInfo { 20 | String vendorId; 21 | String productId; 22 | String manufacturer; 23 | String product; 24 | String name; 25 | String? model; 26 | bool isDefault = false; 27 | String deviceId; 28 | UsbPrinterInfo.Android({ 29 | required this.vendorId, 30 | required this.productId, 31 | required this.manufacturer, 32 | required this.product, 33 | required this.name, 34 | required this.deviceId, 35 | }); 36 | UsbPrinterInfo.Windows({ 37 | required this.name, 38 | required this.model, 39 | required this.isDefault, 40 | this.vendorId = '', 41 | this.productId = '', 42 | this.manufacturer = '', 43 | this.product = '', 44 | this.deviceId = '', 45 | }); 46 | } 47 | 48 | class UsbPrinterConnector implements PrinterConnector { 49 | UsbPrinterConnector._() 50 | : vendorId = '', 51 | productId = '', 52 | name = '' { 53 | if (Platform.isAndroid) 54 | flutterPrinterEventChannelUSB.receiveBroadcastStream().listen((data) { 55 | if (data is int) { 56 | // log('Received event status usb: $data'); 57 | _status = USBStatus.values[data]; 58 | _statusStreamController.add(_status); 59 | } 60 | }); 61 | } 62 | 63 | static UsbPrinterConnector _instance = UsbPrinterConnector._(); 64 | 65 | static UsbPrinterConnector get instance => _instance; 66 | 67 | Stream get _statusStream => _statusStreamController.stream; 68 | final StreamController _statusStreamController = StreamController.broadcast(); 69 | 70 | UsbPrinterConnector.Android({required this.vendorId, required this.productId}) : name = ''; 71 | UsbPrinterConnector.Windows({required this.name}) 72 | : vendorId = '', 73 | productId = ''; 74 | 75 | String vendorId; 76 | String productId; 77 | String name; 78 | USBStatus _status = USBStatus.none; 79 | USBStatus get status => _status; 80 | 81 | setVendor(String vendorId) => this.vendorId = vendorId; 82 | setProduct(String productId) => this.productId = productId; 83 | setName(String name) => this.name = name; 84 | 85 | /// Gets the current state of the Bluetooth module 86 | Stream get currentStatus async* { 87 | if (Platform.isAndroid) { 88 | yield* _statusStream.cast(); 89 | } 90 | } 91 | 92 | static DiscoverResult discoverPrinters() async { 93 | if (Platform.isAndroid) { 94 | final List results = await flutterPrinterChannel.invokeMethod('getList'); 95 | return results 96 | .map((dynamic r) => PrinterDiscovered( 97 | name: r['product'], 98 | detail: UsbPrinterInfo.Android( 99 | vendorId: r['vendorId'], 100 | productId: r['productId'], 101 | manufacturer: r['manufacturer'], 102 | product: r['product'], 103 | name: r['name'], 104 | deviceId: r['deviceId'], 105 | ), 106 | )) 107 | .toList(); 108 | } 109 | if (Platform.isWindows) { 110 | final List results = await flutterPrinterChannel.invokeMethod('getList'); 111 | return results 112 | .map((dynamic result) => PrinterDiscovered( 113 | name: result['name'], 114 | detail: UsbPrinterInfo.Windows(isDefault: result['default'], name: result['name'], model: result['model']), 115 | )) 116 | .toList(); 117 | } 118 | return []; 119 | } 120 | 121 | Stream discovery() async* { 122 | if (Platform.isAndroid) { 123 | final List results = await flutterPrinterChannel.invokeMethod('getList'); 124 | for (final device in results) { 125 | var r = await device; 126 | yield PrinterDevice( 127 | name: r['product'], 128 | vendorId: r['vendorId'], 129 | productId: r['productId'], 130 | // name: r['name'], 131 | ); 132 | } 133 | } else if (Platform.isWindows) { 134 | final List results = await flutterPrinterChannel.invokeMethod('getList'); 135 | for (final device in results) { 136 | var r = await device; 137 | yield PrinterDevice( 138 | name: r['name'], 139 | // model: r['model'], 140 | ); 141 | } 142 | } 143 | } 144 | 145 | Future _connect({UsbPrinterInput? model}) async { 146 | if (Platform.isAndroid) { 147 | Map params = {"vendor": int.parse(model?.vendorId ?? vendorId), "product": int.parse(model?.productId ?? productId)}; 148 | return await flutterPrinterChannel.invokeMethod('connectPrinter', params); 149 | } else if (Platform.isWindows) { 150 | Map params = {"name": model?.name ?? name}; 151 | return await flutterPrinterChannel.invokeMethod('connectPrinter', params) == 1 ? true : false; 152 | } 153 | return false; 154 | } 155 | 156 | Future _close() async { 157 | if (Platform.isWindows) return await flutterPrinterChannel.invokeMethod('close') == 1 ? true : false; 158 | return false; 159 | } 160 | 161 | @override 162 | Future connect(UsbPrinterInput model) async { 163 | try { 164 | return await _connect(model: model); 165 | } catch (e) { 166 | return false; 167 | } 168 | } 169 | 170 | @override 171 | Future disconnect({int? delayMs}) async { 172 | try { 173 | return await _close(); 174 | } catch (e) { 175 | return false; 176 | } 177 | } 178 | 179 | @override 180 | Future send(List bytes) async { 181 | if (Platform.isAndroid) 182 | try { 183 | // final connected = await _connect(); 184 | // if (!connected) return false; 185 | Map params = {"bytes": bytes}; 186 | return await flutterPrinterChannel.invokeMethod('printBytes', params); 187 | } catch (e) { 188 | return false; 189 | } 190 | else if (Platform.isWindows) 191 | try { 192 | Map params = {"bytes": Uint8List.fromList(bytes)}; 193 | return await flutterPrinterChannel.invokeMethod('printBytes', params) == 1 ? true : false; 194 | } catch (e) { 195 | await this._close(); 196 | return false; 197 | } 198 | else 199 | return false; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /lib/src/models/printer_device.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class PrinterDevice { 4 | String name; 5 | String operatingSystem = Platform.operatingSystem; 6 | String? vendorId; 7 | String? productId; 8 | String? address; 9 | 10 | PrinterDevice({required this.name, this.address, this.vendorId, this.productId}); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/printer_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:thermal_printer/thermal_printer.dart'; 4 | 5 | enum PrinterType { bluetooth, usb, network } 6 | 7 | class PrinterManager { 8 | final bluetoothPrinterConnector = BluetoothPrinterConnector.instance; 9 | final tcpPrinterConnector = TcpPrinterConnector.instance; 10 | final usbPrinterConnector = UsbPrinterConnector.instance; 11 | 12 | PrinterManager._(); 13 | 14 | static PrinterManager _instance = PrinterManager._(); 15 | 16 | static PrinterManager get instance => _instance; 17 | 18 | Stream discovery({required PrinterType type, bool isBle = false, TcpPrinterInput? model}) { 19 | if (type == PrinterType.bluetooth && (Platform.isIOS || Platform.isAndroid)) { 20 | return bluetoothPrinterConnector.discovery(isBle: isBle); 21 | } else if (type == PrinterType.usb && (Platform.isAndroid || Platform.isWindows)) { 22 | return usbPrinterConnector.discovery(); 23 | } else { 24 | return tcpPrinterConnector.discovery(model: model); 25 | } 26 | } 27 | 28 | Future connect({required PrinterType type, required BasePrinterInput model}) async { 29 | if (type == PrinterType.bluetooth && (Platform.isIOS || Platform.isAndroid)) { 30 | try { 31 | var conn = await bluetoothPrinterConnector.connect(model as BluetoothPrinterInput); 32 | return conn; 33 | } catch (e) { 34 | throw Exception('model must be type of BluetoothPrinterInput'); 35 | } 36 | } else if (type == PrinterType.usb && (Platform.isAndroid || Platform.isWindows)) { 37 | try { 38 | var conn = await usbPrinterConnector.connect(model as UsbPrinterInput); 39 | return conn; 40 | } catch (e) { 41 | throw Exception('model must be type of UsbPrinterInput'); 42 | } 43 | } else { 44 | try { 45 | var conn = await tcpPrinterConnector.connect(model as TcpPrinterInput); 46 | return conn; 47 | } catch (e) { 48 | throw Exception('model must be type of TcpPrinterInput'); 49 | } 50 | } 51 | } 52 | 53 | Future disconnect({required PrinterType type, int? delayMs}) async { 54 | if (type == PrinterType.bluetooth && (Platform.isIOS || Platform.isAndroid)) { 55 | return await bluetoothPrinterConnector.disconnect(); 56 | } else if (type == PrinterType.usb && (Platform.isAndroid || Platform.isWindows)) { 57 | return await usbPrinterConnector.disconnect(delayMs: delayMs); 58 | } else { 59 | return await tcpPrinterConnector.disconnect(); 60 | } 61 | } 62 | 63 | Future send({required PrinterType type, required List bytes}) async { 64 | if (type == PrinterType.bluetooth && (Platform.isIOS || Platform.isAndroid)) { 65 | return await bluetoothPrinterConnector.send(bytes); 66 | } else if (type == PrinterType.usb && (Platform.isAndroid || Platform.isWindows)) { 67 | return await usbPrinterConnector.send(bytes); 68 | } else { 69 | return await tcpPrinterConnector.send(bytes); 70 | } 71 | } 72 | 73 | Stream get stateBluetooth => bluetoothPrinterConnector.currentStatus.cast(); 74 | Stream get stateUSB => usbPrinterConnector.currentStatus.cast(); 75 | Stream get stateTCP => tcpPrinterConnector.currentStatus.cast(); 76 | 77 | BTStatus get currentStatusBT => bluetoothPrinterConnector.status; 78 | USBStatus get currentStatusUSB => usbPrinterConnector.status; 79 | TCPStatus get currentStatusTCP => tcpPrinterConnector.status; 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/printers/escpos.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:thermal_printer/printer.dart'; 4 | import 'package:thermal_printer/src/utils.dart'; 5 | import 'package:image/image.dart' as img; 6 | 7 | class EscPosPrinter extends GenericPrinter { 8 | EscPosPrinter(PrinterConnector connector, T model, {this.dpi = 200, required this.width, this.beepCount = 4}) : super(connector, model); 9 | 10 | final int width; 11 | final int dpi; 12 | final int beepCount; 13 | 14 | @override 15 | Future beep() async { 16 | // return await sendToConnector(() => generator.beepFlash(n: beepCount)); 17 | return true; 18 | } 19 | 20 | @override 21 | Future image(Uint8List image, {int threshold = 150}) async { 22 | final decodedImage = img.decodeImage(image)!; 23 | 24 | var imgData = ImageData(width: decodedImage.width, height: decodedImage.height); 25 | final converted = toPixel(imgData, paperWidth: width, dpi: dpi, isTspl: false); 26 | 27 | // final resizedImage = copyResize(decodedImage, width: converted.width, height: converted.height, interpolation: Interpolation.cubic); 28 | 29 | final ms = 1000 + (converted.height * 0.5).toInt(); 30 | 31 | return await sendToConnector(() { 32 | // final printerImage = generator.image(resizedImage, threshold: threshold); 33 | List bytes = []; 34 | // bytes += generator.reset(); 35 | // bytes += generator.setLineSpacing(0); 36 | // bytes += printerImage; 37 | // bytes += generator.resetLineSpacing(); 38 | // bytes += generator.cut(); 39 | return bytes; 40 | }, delayMs: ms); 41 | } 42 | 43 | @override 44 | Future pulseDrawer() async { 45 | return await sendToConnector(() => [0x1b, 0x70, 0x00, 0x1e, 0xff, 0x00]); 46 | } 47 | 48 | @override 49 | Future selfTest() async { 50 | return true; 51 | } 52 | 53 | @override 54 | Future setIp(String ipAddress) async { 55 | return await sendToConnector(() => encodeSetIP(ipAddress)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/printers/star.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:enum_to_string/enum_to_string.dart'; 4 | import 'package:thermal_printer/printer.dart'; 5 | // import 'package:flutter_star_prnt/flutter_star_prnt.dart'; 6 | 7 | enum StarEmulation { StarPRNT, StarLine, StarGraphic } 8 | 9 | class StarPrinter extends Printer { 10 | StarPrinter({StarEmulation emulation = StarEmulation.StarGraphic, int width = 580}) { 11 | this._emulation = EnumToString.convertToString(emulation); 12 | this._width = width; 13 | } 14 | 15 | // ignore: unused_field 16 | late final String _emulation; 17 | // ignore: unused_field 18 | late final int _width; 19 | late final String? _selectedPrinter; 20 | 21 | // static DiscoverResult discoverStarPrinter() async { 22 | // return (await StarPrnt.portDiscovery(StarPortType.All)) 23 | // .map((e) => PrinterDiscovered( 24 | // name: e.modelName ?? 'Star Printer', 25 | // detail: e, 26 | // )) 27 | // .toList(); 28 | // } 29 | 30 | @override 31 | Future beep() async { 32 | return false; 33 | } 34 | 35 | @override 36 | Future image(Uint8List bytes, {int threshold = 150}) async { 37 | if (this._selectedPrinter == null) { 38 | throw new Exception("No printer available, please connect before sending."); 39 | } 40 | // final commands = PrintCommands(); 41 | // commands.appendBitmapByte(byteData: bytes, width: this._width, diffusion: true, bothScale: true, alignment: StarAlignmentPosition.Center); 42 | // commands.appendCutPaper(StarCutPaperAction.PartialCutWithFeed); 43 | // final result = await StarPrnt.sendCommands(portName: this._selectedPrinter!, emulation: this._emulation, printCommands: commands); 44 | // return result.isSuccess; 45 | return true; 46 | } 47 | 48 | @override 49 | Future pulseDrawer() async { 50 | // final commands = PrintCommands(); 51 | // commands.openCashDrawer(1); 52 | // commands.openCashDrawer(2); 53 | // final result = await StarPrnt.sendCommands(portName: this._selectedPrinter!, emulation: this._emulation, printCommands: commands); 54 | // return result.isSuccess; 55 | return true; 56 | } 57 | 58 | @override 59 | Future selfTest() async { 60 | return false; 61 | } 62 | 63 | @override 64 | Future setIp(String ipAddress) async { 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | class ImageData { 2 | final int width; 3 | final int height; 4 | ImageData({ 5 | required this.width, 6 | required this.height, 7 | }); 8 | } 9 | 10 | ImageData toPixel(ImageData image, {required int paperWidth, required int dpi, required bool isTspl}) { 11 | final double mmToInch = 0.036; 12 | 13 | int targetWidthPx = (paperWidth.toDouble() * dpi.toDouble() * mmToInch).toInt(); 14 | final int nearest = 8; 15 | targetWidthPx = (targetWidthPx - (targetWidthPx % nearest)).round(); 16 | final double widthRatio = targetWidthPx / image.width; 17 | 18 | int targetHeightPx = 0; 19 | if (isTspl) { 20 | targetHeightPx = (image.height.toDouble() * dpi.toDouble() * mmToInch).toInt(); 21 | } else { 22 | targetHeightPx = (image.height * widthRatio).toInt(); 23 | } 24 | return ImageData(width: targetWidthPx, height: targetHeightPx); 25 | } 26 | -------------------------------------------------------------------------------- /lib/thermal_printer.dart: -------------------------------------------------------------------------------- 1 | library thermal_printer; 2 | 3 | export './src/connectors/usb.dart'; 4 | export './src/connectors/bluetooth.dart'; 5 | export './src/utils.dart'; 6 | export './src/connectors/tcp.dart'; 7 | export './src/printers/escpos.dart'; 8 | export './src/printers/star.dart'; 9 | export './src/printers/tspl.dart'; 10 | export './printer.dart'; 11 | export './src/printer_manager.dart'; 12 | export './src/models/printer_device.dart'; 13 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: thermal_printer 2 | description: A flutter plugin that prints esc commands to printers in different platforms such as android, ios, windows and different interfaces Bluetooth and BLE, TCP and USB 3 | version: 1.0.5 4 | homepage: https://github.com/codingdevs/thermal_printer 5 | 6 | # This package supports all platforms listed below. 7 | platforms: 8 | android: 9 | ios: 10 | windows: 11 | 12 | environment: 13 | sdk: '>=2.18.4 <4.0.0' 14 | flutter: ">=1.20.0" 15 | 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | 20 | enum_to_string: ^2.0.1 21 | image: ^4.1.3 22 | 23 | network_info_plus: ^4.1.0 24 | ping_discover_network_forked: ^0.0.1 25 | rxdart: ^0.27.7 26 | gbk_codec: ^0.4.0 27 | hex: ^0.2.0 28 | dart_ping: ^9.0.1 29 | 30 | dev_dependencies: 31 | flutter_test: 32 | sdk: flutter 33 | 34 | # For information on the generic Dart part of this file, see the 35 | # following page: https://dart.dev/tools/pub/pubspec 36 | 37 | # The following section is specific to Flutter. 38 | flutter: 39 | # This section identifies this Flutter project as a plugin project. 40 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily 41 | # be modified. They are used by the tooling to maintain consistency when 42 | # adding or updating assets for this project. 43 | plugin: 44 | platforms: 45 | android: 46 | package: com.codingdevs.thermal_printer 47 | pluginClass: ThermalPrinterPlugin 48 | ios: 49 | pluginClass: ThermalPrinterPlugin 50 | windows: 51 | pluginClass: ThermalPrinterPlugin 52 | 53 | assets: 54 | - assets/resources/capabilities.json 55 | - packages/thermal_printer/resources/capabilities.json 56 | 57 | # To add assets to your plugin package, add an assets section, like this: 58 | # assets: 59 | # - images/a_dot_burr.jpeg 60 | # - images/a_dot_ham.jpeg 61 | # 62 | # For details regarding assets in packages, see 63 | # https://flutter.dev/assets-and-images/#from-packages 64 | # 65 | # An image asset can refer to one or more resolution-specific "variants", see 66 | # https://flutter.dev/assets-and-images/#resolution-aware. 67 | 68 | # To add custom fonts to your plugin package, add a fonts section here, 69 | # in this "flutter" section. Each entry in this list should have a 70 | # "family" key with the font family name, and a "fonts" key with a 71 | # list giving the asset and other descriptors for the font. For 72 | # example: 73 | # fonts: 74 | # - family: Schyler 75 | # fonts: 76 | # - asset: fonts/Schyler-Regular.ttf 77 | # - asset: fonts/Schyler-Italic.ttf 78 | # style: italic 79 | # - family: Trajan Pro 80 | # fonts: 81 | # - asset: fonts/TrajanPro.ttf 82 | # - asset: fonts/TrajanPro_Bold.ttf 83 | # weight: 700 84 | # 85 | # For details regarding fonts in packages, see 86 | # https://flutter.dev/custom-fonts/#from-packages 87 | -------------------------------------------------------------------------------- /test/thermal_printer_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('thermal_printer'); 6 | 7 | TestWidgetsFlutterBinding.ensureInitialized(); 8 | 9 | setUp(() { 10 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 11 | return '42'; 12 | }); 13 | }); 14 | 15 | tearDown(() { 16 | channel.setMockMethodCallHandler(null); 17 | }); 18 | 19 | test('getPlatformVersion', () async {}); 20 | } 21 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | set(PROJECT_NAME "thermal_printer") 3 | project(${PROJECT_NAME} LANGUAGES CXX) 4 | 5 | # This value is used when generating builds using this plugin, so it must 6 | # not be changed 7 | set(PLUGIN_NAME "thermal_printer_plugin") 8 | 9 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 10 | 11 | add_library(${PLUGIN_NAME} SHARED 12 | "thermal_printer_plugin.cpp" 13 | "include/printer.cpp" 14 | "include/printer.h" 15 | "include/utils.hpp" 16 | ) 17 | apply_standard_settings(${PLUGIN_NAME}) 18 | set_target_properties(${PLUGIN_NAME} PROPERTIES 19 | CXX_VISIBILITY_PRESET hidden) 20 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) 21 | target_include_directories(${PLUGIN_NAME} INTERFACE 22 | "${CMAKE_CURRENT_SOURCE_DIR}/include") 23 | target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) 24 | 25 | # List of absolute paths to libraries that should be bundled with the plugin 26 | set(thermal_printer_bundled_libraries 27 | "" 28 | PARENT_SCOPE 29 | ) 30 | -------------------------------------------------------------------------------- /windows/include/printer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "printer.h" 9 | #include "utils.hpp" 10 | 11 | HANDLE PrintManager::_hPrinter; 12 | 13 | std::vector PrintManager::listPrinters() 14 | { 15 | LPTSTR defaultPrinter; 16 | DWORD size = 0; 17 | GetDefaultPrinter(nullptr, &size); 18 | 19 | defaultPrinter = static_cast(malloc(size * sizeof(TCHAR))); 20 | if (!GetDefaultPrinter(defaultPrinter, &size)) 21 | { 22 | size = 0; 23 | } 24 | 25 | auto printers = std::vector{}; 26 | 27 | DWORD needed = 0, 28 | returned = 0, 29 | flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS; 30 | 31 | EnumPrintersW(flags, nullptr, 2, nullptr, 0, &needed, &returned); 32 | 33 | auto buffer = (PRINTER_INFO_2 *)malloc(needed); 34 | if (!buffer) 35 | { 36 | return printers; 37 | } 38 | 39 | auto result = EnumPrintersW(flags, nullptr, 2, (LPBYTE)buffer, needed, &needed, 40 | &returned); 41 | 42 | if (!result) 43 | { 44 | free(buffer); 45 | return printers; 46 | } 47 | 48 | for (DWORD i = 0; i < returned; i++) 49 | { 50 | printers.push_back(Printer{ 51 | toUtf8(buffer[i].pPrinterName), 52 | toUtf8(buffer[i].pDriverName), 53 | size > 0 && _tcsncmp(buffer[i].pPrinterName, defaultPrinter, size) == 0, // if this is the defaultprinter 54 | (buffer[i].Status & 55 | (PRINTER_STATUS_NOT_AVAILABLE | PRINTER_STATUS_ERROR | 56 | PRINTER_STATUS_OFFLINE | PRINTER_STATUS_PAUSED)) == 0}); 57 | } 58 | 59 | free(buffer); 60 | free(defaultPrinter); 61 | return printers; 62 | } 63 | 64 | BOOL PrintManager::pickPrinter(std::string printerName) 65 | { 66 | return OpenPrinterW((LPWSTR)fromUtf8(printerName).c_str(), &_hPrinter, NULL); 67 | } 68 | 69 | BOOL PrintManager::printBytes(std::vector data) 70 | { 71 | BOOL status = false; 72 | BOOL success = true; 73 | DOC_INFO_1W docInfo; 74 | DWORD dwJob = 0; 75 | DWORD written = 0; 76 | 77 | if (_hPrinter == INVALID_HANDLE_VALUE) 78 | { 79 | // throw std::exception("Printer handle is invalid."); 80 | success = false; 81 | } 82 | 83 | // Fill in default value of the print document 84 | docInfo.pDocName = L"FeedMe POS Print Job"; 85 | docInfo.pOutputFile = NULL; 86 | docInfo.pDatatype = L"RAW"; 87 | 88 | // Inform the spooler there is a new document 89 | dwJob = StartDocPrinterW(_hPrinter, 1, (LPBYTE)&docInfo); 90 | if (dwJob > 0) 91 | { 92 | // Start page 93 | status = StartPagePrinter(_hPrinter); 94 | if (status) 95 | { 96 | // Send data to the printer 97 | status = WritePrinter(_hPrinter, (LPVOID)std::data(data), (DWORD)data.size(), &written); 98 | EndPagePrinter(_hPrinter); 99 | } 100 | else 101 | { 102 | // throw std::exception("StartPagePrinter error."); 103 | success = false; 104 | } 105 | // Inform the spooler that the document hsa ended 106 | EndDocPrinter(_hPrinter); 107 | } 108 | else 109 | { 110 | // throw std::exception("StartDocPrinterW error."); 111 | success = false; 112 | } 113 | 114 | // Check if all data are flushed 115 | if (written != data.size()) 116 | { 117 | // throw std::exception("Fail to send all bytes"); 118 | success = false; 119 | } 120 | 121 | return success; 122 | } 123 | 124 | BOOL PrintManager::close() 125 | { 126 | if (_hPrinter != INVALID_HANDLE_VALUE) 127 | return ClosePrinter(_hPrinter); 128 | 129 | return false; 130 | } 131 | -------------------------------------------------------------------------------- /windows/include/printer.h: -------------------------------------------------------------------------------- 1 | #ifndef PRINTER_H_ 2 | #define PRINTER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct Printer 10 | { 11 | const std::string name; 12 | const std::string model; 13 | const bool default; 14 | const bool available; 15 | 16 | Printer(std::string name, 17 | std::string model, 18 | bool default, 19 | bool available) 20 | : name(name), 21 | model(model), 22 | default(default), 23 | available(available) {} 24 | }; 25 | 26 | class PrintManager 27 | { 28 | private: 29 | static HANDLE _hPrinter; 30 | 31 | public: 32 | PrintManager(){}; 33 | static std::vector listPrinters(); 34 | static BOOL pickPrinter(std::string pPrinterName); 35 | static BOOL printBytes(std::vector data); 36 | static BOOL close(); 37 | operator HANDLE() { return _hPrinter; } 38 | }; 39 | 40 | #endif // PRINTER_H_ 41 | -------------------------------------------------------------------------------- /windows/include/thermal_printer/thermal_printer_plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_PLUGIN_THERMAL_PRINTER_PLUGIN_C_API_H_ 2 | #define FLUTTER_PLUGIN_THERMAL_PRINTER_PLUGIN_C_API_H_ 3 | 4 | #include 5 | 6 | #ifdef FLUTTER_PLUGIN_IMPL 7 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) 8 | #else 9 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) 10 | #endif 11 | 12 | #if defined(__cplusplus) 13 | extern "C" { 14 | #endif 15 | 16 | FLUTTER_PLUGIN_EXPORT void ThermalPrinterPluginRegisterWithRegistrar( 17 | FlutterDesktopPluginRegistrarRef registrar); 18 | 19 | #if defined(__cplusplus) 20 | } // extern "C" 21 | #endif 22 | 23 | #endif // FLUTTER_PLUGIN_THERMAL_PRINTER_PLUGIN_C_API_H_ 24 | -------------------------------------------------------------------------------- /windows/include/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H_ 2 | #define UTILS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | std::string toUtf8(std::wstring wstr) 12 | { 13 | int cbMultiByte = 14 | WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); 15 | LPSTR lpMultiByteStr = (LPSTR)malloc(cbMultiByte); 16 | cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 17 | lpMultiByteStr, cbMultiByte, NULL, NULL); 18 | std::string ret = lpMultiByteStr; 19 | free(lpMultiByteStr); 20 | return ret; 21 | } 22 | 23 | std::string toUtf8(TCHAR *tstr) 24 | { 25 | #ifndef UNICODE 26 | #error "Non unicode build not supported" 27 | #endif 28 | 29 | if (!tstr) 30 | { 31 | return std::string{}; 32 | } 33 | 34 | return toUtf8(std::wstring{tstr}); 35 | } 36 | 37 | std::wstring fromUtf8(std::string str) 38 | { 39 | auto len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), 40 | static_cast(str.length()), nullptr, 0); 41 | if (len <= 0) 42 | { 43 | return false; 44 | } 45 | 46 | auto wstr = std::wstring{}; 47 | wstr.resize(len); 48 | MultiByteToWideChar(CP_ACP, 0, str.c_str(), static_cast(str.length()), 49 | &wstr[0], len); 50 | 51 | return wstr; 52 | } 53 | 54 | #endif -------------------------------------------------------------------------------- /windows/thermal_printer_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "include/thermal_printer/thermal_printer_plugin.h" 2 | 3 | // This must be included before many other Windows headers. 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "include/printer.h" 15 | 16 | namespace 17 | { 18 | using flutter::EncodableList; 19 | using flutter::EncodableMap; 20 | using flutter::EncodableValue; 21 | 22 | class ThermalPrinterPlugin: public flutter::Plugin 23 | { 24 | public: 25 | static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); 26 | 27 | ThermalPrinterPlugin(); 28 | 29 | virtual ~ThermalPrinterPlugin(); 30 | 31 | private: 32 | // Called when a method is called on this plugin's channel from Dart. 33 | void HandleMethodCall( 34 | const flutter::MethodCall &method_call, 35 | std::unique_ptr> result); 36 | }; 37 | 38 | // static 39 | void ThermalPrinterPlugin::RegisterWithRegistrar( 40 | flutter::PluginRegistrarWindows *registrar) 41 | { 42 | auto channel = 43 | std::make_unique>( 44 | registrar->messenger(), "com.codingdevs.thermal_printer", 45 | &flutter::StandardMethodCodec::GetInstance()); 46 | 47 | auto plugin = std::make_unique(); 48 | 49 | channel->SetMethodCallHandler( 50 | [plugin_pointer = plugin.get()](const auto &call, auto result) 51 | { 52 | plugin_pointer->HandleMethodCall(call, std::move(result)); 53 | }); 54 | 55 | registrar->AddPlugin(std::move(plugin)); 56 | } 57 | 58 | ThermalPrinterPlugin::ThermalPrinterPlugin() {} 59 | 60 | ThermalPrinterPlugin::~ThermalPrinterPlugin() {} 61 | 62 | void ThermalPrinterPlugin::HandleMethodCall( 63 | const flutter::MethodCall &method_call, 64 | std::unique_ptr> result) 65 | { 66 | // Get arguments the C++ way 67 | const auto *args = std::get_if(method_call.arguments()); 68 | 69 | if (method_call.method_name().compare("getList") == 0) 70 | { 71 | auto printers = PrintManager::listPrinters(); 72 | auto list = EncodableList{}; 73 | for (auto printer : printers) 74 | { 75 | auto map = EncodableMap{}; 76 | map[EncodableValue("name")] = 77 | EncodableValue(printer.name); 78 | map[EncodableValue("model")] = 79 | EncodableValue(printer.model); 80 | map[EncodableValue("default")] = 81 | EncodableValue(printer.default); 82 | map[EncodableValue("available")] = 83 | EncodableValue(printer.available); 84 | list.push_back(map); 85 | } 86 | 87 | return result->Success(list); 88 | } 89 | else if (method_call.method_name().compare("connectPrinter") == 0) 90 | { 91 | std::string printerName; 92 | 93 | if (args) 94 | { 95 | auto name_it = args->find(EncodableValue("name")); 96 | if (name_it != args->end()) 97 | { 98 | printerName = std::get(name_it->second); 99 | } 100 | 101 | auto success = PrintManager::pickPrinter(printerName); 102 | return result->Success(EncodableValue(success)); 103 | } 104 | 105 | return result->Success(EncodableValue(false)); 106 | } 107 | else if (method_call.method_name().compare("close") == 0) 108 | { 109 | auto success = PrintManager::close(); 110 | return result->Success(EncodableValue(success)); 111 | } 112 | else if (method_call.method_name().compare("printBytes") == 0) 113 | { 114 | std::vector bytes; 115 | 116 | if (args) 117 | { 118 | auto bytes_it = args->find(EncodableValue("bytes")); 119 | if (bytes_it != args->end()) 120 | { 121 | bytes = std::get>(bytes_it->second); 122 | } 123 | 124 | auto success = PrintManager::printBytes(bytes); 125 | return result->Success(EncodableValue(success)); 126 | } 127 | } 128 | else 129 | { 130 | result->NotImplemented(); 131 | } 132 | } 133 | 134 | } // namespace 135 | 136 | void ThermalPrinterPluginRegisterWithRegistrar( 137 | FlutterDesktopPluginRegistrarRef registrar) 138 | { 139 | ThermalPrinterPlugin::RegisterWithRegistrar( 140 | flutter::PluginRegistrarManager::GetInstance() 141 | ->GetRegistrar(registrar)); 142 | } 143 | --------------------------------------------------------------------------------