227 lines
6.9 KiB
Dart
227 lines
6.9 KiB
Dart
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:intl/intl.dart';
|
||
|
|
|
||
|
|
import '../../../../common/theme/theme.dart';
|
||
|
|
import '../../../../sample/community_sample_data.dart';
|
||
|
|
import '../community_ui.dart';
|
||
|
|
|
||
|
|
class CommunityEventCard extends StatelessWidget {
|
||
|
|
const CommunityEventCard({
|
||
|
|
super.key,
|
||
|
|
required this.event,
|
||
|
|
required this.isGoing,
|
||
|
|
required this.onToggle,
|
||
|
|
});
|
||
|
|
|
||
|
|
final CommunityEvent event;
|
||
|
|
final bool isGoing;
|
||
|
|
final VoidCallback onToggle;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final attendeeCount = event.attendeeCount + (isGoing ? 1 : 0);
|
||
|
|
final remaining = event.quota - attendeeCount;
|
||
|
|
|
||
|
|
return Container(
|
||
|
|
margin: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||
|
|
decoration: communityCardDecoration(radius: 24),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Stack(
|
||
|
|
children: [
|
||
|
|
ClipRRect(
|
||
|
|
borderRadius: const BorderRadius.vertical(
|
||
|
|
top: Radius.circular(24),
|
||
|
|
),
|
||
|
|
child: AspectRatio(
|
||
|
|
aspectRatio: 2.2,
|
||
|
|
child: Image.asset(event.image, fit: BoxFit.cover),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Positioned(
|
||
|
|
left: 12,
|
||
|
|
top: 12,
|
||
|
|
child: _DateBadge(date: event.startAt),
|
||
|
|
),
|
||
|
|
if (event.reward != null)
|
||
|
|
Positioned(
|
||
|
|
right: 12,
|
||
|
|
top: 12,
|
||
|
|
child: Container(
|
||
|
|
padding: const EdgeInsets.symmetric(
|
||
|
|
horizontal: 10,
|
||
|
|
vertical: 6,
|
||
|
|
),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: AppColor.white.withOpacity(0.92),
|
||
|
|
borderRadius: BorderRadius.circular(999),
|
||
|
|
),
|
||
|
|
child: Text(
|
||
|
|
'🎁 ${event.reward}',
|
||
|
|
style: AppStyle.xs.copyWith(fontWeight: FontWeight.w700),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
Padding(
|
||
|
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 16),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
event.title,
|
||
|
|
style: AppStyle.lg.copyWith(fontWeight: FontWeight.w700),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
_InfoRow(
|
||
|
|
icon: Icons.schedule_rounded,
|
||
|
|
text:
|
||
|
|
'${DateFormat('EEEE, d MMM', 'id_ID').format(event.startAt)}'
|
||
|
|
' · ${DateFormat('HH:mm', 'id_ID').format(event.startAt)}',
|
||
|
|
),
|
||
|
|
const SizedBox(height: 4),
|
||
|
|
_InfoRow(icon: Icons.place_outlined, text: event.location),
|
||
|
|
const SizedBox(height: 14),
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
CommunityAvatarStack(members: event.attendees),
|
||
|
|
const SizedBox(width: 8),
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
'$attendeeCount akan hadir',
|
||
|
|
style: AppStyle.xs.copyWith(
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Text(
|
||
|
|
remaining > 0
|
||
|
|
? 'Sisa $remaining kursi'
|
||
|
|
: 'Kuota penuh',
|
||
|
|
style: AppStyle.xs.copyWith(
|
||
|
|
fontSize: 10,
|
||
|
|
color: remaining <= 5
|
||
|
|
? AppColor.primary
|
||
|
|
: CommunityColor.muted,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
_GoingButton(isGoing: isGoing, onTap: onToggle),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class _DateBadge extends StatelessWidget {
|
||
|
|
const _DateBadge({required this.date});
|
||
|
|
|
||
|
|
final DateTime date;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Container(
|
||
|
|
width: 48,
|
||
|
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: AppColor.white.withOpacity(0.95),
|
||
|
|
borderRadius: BorderRadius.circular(14),
|
||
|
|
),
|
||
|
|
child: Column(
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
DateFormat('d', 'id_ID').format(date),
|
||
|
|
style: AppStyle.xl.copyWith(
|
||
|
|
height: 1.1,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Text(
|
||
|
|
DateFormat('MMM', 'id_ID').format(date).toUpperCase(),
|
||
|
|
style: AppStyle.xs.copyWith(
|
||
|
|
fontSize: 10,
|
||
|
|
color: AppColor.primary,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class _InfoRow extends StatelessWidget {
|
||
|
|
const _InfoRow({required this.icon, required this.text});
|
||
|
|
|
||
|
|
final IconData icon;
|
||
|
|
final String text;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Row(
|
||
|
|
children: [
|
||
|
|
Icon(icon, size: 15, color: CommunityColor.muted),
|
||
|
|
const SizedBox(width: 6),
|
||
|
|
Expanded(
|
||
|
|
child: Text(
|
||
|
|
text,
|
||
|
|
maxLines: 1,
|
||
|
|
overflow: TextOverflow.ellipsis,
|
||
|
|
style: AppStyle.sm.copyWith(
|
||
|
|
color: CommunityColor.muted,
|
||
|
|
fontWeight: FontWeight.w500,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class _GoingButton extends StatelessWidget {
|
||
|
|
const _GoingButton({required this.isGoing, required this.onTap});
|
||
|
|
|
||
|
|
final bool isGoing;
|
||
|
|
final VoidCallback onTap;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return AnimatedContainer(
|
||
|
|
duration: const Duration(milliseconds: 200),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: isGoing ? CommunityColor.blush : AppColor.primary,
|
||
|
|
borderRadius: BorderRadius.circular(14),
|
||
|
|
),
|
||
|
|
child: Material(
|
||
|
|
type: MaterialType.transparency,
|
||
|
|
child: InkWell(
|
||
|
|
borderRadius: BorderRadius.circular(14),
|
||
|
|
onTap: onTap,
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||
|
|
child: Text(
|
||
|
|
isGoing ? 'Terdaftar ✓' : 'Ikut',
|
||
|
|
style: AppStyle.sm.copyWith(
|
||
|
|
color: isGoing ? AppColor.primary : AppColor.white,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|