import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../models/kontrakan.dart'; import '../screens/kontrakan_detail_screen.dart'; class KontrakanCard extends StatelessWidget { final Kontrakan kontrakan; final int? ranking; final double? skor; final bool showRanking; const KontrakanCard({ super.key, required this.kontrakan, this.ranking, this.skor, this.showRanking = false, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => KontrakanDetailScreen(kontrakan: kontrakan), ), ); }, child: Card( elevation: 2, shadowColor: Colors.black.withOpacity(0.1), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Ranking Badge (if showRanking is true) if (showRanking && ranking != null) ...[ Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), decoration: BoxDecoration( color: _getRankingColor(ranking!), borderRadius: const BorderRadius.only( topLeft: Radius.circular(16), topRight: Radius.circular(16), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon( _getRankingIcon(ranking!), color: Colors.white, size: 18, ), const SizedBox(width: 6), Text( 'Ranking #$ranking', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), if (skor != null) Text( '${(skor! * 100).toStringAsFixed(0)}%', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), ], // Image Stack( children: [ ClipRRect( borderRadius: BorderRadius.vertical( top: Radius.circular( showRanking && ranking != null ? 0 : 16, ), ), child: CachedNetworkImage( imageUrl: kontrakan.primaryPhoto, height: 180, width: double.infinity, fit: BoxFit.cover, placeholder: (context, url) => Container( height: 180, color: Colors.grey[300], child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 180, color: Colors.grey[300], child: const Icon(Icons.image_not_supported, size: 50), ), ), ), Positioned( top: 10, right: 10, child: Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: kontrakan.isAvailable ? Colors.green : Colors.red, borderRadius: BorderRadius.circular(20), ), child: Text( kontrakan.isAvailable ? 'Tersedia' : 'Penuh', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ), ], ), // Content Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( kontrakan.nama, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Row( children: [ const Icon( Icons.location_on, size: 14, color: Colors.grey, ), const SizedBox(width: 4), Expanded( child: Text( kontrakan.alamat, style: const TextStyle( fontSize: 12, color: Colors.grey, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Row( children: [ Text( kontrakan.formattedHarga, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1565C0), ), ), Text( '/bln', style: TextStyle( fontSize: 12, color: Colors.grey[500], fontWeight: FontWeight.w500, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: const Color(0xFF1565C0).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon( Icons.bed, size: 14, color: Color(0xFF1565C0), ), const SizedBox(width: 4), Text( '${kontrakan.jumlahKamar}', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Color(0xFF1565C0), ), ), ], ), ), ], ), const SizedBox(height: 10), Row( children: [ const Icon( Icons.near_me_rounded, size: 14, color: Colors.orange, ), const SizedBox(width: 4), Text( '${kontrakan.jarakKampus.toStringAsFixed(1)} km dari kampus', style: TextStyle(fontSize: 11, color: Colors.grey[500]), ), ], ), if (kontrakan.avgRating != null) ...[ const SizedBox(height: 6), Row( children: [ const Icon(Icons.star, size: 14, color: Colors.amber), const SizedBox(width: 4), Text( '${kontrakan.avgRating!.toStringAsFixed(1)}', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( ' (${kontrakan.totalReviews ?? 0} review)', style: const TextStyle( fontSize: 11, color: Colors.grey, ), ), ], ), ], ], ), ), ], ), ), ); } Color _getRankingColor(int ranking) { if (ranking == 1) return Colors.amber; if (ranking == 2) return Colors.grey[400]!; if (ranking == 3) return Colors.brown[300]!; return Colors.blue; } IconData _getRankingIcon(int ranking) { if (ranking <= 3) return Icons.emoji_events; return Icons.star; } }