Untitled
unknown
plain_text
a year ago
13 kB
10
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