staff_policy_upload_pdf
This commit is contained in:
parent
bfe7055dc1
commit
872b4e4649
File diff suppressed because one or more lines are too long
@ -1,4 +1,6 @@
|
||||
// import 'dart:io' as html;
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@ -26,6 +28,7 @@ import '../../../../layouts/main_layout.dart';
|
||||
import '../../../../layouts/responsive_layout.dart';
|
||||
import '../../../../providers/manager_provider.dart';
|
||||
import '../../../../providers/quotation_staff_proivder.dart';
|
||||
import '../../../../themes/indicators/custom_loader.dart';
|
||||
import '../../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../../themes/indicators/date_field_theme.dart';
|
||||
import '../../../../themes/indicators/text_field_theme.dart';
|
||||
@ -122,15 +125,20 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
"manager_id": managerId,
|
||||
// "policy_pdf_file_name": "policy_doc.pdf",
|
||||
// "policy_payment_receipt_file_name": "receipt_doc.pdf",
|
||||
"created_by": userId,
|
||||
// "created_by": userId,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
Map<String, dynamic> dataForPolicyPDFDetails() {
|
||||
final data = {"quotation_id": acceptedQuotationId, "manager_id": managerId};
|
||||
return data;
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> quotationData = [];
|
||||
List<Map<String, dynamic>> originalData = [];
|
||||
List<Map<String, dynamic>> filteredData = [];
|
||||
// bool isLoading = false;
|
||||
bool isLoader = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -336,6 +344,50 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
});
|
||||
}
|
||||
|
||||
String _formatDate(String rawDate) {
|
||||
try {
|
||||
final dateTime = DateTime.parse(rawDate);
|
||||
return DateFormat('dd-MM-yyyy').format(dateTime); // 24-hour format
|
||||
} catch (e) {
|
||||
return rawDate; // fallback if parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
void updateUploadPDFPolicyData(Map<String, dynamic> policyData) {
|
||||
print('updateUploadPDFPolicyData - $policyData');
|
||||
if (!mounted || policyData.isEmpty) return;
|
||||
|
||||
setState(() {
|
||||
hasPolicyData = true;
|
||||
choosedPolcyId = policyData['id']?.toString() ?? '';
|
||||
|
||||
controllers["policyNumber"]?.text =
|
||||
policyData['policy_number']?.toString() ?? '';
|
||||
controllers["policyPaymentMode"]?.text =
|
||||
policyData['payment_mode']?.toString() ?? '';
|
||||
controllers["issueDate"]?.text =
|
||||
_formatDate(policyData['issued_date']!.toString()) ?? '';
|
||||
controllers["startDate"]?.text =
|
||||
_formatDate(policyData['start_date']!.toString()) ?? '';
|
||||
controllers["endDate"]?.text =
|
||||
_formatDate(policyData['end_date']!.toString()) ?? '';
|
||||
|
||||
// RC FILE
|
||||
String? rcPath = policyData["policy_pdf_file_name"];
|
||||
if (rcPath != null && rcPath.isNotEmpty) {
|
||||
selectedPDFFileNames = rcPath.split('/').last; // UI filename
|
||||
docUploadedPDFFileUrlFromApi = rcPath; // API download URL
|
||||
docPDFUploadedFile = null;
|
||||
// docUploadedRCFile =
|
||||
// rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet
|
||||
} else {
|
||||
selectedPDFFileNames = null; // no UI file shown
|
||||
docUploadedPDFFileUrlFromApi = null;
|
||||
docPDFUploadedFile = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
setState(() {
|
||||
@ -345,21 +397,12 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
print("dataSetPolicy - $dataSet");
|
||||
|
||||
// File validations
|
||||
if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
(docUploadedPDFFileUrlFromApi == null ||
|
||||
docUploadedPDFFileUrlFromApi!.isEmpty)) {
|
||||
ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
return;
|
||||
}
|
||||
|
||||
// if ((docUploadedFile == null || docUploadedFile?.bytes == null) &&
|
||||
// (docUploadedFileUrlFromApi == null ||
|
||||
// docUploadedFileUrlFromApi!.isEmpty)) {
|
||||
// ToastHelper.showErrorToast(context, "Payment Receipt is required");
|
||||
// if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
// (docUploadedPDFFileUrlFromApi == null ||
|
||||
// docUploadedPDFFileUrlFromApi!.isEmpty)) {
|
||||
// ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// print("managerId - $managerId ,userId - $userId ");
|
||||
createUserData(dataSet);
|
||||
} else {
|
||||
// isDi sable = false;
|
||||
@ -367,6 +410,22 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
});
|
||||
}
|
||||
|
||||
void handleUploadPolicy() {
|
||||
setState(() {
|
||||
dataForPolicyPDFDetails();
|
||||
final dataSet = dataForPolicyPDFDetails();
|
||||
print("dataSetPolicy - $dataSet");
|
||||
// File validations
|
||||
if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
(docUploadedPDFFileUrlFromApi == null ||
|
||||
docUploadedPDFFileUrlFromApi!.isEmpty)) {
|
||||
ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
return;
|
||||
}
|
||||
createPolicyPDFData(dataSet);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> attachFiles(http.MultipartRequest request) async {
|
||||
Future<void> addFileOrKeepName(
|
||||
PlatformFile? file,
|
||||
@ -418,13 +477,16 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
Future<void> createUserData(Map<String, dynamic> userData) async {
|
||||
final bool isUpdating = choosedPolcyId != null;
|
||||
final id = choosedPolcyId;
|
||||
// final id = '1';
|
||||
// print('UPDId- $id');
|
||||
|
||||
// final bool isUpdating = false;
|
||||
final uri = Uri.parse(
|
||||
isUpdating
|
||||
? '${Env.apiUrl}policy/updatePolicy'
|
||||
: '${Env.apiUrl}policy/createPolicy',
|
||||
'${Env.apiUrl}policy/updatePolicy',
|
||||
// isUpdating
|
||||
// ?
|
||||
// :
|
||||
// '${Env.apiUrl}policy/createPolicy',
|
||||
);
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
@ -517,14 +579,122 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createPolicyPDFData(Map<String, dynamic> userData) async {
|
||||
final bool isUpdating = choosedPolcyId != null;
|
||||
final id = choosedPolcyId;
|
||||
// final id = '1';
|
||||
// print('UPDId- $id');
|
||||
|
||||
final uri = Uri.parse('${Env.apiUrl}policy/uploadPolicyFile');
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
// Use MultipartRequest (POST only)
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
request.headers['Authorization'] = 'Bearer $_token';
|
||||
request.headers['app-signature'] = Env.App_Signature;
|
||||
|
||||
// If updating, spoof the method Laravel-style
|
||||
if (isUpdating) {
|
||||
request.fields['id'] = id!;
|
||||
request.fields['updated_by'] = userId!.toString();
|
||||
} else {
|
||||
request.fields['created_by'] = userId!.toString();
|
||||
}
|
||||
|
||||
print("USerDAta - $userData");
|
||||
|
||||
await attachFiles(request);
|
||||
userData.forEach((key, value) {
|
||||
if (key != 'policy_pdf_file_name') {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded $key: ${request.fields[key]}");
|
||||
} else {
|
||||
print('Something Missing..');
|
||||
}
|
||||
});
|
||||
|
||||
print(" Sending request with fields: ${request.fields}");
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const CustomLoader(
|
||||
message: 'Loading data...',
|
||||
loaderColor: Colors.deepPurple,
|
||||
useBlurBackground: true,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final streamedResponse = await request.send();
|
||||
final response = await http.Response.fromStream(streamedResponse);
|
||||
print("Response status: ${response.statusCode}");
|
||||
print("Response body: ${response.body}");
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
// Close the loader before showing any message
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
// dispose();
|
||||
print("✅ PDF submitted successfully!");
|
||||
ToastHelper.showSuccessToast(context, 'PDF Uploaded');
|
||||
print("Response: ${response.body}");
|
||||
|
||||
final Map<String, dynamic> jsonResponse = jsonDecode(response.body);
|
||||
final policy =
|
||||
jsonResponse['data']?['file_read_data']?['value']?['policy'];
|
||||
if (policy != null) {
|
||||
final extractedData = {
|
||||
'id': jsonResponse['data']?['policy_id']?.toString(),
|
||||
'policy_number': policy['policy_number'],
|
||||
'issued_date': policy['issue_date'],
|
||||
'start_date': policy['period']?['start'],
|
||||
'end_date': policy['period']?['end'],
|
||||
'payment_mode': policy['payment_mode'] ?? 'online',
|
||||
// optional if available
|
||||
'policy_pdf_file_name':
|
||||
jsonResponse['data']?['policy_pdf_file_name'],
|
||||
// optional if your API returns it
|
||||
};
|
||||
|
||||
print('extractedData - $extractedData');
|
||||
updateUploadPDFPolicyData(extractedData);
|
||||
}
|
||||
} else if (response.statusCode == 403) {
|
||||
await apiService.clearLocalStorageAndRedirect();
|
||||
} else {
|
||||
print("❌ Submission failed. Status: ${response.statusCode}");
|
||||
print("Body: ${response.body}");
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(" Upload Policy Failed"),
|
||||
content: Text(
|
||||
"There was a problem in uploading pdf. Please try again.",
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text("OK"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print("🔥 Error submitting user: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// height: MediaQuery.of(context).size.height,
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// padding: const EdgeInsets.all(20),
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -540,17 +710,12 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// SingleChildScrollView(child: buildFormFields(context)),
|
||||
buildFormFields(context),
|
||||
|
||||
// const SizedBox(height: 20),
|
||||
|
||||
//confirm button
|
||||
if (!hasPolicyData && roleId != 'manager') ...[
|
||||
if (roleId != 'manager') ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
InkWell(
|
||||
onTap: () {
|
||||
handleSave();
|
||||
},
|
||||
@ -565,7 +730,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
color: Color(0xFF425B5B),
|
||||
),
|
||||
child: Text(
|
||||
'Submit',
|
||||
!hasPolicyData ? 'Submit' : 'Update',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
@ -581,46 +746,6 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// const SizedBox(height: 20),
|
||||
|
||||
// Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: const Color(0xFFEDF6F5),
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// ),
|
||||
// padding: const EdgeInsets.all(20),
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Enquiry Details",
|
||||
// style: GoogleFonts.inter(
|
||||
// fontSize: 16,
|
||||
// fontWeight: FontWeight.w700,
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 15),
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Expanded(flex: 2, child: registernumber(context)),
|
||||
// const SizedBox(width: 20),
|
||||
// Expanded(flex: 2, child: insurer(context)),
|
||||
// const SizedBox(width: 20),
|
||||
// Expanded(flex: 2, child: buildIdv(context)),
|
||||
// const SizedBox(width: 20),
|
||||
// Expanded(flex: 2, child: buildPremiumAmnt(context)),
|
||||
// const SizedBox(width: 20),
|
||||
// Expanded(flex: 2, child: buildPlanType(context)),
|
||||
// const SizedBox(width: 20),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
// ),
|
||||
@ -995,7 +1120,6 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['premium_amount']!,
|
||||
readOnly: true,
|
||||
// validator: (value) =>
|
||||
// Validators.requiredField(value, "policyPremiumAmount"),
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
@ -1215,17 +1339,32 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
children: [
|
||||
ThemedUploadField(
|
||||
hintText: selectedPDFFileNames ?? "Upload Document",
|
||||
allowedExtensions: ['pdf'],
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: (docUploadedPDFFileUrlFromApi != null)
|
||||
? MediaQuery.of(context).size.width * 0.15
|
||||
: MediaQuery.of(context).size.width * 0.175,
|
||||
// backgroundColor: Color(0xFFEDF6F5),
|
||||
onFileSelected: (fileName, file) {
|
||||
onFileSelected: (fileName, file) async {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
|
||||
// if (!fileName.toLowerCase().endsWith('.pdf')) {
|
||||
// ToastHelper.showErrorToast(context, Only PDF files are allowed.)
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(
|
||||
// content: Text('Only PDF files are allowed.'),
|
||||
// backgroundColor: Colors.red,
|
||||
// ),
|
||||
// );
|
||||
// return; // Stop here
|
||||
// }
|
||||
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
setState(() {
|
||||
docPDFUploadedFile = file;
|
||||
});
|
||||
handleUploadPolicy();
|
||||
},
|
||||
),
|
||||
|
||||
|
||||
62
lib/presentation/themes/indicators/custom_loader.dart
Normal file
62
lib/presentation/themes/indicators/custom_loader.dart
Normal file
@ -0,0 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomLoader extends StatelessWidget {
|
||||
final String? message;
|
||||
final Color? backgroundColor;
|
||||
final double size;
|
||||
final Color loaderColor;
|
||||
final bool useBlurBackground;
|
||||
|
||||
const CustomLoader({
|
||||
super.key,
|
||||
this.message,
|
||||
this.backgroundColor,
|
||||
this.size = 50,
|
||||
this.loaderColor = Colors.blue,
|
||||
this.useBlurBackground = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
if (useBlurBackground) Container(color: Colors.black.withOpacity(0.4)),
|
||||
Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 10),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: CircularProgressIndicator(
|
||||
color: loaderColor,
|
||||
strokeWidth: 4,
|
||||
),
|
||||
),
|
||||
if (message != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
message!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -108,6 +108,7 @@ class ThemedUploadField extends StatefulWidget {
|
||||
final Color? errorBorderColor;
|
||||
final Function(String fileName, PlatformFile file)? onFileSelected;
|
||||
FormFieldValidator<String>? validator;
|
||||
final List<String>? allowedExtensions;
|
||||
|
||||
ThemedUploadField({
|
||||
super.key,
|
||||
@ -119,6 +120,7 @@ class ThemedUploadField extends StatefulWidget {
|
||||
this.errorBorderColor,
|
||||
this.onFileSelected,
|
||||
this.validator,
|
||||
this.allowedExtensions,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -131,7 +133,10 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
|
||||
String? errorMessage;
|
||||
|
||||
Future<void> _pickFile() async {
|
||||
final error = await fileService.pickSingleFile(maxFileSizeInMB: 3);
|
||||
final error = await fileService.pickSingleFile(
|
||||
maxFileSizeInMB: 3,
|
||||
allowedExtensions: widget.allowedExtensions,
|
||||
);
|
||||
if (error != null) {
|
||||
setState(() => errorMessage = error);
|
||||
ScaffoldMessenger.of(
|
||||
@ -162,6 +167,7 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
||||
child: InkWell(
|
||||
onTap: _pickFile,
|
||||
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
||||
@ -40,7 +40,8 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
BuildContext context,
|
||||
FormFieldState<String> field,
|
||||
) async {
|
||||
DateTime initialDate = selectedDate ??
|
||||
DateTime initialDate =
|
||||
selectedDate ??
|
||||
(widget.controller?.text.isNotEmpty == true
|
||||
? DateFormat('dd-MM-yyyy').parse(widget.controller!.text)
|
||||
: DateTime.now());
|
||||
@ -78,6 +79,7 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => pickDate(context, field),
|
||||
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Container(
|
||||
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
||||
@ -120,6 +122,7 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (hasError)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5, left: 5),
|
||||
|
||||
@ -7,14 +7,20 @@ class FileUploadService {
|
||||
|
||||
PlatformFile? singleFile;
|
||||
|
||||
static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
|
||||
/// Default allowed extensions
|
||||
static const defaultAllowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
|
||||
// static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
|
||||
|
||||
Future<String?> pickSingleFile({int maxFileSizeInMB = 5}) async {
|
||||
Future<String?> pickSingleFile({
|
||||
int maxFileSizeInMB = 5,
|
||||
List<String>? allowedExtensions,
|
||||
}) async {
|
||||
final extensions = allowedExtensions ?? defaultAllowedExtensions;
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
allowMultiple: false,
|
||||
withData: true,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: allowedExtensions,
|
||||
allowedExtensions: extensions,
|
||||
);
|
||||
|
||||
if (result != null && result.files.isNotEmpty) {
|
||||
@ -22,7 +28,7 @@ class FileUploadService {
|
||||
final ext = file.extension?.toLowerCase() ?? '';
|
||||
final sizeInMB = file.size / (1024 * 1024);
|
||||
|
||||
if (!allowedExtensions.contains(ext)) {
|
||||
if (!extensions.contains(ext)) {
|
||||
return "Unsupported format: ${file.name}";
|
||||
}
|
||||
if (sizeInMB > maxFileSizeInMB) {
|
||||
|
||||
@ -24,6 +24,7 @@ class ThemedFormField extends HookWidget {
|
||||
this.keyboardType, // ✅ new
|
||||
this.maxLength, // ✅ new
|
||||
this.inputFormatters,
|
||||
this.isShowCursor,
|
||||
});
|
||||
|
||||
final String? hintText;
|
||||
@ -38,6 +39,7 @@ class ThemedFormField extends HookWidget {
|
||||
final Color? errorTextColor;
|
||||
final double? txtwidth;
|
||||
final bool readOnly;
|
||||
final bool? isShowCursor;
|
||||
final TextInputType? keyboardType;
|
||||
final int? maxLength;
|
||||
final double? txtheight;
|
||||
@ -59,6 +61,7 @@ class ThemedFormField extends HookWidget {
|
||||
);
|
||||
final obscureBtn = IconButton(
|
||||
onPressed: () => isObscured.value = !isObscured.value,
|
||||
|
||||
icon: Icon(
|
||||
isObscured.value
|
||||
? Icons.visibility_off_outlined
|
||||
@ -143,10 +146,11 @@ class ThemedFormField extends HookWidget {
|
||||
readOnly: readOnly, // ✅ now supported
|
||||
keyboardType: keyboardType, // ✅ e.g. TextInputType.number
|
||||
maxLength: maxLength, // ✅ max length
|
||||
showCursor: isShowCursor ?? true,
|
||||
enableInteractiveSelection: isShowCursor ?? true,
|
||||
inputFormatters:
|
||||
inputFormatters ??
|
||||
[FilteringTextInputFormatter.allow(RegExp(r'[a-z A-Z]'))],
|
||||
|
||||
// ✅ allow multiline if user sets maxLines / minLines
|
||||
minLines: (keyboardType == TextInputType.multiline) ? 3 : 1,
|
||||
maxLines: (keyboardType == TextInputType.multiline) ? null : 1,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user