scan_page.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:flutter/material.dart';
  4. import 'package:image/image.dart' as img;
  5. import 'package:image_picker/image_picker.dart';
  6. import 'package:qr_code_scanner/qr_code_scanner.dart';
  7. import 'package:zxing2/qrcode.dart';
  8. import '../../common.dart';
  9. import '../../models/platform_model.dart';
  10. import '../widgets/dialog.dart';
  11. class ScanPage extends StatefulWidget {
  12. @override
  13. State<ScanPage> createState() => _ScanPageState();
  14. }
  15. class _ScanPageState extends State<ScanPage> {
  16. QRViewController? controller;
  17. final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  18. StreamSubscription? scanSubscription;
  19. @override
  20. void reassemble() {
  21. super.reassemble();
  22. if (isAndroid && controller != null) {
  23. controller!.pauseCamera();
  24. } else if (controller != null) {
  25. controller!.resumeCamera();
  26. }
  27. }
  28. @override
  29. Widget build(BuildContext context) {
  30. return Scaffold(
  31. appBar: AppBar(
  32. title: const Text('Scan QR'),
  33. actions: [
  34. _buildImagePickerButton(),
  35. _buildFlashToggleButton(),
  36. _buildCameraSwitchButton(),
  37. ],
  38. ),
  39. body: _buildQrView(context),
  40. );
  41. }
  42. Widget _buildQrView(BuildContext context) {
  43. var scanArea = MediaQuery.of(context).size.width < 400 ||
  44. MediaQuery.of(context).size.height < 400
  45. ? 150.0
  46. : 300.0;
  47. return QRView(
  48. key: qrKey,
  49. onQRViewCreated: _onQRViewCreated,
  50. overlay: QrScannerOverlayShape(
  51. borderColor: Colors.red,
  52. borderRadius: 10,
  53. borderLength: 30,
  54. borderWidth: 10,
  55. cutOutSize: scanArea,
  56. ),
  57. onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
  58. );
  59. }
  60. void _onQRViewCreated(QRViewController controller) {
  61. setState(() {
  62. this.controller = controller;
  63. });
  64. scanSubscription = controller.scannedDataStream.listen((scanData) {
  65. if (scanData.code != null) {
  66. showServerSettingFromQr(scanData.code!);
  67. }
  68. });
  69. }
  70. void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
  71. if (!p) {
  72. showToast('No permission');
  73. }
  74. }
  75. Future<void> _pickImage() async {
  76. final ImagePicker picker = ImagePicker();
  77. final XFile? file = await picker.pickImage(source: ImageSource.gallery);
  78. if (file != null) {
  79. try {
  80. var image = img.decodeImage(await File(file.path).readAsBytes())!;
  81. LuminanceSource source = RGBLuminanceSource(
  82. image.width,
  83. image.height,
  84. image.getBytes(order: img.ChannelOrder.abgr).buffer.asInt32List(),
  85. );
  86. var bitmap = BinaryBitmap(HybridBinarizer(source));
  87. var reader = QRCodeReader();
  88. var result = reader.decode(bitmap);
  89. if (result.text.startsWith(bind.mainUriPrefixSync())) {
  90. handleUriLink(uriString: result.text);
  91. } else {
  92. showServerSettingFromQr(result.text);
  93. }
  94. } catch (e) {
  95. showToast('No QR code found');
  96. }
  97. }
  98. }
  99. Widget _buildImagePickerButton() {
  100. return IconButton(
  101. color: Colors.white,
  102. icon: Icon(Icons.image_search),
  103. iconSize: 32.0,
  104. onPressed: _pickImage,
  105. );
  106. }
  107. Widget _buildFlashToggleButton() {
  108. return IconButton(
  109. color: Colors.yellow,
  110. icon: Icon(Icons.flash_on),
  111. iconSize: 32.0,
  112. onPressed: () async {
  113. await controller?.toggleFlash();
  114. },
  115. );
  116. }
  117. Widget _buildCameraSwitchButton() {
  118. return IconButton(
  119. color: Colors.white,
  120. icon: Icon(Icons.switch_camera),
  121. iconSize: 32.0,
  122. onPressed: () async {
  123. await controller?.flipCamera();
  124. },
  125. );
  126. }
  127. @override
  128. void dispose() {
  129. scanSubscription?.cancel();
  130. controller?.dispose();
  131. super.dispose();
  132. }
  133. void showServerSettingFromQr(String data) async {
  134. closeConnection();
  135. await controller?.pauseCamera();
  136. if (!data.startsWith('config=')) {
  137. showToast('Invalid QR code');
  138. return;
  139. }
  140. try {
  141. final sc = ServerConfig.decode(data.substring(7));
  142. Timer(Duration(milliseconds: 60), () {
  143. showServerSettingsWithValue(sc, gFFI.dialogManager);
  144. });
  145. } catch (e) {
  146. showToast('Invalid QR code');
  147. }
  148. }
  149. }