import 'dart:async'; import 'package:flutter/services.dart'; import 'package:rxdart/rxdart.dart'; import 'bluetooth_print_model.dart'; class BluetoothPrint { static const String namespace = 'bluetooth_print'; static const int connected = 1; static const int disconnected = 0; static const MethodChannel _channel = MethodChannel('$namespace/methods'); static const EventChannel _stateChannel = EventChannel('$namespace/state'); Stream get _methodStream => _methodStreamController.stream; final StreamController _methodStreamController = StreamController.broadcast(); BluetoothPrint._() { _channel.setMethodCallHandler((MethodCall call) async { _methodStreamController.add(call); }); } static BluetoothPrint _instance = new BluetoothPrint._(); static BluetoothPrint get instance => _instance; Future get isAvailable async => await _channel.invokeMethod('isAvailable').then((d) => d); Future get isOn async => await _channel.invokeMethod('isOn').then((d) => d); Future get isConnected async => await _channel.invokeMethod('isConnected'); BehaviorSubject _isScanning = BehaviorSubject.seeded(false); Stream get isScanning => _isScanning.stream; BehaviorSubject> _scanResults = BehaviorSubject.seeded([]); Stream> get scanResults => _scanResults.stream; PublishSubject _stopScanPill = new PublishSubject(); /// Gets the current state of the Bluetooth module Stream get state async* { yield await _channel.invokeMethod('state').then((s) => s); yield* _stateChannel.receiveBroadcastStream().map((s) => s); } /// Starts a scan for Bluetooth Low Energy devices /// Timeout closes the stream after a specified [Duration] Stream scan({ Duration? timeout, }) async* { if (_isScanning.value == true) { throw Exception('Another scan is already in progress.'); } // Emit to isScanning _isScanning.add(true); final killStreams = []; killStreams.add(_stopScanPill); if (timeout != null) { killStreams.add(Rx.timer(null, timeout)); } // Clear scan results list _scanResults.add([]); try { await _channel.invokeMethod('startScan'); } catch (e) { print('Error starting scan.'); _stopScanPill.add(null); _isScanning.add(false); throw e; } yield* BluetoothPrint.instance._methodStream .where((m) => m.method == "ScanResult") .map((m) => m.arguments) .takeUntil(Rx.merge(killStreams)) .doOnDone(stopScan) .map((map) { final device = BluetoothDevice.fromJson(Map.from(map)); final List list = _scanResults.value; int newIndex = -1; list.asMap().forEach((index, e) { if (e.address == device.address) { newIndex = index; } }); if (newIndex != -1) { list[newIndex] = device; } else { list.add(device); } _scanResults.add(list); return device; }); } Future startScan({ Duration? timeout, }) async { await scan(timeout: timeout).drain(); return _scanResults.value; } /// Stops a scan for Bluetooth Low Energy devices Future stopScan() async { await _channel.invokeMethod('stopScan'); _stopScanPill.add(null); _isScanning.add(false); } Future connect(BluetoothDevice device) => _channel.invokeMethod('connect', device.toJson()); Future disconnect() => _channel.invokeMethod('disconnect'); Future destroy() => _channel.invokeMethod('destroy'); Future printReceipt( Map config, List data) { Map args = {}; args['config'] = config; args['data'] = data.map((m) { return m.toJson(); }).toList(); _channel.invokeMethod('printReceipt', args); return Future.value(true); } Future printLabel(Map config, List data) { Map args = {}; args['config'] = config; args['data'] = data.map((m) { return m.toJson(); }).toList(); _channel.invokeMethod('printLabel', args); return Future.value(true); } Future printTest() => _channel.invokeMethod('printTest'); Future printRawData(Uint8List data) { return _channel.invokeMethod('printRawData', {'data': data}); } }