scan_view.dart 9.4 KB

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