Untitled

 avatar
unknown
plain_text
4 months ago
13 kB
4
Indexable
import 'dart:io';

import 'package:edusafair/core/helpers/device_size_helper.dart';
import 'package:edusafair/core/helpers/permision_helper.dart';
import 'package:edusafair/core/theme/colors.dart';
import 'package:edusafair/core/utils/app_asset.dart';
import 'package:edusafair/core/utils/constants.dart';
import 'package:edusafair/data/repository/repository_impl.dart';
import 'package:edusafair/domain/model/response_model.dart';
import 'package:edusafair/domain/model/result.dart';
import 'package:edusafair/presentation/screen/home/widget/result_card_widget.dart';
import 'package:edusafair/presentation/utils/view_constants.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';

class QRScanPage extends StatefulWidget {
  final String securityKey;

  const QRScanPage({super.key, required this.securityKey});

  @override
  State<QRScanPage> createState() => _QRScanPageState();
}

class _QRScanPageState extends State<QRScanPage> with TickerProviderStateMixin {
  final GlobalKey _qrKey = GlobalKey(debugLabel: 'QR');
  Barcode? _result;
  ResponseModel? _responseModel;

  QRViewController? _controller;
  bool _haveCameraPermission = false;

  late AnimationController _animationController;

  DataLoadingState loadingState = DataLoadingState.init;
  VerificationStatus verificationStatus = VerificationStatus.none;

