diff --git a/ln_jq_app/android/app/src/main/AndroidManifest.xml b/ln_jq_app/android/app/src/main/AndroidManifest.xml index 757e51d..c7c3c0b 100644 --- a/ln_jq_app/android/app/src/main/AndroidManifest.xml +++ b/ln_jq_app/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,19 @@ + + + + + + + + + + + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Ln Jq App + 小羚羚 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -41,6 +43,12 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + + NSCameraUsageDescription + 需要访问您的相机以扫描二维码 + NSPhotoLibraryUsageDescription + 需要访问您的相册以选择二维码图片进行识别 + CADisableMinimumFrameDurationOnPhone UIApplicationSupportsIndirectInputEvents diff --git a/ln_jq_app/lib/pages/c_page/reservation/controller.dart b/ln_jq_app/lib/pages/c_page/reservation/controller.dart index 4e1625d..4322612 100644 --- a/ln_jq_app/lib/pages/c_page/reservation/controller.dart +++ b/ln_jq_app/lib/pages/c_page/reservation/controller.dart @@ -30,7 +30,7 @@ class ReservationController extends GetxController with BaseControllerMixin { final TextEditingController amountController = TextEditingController(); // 车牌号 - final TextEditingController plateNumberController = TextEditingController(text: "浙F"); + TextEditingController plateNumberController = TextEditingController(); // 加氢站 final List stationOptions = [ @@ -343,6 +343,7 @@ class ReservationController extends GetxController with BaseControllerMixin { String name = ""; String leftHydrogen = ""; String workEfficiency = ""; + //累计数据 String fillingWeight = ""; String fillingTimes = ""; @@ -352,10 +353,10 @@ class ReservationController extends GetxController with BaseControllerMixin { void onInit() { phone = StorageService.to.phone ?? ""; name = StorageService.to.name ?? ""; - + plateNumberController = TextEditingController(text: plateNumber); getCatinfo(); getJqinfo(); - // getSiteList(); + getSiteList(); super.onInit(); } @@ -363,7 +364,7 @@ class ReservationController extends GetxController with BaseControllerMixin { try { HttpService.to.setBaseUrl(AppTheme.test_service_url); var responseData = await HttpService.to.get( - 'appointment/truck/history-filling-summary?vin=LSFGL23Z2ND214377' + 'appointment/truck/history-filling-summary?vin=LSFGL23Z2ND214377', ); if (responseData == null || responseData.data == null) { showToast('服务暂不可用,请稍后'); @@ -372,7 +373,8 @@ class ReservationController extends GetxController with BaseControllerMixin { var result = BaseModel.fromJson(responseData.data); - fillingWeight = "${result.data["fillingWeight"]}${result.data["fillingWeightUnit"]}"; + fillingWeight = + "${result.data["fillingWeight"]}${result.data["fillingWeightUnit"]}"; fillingTimes = "${result.data["fillingTimes"]}${result.data["fillingTimesUnit"]}"; updateUi(); @@ -411,6 +413,7 @@ class ReservationController extends GetxController with BaseControllerMixin { } void getSiteList() async { + showLoading("加载中"); final originalHeaders = Map.from(HttpService.to.dio.options.headers); try { HttpService.to.setBaseUrl(AppTheme.jiaqing_service_url); @@ -420,10 +423,12 @@ class ReservationController extends GetxController with BaseControllerMixin { if (responseData == null && responseData!.data == null) { showToast('暂时无法获取站点信息'); + dismissLoading(); return; } try { + dismissLoading(); var result = BaseModel.fromJson(responseData.data); // showToast(result.data["data"].toString()); } catch (e) { @@ -431,6 +436,7 @@ class ReservationController extends GetxController with BaseControllerMixin { } } catch (e) { } finally { + dismissLoading(); HttpService.to.setBaseUrl(AppTheme.test_service_url); HttpService.to.dio.options.headers = originalHeaders; } diff --git a/ln_jq_app/lib/pages/c_page/reservation/view.dart b/ln_jq_app/lib/pages/c_page/reservation/view.dart index 7b7474a..e8befe9 100644 --- a/ln_jq_app/lib/pages/c_page/reservation/view.dart +++ b/ln_jq_app/lib/pages/c_page/reservation/view.dart @@ -2,8 +2,8 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; +import 'package:ln_jq_app/pages/qr_code/view.dart'; -import '../../../storage_service.dart'; import 'controller.dart'; class ReservationPage extends GetView { @@ -23,9 +23,9 @@ class ReservationPage extends GetView { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildUserInfoCard(), - const SizedBox(height: 16), + const SizedBox(height: 5), _buildCarInfoCard(), - const SizedBox(height: 16), + const SizedBox(height: 5), _buildReservationFormCard(context), ], ), @@ -48,9 +48,9 @@ class ReservationPage extends GetView { child: Row( children: [ const CircleAvatar( - radius: 24, + radius: 20, backgroundColor: Colors.blue, - child: Icon(Icons.person, color: Colors.white, size: 30), + child: Icon(Icons.person, color: Colors.white, size: 34), ), const SizedBox(width: 12), Expanded( @@ -59,12 +59,12 @@ class ReservationPage extends GetView { children: [ Text( controller.name, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), - SizedBox(height: 4), + SizedBox(height: 6), Text( controller.phone, - style: TextStyle(color: Colors.grey, fontSize: 12), + style: TextStyle(color: Colors.grey, fontSize: 11), ), ], ), @@ -96,7 +96,7 @@ class ReservationPage extends GetView { ), const Divider(height: 1, indent: 16, endIndent: 16), Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), + padding: const EdgeInsets.symmetric(vertical: 11.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -114,9 +114,9 @@ class ReservationPage extends GetView { Widget _buildStatItem(String value, String label) { return Column( children: [ - Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + Text(value, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), const SizedBox(height: 4), - Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)), + Text(label, style: const TextStyle(color: Colors.grey, fontSize: 11 )), ], ); } @@ -127,7 +127,7 @@ class ReservationPage extends GetView { elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(11), child: Row( children: [ Expanded( @@ -141,10 +141,10 @@ class ReservationPage extends GetView { ], ), ), - const SizedBox(width: 16), + const SizedBox(width: 8), Icon( - Icons.propane_tank_outlined, - size: 80, + Icons.propane_rounded, + size: 50, color: Colors.blue.withOpacity(0.5), ), ], @@ -158,18 +158,33 @@ class ReservationPage extends GetView { bool isButton = value == '扫码绑定'; return Row( children: [ - Text(label, style: const TextStyle(color: Colors.grey, fontSize: 14)), + Text(label, style: const TextStyle(color: Colors.grey, fontSize: 11)), const SizedBox(width: 8), isButton - ? ElevatedButton.icon( - onPressed: () { - /* TODO: 扫码绑定逻辑 */ + ? GestureDetector( + onTap: () { + Get.to(() => const QrCodePage()); }, - icon: const Icon(Icons.qr_code_scanner, size: 16), - label: Text(value), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: Container( + margin: EdgeInsetsGeometry.only(left: 10.w), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5), + decoration: BoxDecoration( + border: Border.all(color: Colors.blue.shade300, width: 1), + borderRadius: BorderRadius.circular(5), + color: Colors.blue.withOpacity(0.05), + ), + child: Row( + mainAxisSize: MainAxisSize.min, // Keep the row compact + children: [ + const Icon(Icons.search, size: 13, color: Colors.blue), + const SizedBox(width: 3), + Text( + value, + style: const TextStyle( + color: Colors.blue, fontSize: 11, fontWeight: FontWeight.w500), + ), + ], + ), ), ) : Text( @@ -396,9 +411,13 @@ class ReservationPage extends GetView { ), dropdownStyleData: DropdownStyleData( maxHeight: 200, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + ), + ), + menuItemStyleData: const MenuItemStyleData( + height: 40, ), - menuItemStyleData: const MenuItemStyleData(height: 40), ), ), ), diff --git a/ln_jq_app/lib/pages/login/view.dart b/ln_jq_app/lib/pages/login/view.dart index 844c495..890fa90 100644 --- a/ln_jq_app/lib/pages/login/view.dart +++ b/ln_jq_app/lib/pages/login/view.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:ln_jq_app/common/login_util.dart'; @@ -71,7 +69,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix margin: EdgeInsets.all(15), elevation: 4, child: Container( - height: cLogin ? 260.h : 320.h, + height: cLogin ? 285.h : 350.h, padding: EdgeInsets.all(15), child: // TabBar切换 Column( diff --git a/ln_jq_app/lib/pages/qr_code/controller.dart b/ln_jq_app/lib/pages/qr_code/controller.dart new file mode 100644 index 0000000..e6083f4 --- /dev/null +++ b/ln_jq_app/lib/pages/qr_code/controller.dart @@ -0,0 +1,165 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:getx_scaffold/getx_scaffold.dart'; +import 'package:image/image.dart' as img; +import 'package:image_picker/image_picker.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; +import 'package:zxing_lib/common.dart'; +import 'package:zxing_lib/qrcode.dart'; +import 'package:zxing_lib/zxing.dart'; + +class QrCodeController extends GetxController + with BaseControllerMixin, GetSingleTickerProviderStateMixin { + @override + String get builderId => 'qrcode'; + + // --- Animation --- + late final AnimationController animationController; + late final Animation scanAnimation; + + // --- QR Scanning --- + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? qrViewController; + final Rx result = Rx(null); + final RxBool isFlashOn = false.obs; + + @override + void onInit() { + super.onInit(); + requestPermission(); + + animationController = AnimationController( + duration: const Duration(milliseconds: 2500), + vsync: this, + ); + scanAnimation = Tween(begin: 0, end: 1).animate(animationController); + animationController.repeat(reverse: false); + } + + /// 当 QRView 创建时调用 + void onQRViewCreated(QRViewController controller) { + this.qrViewController = controller; + // 监听扫描到的数据 + controller.scannedDataStream.listen((scanData) { + if (scanData.code != null && result.value?.code != scanData.code) { + result.value = scanData; + qrViewController?.pauseCamera(); + + animationController.stop(); + + renderResult(scanData.code!); + } + }); + } + + void resumeScanner() { + result.value = null; + qrViewController?.resumeCamera(); + animationController.repeat(reverse: false); + } + + /// 从相册选择图片并扫描二维码 + void scanFromGallery() async { + try { + final XFile? imageFile = await ImagePicker().pickImage(source: ImageSource.gallery); + if (imageFile == null) return; // 用户取消了选择 + + qrViewController?.pauseCamera(); + animationController.stop(); + + String? scanResult; + try { + final image = img.decodeImage(await File(imageFile.path).readAsBytes()); + if (image != null) { + //扫描图片 + final pixels = Int32List.fromList( + image.map((pixel) { + return (pixel.a.toInt() << 24) | + (pixel.r.toInt() << 16) | + (pixel.g.toInt() << 8) | + pixel.b.toInt(); + }).toList(), + ); + + final source = RGBLuminanceSource(image.width, image.height, pixels); + + final bitmap = BinaryBitmap(HybridBinarizer(source)); + final reader = QRCodeReader(); + final result = reader.decode(bitmap); + scanResult = result.text; + } + } on NotFoundException { + scanResult = null; + } catch (e) { + //异常 + scanResult = null; + } + + if (scanResult != null) { + renderResult(scanResult); + } else { + showErrorToast('未识别到二维码'); + resumeScanner(); + } + } catch (e) { + showErrorToast('从相册选择失败'); + resumeScanner(); + } + } + + /// 切换闪光灯 + void toggleFlash() async { + await qrViewController?.toggleFlash(); + isFlashOn.value = (await qrViewController?.getFlashStatus()) ?? false; + } + + /// 翻转相机 + void flipCamera() async { + await qrViewController?.flipCamera(); + } + + void requestPermission() async { + final List results = await Future.wait([ + requestCameraPermission(), + requestPhotosPermission(), + ]); + + final isCameraGranted = results[0]; + final isPhotosGranted = results[1]; + + if (!isCameraGranted) { + showErrorToast('相机权限未被授予,请到权限管理中打开'); + } + if (!isPhotosGranted) { + showErrorToast( + '相册权限未被授予,请到权限管理中打开', + ); + } + } + + //扫码结果处理 + void renderResult(String resultStr) { + Get.defaultDialog( + title: "扫描结果", + middleText: resultStr, + onConfirm: () { + Get.back(); + resumeScanner(); + }, + onCancel: () { + resumeScanner(); + }, + ); + + } + + @override + void onClose() { + qrViewController?.dispose(); + animationController.dispose(); + super.onClose(); + } +} diff --git a/ln_jq_app/lib/pages/qr_code/view.dart b/ln_jq_app/lib/pages/qr_code/view.dart new file mode 100644 index 0000000..a2f3b54 --- /dev/null +++ b/ln_jq_app/lib/pages/qr_code/view.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:getx_scaffold/getx_scaffold.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; + +import 'controller.dart'; + +class QrCodePage extends GetView { + const QrCodePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GetBuilder( + init: QrCodeController(), + id: 'qrcode', + builder: (_) { + 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.scanFromGallery, + child: const Text( + '相册', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ), + ], + ), + body: Stack( + children: [ + _buildQrView(context), + Positioned( + bottom: 80.h, + left: 0, + right: 0, + child: _buildControlButtons(), + ), + ], + ), + ); + }, + ); + } + + /// 构建二维码扫描视图(带动画) + Widget _buildQrView(BuildContext context) { + // 定义扫描区域的大小 + var scanArea = (MediaQuery.of(context).size.width < 400 || + MediaQuery.of(context).size.height < 400) + ? 250.0 + : 300.0; + + return Stack( + alignment: Alignment.center, + children: [ + // 底层是相机视图和半透明遮罩 + QRView( + key: controller.qrKey, + onQRViewCreated: controller.onQRViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.blueAccent, + borderRadius: 10, + borderLength: 30, + borderWidth: 10, + cutOutSize: scanArea, + ), + ), + // 上层是扫描动画 + AnimatedBuilder( + animation: controller.scanAnimation, + builder: (context, child) { + return Positioned( + // 计算扫描框的顶部位置,以便动画从顶部开始 + top: (MediaQuery.of(context).size.height - scanArea) / 2, + child: Transform.translate( + offset: Offset(0, controller.scanAnimation.value * scanArea), + child: Container( + width: scanArea, + height: 2, // 扫描线的高度 + decoration: BoxDecoration( + color: Colors.blueAccent, + boxShadow: [ + BoxShadow( + color: Colors.blueAccent.withOpacity(0.7), + blurRadius: 8, + spreadRadius: 2, + ), + ], + ), + ), + ), + ); + }, + ), + ], + ); + } + + /// 构建底部的控制按钮 + Widget _buildControlButtons() { + 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(() => Icon( + controller.isFlashOn.value ? Icons.flash_on : Icons.flash_off, + color: Colors.white, + size: 28, + )), + ), + // 翻转相机按钮 + _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, // 增大点击区域 + ), + ); + } +} diff --git a/ln_jq_app/pubspec.lock b/ln_jq_app/pubspec.lock index cd24a4e..d6dcb06 100644 --- a/ln_jq_app/pubspec.lock +++ b/ln_jq_app/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" + charset: + dependency: transitive + description: + name: charset + sha256: "27802032a581e01ac565904ece8c8962564b1070690794f0072f6865958ce8b9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" clock: dependency: transitive description: @@ -105,6 +113,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.5" crypto: dependency: transitive description: @@ -241,6 +257,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.3+2" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "88707a3bec4b988aaed3b4df5d7441ee4e987f20b286cddca5d6a8270cab23f2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.4+5" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.7.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.3+4" flutter: dependency: "direct main" description: flutter @@ -270,6 +318,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.4.7" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "306f0596590e077338312f38837f595c04f28d6cdeeac392d3d74df2f0003687" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.32" flutter_screenutil: dependency: transitive description: @@ -361,13 +417,77 @@ packages: source: hosted version: "4.1.2" image: - dependency: transitive + dependency: "direct main" description: name: image sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.flutter-io.cn" source: hosted version: "4.5.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.1" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: ca2a3b04d34e76157e9ae680ef16014fb4c2d20484e78417eaed6139330056f6 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.13+7" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: e675c22790bcc24e9abd455deead2b7a88de4b79f7327a281812f14de1a56f58 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.13+1" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.2+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.11.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.2" intl: dependency: transitive description: @@ -640,6 +760,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.0.3" + qr_code_scanner_plus: + dependency: "direct main" + description: + name: qr_code_scanner_plus + sha256: b764e5004251c58d9dee0c295e6006e05bd8d249e78ac3383abdb5afe0a996cd + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.14" rational: dependency: transitive description: @@ -933,6 +1061,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" + zxing_lib: + dependency: "direct main" + description: + name: zxing_lib + sha256: f9170470b6bc947d21a6783486f88ef48aad66fc1380c8acd02b118418ec0ce0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.4" sdks: dart: ">=3.9.0 <4.0.0" flutter: ">=3.35.0" diff --git a/ln_jq_app/pubspec.yaml b/ln_jq_app/pubspec.yaml index 808fb2e..f76b9d3 100644 --- a/ln_jq_app/pubspec.yaml +++ b/ln_jq_app/pubspec.yaml @@ -40,6 +40,10 @@ dependencies: flutter_native_splash: ^2.4.7 dropdown_button2: ^2.3.8 + qr_code_scanner_plus: ^2.0.14 + image_picker: ^1.2.1 # 用于从相册选择图片 + image: ^4.5.4 + zxing_lib: ^1.1.4 dev_dependencies: flutter_test: