MIF_E31231623/android/wisata_app/lib/widgets/destination_card_modern.dart

317 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:wisata_app/models/destination.dart';
class DestinationCardModern extends StatefulWidget {
final Destination destination;
final VoidCallback onTap;
const DestinationCardModern({
super.key,
required this.destination,
required this.onTap,
});
@override
State<DestinationCardModern> createState() => _DestinationCardModernState();
}
class _DestinationCardModernState extends State<DestinationCardModern>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) {
_controller.forward();
}
void _onTapUp(TapUpDetails details) {
_controller.reverse();
widget.onTap();
}
void _onTapCancel() {
_controller.reverse();
}
Color _getCategoryColor(String kategori) {
switch (kategori.toLowerCase()) {
case 'gunung':
return const Color(0xFF6B7280);
case 'danau':
return const Color(0xFF2563EB);
case 'pantai':
return const Color(0xFF0EA5E9);
case 'air terjun':
return const Color(0xFF14B8C4);
default:
return const Color(0xFF0F4C81);
}
}
String _getCategoryLabel(String kategori) {
switch (kategori.toLowerCase()) {
case 'gunung':
return 'Gunung';
case 'danau':
return 'Danau';
case 'pantai':
return 'Pantai';
case 'air terjun':
return 'Air Terjun';
default:
return 'Wisata';
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: ScaleTransition(
scale: _scaleAnimation,
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image Section with Gradient Overlay
Expanded(
flex: 2,
child: Stack(
children: [
// Background Image
Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF0F4C81).withValues(alpha: 0.7),
const Color(0xFF14B8C4).withValues(alpha: 0.5),
],
),
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
child: Image.network(
widget.destination.imagePath,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
debugPrint(
'Gagal memuat gambar wisata: ${widget.destination.imagePath} | $error',
);
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF0F4C81),
Color(0xFF14B8C4),
],
),
),
child: Center(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.broken_image_rounded,
color: Colors.white,
size: 42,
),
const SizedBox(height: 8),
Text(
'Gambar gagal dimuat\n${widget.destination.imagePath}',
textAlign: TextAlign.center,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.w800,
),
),
],
),
),
),
);
},
),
),
),
// Gradient Overlay untuk readability
Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.3),
],
),
),
),
// Rating Badge
Positioned(
top: 10,
right: 10,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color:
const Color(0xFFE28D42).withValues(alpha: 0.95),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.star,
color: Colors.white,
size: 14,
),
const SizedBox(width: 3),
Text(
'${widget.destination.rating.toStringAsFixed(1)}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 11,
),
),
],
),
),
),
// Category Badge
Positioned(
top: 10,
left: 10,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color:
_getCategoryColor(widget.destination.kategori)
.withValues(alpha: 0.95),
borderRadius: BorderRadius.circular(8),
),
child: Text(
_getCategoryLabel(widget.destination.kategori),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 11,
),
),
),
),
],
),
),
// Content Section
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Nama Wisata
Text(
widget.destination.nama,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFF0F4C81),
),
),
const SizedBox(height: 8),
// Lokasi
Row(
children: [
const Icon(
Icons.location_on,
size: 12,
color: Color(0xFF8B6F47),
),
const SizedBox(width: 4),
Expanded(
child: Text(
widget.destination.lokasi,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 11,
color: Color(0xFF4B6475),
),
),
),
],
),
],
),
),
),
],
),
),
),
),
);
}
}