add promotion/detail

This commit is contained in:
Phuoc Nguyen
2025-10-24 14:42:14 +07:00
parent fbeaa3c9e8
commit 338d26a38a
12 changed files with 1681 additions and 122 deletions

View File

@@ -0,0 +1,220 @@
/// Promotion Section Widget
///
/// A reusable section widget for organizing content in promotion details.
/// Each section has a title with icon and customizable content.
library;
import 'package:flutter/material.dart';
import 'package:worker/core/theme/colors.dart';
/// Promotion Section Widget
///
/// Displays a content section with:
/// - Icon and title
/// - Custom content widget
/// - Bottom border separator
class PromotionSection extends StatelessWidget {
const PromotionSection({
required this.title,
required this.icon,
required this.content,
this.isLast = false,
super.key,
});
/// Section title
final String title;
/// Icon to display next to title
final IconData icon;
/// Content widget to display in the section
final Widget content;
/// Whether this is the last section (no bottom border)
final bool isLast;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
decoration: BoxDecoration(
border: isLast
? null
: const Border(
bottom: BorderSide(
color: Color(0xFFE2E8F0), // --border-color
width: 1,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Section Title with Icon
Row(
children: [
Icon(
icon,
size: 20,
color: AppColors.primaryBlue,
),
const SizedBox(width: 8),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF1E293B), // --text-primary
),
),
],
),
const SizedBox(height: 16),
// Section Content
content,
],
),
);
}
}
/// Promotion Content Text Widget
///
/// Standard text styling for section content with proper line height.
class PromotionContentText extends StatelessWidget {
const PromotionContentText(
this.text, {
this.isBold = false,
super.key,
});
final String text;
final bool isBold;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(
text,
style: TextStyle(
fontSize: 15,
color: const Color(0xFF64748B), // --text-secondary
height: 1.7,
fontWeight: isBold ? FontWeight.w600 : FontWeight.normal,
),
),
);
}
}
/// Promotion Bullet List Widget
///
/// Displays a list with custom bullet points.
class PromotionBulletList extends StatelessWidget {
const PromotionBulletList({
required this.items,
super.key,
});
final List<String> items;
@override
Widget build(BuildContext context) {
return Column(
children: items.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
final isLast = index == items.length - 1;
return Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
border: isLast
? null
: const Border(
bottom: BorderSide(
color: Color(0xFFF1F5F9), // Light border
width: 1,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Custom bullet point
const Text(
'',
style: TextStyle(
color: AppColors.primaryBlue,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 12),
// Item text
Expanded(
child: Text(
item,
style: const TextStyle(
fontSize: 15,
color: Color(0xFF64748B), // --text-secondary
height: 1.7,
),
),
),
],
),
);
}).toList(),
);
}
}
/// Contact Info Widget
///
/// Displays contact information with labels and values.
class ContactInfo extends StatelessWidget {
const ContactInfo({
required this.label,
required this.value,
super.key,
});
final String label;
final String value;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '$label: ',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF64748B),
height: 1.7,
),
),
TextSpan(
text: value,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.normal,
color: Color(0xFF64748B),
height: 1.7,
),
),
],
),
),
);
}
}