155 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:async';
 | |
| import 'dart:io';
 | |
| 
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:image/image.dart' as img;
 | |
| import 'package:image_picker/image_picker.dart';
 | |
| import 'package:qr_code_scanner/qr_code_scanner.dart';
 | |
| import 'package:zxing2/qrcode.dart';
 | |
| 
 | |
| import '../../common.dart';
 | |
| import '../../models/platform_model.dart';
 | |
| import '../widgets/dialog.dart';
 | |
| 
 | |
| class ScanPage extends StatefulWidget {
 | |
|   @override
 | |
|   State<ScanPage> createState() => _ScanPageState();
 | |
| }
 | |
| 
 | |
| class _ScanPageState extends State<ScanPage> {
 | |
|   QRViewController? controller;
 | |
|   final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
 | |
| 
 | |
|   // In order to get hot reload to work we need to pause the camera if the platform
 | |
|   // is android, or resume the camera if the platform is iOS.
 | |
|   @override
 | |
|   void reassemble() {
 | |
|     super.reassemble();
 | |
|     if (isAndroid) {
 | |
|       controller!.pauseCamera();
 | |
|     }
 | |
|     controller!.resumeCamera();
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Scaffold(
 | |
|         appBar: AppBar(
 | |
|           title: const Text('Scan QR'),
 | |
|           actions: [
 | |
|             IconButton(
 | |
|                 color: Colors.white,
 | |
|                 icon: Icon(Icons.image_search),
 | |
|                 iconSize: 32.0,
 | |
|                 onPressed: () async {
 | |
|                   final ImagePicker picker = ImagePicker();
 | |
|                   final XFile? file =
 | |
|                       await picker.pickImage(source: ImageSource.gallery);
 | |
|                   if (file != null) {
 | |
|                     var image = img.decodeNamedImage(
 | |
|                         file.path, File(file.path).readAsBytesSync())!;
 | |
| 
 | |
|                     LuminanceSource source = RGBLuminanceSource(
 | |
|                         image.width,
 | |
|                         image.height,
 | |
|                         image
 | |
|                             .getBytes(order: img.ChannelOrder.abgr)
 | |
|                             .buffer
 | |
|                             .asInt32List());
 | |
|                     var bitmap = BinaryBitmap(HybridBinarizer(source));
 | |
| 
 | |
|                     var reader = QRCodeReader();
 | |
|                     try {
 | |
|                       var result = reader.decode(bitmap);
 | |
|                       if (result.text.startsWith(bind.mainUriPrefixSync())) {
 | |
|                         handleUriLink(uriString: result.text);
 | |
|                       } else {
 | |
|                         showServerSettingFromQr(result.text);
 | |
|                       }
 | |
|                     } catch (e) {
 | |
|                       showToast('No QR code found');
 | |
|                     }
 | |
|                   }
 | |
|                 }),
 | |
|             IconButton(
 | |
|                 color: Colors.yellow,
 | |
|                 icon: Icon(Icons.flash_on),
 | |
|                 iconSize: 32.0,
 | |
|                 onPressed: () async {
 | |
|                   await controller?.toggleFlash();
 | |
|                 }),
 | |
|             IconButton(
 | |
|               color: Colors.white,
 | |
|               icon: Icon(Icons.switch_camera),
 | |
|               iconSize: 32.0,
 | |
|               onPressed: () async {
 | |
|                 await controller?.flipCamera();
 | |
|               },
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|         body: _buildQrView(context));
 | |
|   }
 | |
| 
 | |
|   Widget _buildQrView(BuildContext context) {
 | |
|     // For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
 | |
|     var scanArea = (MediaQuery.of(context).size.width < 400 ||
 | |
|             MediaQuery.of(context).size.height < 400)
 | |
|         ? 150.0
 | |
|         : 300.0;
 | |
|     // To ensure the Scanner view is properly sizes after rotation
 | |
|     // we need to listen for Flutter SizeChanged notification and update controller
 | |
|     return QRView(
 | |
|       key: qrKey,
 | |
|       onQRViewCreated: _onQRViewCreated,
 | |
|       overlay: QrScannerOverlayShape(
 | |
|           borderColor: Colors.red,
 | |
|           borderRadius: 10,
 | |
|           borderLength: 30,
 | |
|           borderWidth: 10,
 | |
|           cutOutSize: scanArea),
 | |
|       onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   void _onQRViewCreated(QRViewController controller) {
 | |
|     setState(() {
 | |
|       this.controller = controller;
 | |
|     });
 | |
|     controller.scannedDataStream.listen((scanData) {
 | |
|       if (scanData.code != null) {
 | |
|         showServerSettingFromQr(scanData.code!);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
 | |
|     if (!p) {
 | |
|       showToast('No permission');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   void dispose() {
 | |
|     controller?.dispose();
 | |
|     super.dispose();
 | |
|   }
 | |
| 
 | |
|   void showServerSettingFromQr(String data) async {
 | |
|     closeConnection();
 | |
|     await controller?.pauseCamera();
 | |
|     if (!data.startsWith('config=')) {
 | |
|       showToast('Invalid QR code');
 | |
|       return;
 | |
|     }
 | |
|     try {
 | |
|       final sc = ServerConfig.decode(data.substring(7));
 | |
|       Timer(Duration(milliseconds: 60), () {
 | |
|         showServerSettingsWithValue(sc, gFFI.dialogManager);
 | |
|       });
 | |
|     } catch (e) {
 | |
|       showToast('Invalid QR code');
 | |
|     }
 | |
|   }
 | |
| }
 |