medora-provider/lib/screens/patientScreens/patientDashboard/patient_home_screen.dart
DhanshCOSQ b57523599c feature/medora-55 (#6)
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>
2024-11-05 08:22:13 +00:00

675 lines
20 KiB
Dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:medora/data/models/consultation_booking.dart';
import 'package:medora/data/services/consultation_booking_service.dart';
import 'package:medora/route/route_names.dart';
import 'package:medora/screens/patientScreens/appoinmentBooking/speciality_screen.dart';
class PatientHomeScreen extends StatefulWidget {
const PatientHomeScreen({super.key});
@override
State<PatientHomeScreen> createState() => _PatientHomeScreenState();
}
class _PatientHomeScreenState extends State<PatientHomeScreen>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
final BookingService _bookingService = BookingService();
late Stream<List<Booking>> _bookingsStream;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_animationController.forward();
final User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
final String userId = user.uid;
_bookingsStream = _bookingService.getPatientBookings(userId);
} else {
_bookingsStream = const Stream.empty();
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
_buildSearchBar(),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildRealTimeCard(),
const SizedBox(height: 20),
_buildConsultationsSection(),
const SizedBox(height: 20),
_buildFindDoctorSection(),
],
),
),
],
),
),
);
}
Widget _buildSearchBar() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(30.0),
),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: TextField(
decoration: InputDecoration(
hintText: 'Search Doctor/Hospital/Symptoms',
hintStyle: GoogleFonts.poppins(
color: Colors.grey[400],
),
prefixIcon: const Icon(Icons.search, color: Colors.blue),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
),
),
),
],
),
);
}
Widget _buildRealTimeCard() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[400]!, Colors.white],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Real-time care\nat your fingertips.',
style: GoogleFonts.poppins(
fontSize: 30,
fontWeight: FontWeight.bold,
color: const Color.fromARGB(221, 67, 67, 67),
),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SpecialtyScreen()),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.blue[700],
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
elevation: 5,
),
child: Text(
'Start Consultation',
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
Widget _buildConsultationsSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Upcoming Consultations',
style: GoogleFonts.poppins(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
],
),
),
const SizedBox(height: 20),
SizedBox(
height: 201,
child: StreamBuilder<List<Booking>>(
stream: _bookingsStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 12),
Text(
'Loading consultations...',
style: GoogleFonts.poppins(
color: Colors.grey[600],
),
),
],
),
);
}
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline,
color: Colors.red[400], size: 48),
const SizedBox(height: 12),
Text(
'Error loading consultations',
style: GoogleFonts.poppins(
color: Colors.red[400],
fontWeight: FontWeight.w500,
),
),
TextButton(
onPressed: () {
// Implement refresh logic
},
child: Text(
'Try Again',
style: GoogleFonts.poppins(
color: Colors.blue[700],
),
),
),
],
),
);
}
final bookings = snapshot.data ?? [];
if (bookings.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.calendar_today,
color: Colors.grey[400], size: 48),
const SizedBox(height: 12),
Text(
'No upcoming consultations',
style: GoogleFonts.poppins(
color: Colors.grey[600],
fontSize: 16,
),
),
TextButton(
onPressed: () {
// Navigate to book consultation
},
child: Text(
'Book a Consultation',
style: GoogleFonts.poppins(
color: Colors.blue[700],
fontWeight: FontWeight.w600,
),
),
),
],
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 20),
scrollDirection: Axis.horizontal,
itemCount: bookings.length,
itemBuilder: (context, index) {
final booking = bookings[index];
return Padding(
padding: const EdgeInsets.only(right: 16),
child: Hero(
tag: 'consultation_${booking.id}',
child: Material(
child: _consultationCard(
booking.doctorName,
'${DateFormat('EEE, MMM d, yyyy').format(booking.appointmentDate)}\n${booking.appointmentTime}',
booking.specialization,
booking.paymentStatus,
),
),
),
);
},
);
},
),
),
],
);
}
Widget _consultationCard(
String name,
String schedule,
String speciality,
PaymentStatus paymentStatus,
) {
return Container(
width: 300,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.grey[50]!],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[400]!, Colors.blue[600]!],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.blue[300]!.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: const Icon(
Icons.person,
size: 36,
color: Colors.white,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
Text(
speciality,
style: GoogleFonts.poppins(
color: Colors.grey[600],
fontSize: 14,
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 4,
),
decoration: BoxDecoration(
color: _getStatusColor(paymentStatus).withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color:
_getStatusColor(paymentStatus).withOpacity(0.2),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_getStatusIcon(paymentStatus),
size: 14,
color: _getStatusColor(paymentStatus),
),
const SizedBox(width: 4),
Text(
_getStatusText(paymentStatus),
style: GoogleFonts.poppins(
fontSize: 12,
color: _getStatusColor(paymentStatus),
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
],
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.blue[100]!,
),
),
child: Row(
children: [
Icon(
Icons.calendar_today,
size: 18,
color: Colors.blue[700],
),
const SizedBox(width: 8),
Text(
schedule,
style: GoogleFonts.poppins(
color: Colors.blue[700],
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
);
}
IconData _getStatusIcon(PaymentStatus status) {
switch (status) {
case PaymentStatus.completed:
return Icons.check_circle;
case PaymentStatus.pending:
return Icons.access_time;
case PaymentStatus.failed:
return Icons.error;
default:
return Icons.info;
}
}
Color _getStatusColor(PaymentStatus status) {
switch (status) {
case PaymentStatus.pending:
return Colors.orange;
case PaymentStatus.completed:
return Colors.green;
case PaymentStatus.failed:
return Colors.red;
default:
return Colors.grey;
}
}
String _getStatusText(PaymentStatus status) {
switch (status) {
case PaymentStatus.pending:
return 'Payment Pending';
case PaymentStatus.completed:
return 'Confirmed';
case PaymentStatus.failed:
return 'Payment Failed';
default:
return 'Unknown';
}
}
Widget _buildFindDoctorSection() {
final specialistData = [
{
'icon': Icons.local_hospital,
'label': 'General',
'color': Colors.blue,
'description': 'Primary Healthcare'
},
{
'icon': Icons.remove_red_eye,
'label': 'Eye',
'color': Colors.indigo,
'description': 'Vision Care'
},
{
'icon': Icons.medical_services,
'label': 'Dental',
'color': Colors.amber,
'description': 'Oral Health'
},
{
'icon': Icons.favorite,
'label': 'Cardio',
'color': Colors.red,
'description': 'Heart Specialist'
},
{
'icon': Icons.psychology,
'label': 'Mental',
'color': Colors.green,
'description': 'Mental Health'
},
{
'icon': Icons.child_care,
'label': 'Pediatric',
'color': Colors.purple,
'description': 'Child Care'
},
{
'icon': Icons.elderly,
'label': 'Geriatric',
'color': Colors.teal,
'description': 'Senior Care'
},
{
'icon': Icons.fitness_center,
'label': 'Physio',
'color': Colors.orange,
'description': 'Physical Therapy'
},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
'Find Specialists',
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (final data in specialistData)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: _specialistCard(
icon: data['icon'] as IconData,
label: data['label'] as String,
color: data['color'] as Color,
description: data['description'] as String,
),
),
const SizedBox(width: 8),
],
),
),
),
IconButton(
icon: const Icon(Icons.arrow_forward, color: Colors.blue),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SpecialtyScreen()),
);
},
),
],
),
],
);
}
Widget _specialistCard({
required IconData icon,
required String label,
required Color color,
required String description,
}) {
return GestureDetector(
onTap: () {
Navigator.pushNamed(
context,
RouteNames.doctorListScreen,
arguments: {
'specialty': label,
},
);
},
child: Container(
width: 140,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: color.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 24),
),
const SizedBox(height: 8),
Text(
label,
style: GoogleFonts.poppins(
color: Colors.black87,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
description,
style: GoogleFonts.poppins(
color: Colors.grey[600],
fontSize: 12,
),
),
],
),
),
);
}
}