Files
CheckTheethKids/lib/consultorios/clinic_card.dart
Carlos Correia 89b27e5e86 CTK 1.1.0
2026-07-22 01:34:26 +01:00

213 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import '../colors/app_colors.dart';
import 'package:url_launcher/url_launcher.dart';
import '../strings/consultorios_strings.dart';
import '../widgets/pill_snackbar.dart';
import '../widgets/tap_bounce.dart';
import 'clinic.dart';
/// Cartão de um consultório — reutilizado no separador Consultórios e na
/// pré-visualização "Consultórios próximos" da Home. A Overpass API não
/// devolve fotos, por isso a caixa de imagem é sempre um placeholder liso
/// (sem ícone).
class ClinicCard extends StatelessWidget {
const ClinicCard({
super.key,
required this.clinic,
required this.isFavorite,
required this.onToggleFavorite,
});
final Clinic clinic;
final bool isFavorite;
final ValueChanged<String> onToggleFavorite;
Future<void> _call(BuildContext context) async {
final uri = Uri(scheme: 'tel', path: clinic.phone);
try {
final launched = await launchUrl(uri);
if (!launched && context.mounted) {
showPillSnackBar(context, ConsultoriosStrings.noPhoneAppAvailable);
}
} catch (e) {
if (context.mounted) showPillSnackBar(context, ConsultoriosStrings.callFailed(e));
}
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 14,
offset: const Offset(0, 8),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: AppColors.pinkBackground,
borderRadius: BorderRadius.circular(14),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
clinic.name,
style: const TextStyle(
fontWeight: FontWeight.w900,
fontSize: 14.5,
color: AppColors.teal,
),
),
),
TapBounce(
scale: 0.85,
child: InkWell(
borderRadius: BorderRadius.circular(999),
onTap: () => onToggleFavorite(clinic.id),
child: Padding(
padding: const EdgeInsets.all(2),
child: Icon(
isFavorite
? Icons.favorite_rounded
: Icons.favorite_border_rounded,
size: 20,
color: isFavorite
? AppColors.pink
: Colors.black.withValues(alpha: 0.3),
),
),
),
),
],
),
if (clinic.distanceKm != null) ...[
const SizedBox(height: 2),
Text(
'${clinic.distanceKm!.toStringAsFixed(1)} ${ConsultoriosStrings.distanceKm}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: Colors.black.withValues(alpha: 0.45),
),
),
],
const SizedBox(height: 6),
_ClinicInfoRow(
icon: Icons.access_time_rounded,
text: clinic.openingHours,
muted: !clinic.hasOpeningHours,
),
const SizedBox(height: 3),
_ClinicInfoRow(
icon: Icons.place_outlined,
text: clinic.address,
muted: false,
),
],
),
),
],
),
const SizedBox(height: 10),
TapBounce(
child: Material(
color: clinic.hasPhone
? AppColors.teal
: Colors.black.withValues(alpha: 0.06),
borderRadius: BorderRadius.circular(999),
child: InkWell(
borderRadius: BorderRadius.circular(999),
onTap: clinic.hasPhone ? () => _call(context) : null,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.call_rounded,
size: 16,
color: clinic.hasPhone
? Colors.white
: Colors.black.withValues(alpha: 0.35),
),
const SizedBox(width: 8),
Text(
clinic.phone,
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 13,
color: clinic.hasPhone
? Colors.white
: Colors.black.withValues(alpha: 0.35),
),
),
],
),
),
),
),
),
],
),
);
}
}
class _ClinicInfoRow extends StatelessWidget {
const _ClinicInfoRow({
required this.icon,
required this.text,
required this.muted,
});
final IconData icon;
final String text;
final bool muted;
@override
Widget build(BuildContext context) {
final color = Colors.black.withValues(alpha: muted ? 0.35 : 0.6);
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 14, color: color),
const SizedBox(width: 6),
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontStyle: muted ? FontStyle.italic : FontStyle.normal,
color: color,
),
),
),
],
);
}
}