236 lines
6.9 KiB
Dart
236 lines
6.9 KiB
Dart
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<QrCodeController> {
|
|
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: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
// 1. 使用 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 _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: <Widget>[
|
|
// 闪光灯按钮
|
|
_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;
|
|
}
|
|
}
|