import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:ln_jq_app/common/styles/theme.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'controller.dart'; class QrCodePage extends GetView { const QrCodePage({super.key}); @override Widget build(BuildContext context) { Get.put(QrCodeController()); return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar( title: const Text('扫码', style: TextStyle(color: Colors.white)), centerTitle: true, backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: Colors.white), onPressed: () => Get.back(), ), actions: [ TextButton( onPressed: controller.requestPhotoPermission, child: const Text('相册', style: TextStyle(color: Colors.white, fontSize: 16)), ), ], ), body: Obx(() { // 1. 使用 Obx 包裹整个 body // 根据权限状态来决定显示什么 if (controller.hasPermission.value) { // 如果有权限,显示扫描器 return _buildScannerView(context); } else { // 如果没有权限,显示引导界面 return _buildPermissionDeniedView(); } }), ); } Widget _buildScannerView(BuildContext context){ if (!controller.animationController.isAnimating) { controller.animationController.repeat(reverse: false); } return Stack( alignment: Alignment.center, children: [ // 使用 MobileScanner 作为扫描视图 MobileScanner( controller: controller.scannerController, onDetect: controller.onDetect, // 您可以自定义扫描框的样式 scanWindow: Rect.fromCenter( center: Offset( MediaQuery.of(context).size.width / 2, MediaQuery.of(context).size.height / 2 - 50, ), width: 250, height: 250, ), ), // 扫描动画和覆盖层 _buildScannerOverlay(context), // 底部的功能按钮 Positioned(bottom: 80, left: 0, right: 0, child: _buildActionButtons()), ], ); } Widget _buildPermissionDeniedView() { // 确保动画是停止的 if (controller.animationController.isAnimating) { controller.animationController.stop(); } return Container( color: Colors.black, alignment: Alignment.center, padding: const EdgeInsets.all(24.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.no_photography, color: Colors.white70, size: 64), const SizedBox(height: 16), const Text( '需要相机权限', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( '请授予相机权限以使用扫码功能。', style: TextStyle(color: Colors.white70, fontSize: 14), textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton( onPressed: controller.requestPermission, // 点击按钮重新请求权限 child: const Text('授予权限'), ), ], ), ); } /// 构建扫描区域的覆盖层和动画 Widget _buildScannerOverlay(BuildContext context) { // 模拟扫描框的位置和大小 const double scanAreaSize = 250.0; return Stack( children: [ // 半透明的覆盖层 ColorFiltered( colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.5), BlendMode.srcOut), child: Stack( children: [ Container(decoration: const BoxDecoration(color: Colors.transparent)), Align( alignment: Alignment.center, child: Container( margin: const EdgeInsets.only(bottom: 100), // 微调位置 width: scanAreaSize, height: scanAreaSize, decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(12), ), ), ), ], ), ), // 扫描动画 Align( alignment: Alignment.center, child: Container( margin: const EdgeInsets.only(bottom: 100), width: scanAreaSize, height: scanAreaSize, child: AnimatedBuilder( animation: controller.scanAnimation, builder: (context, child) { return CustomPaint( painter: ScannerAnimationPainter( controller.scanAnimation.value, AppTheme.themeColor, ), ); }, ), ), ), ], ); } /// 构建底部的功能按钮(闪光灯、相册) Widget _buildActionButtons() { return Column( mainAxisSize: MainAxisSize.min, children: [ const Text( '将二维码/条形码放入框内,即可自动扫描', style: TextStyle(color: Colors.white, fontSize: 14), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 闪光灯按钮 _buildIconButton( onPressed: controller.toggleFlash, //闪光灯状态的变化 child: Obx( () => IconButton( icon: Icon( controller.isFlashOn.value ? Icons.flash_on : Icons.flash_off, color: Colors.white, size: 28, ), onPressed: controller.toggleFlash, ), ), ), // 翻转相机按钮 _buildIconButton( onPressed: controller.flipCamera, child: const Icon( Icons.flip_camera_ios, color: Colors.white, size: 28, ), ), ], ), ], ); } Widget _buildIconButton({required VoidCallback onPressed, required Widget child}) { return Container( decoration: BoxDecoration( color: Colors.black.withOpacity(0.3), shape: BoxShape.circle, ), child: IconButton( onPressed: onPressed, icon: child, iconSize: 32, // 增大点击区域 ), ); } } /// 扫描动画的绘制器 class ScannerAnimationPainter extends CustomPainter { final double value; final Color borderColor; ScannerAnimationPainter(this.value, this.borderColor); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = borderColor ..strokeWidth = 3 ..style = PaintingStyle.stroke; final cornerLength = 20.0; // 绘制四个角的边框 // Top-left canvas.drawPath( Path() ..moveTo(0, cornerLength) ..lineTo(0, 0) ..lineTo(cornerLength, 0), paint, ); // Top-right canvas.drawPath( Path() ..moveTo(size.width - cornerLength, 0) ..lineTo(size.width, 0) ..lineTo(size.width, cornerLength), paint, ); // Bottom-left canvas.drawPath( Path() ..moveTo(0, size.height - cornerLength) ..lineTo(0, size.height) ..lineTo(cornerLength, size.height), paint, ); // Bottom-right canvas.drawPath( Path() ..moveTo(size.width - cornerLength, size.height) ..lineTo(size.width, size.height) ..lineTo(size.width, size.height - cornerLength), paint, ); // 绘制扫描线 final linePaint = Paint() ..color = borderColor.withOpacity(0.8) ..strokeWidth = 2 ..shader = LinearGradient( colors: [borderColor.withOpacity(0), borderColor, borderColor.withOpacity(0)], stops: const [0.0, 0.5, 1.0], ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); final y = size.height * value; canvas.drawLine(Offset(0, y), Offset(size.width, y), linePaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }