Booking physical consultation , bugs fixed and Profile picture adding using firebase storage is complete. Co-authored-by: Jipson George <152465898+Jipson-cosq@users.noreply.github.com> Reviewed-on: cosqnet/telemednet#6 Co-authored-by: DhanshCOSQ <dhanshas@cosq.net> Co-committed-by: DhanshCOSQ <dhanshas@cosq.net>
286 lines
8.5 KiB
Dart
286 lines
8.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:medora/controllers/patient_controller.dart';
|
|
import 'package:medora/data/models/patient.dart';
|
|
|
|
class FamilyMembersEditScreen extends StatefulWidget {
|
|
final FamilyMember? familyMember;
|
|
final PatientController controller;
|
|
|
|
const FamilyMembersEditScreen(
|
|
{super.key, this.familyMember, required this.controller});
|
|
|
|
@override
|
|
State<FamilyMembersEditScreen> createState() =>
|
|
_FamilyMembersEditScreenState();
|
|
}
|
|
|
|
class _FamilyMembersEditScreenState extends State<FamilyMembersEditScreen> {
|
|
late TextEditingController nameController;
|
|
late TextEditingController relationController;
|
|
late TextEditingController genderController;
|
|
late TextEditingController dobController;
|
|
Map<String, String> errors = {};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
nameController =
|
|
TextEditingController(text: widget.familyMember?.name ?? '');
|
|
relationController =
|
|
TextEditingController(text: widget.familyMember?.relation ?? '');
|
|
genderController =
|
|
TextEditingController(text: widget.familyMember?.gender ?? '');
|
|
dobController = TextEditingController(
|
|
text: widget.familyMember?.dateOfBirth?.toString().split(' ')[0] ?? '');
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Edit Family Member'),
|
|
actions: _buildAppBarActions(),
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildTextField(nameController, 'Name', Icons.person, 'name'),
|
|
_buildDropdownField(
|
|
'Relation',
|
|
relationController.text,
|
|
(String? newValue) {
|
|
setState(() {
|
|
relationController.text = newValue ?? '';
|
|
});
|
|
},
|
|
Icons.family_restroom,
|
|
),
|
|
_buildDropdownField(
|
|
'Gender',
|
|
genderController.text,
|
|
(String? newValue) {
|
|
setState(() {
|
|
genderController.text = newValue ?? '';
|
|
});
|
|
},
|
|
Icons.transgender,
|
|
),
|
|
_buildDateField(context),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDropdownField(
|
|
String label, String value, Function(String?) onChanged, IconData icon) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: DropdownButtonFormField<String>(
|
|
decoration: InputDecoration(
|
|
labelText: label,
|
|
prefixIcon: Icon(icon, color: Colors.blue),
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
value: value.isEmpty ? null : value,
|
|
onChanged: onChanged,
|
|
items: label == 'Relation'
|
|
? <String>['Father', 'Mother', 'Son', 'Daughter', 'Other']
|
|
.map<DropdownMenuItem<String>>((String value) {
|
|
return DropdownMenuItem<String>(
|
|
value: value,
|
|
child: Text(value),
|
|
);
|
|
}).toList()
|
|
: <String>['Male', 'Female', 'Other']
|
|
.map<DropdownMenuItem<String>>((String value) {
|
|
return DropdownMenuItem<String>(
|
|
value: value,
|
|
child: Text(value),
|
|
);
|
|
}).toList(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDateField(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: TextField(
|
|
controller: dobController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Date of Birth',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.calendar_today, color: Colors.blue),
|
|
),
|
|
readOnly: true,
|
|
onTap: () async {
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now().subtract(const Duration(days: 365)),
|
|
firstDate: DateTime(1900),
|
|
lastDate: DateTime.now().subtract(const Duration(days: 365)),
|
|
);
|
|
if (pickedDate != null) {
|
|
setState(() {
|
|
dobController.text = pickedDate.toString().split(' ')[0];
|
|
});
|
|
}
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
bool _validateFields() {
|
|
errors.clear();
|
|
|
|
if (nameController.text.trim().isEmpty) {
|
|
errors['name'] = 'Name is required';
|
|
} else if (nameController.text.trim().length < 2) {
|
|
errors['name'] = 'Name must be at least 2 characters';
|
|
}
|
|
|
|
if (relationController.text.isEmpty) {
|
|
errors['relation'] = 'Please select a relation';
|
|
}
|
|
|
|
if (genderController.text.isEmpty) {
|
|
errors['gender'] = 'Please select a gender';
|
|
}
|
|
|
|
if (dobController.text.isEmpty) {
|
|
errors['dob'] = 'Date of Birth is required';
|
|
} else {
|
|
final dob = DateTime.tryParse(dobController.text);
|
|
if (dob == null) {
|
|
errors['dob'] = 'Invalid date format';
|
|
} else if (dob.isAfter(DateTime.now())) {
|
|
errors['dob'] = 'Date of Birth cannot be in the future';
|
|
}
|
|
}
|
|
|
|
setState(() {});
|
|
return errors.isEmpty;
|
|
}
|
|
|
|
void _showValidationErrors() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Row(
|
|
children: [
|
|
Icon(Icons.error_outline, color: Colors.red),
|
|
SizedBox(width: 8),
|
|
Text('Validation Errors'),
|
|
],
|
|
),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: errors.entries
|
|
.map((error) => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: Text(
|
|
'• ${error.value}',
|
|
style: const TextStyle(color: Colors.red),
|
|
),
|
|
))
|
|
.toList(),
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTextField(
|
|
TextEditingController controller,
|
|
String label,
|
|
IconData icon,
|
|
String errorKey,
|
|
) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
TextField(
|
|
controller: controller,
|
|
decoration: InputDecoration(
|
|
labelText: label,
|
|
prefixIcon: Icon(
|
|
icon,
|
|
color: errors.containsKey(errorKey) ? Colors.red : Colors.blue,
|
|
),
|
|
border: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
errors.containsKey(errorKey) ? Colors.red : Colors.grey,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
errors.containsKey(errorKey) ? Colors.red : Colors.grey,
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
errors.containsKey(errorKey) ? Colors.red : Colors.blue,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (errors.containsKey(errorKey))
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 4, left: 12),
|
|
child: Text(
|
|
errors[errorKey]!,
|
|
style: const TextStyle(color: Colors.red, fontSize: 12),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildAppBarActions() {
|
|
return [
|
|
TextButton(
|
|
onPressed: () {
|
|
if (_validateFields()) {
|
|
FamilyMember newMember = FamilyMember(
|
|
name: nameController.text,
|
|
relation: relationController.text,
|
|
gender: genderController.text,
|
|
dateOfBirth: DateTime.tryParse(dobController.text),
|
|
);
|
|
Navigator.pop(context, newMember);
|
|
} else {
|
|
_showValidationErrors();
|
|
}
|
|
},
|
|
child: const Text('Done', style: TextStyle(color: Colors.blue)),
|
|
),
|
|
];
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
nameController.dispose();
|
|
relationController.dispose();
|
|
genderController.dispose();
|
|
dobController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|