EnQList added policy detls

This commit is contained in:
venbaittech 2025-11-29 10:58:21 +05:30
parent 53a86a8e21
commit 485894ba98
17 changed files with 4627 additions and 221 deletions

File diff suppressed because one or more lines are too long

View File

@ -27,6 +27,7 @@ import '../../presentation/screens/home/home_screen.dart';
import '../../presentation/screens/login/login_screen.dart';
import '../../presentation/screens/payout/payout_screen.dart';
import '../../presentation/screens/splash/splash_screen.dart';
import '../../presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.dart';
import '../../presentation/screens/staff/Enquiry/enquiry_inline_list.dart';
import '../../presentation/screens/staff/Enquiry/enquiry_list.dart';
import '../../presentation/screens/staff/policy/policy.dart';
@ -178,6 +179,8 @@ final GoRouter appRouter = GoRouter(
GoRoute(
path: AppRoutes.enquiryForStaff,
builder: (context, state) => const EnquiryListStaffInline(),
// builder: (context, state) => const EnquiryListStaffInlineTST(),
// builder: (context, state) => const EnquiryStaff(),
),
GoRoute(

View File

@ -91,6 +91,44 @@ class ApiService {
}
// --------------------------- COMMON FILES __________________________________________
Future<Map<String, dynamic>> createUserData(data, path) async {
print('path - $path');
final url = Uri.parse('${Env.apiUrl}$path');
if (_token == null) {
await _initializeToken();
}
print("data------- $data}");
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'Content-Type': 'application/json',
'app-signature': Env.App_Signature,
};
final response = await _makePostRequestJson(url, data, headers);
return response;
}
Future<Map<String, dynamic>> fetchUploadEnquiryFiles(dynamic enqId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}enquiry/enquiryFiles?enquiry_id=$enqId',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'app-signature': Env.App_Signature,
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> logoutUsingAPI(agentId) async {
// print(_token);
if (_token == null) {
@ -762,6 +800,23 @@ class ApiService {
//------------------------------------ Quotation ------------------------------------------------
Future<Map<String, dynamic>> fetchQuickQuotationList(int managerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}enquiry/getQuickQuoteEnquiryList?manager_id=${managerId}',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'app-signature': Env.App_Signature,
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchQuotationList(int managerId) async {
// print(_token);
if (_token == null) {
@ -1212,7 +1267,6 @@ class ApiService {
// --------------------------------- PayOut Module----------------------------------------------
Future<Map<String, dynamic>> getCommissionRateList(data) async {
final url = Uri.parse('${Env.apiUrl}/invoice/commission-rate-list');
// final token = await getToken(); // Fetch token
@ -1235,7 +1289,6 @@ class ApiService {
}
Future<Map<String, dynamic>> getCreateOrUpdate(data) async {
final url = Uri.parse('${Env.apiUrl}/invoice/create-or-update');
// final token = await getToken(); // Fetch token
@ -1289,6 +1342,22 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> delete(ID, path) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse('${Env.apiUrl}$path');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'app-signature': Env.App_Signature,
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> getInvoiceDetails(ID) async {
// print(_token);
if (_token == null) {

View File

@ -11,6 +11,8 @@ import '../../data/services/auth_service.dart';
import '../providers/manager_provider.dart';
import '../providers/userRoleProvider.dart';
import '../screens/UserManagement/Profile/profile_web.dart';
import '../screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart';
import '../themes/indicators/side_Drawer_Panel.dart';
class AppHeader extends ConsumerStatefulWidget implements PreferredSizeWidget {
const AppHeader({super.key});
@ -380,7 +382,11 @@ class _AppHeaderState extends ConsumerState<AppHeader> {
size: 20,
),
onPressed: () {
debugPrint("Notifications tapped");
SideDrawerPanel.show(
context: context,
title: 'Proposal Creation',
child: CreateProposal_Quick(),
);
},
tooltip: 'Create Proposal',
),

File diff suppressed because it is too large Load Diff

View File

@ -39,13 +39,13 @@ import '../../../themes/indicators/text_field_theme_inline_editor.dart'
import '../../../widgets/custom_Stdate_EnDate_Filter.dart';
import '../assignStaff.dart';
class EnquiryListStaffInline extends ConsumerStatefulWidget {
const EnquiryListStaffInline({super.key});
class EnquiryListStaffInlineTST extends ConsumerStatefulWidget {
const EnquiryListStaffInlineTST({super.key});
@override
ConsumerState<EnquiryListStaffInline> createState() => EnquiryStaffState();
ConsumerState<EnquiryListStaffInlineTST> createState() => EnquiryStaffState();
}
class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
class EnquiryStaffState extends ConsumerState<EnquiryListStaffInlineTST> {
int currentPage = 1;
int itemsPerPage = 10;
late ApiService apiService;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,293 @@
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:nhance_partner/presentation/themes/indicators/mutli_FileUpload.dart';
import '../../../../core/config/env.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/toastNotification.dart';
import '../../../themes/indicators/multi_fileUploadService.dart';
class MultipleFileWidget extends ConsumerStatefulWidget {
dynamic enqId;
MultipleFileWidget({super.key, required this.enqId});
@override
ConsumerState<MultipleFileWidget> createState() => MultipleFileWidgetState();
}
class MultipleFileWidgetState extends ConsumerState<MultipleFileWidget> {
List<PlatformFile> uploadedFiles = [];
final fileService = MultiFileUploadService();
late ApiService apiService;
bool isLoading = false;
dynamic _token;
List<Map<String, dynamic>> getUploadedData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
@override
void initState() {
super.initState();
fileService.clearAll();
apiService = ApiService();
_initializeToken();
getList();
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
Future<void> getList() async {
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchUploadEnquiryFiles(widget.enqId);
if (response['status'] == 'success') {
print('E113 => getfetchUploadEnquiryFiles=');
print('E113 => getfetchUploadEnquiryFiles=> ${response['data']}');
setState(() {
getUploadedData = List<Map<String, dynamic>>.from(response['data']);
originalData = getUploadedData;
filteredData = List.from(originalData);
print('originalData - $originalData');
});
} else {
getUploadedData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
Future<void> handleSave() async {
final request = http.MultipartRequest(
'POST',
Uri.parse('${Env.apiUrl}enquiry/uploadEnquiryFiles'),
);
if (_token == null) {
throw Exception('Token not found. Please log in.');
}
request.headers['Authorization'] = 'Bearer $_token';
request.headers['app-signature'] = Env.App_Signature;
if (MultiFileUploadService().files.isEmpty) {
setState(() {
MultiFileUploadWidget.hasFiles = false;
});
ToastHelper.showErrorToast(
context,
'Please upload at least one document',
);
return;
}
// final id = int.tryParse(enqId.toString()) ?? 0;
// get uploaded files
final uploadedFiles = MultiFileUploadService().files;
print("📁 Total uploaded files: ${uploadedFiles.length}");
print(
"📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}",
);
// Validate all labels present (optional but recommended)
for (final uf in uploadedFiles) {
print(
"📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'",
);
if ((uf.label ?? '').trim().isEmpty) {
ToastHelper.showErrorToast(
context,
'Please enter name for all uploaded documents',
);
setState(() => isLoading = false);
return;
}
}
// Add files
for (var uf in uploadedFiles) {
final pf = uf.file;
if (pf.bytes != null) {
request.files.add(
http.MultipartFile.fromBytes('files[]', pf.bytes!, filename: pf.name),
);
}
}
// Combine all names into a JSON array string
final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList();
final encodedNames = jsonEncode(claimDocNames);
request.fields['files_doc_names'] = encodedNames;
request.fields['enquiry_id'] = widget.enqId!;
// Debug print
print("Payload being sent:");
print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}");
print("Names (JSON): $encodedNames");
final response = await request.send();
final responseBody = await response.stream.bytesToString();
final decoded = jsonDecode(responseBody);
if (decoded['status'] == 'success') {
ToastHelper.showSuccessToast(context, 'File Uploaded');
setState(() {
isLoading = false;
});
print('Form data submitted successfully');
fileService.clearAll();
getList();
} else {
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
}
}
Future<void> handleDelete(id) async {
setState(() {
isLoading = true;
});
final path = 'enquiry/deleteEnquiryFile?file_id=$id';
print('path - $path');
try {
final response = await apiService.delete(id, path);
print("response");
print("response - $response");
print("response - ${response['status']}");
if (response['status'] == 'success') {
ToastHelper.showSuccessToast(context, response['message']);
getList();
} else {
ToastHelper.showSuccessToast(context, response['message']);
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Container(
color: Color(0xFFFFFFFF),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('File Upload', style: GoogleFonts.poppins(fontSize: 11)),
SizedBox(height: 3),
MultiFileUploadWidget(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
handleSave();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF2E7D6E),
// color: Color(0xFF425B5B),
),
child: Text(
'Save',
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 11,
),
),
),
),
],
),
SizedBox(height: 5),
Column(
children: List.generate(originalData.length, (index) {
final item = originalData[index];
final fileName = item['file_name'] ?? '';
final fileId = item['id'] ?? '';
return Container(
// margin: const EdgeInsets.only(bottom: 6),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(2),
border: Border(
bottom: BorderSide(
width: 0.2,
color: Colors.blueGrey.shade100,
),
),
),
child: Row(
children: [
Icon(
Icons.file_present_outlined,
size: 16,
color: Colors.grey[700],
),
const SizedBox(width: 8),
Expanded(
child: Text(
fileName,
style: GoogleFonts.poppins(fontSize: 11),
overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
onTap: () {
final path =
'api/enquiry/downloadEnquiryFiles?file_id=$fileId';
print("Uploadpath $path");
apiService.getPdfDownload(path, fileId);
},
child: Icon(Icons.download, size: 16, color: Colors.blue),
),
// DELETE BUTTON (optional)
GestureDetector(
onTap: () {
handleDelete(fileId);
},
child: Icon(Icons.delete, size: 16, color: Colors.red),
),
],
),
);
}),
),
],
),
);
}
}

View File

@ -483,6 +483,11 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
label: "Partner Name",
).copyWith(
filled: true,
// errorStyle: const TextStyle(
// fontSize: 9,
//
// // height: 0.0
// ),
fillColor:
Colors.white, // 👈 makes the dropdown input white
isDense: true,

View File

@ -503,38 +503,6 @@ class CreateProposalFormState extends ConsumerState<CreateProposalForm> {
height: MediaQuery.of(context).size.height * 0.525,
child: buildFormFields(context),
),
// Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// GestureDetector(
// onTap: () {
// handleDone();
// // widget.onSubmit(controller.text.trim());
// // Navigator.of(context).pop();
// },
// child: Container(
// padding: const EdgeInsets.symmetric(
// horizontal: 25.0,
// vertical: 10,
// ),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8.0),
// color: const Color(0xFF425B5B),
// ),
// child: Text(
// 'Save',
// style: GoogleFonts.poppins(
// color: Colors.white,
// fontSize: 12,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
// ),
// ],
// ),
],
);
}

View File

@ -8,6 +8,8 @@ class ThemedUploadField extends StatefulWidget {
final String? hintText;
final double? txtwidth;
final double? txtheight;
final double? fontSZ;
final double? borderCirculr;
final Color? backgroundColor;
final Color? borderColor;
final Color? errorBorderColor;
@ -23,6 +25,8 @@ class ThemedUploadField extends StatefulWidget {
super.key,
this.hintText,
this.txtwidth,
this.borderCirculr,
this.fontSZ,
this.txtheight,
this.backgroundColor,
this.borderColor,
@ -89,7 +93,9 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
),
decoration: BoxDecoration(
color: widget.backgroundColor ?? Colors.white,
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(
widget.borderCirculr ?? 10,
),
border: Border.all(
color: errorMessage != null
? (widget.errorBorderColor ?? Colors.red)
@ -108,7 +114,7 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
widget.hintText ??
"Upload Document",
style: TextStyle(
fontSize: 12,
fontSize: widget.fontSZ ?? 12,
color: errorMessage != null
? Colors.red
: Colors.black,
@ -119,6 +125,7 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
const Icon(
Icons.file_upload_outlined,
color: Colors.black,
size: 12,
),
],

View File

@ -51,7 +51,7 @@ class _ThemedMultiFileUploadFieldState
Future<void> _pickFile() async {
final error = await fileService.pickFiles(
maxFileSizeInMB: 3,
allowedExtensions: widget.allowedExtensions,
// allowedExtensions: widget.allowedExtensions,
);
if (error != null) {
@ -60,6 +60,9 @@ class _ThemedMultiFileUploadFieldState
context,
).showSnackBar(SnackBar(content: Text(error)));
} else if (fileService.pickFiles != null) {
print('Files Picked ');
print('Files Picked fileService - ${fileService.pickFiles}');
// setState(() {
// selectedFileName = fileService.pickFiles!.name;
// errorMessage = null;

View File

@ -1,51 +1,71 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/widgets.dart';
class UploadedFile {
final PlatformFile file;
final TextEditingController controller;
UploadedFile({required this.file})
: controller = TextEditingController(
text: file.extension != null
? file.name.replaceAll('.${file.extension}', '')
: file.name,
);
String get label => controller.text;
void dispose() => controller.dispose();
}
class MultiFileUploadService {
MultiFileUploadService._();
static final MultiFileUploadService _instance = MultiFileUploadService._();
factory MultiFileUploadService() => _instance;
final List<PlatformFile> _files = [];
final List<UploadedFile> _files = [];
List<PlatformFile> get files => List.unmodifiable(_files);
// Expose as unmodifiable but containing UploadedFile objects
List<UploadedFile> get files => List.unmodifiable(_files);
// Allowed extensions
// static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg', 'heic'];
static const defaultAllowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg', 'heic'];
Future<String?> pickFiles({
int maxFileSizeInMB = 3,
List<String>? allowedExtensions,
}) async {
final extensions = allowedExtensions ?? defaultAllowedExtensions;
Future<String?> pickFiles({int maxFileSizeInMB = 3}) async {
final result = await FilePicker.platform.pickFiles(
allowMultiple: true,
withData: true,
type: FileType.custom,
allowedExtensions: extensions,
allowedExtensions: allowedExtensions,
);
if (result != null) {
for (final file in result.files) {
final ext = file.extension?.toLowerCase() ?? '';
final sizeInMB = (file.size / (1024 * 1024));
for (final pf in result.files) {
final ext = pf.extension?.toLowerCase() ?? '';
final sizeInMB = (pf.size / (1024 * 1024));
if (!allowedExtensions!.contains(ext)) {
return "Unsupported file format: ${file.name}";
if (!allowedExtensions.contains(ext)) {
return "Unsupported file format: ${pf.name}";
}
if (sizeInMB > maxFileSizeInMB) {
return "File too large (${file.name}). Max $maxFileSizeInMB MB allowed.";
return "File too large (${pf.name}). Max $maxFileSizeInMB MB allowed.";
}
}
_files.addAll(result.files);
// Wrap PlatformFile in our UploadedFile model
_files.add(UploadedFile(file: pf));
}
}
return null; // success
return null;
}
void removeFileAt(int index) {
if (index >= 0 && index < _files.length) _files.removeAt(index);
if (index >= 0 && index < _files.length) {
_files[index].dispose();
_files.removeAt(index);
}
}
void clearAll() => _files.clear();
void clearAll() {
for (final f in _files) {
f.dispose();
}
_files.clear();
}
}

View File

@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../layouts/responsive_layout.dart';
import 'multi_fileUploadService.dart';
class MultiFileUploadWidget extends StatefulWidget {
@ -6,6 +9,8 @@ class MultiFileUploadWidget extends StatefulWidget {
@override
State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState();
static bool hasFiles = false; // Static variable to check from parent
}
class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
@ -40,13 +45,16 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
} else {
setState(() {
errorMessage = null;
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
});
}
}
void _removeFile(int index) {
fileService.removeFileAt(index);
setState(() {});
setState(() {
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
});
}
@override
@ -56,44 +64,117 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// if (ResponsiveLayout.isMobile(context)) ...[
OutlinedButton.icon(
onPressed: _pickFiles,
icon: const Icon(
Icons.file_upload_outlined,
color: Color(0xFFE26728),
size: 24,
color: Color(0xFF2E7D6E),
size: 18,
),
label: const Text(
"Upload Documents",
style: TextStyle(fontSize: 14, color: Colors.black),
style: TextStyle(fontSize: 11, color: Colors.black),
overflow: TextOverflow.ellipsis,
),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFFE26728)),
side: const BorderSide(color: Color(0xFF2E7D6E)),
),
),
const SizedBox(height: 3),
Text(
"Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)",
style: GoogleFonts.poppins(fontSize: 8, color: Colors.grey),
),
// ]
// else ...[
// Row(
// children: [
// OutlinedButton.icon(
// onPressed: _pickFiles,
// icon: const Icon(
// Icons.file_upload_outlined,
// color: Color(0xFFE26728),
// size: 24,
// ),
// label: const Text(
// "Upload Documents",
// style: TextStyle(fontSize: 14, color: Colors.black),
// overflow: TextOverflow.ellipsis,
// ),
// style: OutlinedButton.styleFrom(
// side: const BorderSide(color: Color(0xFFE26728)),
// ),
// ),
// const SizedBox(width: 12),
// // const Expanded(
// // child: Text(
// // "Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)",
// // style: TextStyle(fontSize: 12, color: Colors.grey),
// // overflow: TextOverflow.ellipsis,
// // ),
// // ),
// ],
// ),
// ],
// if (fileService.files.isEmpty && errorMessage == null) ...[
// const SizedBox(height: 4),
// const Text(
// "Required",
// style: TextStyle(color: Colors.red, fontSize: 12),
// ),
// ],
if (errorMessage != null) ...[
const SizedBox(height: 4),
Text(
errorMessage!,
style: const TextStyle(color: Colors.red, fontSize: 12),
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
const SizedBox(height: 8),
...files.asMap().entries.map((entry) {
final index = entry.key;
final file = entry.value;
return ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
title: Text(file.name, style: const TextStyle(fontSize: 14)),
trailing: IconButton(
icon: const Icon(Icons.close, color: Colors.red),
onPressed: () => _removeFile(index),
),
...fileService.files.asMap().entries.map((entry) {
final index = entry.key;
final uploaded = entry.value; // UploadedFile
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
title: Text(
uploaded.file.name,
style: GoogleFonts.poppins(fontSize: 11),
),
trailing: IconButton(
padding: EdgeInsets.zero,
splashRadius: 2,
icon: const Icon(Icons.close, color: Colors.red, size: 14),
onPressed: () => _removeFile(index),
),
),
// Padding(
// padding: const EdgeInsets.only(
// left: 8.0,
// bottom: 8.0,
// right: 8.0,
// ),
// child: TextField(
// controller: uploaded.controller,
// style: GoogleFonts.poppins(fontSize: 11),
// decoration: InputDecoration(
// labelText: 'Enter document name',
// labelStyle: GoogleFonts.poppins(fontSize: 11),
//
// border: const OutlineInputBorder(),
// isDense: true,
// ),
// ),
// ),
],
);
}),
],

View File

@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
/// A reusable side drawer that slides in from the right
/// Takes up 1/3 of the screen width
class SideDrawerPanel extends StatelessWidget {
final Widget child;
final String? title;
final VoidCallback? onClose;
final double? width;
const SideDrawerPanel({
Key? key,
required this.child,
this.title,
this.onClose,
this.width,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final drawerWidth = width ?? screenWidth / 2;
return Container(
width: drawerWidth,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 2,
offset: const Offset(-2, 0),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
// color: const Color(0xFFF8F9FF),
border: Border(bottom: BorderSide(color: Colors.grey.shade200)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (title != null)
Text(
'Proposal Creation',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.close),
onPressed: onClose ?? () => Navigator.of(context).pop(),
color: Colors.grey.shade600,
tooltip: 'Close',
),
],
),
),
// Content
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(8),
// child: child,
child: Material(
color: Colors.white,
elevation: 0,
child: child, // Your CreateProposal_Quick()
),
),
),
],
),
);
}
/// Show the drawer as a modal overlay
static Future<T?> show<T>({
required BuildContext context,
required Widget child,
String? title,
double? width,
bool barrierDismissible = true,
}) {
return showGeneralDialog<T>(
context: context,
barrierDismissible: barrierDismissible,
barrierLabel: 'Drawer',
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
return Align(
alignment: Alignment.centerRight,
child: SideDrawerPanel(title: title, width: width, child: child),
);
},
transitionBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(begin: const Offset(1, 0), end: Offset.zero)
.animate(
CurvedAnimation(parent: animation, curve: Curves.easeInOut),
),
child: child,
);
},
);
}
}

View File

@ -20,6 +20,7 @@ class ThemedFormField extends HookWidget {
this.backgroundColor,
this.highlightColor,
this.borderColor,
this.bordedRad,
this.errorBorderColor,
this.errorTextColor,
this.readOnly = false, // new
@ -37,6 +38,7 @@ class ThemedFormField extends HookWidget {
final double? verticalPad;
final double? horizonalPad;
final double? bordedRad;
final double? enableBorderWidth;
final String? hintText;
final Color? textColr;
@ -116,7 +118,7 @@ class ThemedFormField extends HookWidget {
horizontal: 10,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(bordedRad ?? 10),
// borderSide: BorderSide(color: Color(0xFF50A398)),
borderSide: BorderSide(
color: borderColor ?? Color(0xFFFFFFFF),
@ -124,7 +126,7 @@ class ThemedFormField extends HookWidget {
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(bordedRad ?? 10),
// borderSide: BorderSide(color: Color(0xFF50A398)),
borderSide: BorderSide(
color: borderColor ?? backgroundColor ?? Color(0xFFFFFFFF),