feat: Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:krow/core/presentation/gen/assets.gen.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_box_decorations.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
|
||||
import 'package:krow/core/presentation/styles/theme.dart';
|
||||
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
|
||||
import 'package:krow/core/presentation/widgets/uploud_image_card.dart';
|
||||
import 'package:krow/features/profile/certificates/data/models/staff_certificate.dart';
|
||||
import 'package:krow/features/profile/certificates/domain/bloc/certificates_bloc.dart';
|
||||
import 'package:krow/features/profile/certificates/domain/bloc/certificates_event.dart';
|
||||
import 'package:krow/features/profile/certificates/domain/bloc/certificates_state.dart';
|
||||
import 'package:krow/features/profile/certificates/presentation/screen/certificates_upload_dialog.dart';
|
||||
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CertificatesScreen extends StatelessWidget implements AutoRouteWrapper {
|
||||
const CertificatesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => CertificatesBloc()..add(CertificatesEventFetch()),
|
||||
child: this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: KwAppBar(
|
||||
titleText: 'certificates'.tr(),
|
||||
),
|
||||
body: BlocConsumer<CertificatesBloc, CertificatesState>(
|
||||
listener: (context, state) {
|
||||
if (state.success) {
|
||||
context.router.maybePop();
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return ModalProgressHUD(
|
||||
inAsyncCall: state.loading,
|
||||
child: ScrollLayoutHelper(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
upperWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
'please_indicate_certificates'.tr(),
|
||||
style: AppTextStyles.bodyTinyMed
|
||||
.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
if (state.showError) _buildErrorMessage(),
|
||||
const Gap(24),
|
||||
_buildUploadProofItems(
|
||||
context,
|
||||
state.certificatesItems,
|
||||
state.showError,
|
||||
),
|
||||
],
|
||||
),
|
||||
lowerWidget: KwButton.primary(
|
||||
label: 'confirm'.tr(),
|
||||
onPressed: () {
|
||||
BlocProvider.of<CertificatesBloc>(context)
|
||||
.add(CertificatesEventSubmit());
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildUploadProofItems(
|
||||
context,
|
||||
List<CertificatesViewModel> items,
|
||||
bool showError,
|
||||
) {
|
||||
return Column(
|
||||
children: items.map(
|
||||
(item) {
|
||||
Color? statusColor = _getStatusColor(showError, item);
|
||||
|
||||
return UploadImageCard(
|
||||
title: item.title,
|
||||
onSelectImage: () {
|
||||
ImagePicker()
|
||||
.pickImage(source: ImageSource.gallery)
|
||||
.then((value) {
|
||||
if (value != null) {
|
||||
_showSelectCertificateDialog(context, item, value.path);
|
||||
}
|
||||
});
|
||||
},
|
||||
onDeleteTap: () {
|
||||
BlocProvider.of<CertificatesBloc>(context)
|
||||
.add(CertificatesEventDelete(item));
|
||||
},
|
||||
onTap: () {},
|
||||
imageUrl: item.imageUrl,
|
||||
inUploading: item.uploading,
|
||||
statusColor: statusColor,
|
||||
message: showError && item.imageUrl == null
|
||||
? 'availability_requires_confirmation'.tr()
|
||||
: item.status == null
|
||||
? 'supported_format'.tr()
|
||||
: (item.status!.name[0].toUpperCase() +
|
||||
item.status!.name.substring(1).toLowerCase()),
|
||||
hasError: showError,
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _buildExpirationRow(item),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Color? _getStatusColor(bool showError, CertificatesViewModel item) {
|
||||
var statusColor = (showError && item.status == null) ||
|
||||
item.status == CertificateStatus.declined
|
||||
? AppColors.statusError
|
||||
: item.status == CertificateStatus.verified
|
||||
? AppColors.statusSuccess
|
||||
: item.status == CertificateStatus.pending
|
||||
? AppColors.primaryBlue
|
||||
: null;
|
||||
return statusColor;
|
||||
}
|
||||
|
||||
Container _buildErrorMessage() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 12),
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: KwBoxDecorations.primaryLight8,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 28,
|
||||
width: 28,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle, color: AppColors.tintRed),
|
||||
child: Center(
|
||||
child: Assets.images.icons.alertCircle.svg(),
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'listed_certificates_mandatory'.tr(),
|
||||
style: AppTextStyles.bodyTinyMed
|
||||
.copyWith(color: AppColors.statusError),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExpirationRow(CertificatesViewModel item) {
|
||||
var bgColor = item.isExpired
|
||||
? AppColors.tintRed
|
||||
: item.expireSoon()
|
||||
? AppColors.tintYellow
|
||||
: AppColors.tintGray;
|
||||
|
||||
var textColor = item.isExpired
|
||||
? AppColors.statusError
|
||||
: item.expireSoon()
|
||||
? AppColors.statusWarningBody
|
||||
: null;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 6, right: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grayWhite,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 6, right: 6, top: 3, bottom: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor, borderRadius: BorderRadius.circular(4)),
|
||||
child: Text(
|
||||
'${'expiration_date'.tr()} ',
|
||||
style: AppTextStyles.bodyTinyReg.copyWith(color: textColor),
|
||||
)),
|
||||
const Gap(7),
|
||||
if (item.expirationDate != null)
|
||||
Text(
|
||||
item.getExpirationInfo(),
|
||||
style: AppTextStyles.bodyTinyMed
|
||||
.copyWith(color: textColor ?? AppColors.blackGray),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSelectCertificateDialog(
|
||||
context, CertificatesViewModel item, imagePath) {
|
||||
CertificatesUploadDialog.show(context, imagePath).then((expireDate) {
|
||||
if (expireDate != null) {
|
||||
BlocProvider.of<CertificatesBloc>(context).add(CertificatesEventUpload(
|
||||
item: item, path: imagePath, expirationDate: expireDate));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:krow/core/application/common/text_formatters/expiration_date_formatter.dart';
|
||||
import 'package:krow/core/application/common/validators/certificate_date_validator.dart';
|
||||
import 'package:krow/core/presentation/gen/assets.gen.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
|
||||
import 'package:krow/core/presentation/styles/theme.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
|
||||
|
||||
class CertificatesUploadDialog extends StatefulWidget {
|
||||
final String path;
|
||||
|
||||
const CertificatesUploadDialog({
|
||||
super.key,
|
||||
required this.path,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CertificatesUploadDialog> createState() =>
|
||||
_CertificatesUploadDialogState();
|
||||
|
||||
static Future<String?> show(BuildContext context, String path) {
|
||||
return showDialog<String?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CertificatesUploadDialog(
|
||||
path: path,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CertificatesUploadDialogState extends State<CertificatesUploadDialog> {
|
||||
final TextEditingController expiryDateController = TextEditingController();
|
||||
String? inputError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
expiryDateController.addListener(() {
|
||||
if(expiryDateController.text.length == 10) {
|
||||
inputError =
|
||||
CertificateDateValidator.validate(expiryDateController.text);
|
||||
}else{
|
||||
inputError = null;
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Center(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.9),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grayWhite,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
..._buildDialogTitle(context),
|
||||
Material(
|
||||
type: MaterialType.transparency,
|
||||
child: KwTextInput(
|
||||
title: 'expiry_date_1'.tr(),
|
||||
hintText: 'enter_certificate_expiry_date'.tr(),
|
||||
controller: expiryDateController,
|
||||
maxLength: 10,
|
||||
inputFormatters: [DateTextFormatter()],
|
||||
keyboardType: TextInputType.datetime,
|
||||
helperText: inputError,
|
||||
showError: inputError != null,
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
_buildImage(context),
|
||||
const Gap(24),
|
||||
KwButton.primary(
|
||||
label: 'save_certificate'.tr(),
|
||||
disabled: expiryDateController.text.length != 10 || inputError != null,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(expiryDateController.text);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ClipRRect _buildImage(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Image.file(
|
||||
File(widget.path),
|
||||
width: MediaQuery.of(context).size.width - 80,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildDialogTitle(BuildContext context) {
|
||||
return [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'add_certificate_expiry_date'.tr(),
|
||||
style: AppTextStyles.bodyLargeMed,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Assets.images.icons.x.svg(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'please_enter_expiry_date'.tr(),
|
||||
style: AppTextStyles.bodySmallReg.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user