  @override
  void initState() {
    super.initState();

    _checkCameraPermission();

    _animationController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 300));
  }

  @override
  void dispose() {
    _controller?.stopCamera();
    _controller?.dispose();
    _animationController.dispose();

    super.dispose();
  }

  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      _controller?.pauseCamera();
    } else if (Platform.isIOS) {
      _controller?.resumeCamera();
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            Expanded(
              flex: 1,
              child: _qrCameraSection(),
            ),
            Expanded(flex: 1, child: _bottomSection()),
          ],
        ),
      ),
    );
  }

  Widget _bottomSection() {
    return Container(
      padding: const EdgeInsets.only(left: 32, right: 32, top: 16, bottom: 16),
      child: Stack(
        //mainAxisAlignment: MainAxisAlignment.center,
        alignment: Alignment.topCenter,
        children: [
          const Text(
            'Please move your camera\nover the QR code',
            textAlign: TextAlign.center,
            style: TextStyle(color: Colors.grey),
          ),
          Center(
            child: _buildResult(
                status: verificationStatus,
                state: loadingState,
                message: _responseModel?.message),
          ),
          const SizedBox(
            height: 16,
          ),
          if (_result != null && loadingState == DataLoadingState.init ||
              loadingState == DataLoadingState.loading)
            Positioned(bottom: 10, child: _verifyButton()),
        ],
      ),
    );
  }

  Widget _buildResult(
      {required DataLoadingState state,
      required VerificationStatus status,
      String? message}) {
    Widget widget;
    if (state == DataLoadingState.completed) {
      if (status == VerificationStatus.verified) {
        widget = ResultCardWidget(
          color: AppColor.colorBlue,
          title: 'Success',
          icon: Icons.check,
          subtitle: message,
          btnOnClick: _resetAll,
          btnText: 'Verify Another',
          btnColor: AppColor.colorBlue,
        );
      } else if (status == VerificationStatus.unverified) {
        widget = ResultCardWidget(
          color: const Color(0xffFCAEAE),
          title: 'Failed',
          icon: Icons.close,
          subtitle: message,
          btnOnClick: _verify,
          btnText: 'Retry',
          btnColor: AppColor.colorRed,
        );
      } else if (status == VerificationStatus.error) {
        widget = ResultCardWidget(
          color: Colors.grey,
          title: 'Error',
          icon: Icons.bug_report_outlined,
          btnOnClick: _resetAll,
          btnText: 'Try Again',
          btnColor: Colors.grey,
        );
      } else {
        widget = const SizedBox.shrink();
      }
    } else if (state == DataLoadingState.error) {
      widget = ResultCardWidget(
        color: Colors.grey,
        title: 'Error',
        icon: Icons.bug_report_outlined,
        btnOnClick: _resetAll,
        btnText: 'Try Again',
        btnColor: Colors.grey,
      );
    } else {
      widget = const SizedBox.shrink();
    }

    return AnimatedScale(
      duration: const Duration(milliseconds: 300),
      scale: loadingState == DataLoadingState.completed ||
              loadingState == DataLoadingState.error
          ? 1
          : 0,
      child: widget,
    );
  }

  Widget _verifyButton() {
    return SizedBox(
      height: 56,
      child: loadingState == DataLoadingState.loading
          ? const Center(
              child: CircularProgressIndicator(
                color: AppColor.colorBlue,
              ),
            )
          : loadingState == DataLoadingState.init
              ? ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: AppColor.colorBlue,
                    foregroundColor: Colors.white,
                    // shape: const BeveledRectangleBorder(
                    //   borderRadius: BorderRadius.all(
                    //     Radius.elliptical(10, 10),
                    //   ),
                    // ),
                  ),
                  onPressed: _verify,
                  child: Text(
                    verificationStatus == VerificationStatus.verified
                        ? 'Verified'
                        : 'Verify',
                    textAlign: TextAlign.center,
                  ),
                )
              : const SizedBox.shrink(),
    );
  }

  Widget _qrCameraSection() {
    const double cameraBoxSpace = 48.0;

    return Stack(
      fit: StackFit.expand,
      children: [
        Align(
            alignment: AlignmentDirectional.topCenter,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const SizedBox(
                  height: 24,
                ),
                Image.asset(
                  AppAsset.appLogo,
                  height: 48,
                )
              ],
            )),

        //camera view
        Positioned.fill(
          top: cameraBoxSpace + cameraBoxSpace,
          left: cameraBoxSpace,
          right: cameraBoxSpace,
          bottom: cameraBoxSpace - cameraBoxSpace,
          child: Container(
            clipBehavior: Clip.hardEdge,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10),
            ),
            child: QRView(
              key: _qrKey,
              onQRViewCreated: _onQrViewCreated,
              cameraFacing: CameraFacing.back,
              onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
              overlay: QrScannerOverlayShape(
                  borderColor: AppColor.colorGreen,
                  overlayColor: Colors.white.withOpacity(0.85),
                  borderRadius: 10,
                  borderLength: 30,
                  borderWidth: 10,
                  cutOutSize: DeviceSizeHelper(context).scanArea),
            ),
          ),
        ),

        //if result captured
        if (_result != null)
          Positioned.fill(
              top: cameraBoxSpace + cameraBoxSpace,
              child: Container(
                decoration: BoxDecoration(color: Colors.white.withOpacity(0.4)),
                child: Center(
                  child: GestureDetector(
                    onTap: () {
                      _resetAll();
                    },
                    child: Container(
                      padding: const EdgeInsets.all(8.0),
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.black.withOpacity(0.4),
                      ),
                      child: const Icon(
                        Icons.restart_alt,
                        size: 30,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              )),

        //flip camera
        Positioned(
          bottom: 10,
          right: 48 + 10,
          child: Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Colors.black.withOpacity(0.2),
              shape: BoxShape.circle,
            ),
            child: AnimatedBuilder(
              animation: _animationController,
              builder: (context, child) {
                return Transform.rotate(
                  angle: _animationController.value * (360 / 100),
                  child: child,
                );
              },
              child: InkWell(
                child: Icon(
                  Icons.adaptive.flip_camera,
                  color: Colors.white,
                  size: 16,
                ),
                onTap: () {
                  _controller?.flipCamera();

                  if (_animationController.isDismissed) {
                    _animationController.forward();
                  } else {
                    _animationController.reverse();
                  }

                  _resetAll();
                },
              ),
            ),
          ),
        ),
      ],
    );
  }

  void _onQrViewCreated(QRViewController controller) {
    _controller = controller;

    _controller?.scannedDataStream.listen((scanData) {
      _result = scanData;
      if (_result != null) {
        _controller?.pauseCamera();
        setState(() {});
      }
    }).onDone(() {
      _controller?.stopCamera();
    });
  }

  void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
    if (!p) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('no Permission')),
      );
    }
  }

  void _checkCameraPermission() async {
    final permission = await PermissionHelper().hasCameraPermission();
    if (!permission) {
      if (context.mounted) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: const Text("Camera Permission Needed"),
          action: SnackBarAction(
            onPressed: () async {
              await openAppSettings();
            },
            label: 'Settings',
          ),
        ));
      }
    }

    if (_haveCameraPermission != permission) {
      setState(() {
        _haveCameraPermission = permission;
      });
    }
  }

  void _resetAll() {
    _controller?.resumeCamera();
    setState(() {
      _result = null;
      _responseModel = null;
      verificationStatus = VerificationStatus.none;
      loadingState = DataLoadingState.init;
    });
  }

  void showAppSnackBar(BuildContext context, String msg) {
    if (context.mounted) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
    }
  }

  Future<void> _verify() async {
    setState(() {
      loadingState = DataLoadingState.loading;
      verificationStatus = VerificationStatus.none;
      _responseModel = null;
    });

    final dataResult =
        await RepositoryImpl().getResponse(AppConstant().generateUrl(
      scannedUrl: _result?.code ?? '',
      userKey: widget.securityKey,
    ));

    if (dataResult is Success) {
      _responseModel = dataResult.data;
      loadingState = DataLoadingState.completed;
      if (_responseModel?.status ?? false) {
        verificationStatus = VerificationStatus.verified;
      } else {
        verificationStatus = VerificationStatus.unverified;
      }
    } else if (dataResult is Failure) {
      _responseModel = null;
      verificationStatus = VerificationStatus.error;
      loadingState = DataLoadingState.error;
    }

    setState(() {});
  }
}
Editor is loading...
Leave a Comment