scan_view.dart 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. part of fqreader;
  2. class ScanView extends StatefulWidget {
  3. /// 扫描事件
  4. final ScanEvent onScan;
  5. /// 扫描区域位置大小
  6. final Rect? scanRect;
  7. /// 扫描区域大小
  8. final Size? scanSize;
  9. /// ScanView控件大小
  10. final Size viewSize;
  11. /// 扫描框的位置(位于图片)
  12. final Alignment scanAilgn;
  13. /// view的位置(位于图片)
  14. final Alignment viewAilgn;
  15. /// 屏幕分辨率
  16. final double devicePixelRatio;
  17. final Color? maskColor;
  18. /// 是否立即扫描
  19. final bool autoScan;
  20. /// 是否连续扫描
  21. final bool continuityScan;
  22. /// 连续扫描间隔
  23. final Duration scanInterval;
  24. /// 扫描的条码类型
  25. final List<ScanType> scanType;
  26. const ScanView(
  27. {Key? key,
  28. required this.onScan,
  29. required this.viewSize,
  30. this.scanRect,
  31. this.scanSize,
  32. this.scanAilgn = Alignment.topLeft,
  33. this.viewAilgn = Alignment.topLeft,
  34. required this.devicePixelRatio,
  35. this.maskColor,
  36. this.scanType = const [ScanType.ALL],
  37. this.autoScan = true,
  38. this.continuityScan = false,
  39. this.scanInterval = const Duration(milliseconds: 500)})
  40. : assert(scanSize != null || scanRect != null),
  41. assert(!(scanSize != null && scanRect != null)),
  42. super(key: key);
  43. @override
  44. State<StatefulWidget> createState() => ScanViewState();
  45. }
  46. class ScanViewState extends State<ScanView> {
  47. int? _textureId;
  48. StreamSubscription? _readySubscription;
  49. Size? _cameraSize;
  50. double? _cameraPixelRatio;
  51. @override
  52. void initState() {
  53. // TODO: implement initState
  54. super.initState();
  55. if (widget.autoScan) {
  56. this.startScan();
  57. }
  58. }
  59. @override
  60. Widget build(BuildContext context) {
  61. return _textureId != null
  62. ? new _ScanViewTexture(
  63. textureId: _textureId!,
  64. cameraSize: _cameraSize!,
  65. scanRect: widget.scanRect ??
  66. _getViewRect(_cameraSize!, widget.scanSize!, widget.scanAilgn),
  67. maskColor: widget.maskColor,
  68. viewRect:
  69. _getViewRect(_cameraSize!, widget.viewSize, widget.viewAilgn),
  70. )
  71. : new SizedBox(
  72. width: widget.viewSize.width, height: widget.viewSize.height);
  73. }
  74. Rect _getViewRect(Size cameraSize, Size viewSize, Alignment alignment) {
  75. var offset = (cameraSize - viewSize) as Offset;
  76. return alignment.alongOffset(offset) & viewSize;
  77. }
  78. Size _cameraToViewSize(Size cameraSize) {
  79. return Size(cameraSize.width * _cameraPixelRatio!,
  80. cameraSize.height * _cameraPixelRatio!);
  81. }
  82. Rect _viewToCameraRect(Rect viewSize) {
  83. return Rect.fromLTWH(
  84. viewSize.left / _cameraPixelRatio!,
  85. viewSize.top / _cameraPixelRatio!,
  86. viewSize.width / _cameraPixelRatio!,
  87. viewSize.height / _cameraPixelRatio!);
  88. }
  89. @override
  90. void dispose() {
  91. // TODO: implement deactivate
  92. super.dispose();
  93. this.release();
  94. }
  95. /**
  96. * 开始扫描
  97. */
  98. Future startScan() async {
  99. if (this._textureId == null) {
  100. var initResult = await Fqreader._initView(
  101. devicePixelRatio: widget.devicePixelRatio,
  102. scanType: widget.scanType);
  103. _cameraPixelRatio = widget.viewSize.width / initResult.cameraSize.width;
  104. _cameraSize = _cameraToViewSize(initResult.cameraSize);
  105. _textureId = initResult.textureID;
  106. Rect scanRect = widget.scanRect ??
  107. _getViewRect(_cameraSize!, widget.scanSize!, widget.scanAilgn);
  108. var left = scanRect.left < 0.0 ? 0.0 : scanRect.left;
  109. var top = scanRect.top < 0.0 ? 0.0 : scanRect.top;
  110. var width = (left + scanRect.width > _cameraSize!.width) ? _cameraSize!.width - left : scanRect.width;
  111. var height = (top + scanRect.height > _cameraSize!.height) ? _cameraSize!.height - top : scanRect.height;
  112. scanRect = Rect.fromLTWH(
  113. left,
  114. top,
  115. width,
  116. height); // 确保不超出界限
  117. scanRect = _viewToCameraRect(scanRect);
  118. Fqreader._setScanRect(scanRect, widget.devicePixelRatio);
  119. _readySubscription = new EventChannel('fqreader/scanEvents$_textureId')
  120. .receiveBroadcastStream()
  121. .listen(_listener);
  122. setState(() {});
  123. }
  124. await Fqreader._startScan();
  125. }
  126. /**
  127. * 暂停扫描
  128. */
  129. Future stopScan() async {
  130. await Fqreader._stopScan();
  131. }
  132. /**
  133. * 开灯
  134. */
  135. Future turnOn() async {
  136. await Fqreader._turnOn();
  137. }
  138. /**
  139. * 关灯
  140. */
  141. Future turnOff() async {
  142. await Fqreader._turnOff();
  143. }
  144. Future release() async {
  145. if (this._textureId != null) {
  146. this._textureId = null;
  147. try {
  148. setState(() {});
  149. } catch (_) {}
  150. await _readySubscription!.cancel();
  151. await Fqreader._release();
  152. }
  153. }
  154. void _listener(dynamic value) {
  155. if (widget != null) {
  156. if (!widget.continuityScan) //是否连续扫描
  157. {
  158. Fqreader._stopScan();
  159. }
  160. var result = ScanResult(
  161. scanType: _parseScanType(value['scanType']),
  162. data: value['data'],
  163. );
  164. widget.onScan(result).then((result) {
  165. if (widget.continuityScan && result) {
  166. Future.delayed(widget.scanInterval, () {
  167. Fqreader._startScan();
  168. });
  169. } else {
  170. Fqreader._stopScan();
  171. }
  172. });
  173. }
  174. }
  175. }
  176. class _ScanViewTexture extends LeafRenderObjectWidget {
  177. final Size cameraSize;
  178. final Rect viewRect;
  179. final Rect? scanRect;
  180. final Color? maskColor;
  181. /// Creates a widget backed by the texture identified by [textureId].
  182. const _ScanViewTexture(
  183. {Key? key,
  184. required this.cameraSize,
  185. required this.viewRect,
  186. required this.textureId,
  187. this.scanRect,
  188. this.maskColor})
  189. : super(key: key);
  190. /// The identity of the backend texture.
  191. final int textureId;
  192. @override
  193. _ScanViewTextureBox createRenderObject(BuildContext context) =>
  194. _ScanViewTextureBox(
  195. textureId: textureId, cameraSize: cameraSize, viewRect: viewRect, maskColor: maskColor,scanRect: scanRect);
  196. @override
  197. void updateRenderObject(
  198. BuildContext context, _ScanViewTextureBox renderObject) {
  199. renderObject.textureId = textureId;
  200. renderObject.maskColor = maskColor;
  201. renderObject.cameraSize = cameraSize;
  202. renderObject.viewRect = viewRect;
  203. renderObject.scanRect = scanRect;
  204. }
  205. }
  206. class _ScanViewTextureBox extends RenderBox {
  207. /// Creates a box backed by the texture identified by [textureId].
  208. _ScanViewTextureBox(
  209. {required int textureId,
  210. required Size cameraSize,
  211. required Rect viewRect,
  212. required Rect? scanRect,
  213. Color? maskColor})
  214. : _textureId = textureId,
  215. _cameraSize = cameraSize,
  216. _viewRect = viewRect,
  217. _scanRect = scanRect,
  218. _maskColor = maskColor;
  219. /// The identity of the backend texture.
  220. int get textureId => _textureId;
  221. int _textureId;
  222. set textureId(int value) {
  223. if (value != _textureId) {
  224. _textureId = value;
  225. markNeedsPaint();
  226. }
  227. }
  228. Size get cameraSize => _cameraSize;
  229. Size _cameraSize;
  230. set cameraSize(Size value) {
  231. if (value != _cameraSize) {
  232. _cameraSize = value;
  233. markNeedsPaint();
  234. }
  235. }
  236. Rect get viewRect => _viewRect;
  237. Rect _viewRect;
  238. set viewRect(Rect value) {
  239. if (value != _viewRect) {
  240. _viewRect = value;
  241. markNeedsPaint();
  242. }
  243. }
  244. Rect? get scanRect => _scanRect;
  245. Rect? _scanRect;
  246. set scanRect(Rect? value) {
  247. if (value != _scanRect) {
  248. _scanRect = value;
  249. markNeedsPaint();
  250. }
  251. }
  252. Color? get maskColor => _maskColor;
  253. Color? _maskColor;
  254. set maskColor(Color? value) {
  255. if (value != _maskColor) {
  256. _maskColor = value;
  257. markNeedsPaint();
  258. }
  259. }
  260. @override
  261. bool get sizedByParent => true;
  262. @override
  263. bool get alwaysNeedsCompositing => true;
  264. @override
  265. bool get isRepaintBoundary => true;
  266. @override
  267. void performResize() {
  268. size = viewRect.size;
  269. }
  270. @override
  271. bool hitTestSelf(Offset position) => true;
  272. @override
  273. void paint(PaintingContext context, Offset offset) {
  274. if (_textureId == null) return;
  275. var cameraSize = Size(viewRect.width,
  276. viewRect.width * (_cameraSize.height / _cameraSize.width));
  277. context.pushClipRect(needsCompositing, offset,
  278. Rect.fromLTWH(0, 0, viewRect.width, viewRect.height),
  279. (PaintingContext context, Offset offset) {
  280. context.addLayer(TextureLayer(
  281. rect: Rect.fromLTWH(offset.dx - viewRect.left, offset.dy - viewRect.top,
  282. cameraSize.width, cameraSize.height),
  283. textureId: _textureId,
  284. ));
  285. if (maskColor != null) {
  286. Paint paint = Paint()..color = maskColor!;
  287. context.canvas
  288. .drawRect(Rect.fromLTWH(0, 0, viewRect.width, scanRect!.top), paint);
  289. context.canvas.drawRect(
  290. Rect.fromLTWH(0, scanRect!.top, scanRect!.left, scanRect!.height),
  291. paint);
  292. context.canvas.drawRect(
  293. Rect.fromLTWH(0, scanRect!.bottom, viewRect.width,
  294. viewRect.height - scanRect!.bottom),
  295. paint);
  296. context.canvas.drawRect(
  297. Rect.fromLTWH(scanRect!.right, scanRect!.top,
  298. viewRect.width - scanRect!.right, scanRect!.height),
  299. paint);
  300. }
  301. });
  302. }
  303. }