소스 검색

身份证识别流程基本稳定

gavin.chen 1 년 전
부모
커밋
33f3826914

+ 23 - 6
lib/pages/facial_recognition/controller.dart

@@ -18,7 +18,7 @@ class FacialRecognitionController extends GetxController
   List<CameraDescription> _cameras = <CameraDescription>[];
   List<CameraDescription> get cameras => _cameras;
 
-  CameraController? kCameraController = null;
+  CameraController? kCameraController;
   double _minAvailableExposureOffset = 0.0;
   double _maxAvailableExposureOffset = 0.0;
   double _minAvailableZoom = 1.0;
@@ -36,6 +36,9 @@ class FacialRecognitionController extends GetxController
   // 屏幕上手指数量
   int pointers = 0;
 
+  // 当前身份证信息
+  IdCardInfoModel idCardInfo = IdCardInfoModel();
+
   /// 开始缩放
   void handleScaleStart(ScaleStartDetails details) {
     _baseScale = _currentScale;
@@ -254,15 +257,28 @@ class FacialRecognitionController extends GetxController
     print('图像已保存到相册!');
   }
 
-  /// 发生拍摄事件
-  void onTakePictureButtonPressed() {
-    // debugShowCache();
-
+  /// 发生拍摄身份证事件
+  void onCaptureIdCardButtonPressed() {
     takePicture().then((XFile? file) {
       // imageFile = file;
       if (file != null) {
-        PromptBox.toast('已临时保存到相册');
         print('Picture saved to ${file.path}');
+
+        /// TODO 上传给server,获取返回值信息
+        if (true) {
+          PromptBox.toast('身份证识别成功');
+          idCardInfo.localCardImagePath = file.path;
+          idCardInfo.idCardName = '金阳';
+          idCardInfo.idCardGender = '女';
+          idCardInfo.idCardNation = '汉';
+          idCardInfo.idCardBirthDate = '1980年10月27日';
+          idCardInfo.idCardAddress = '北京市西城区复兴门外大街999号院11号楼3单元502室';
+          idCardInfo.idCardNumber = '110101198010270000';
+          state.isShowIdCardInfoSwitch = true;
+          state.isIdCardInfoShow = true;
+          state.isInFaceRecognition = true;
+        }
+
         debugShowCache();
       }
     });
@@ -405,6 +421,7 @@ class FacialRecognitionController extends GetxController
 
   @override
   void didChangeAppLifecycleState(AppLifecycleState state) {
+    // FIXME 未执行
     super.didChangeAppLifecycleState(state);
     print('state = $state');
     final CameraController? cameraController = kCameraController;

+ 1 - 0
lib/pages/facial_recognition/index.dart

@@ -4,3 +4,4 @@ export './state.dart';
 export './controller.dart';
 export './bindings.dart';
 export './view.dart';
+export './models.dart';

+ 19 - 0
lib/pages/facial_recognition/models.dart

@@ -0,0 +1,19 @@
+class IdCardInfoModel {
+  String idCardName;
+  String idCardGender;
+  String idCardNation;
+  String idCardBirthDate;
+  String idCardAddress;
+  String idCardNumber;
+  String localCardImagePath;
+
+  IdCardInfoModel({
+    this.idCardName = '',
+    this.idCardGender = '',
+    this.idCardNation = '',
+    this.idCardBirthDate = '',
+    this.idCardAddress = '',
+    this.idCardNumber = '',
+    this.localCardImagePath = '',
+  });
+}

+ 15 - 0
lib/pages/facial_recognition/state.dart

@@ -10,4 +10,19 @@ class FacialRecognitionState {
   final _isCameraReady = false.obs;
   set isCameraReady(value) => _isCameraReady.value = value;
   bool get isCameraReady => _isCameraReady.value;
+
+  /// 身份证信息是否展示
+  final _isIdCardInfoShow = false.obs;
+  set isIdCardInfoShow(value) => _isIdCardInfoShow.value = value;
+  bool get isIdCardInfoShow => _isIdCardInfoShow.value;
+
+  /// 是否显示身份信息开关
+  final _isShowIdCardInfoSwitch = false.obs;
+  set isShowIdCardInfoSwitch(value) => _isShowIdCardInfoSwitch.value = value;
+  bool get isShowIdCardInfoSwitch => _isShowIdCardInfoSwitch.value;
+
+  /// 是否进入人脸识别阶段
+  final _isInFaceRecognition = false.obs;
+  set isInFaceRecognition(value) => _isInFaceRecognition.value = value;
+  bool get isInFaceRecognition => _isInFaceRecognition.value;
 }

+ 101 - 1
lib/pages/facial_recognition/view.dart

@@ -1,3 +1,4 @@
+import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import 'index.dart';
@@ -18,7 +19,8 @@ class FacialRecognitionPage extends GetView<FacialRecognitionController> {
           body: SafeArea(
             child: Obx(
               () => controller.state.isCameraReady
-                  ? const CameraForIdCard()
+                  // ? const CameraForIdCard()
+                  ? _buildCameraArea()
                   : const CircularProgressIndicator(
                       valueColor: AlwaysStoppedAnimation<Color>(Colors.orange),
                     ),
@@ -28,4 +30,102 @@ class FacialRecognitionPage extends GetView<FacialRecognitionController> {
       },
     );
   }
+
+  Widget _buildCameraArea() {
+    return Row(
+      children: [
+        const IdCardInfo(),
+        Expanded(
+          child: ClipRRect(
+            child: LayoutBuilder(builder: (context, constraints) {
+              return Stack(
+                children: <Widget>[
+                  OverflowBox(
+                    maxHeight: constraints.maxHeight + 10,
+                    maxWidth: 2000,
+                    child: Container(
+                      color: Colors.black,
+                      child: Center(
+                        child: _cameraPreviewWidget(),
+                      ),
+                    ),
+                  ),
+                  Center(
+                    child: Stack(
+                      children: [
+                        Align(
+                          alignment: Alignment.centerLeft,
+                          child: _idCardInfoSwitch(),
+                        ),
+                        if (controller.state.isInFaceRecognition)
+                          const CameraForFace()
+                        else
+                          const CameraForIdCard(),
+                      ],
+                    ),
+                  ),
+                ],
+              );
+            }),
+          ),
+        ),
+      ],
+    );
+  }
+
+  /// 相机预览
+  Widget _cameraPreviewWidget() {
+    final CameraController? cameraController = controller.kCameraController;
+    if (cameraController == null || !cameraController.value.isInitialized) {
+      /// 旋转loading
+      return const Center(
+        child: CircularProgressIndicator(),
+      );
+    } else {
+      return Listener(
+        onPointerDown: (_) => controller.pointers++,
+        onPointerUp: (_) => controller.pointers--,
+        child: CameraPreview(
+          cameraController,
+          child: LayoutBuilder(
+              builder: (BuildContext context, BoxConstraints constraints) {
+            return GestureDetector(
+              behavior: HitTestBehavior.opaque,
+              onScaleStart: controller.handleScaleStart,
+              onScaleUpdate: controller.handleScaleUpdate,
+              onTapDown: (TapDownDetails details) =>
+                  controller.onViewFinderTap(details, constraints),
+            );
+          }),
+        ),
+      );
+    }
+  }
+
+  /// 身份证信息开关
+  Widget _idCardInfoSwitch() {
+    return Obx(() {
+      if (!controller.state.isShowIdCardInfoSwitch) {
+        return Container();
+      }
+      return Container(
+        height: 80,
+        width: 40,
+        color: Colors.black.withOpacity(0.5),
+        child: IconButton(
+          onPressed: () {
+            controller.state.isIdCardInfoShow =
+                !controller.state.isIdCardInfoShow;
+          },
+          padding: const EdgeInsets.all(0),
+          icon: Icon(
+              controller.state.isIdCardInfoShow
+                  ? Icons.keyboard_double_arrow_left
+                  : Icons.keyboard_double_arrow_right,
+              size: 30),
+          color: Colors.white,
+        ),
+      );
+    });
+  }
 }

+ 71 - 0
lib/pages/facial_recognition/widgets/camera_for_face.dart

@@ -0,0 +1,71 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import '../index.dart';
+
+class CameraForFace extends GetView<FacialRecognitionController> {
+  const CameraForFace({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Stack(
+      children: <Widget>[
+        Align(
+          alignment: Alignment.center,
+          child: OverflowBox(
+            maxHeight: 2000,
+            maxWidth: 2000,
+            child: Container(
+              decoration: BoxDecoration(
+                border: Border.all(
+                  color: const Color.fromARGB(90, 0, 0, 0),
+                  width: 400,
+                  strokeAlign: BorderSide.strokeAlignOutside,
+                ),
+                borderRadius: BorderRadius.circular(20),
+              ),
+              margin: const EdgeInsets.only(bottom: 60),
+              child: const SizedBox(
+                width: 500,
+                height: 600,
+              ),
+            ),
+          ),
+        ),
+        Align(
+          alignment: Alignment.centerRight,
+          child: Container(
+            child: _captureButton(),
+          ),
+        ),
+        Align(
+          alignment: Alignment.bottomCenter,
+          child: Container(
+            padding: const EdgeInsets.only(bottom: 40),
+            child: const Text(
+              '请将面部保持在识别框内,并确保面部清晰可见,然后按下识别键',
+              style: TextStyle(color: Colors.white, fontSize: 22),
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+
+  /// 拍照按钮
+  Widget _captureButton() {
+    return Container(
+      margin: const EdgeInsets.only(right: 60),
+      width: 100,
+      height: 100,
+      decoration: BoxDecoration(
+        color: Colors.white,
+        borderRadius: BorderRadius.circular(50),
+      ),
+      child: IconButton(
+        icon: const Icon(Icons.sensor_occupied, size: 50),
+        color: Colors.blue,
+        onPressed: controller.onCaptureIdCardButtonPressed,
+      ),
+    );
+  }
+}

+ 7 - 49
lib/pages/facial_recognition/widgets/camera_for_id_card.dart

@@ -1,4 +1,3 @@
-import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import '../index.dart';
@@ -10,14 +9,6 @@ class CameraForIdCard extends GetView<FacialRecognitionController> {
   Widget build(BuildContext context) {
     return Stack(
       children: <Widget>[
-        Center(
-          child: Container(
-            color: Colors.black,
-            child: Center(
-              child: _cameraPreviewWidget(),
-            ),
-          ),
-        ),
         Align(
           alignment: Alignment.center,
           child: OverflowBox(
@@ -42,18 +33,15 @@ class CameraForIdCard extends GetView<FacialRecognitionController> {
         ),
         Align(
           alignment: Alignment.centerRight,
-          child: Container(
-            padding: const EdgeInsets.only(bottom: 20),
-            child: _captureButton(),
-          ),
+          child: _captureButton(),
         ),
         Align(
           alignment: Alignment.bottomCenter,
           child: Container(
-            padding: const EdgeInsets.only(bottom: 50),
+            padding: const EdgeInsets.only(bottom: 70),
             child: const Text(
-              '请将身份证置于虚线框内,并确保图像清晰可见,然后按下拍摄键',
-              style: TextStyle(color: Colors.white, fontSize: 26),
+              '请将身份证(人像面)置于虚线框内,并确保图像清晰可见,然后按下拍摄键',
+              style: TextStyle(color: Colors.white, fontSize: 22),
             ),
           ),
         ),
@@ -61,39 +49,9 @@ class CameraForIdCard extends GetView<FacialRecognitionController> {
     );
   }
 
-  /// Display the preview from the camera (or a message if the preview is not available).
-  Widget _cameraPreviewWidget() {
-    final CameraController? cameraController = controller.kCameraController;
-
-    if (cameraController == null || !cameraController.value.isInitialized) {
-      /// 旋转loading
-      return const Center(
-        child: CircularProgressIndicator(),
-      );
-    } else {
-      return Listener(
-        onPointerDown: (_) => controller.pointers++,
-        onPointerUp: (_) => controller.pointers--,
-        child: CameraPreview(
-          cameraController,
-          child: LayoutBuilder(
-              builder: (BuildContext context, BoxConstraints constraints) {
-            return GestureDetector(
-              behavior: HitTestBehavior.opaque,
-              onScaleStart: controller.handleScaleStart,
-              onScaleUpdate: controller.handleScaleUpdate,
-              onTapDown: (TapDownDetails details) =>
-                  controller.onViewFinderTap(details, constraints),
-            );
-          }),
-        ),
-      );
-    }
-  }
-
   Widget _captureButton() {
     return Container(
-      margin: const EdgeInsets.only(right: 30),
+      margin: const EdgeInsets.only(right: 60),
       width: 100,
       height: 100,
       decoration: BoxDecoration(
@@ -101,9 +59,9 @@ class CameraForIdCard extends GetView<FacialRecognitionController> {
         borderRadius: BorderRadius.circular(50),
       ),
       child: IconButton(
-        icon: const Icon(Icons.camera_alt, size: 30),
+        icon: const Icon(Icons.camera_alt, size: 40),
         color: Colors.blue,
-        onPressed: controller.onTakePictureButtonPressed,
+        onPressed: controller.onCaptureIdCardButtonPressed,
       ),
     );
   }

+ 99 - 0
lib/pages/facial_recognition/widgets/id_info.dart

@@ -0,0 +1,99 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import '../index.dart';
+
+class IdCardInfo extends GetView<FacialRecognitionController> {
+  const IdCardInfo({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Obx(
+      () => AnimatedContainer(
+        duration: const Duration(milliseconds: 300),
+        width: controller.state.isIdCardInfoShow ? 300 : 0,
+        color: const Color.fromARGB(255, 231, 231, 231),
+        child: OverflowBox(
+          maxWidth: 300,
+          minWidth: 300,
+          alignment: Alignment.centerRight,
+          child: Padding(
+            padding: const EdgeInsets.all(20),
+            child: Column(
+              children: [
+                const SizedBox(height: 10),
+                Container(
+                  decoration: BoxDecoration(
+                    borderRadius: BorderRadius.circular(10),
+                  ),
+                  clipBehavior: Clip.antiAlias,
+                  child: Transform.scale(
+                    scale: controller.idCardInfo.localCardImagePath == ''
+                        ? 1
+                        : 1.8,
+                    child: Image.file(
+                      File(controller.idCardInfo.localCardImagePath),
+                      errorBuilder: (context, error, stackTrace) {
+                        return const Image(
+                          image: AssetImage('assets/images/id_card.png'),
+                          color: Colors.grey,
+                          width: 220,
+                        );
+                      },
+                      width: 220,
+                    ),
+                  ),
+                ),
+                const SizedBox(height: 20),
+                _buildLabel('姓名'),
+                _buildValue(controller.idCardInfo.idCardName),
+                const SizedBox(height: 12),
+                _buildLabel('性别'),
+                _buildValue(controller.idCardInfo.idCardGender),
+                const SizedBox(height: 12),
+                _buildLabel('出生'),
+                _buildValue(controller.idCardInfo.idCardBirthDate),
+                const SizedBox(height: 12),
+                _buildLabel('民族'),
+                _buildValue(controller.idCardInfo.idCardNation),
+                const SizedBox(height: 12),
+                _buildLabel('住址'),
+                _buildValue(controller.idCardInfo.idCardAddress),
+                const SizedBox(height: 12),
+                _buildLabel('公民身份号码'),
+                _buildValue(controller.idCardInfo.idCardNumber),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildLabel(String label) {
+    return Align(
+      alignment: Alignment.centerLeft,
+      child: Padding(
+        padding: const EdgeInsets.only(left: 20),
+        child: Text(
+          label,
+          style: const TextStyle(color: Colors.grey, fontSize: 18),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildValue(String value) {
+    return Align(
+      alignment: Alignment.centerLeft,
+      child: Padding(
+        padding: const EdgeInsets.only(left: 20),
+        child: Text(
+          value,
+          style: const TextStyle(color: Colors.black87, fontSize: 22),
+        ),
+      ),
+    );
+  }
+}

+ 2 - 0
lib/pages/facial_recognition/widgets/widgets.dart

@@ -2,3 +2,5 @@ library widgets;
 
 export './debug_camera.dart';
 export './camera_for_id_card.dart';
+export './camera_for_face.dart';
+export './id_info.dart';