proxybuy-flutter/lib/screens/mains/discover/discover.dart

1245 lines
39 KiB
Dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:dots_indicator/dots_indicator.dart';
import 'package:lba/gen/assets.gen.dart';
import 'package:lba/res/colors.dart';
import 'package:lba/widgets/remainingTime.dart';
import 'package:lba/widgets/search_bar.dart';
class Discover extends StatefulWidget {
const Discover({super.key});
@override
State<Discover> createState() => _DiscoverState();
}
class _DiscoverState extends State<Discover> with TickerProviderStateMixin {
final PageController _pageController = PageController();
double _currentPage = 0;
late AnimationController _staggeredController;
late List<Animation<double>> _staggeredAnimations;
final Map<String, bool> _filters = {
'Top 10 Offers': true,
'Flash Sale': true,
'special discount': false,
'Occasion Specials': true,
'First Purchase': false,
};
final List<String> categoryIcons = [
Assets.icons.stashStarsLight.path,
Assets.icons.hugeiconsBabyBoyDress.path,
Assets.icons.girlClothes.path,
Assets.icons.gameIconsWinterGloves.path,
Assets.icons.hugeiconsCheeseCake01.path,
Assets.icons.ionFastFoodOutline.path,
Assets.icons.healthiconsFruitsOutline.path,
];
@override
void initState() {
super.initState();
_pageController.addListener(() {
if (_pageController.hasClients) {
setState(() {
_currentPage = _pageController.page!;
});
}
});
_staggeredController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1500),
);
final int itemCount = 8;
_staggeredAnimations = List.generate(itemCount, (index) {
return Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _staggeredController,
curve: Interval(
(0.1 * index),
(0.5 + 0.1 * index).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
});
_staggeredController.forward();
}
@override
void dispose() {
_pageController.dispose();
_staggeredController.dispose();
super.dispose();
}
Widget _buildAnimatedSection(Widget child, int index) {
return FadeTransition(
opacity: _staggeredAnimations[index],
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.3),
end: Offset.zero,
).animate(_staggeredAnimations[index]),
child: child,
),
);
}
void _showFilterMenu() {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel:
MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: AppColors.textPrimary.withOpacity(0.1),
transitionDuration: const Duration(milliseconds: 400),
pageBuilder: (context, animation1, animation2) {
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 250,
margin:
const EdgeInsets.only(top: kToolbarHeight + 65, right: 16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: AppColors.textPrimary.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 5,
)
],
),
child: StatefulBuilder(
builder: (context, setDialogState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: _filters.keys.map((String filterName) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
setDialogState(() {
setState(() {
_filters[filterName] =
!_filters[filterName]!;
});
});
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18.0, vertical: 12.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
filterName,
style: const TextStyle(fontSize: 16),
),
_buildCustomCheckbox(
_filters[filterName]!),
],
),
),
),
);
}).toList(),
);
},
),
),
),
);
},
transitionBuilder: (context, animation, secondaryAnimation, child) {
final curvedAnimation = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
);
return ScaleTransition(
scale:
Tween<double>(begin: 0.8, end: 1.0).animate(curvedAnimation),
alignment: Alignment.topRight,
child: FadeTransition(
opacity: curvedAnimation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.2, -0.2),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.surface,
appBar: _buildAppBar(),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildAnimatedSection(
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Row(
children: [
const Expanded(child: SearchBarWidget()),
const SizedBox(width: 10),
_buildFilterButton(),
],
),
),
0,
),
const SizedBox(height: 16),
_buildAnimatedSection(
_buildSectionTitle("what's on your mind?"), 1),
const SizedBox(height: 12),
_buildAnimatedSection(_buildCategoryIcons(), 2),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("Top 10 Discount & Offers"), 3),
const SizedBox(height: 12),
_buildAnimatedSection(_buildTopOffersSection(), 4),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("Flash Sale",
showSeeAll: true, onSeeAllTap: () {}),
5),
const SizedBox(height: 12),
_buildAnimatedSection(_buildFlashSaleSection(), 6),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("Special Discount"), 7),
const SizedBox(height: 12),
_buildAnimatedSection(_buildSpecialDiscountSection(), 1),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("Seasonal Discount",
showSeeAll: true, onSeeAllTap: () {}),
2),
const SizedBox(height: 12),
_buildAnimatedSection(_buildSeasonalDiscountSection(), 3),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("Occasion Specials"), 4),
const SizedBox(height: 12),
_buildAnimatedSection(_buildCraftingSomethingSection(), 5),
const SizedBox(height: 24),
_buildAnimatedSection(
_buildSectionTitle("First Purchase Discount",
showSeeAll: true, onSeeAllTap: () {}),
6),
const SizedBox(height: 12),
_buildAnimatedSection(_buildFirstPurchaseSection(), 7),
const SizedBox(height: 100),
],
),
),
);
}
PreferredSizeWidget _buildAppBar() {
return AppBar(
elevation: 0,
backgroundColor: AppColors.scaffoldBackground,
title: Row(
children: [
SvgPicture.asset(Assets.icons.lBALogo.path, height: 32),
const SizedBox(width: 8),
Text(
"Proxibuy",
style: TextStyle(
color: AppColors.hintTitle,
fontWeight: FontWeight.normal,
fontSize: 24,
),
),
],
),
actions: [
IconButton(
icon: SvgPicture.asset(Assets.icons.notificationBing.path,
color: AppColors.isDarkMode ? AppColors.textPrimary : null),
onPressed: () {},
),
const SizedBox(width: 8),
],
);
}
Widget _buildFilterButton() {
return Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: AppColors.isDarkMode
? AppColors.primary
: const Color.fromARGB(255, 14, 63, 102),
borderRadius: BorderRadius.circular(12),
),
child: IconButton(
icon:
SvgPicture.asset(Assets.icons.sort.path, color: AppColors.surface),
onPressed: _showFilterMenu,
),
);
}
Widget _buildCustomCheckbox(bool isChecked) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
width: 24,
height: 24,
decoration: BoxDecoration(
color: isChecked ? AppColors.primary : Colors.transparent,
border: isChecked
? null
: Border.all(
color: const Color.fromARGB(255, 89, 93, 98), width: 2),
borderRadius: BorderRadius.circular(6),
),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
child: isChecked
? Icon(
Icons.check,
color: AppColors.surface,
size: 18,
key: ValueKey('checked'),
)
: const SizedBox(
key: ValueKey('unchecked'),
),
),
);
}
Widget _buildCategoryIcons() {
return SizedBox(
height: 50,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: categoryIcons.length,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemBuilder: (context, index) {
final isFirstIcon = index == 0;
final backgroundColor = isFirstIcon
? (AppColors.isDarkMode
? AppColors.primary.withOpacity(0.3)
: const Color.fromRGBO(186, 222, 251, 1))
: (AppColors.isDarkMode
? AppColors.cardBackground
: const Color(0xFFF3F4F6));
final iconColor = isFirstIcon
? (AppColors.isDarkMode
? AppColors.primary
: const Color.fromARGB(255, 14, 63, 102))
: AppColors.secondaryText;
return Container(
width: 50,
height: 50,
padding: const EdgeInsets.all(11),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
),
child:
SvgPicture.asset(categoryIcons[index], color: iconColor),
);
},
separatorBuilder: (context, index) => const SizedBox(width: 12),
),
);
}
Widget _buildTopOffersSection() {
return Column(
children: [
SizedBox(
height: 180,
child: PageView(
controller: _pageController,
children: [
_buildOfferBanner(),
_buildOfferBanner(),
_buildOfferBanner(),
],
),
),
const SizedBox(height: 12),
DotsIndicator(
dotsCount: 3,
position: _currentPage,
decorator: DotsDecorator(
color: AppColors.divider,
activeColor: AppColors.primary,
size: const Size.square(8.0),
activeSize: const Size(20.0, 8.0),
activeShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
],
);
}
Widget _buildOfferBanner() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image.asset(
Assets.images.image.path,
height: 180,
width: double.infinity,
fit: BoxFit.cover,
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
AppColors.shadowColor.withOpacity(0.6),
AppColors.shadowColor.withOpacity(0.1),
],
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: AppColors.surface.withOpacity(0.8),
borderRadius: BorderRadius.circular(8),
),
child: Text(
"65% OFF",
style: TextStyle(
color: AppColors.offerTimer,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 8),
Text(
"NEW COLLECTION",
style: TextStyle(
color: AppColors.surface,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Row(
children: [
_buildTimeBox("12"),
_buildTimeSeparator(),
_buildTimeBox("25"),
_buildTimeSeparator(),
_buildTimeBox("14"),
],
),
],
),
),
],
),
);
}
Widget _buildTimeBox(String time) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(4),
),
child: Text(
time,
style: TextStyle(
color: AppColors.textPrimary,
fontWeight: FontWeight.bold,
),
),
);
}
Widget _buildTimeSeparator() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Text(
":",
style: TextStyle(
color: AppColors.surface,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
);
}
Widget _buildSectionTitle(String title,
{bool showSeeAll = false, VoidCallback? onSeeAllTap}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.normal,
color: AppColors.textPrimary),
),
const SizedBox(width: 8),
Expanded(
child: Divider(color: AppColors.divider, thickness: 1),
),
if (showSeeAll) ...[
const SizedBox(width: 8),
InkWell(
onTap: onSeeAllTap,
child: Row(
children: [
Text(
'See all',
style: TextStyle(
color: AppColors.primary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 7),
SvgPicture.asset(
Assets.icons.arrowRight.path,
color: AppColors.primary,
),
],
),
),
],
],
),
);
}
Widget _buildFlashSaleSection() {
return SizedBox(
height: 310,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
children: [
SizedBox(
width: 250,
child: FlashSaleCard(
imagePath: Assets.images.media.path,
title: "Amul Cheese Slices",
location: "Fresno (750m away)",
originalPrice: "70",
discountedPrice: "53",
discountPercent: "13",
expiryTimeString: DateTime.now()
.add(const Duration(hours: 8, minutes: 35))
.millisecondsSinceEpoch
.toString(),
categoryIconPath: Assets.icons.phCheese.path,
),
),
const SizedBox(width: 16),
SizedBox(
width: 250,
child: FlashSaleCard(
imagePath:
Assets.images.wp1929534FastFoodWallpapers1.path,
title: "Tulip Luncheon Meat",
location: "Fresno (2km away)",
originalPrice: "370",
discountedPrice: "194",
discountPercent: "50",
expiryTimeString: DateTime.now()
.add(const Duration(hours: 12, minutes: 45))
.millisecondsSinceEpoch
.toString(),
categoryIconPath: Assets.icons.shop.path,
),
),
],
),
);
}
Widget _buildSpecialDiscountSection() {
return Container(
height: 180,
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: DecorationImage(
image: AssetImage(
Assets.images.wp1929534FastFoodWallpapers1.path),
fit: BoxFit.cover,
),
),
);
}
Widget _buildSeasonalDiscountSection() {
final List<Map<String, String>> seasonalItems = [
{
"title": "Boots",
"brand": "Columbia Sportswear",
"discount": "22 - 35% off",
"imagePath": Assets.images.image.path,
},
{
"title": "Hoodie",
"brand": "The North Face",
"discount": "22 - 35% off",
"imagePath": Assets.images.topDealsAndStores.path,
},
{
"title": "Hats",
"brand": "Patagonia",
"discount": "10 - 15% off",
"imagePath": Assets.images.image.path,
},
{
"title": "Jacket",
"brand": "Arc'teryx",
"discount": "20% off",
"imagePath": Assets.images.topDealsAndStores.path,
},
{
"title": "Backpack",
"brand": "Osprey",
"discount": "15% off",
"imagePath": Assets.images.image.path,
},
{
"title": "Gloves",
"brand": "Nike",
"discount": "Up to 40% off",
"imagePath": Assets.images.topDealsAndStores.path,
},
];
final screenWidth = MediaQuery.of(context).size.width;
final cardWidth = screenWidth * 0.8;
final int itemCount = (seasonalItems.length / 2).ceil();
return SizedBox(
height: 110 * 2 + 16,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: itemCount,
itemBuilder: (context, index) {
final topIndex = index * 2;
final bottomIndex = topIndex + 1;
final topCard = SeasonalDiscountCard(
title: seasonalItems[topIndex]['title']!,
brand: seasonalItems[topIndex]['brand']!,
discount: seasonalItems[topIndex]['discount']!,
imagePath: seasonalItems[topIndex]['imagePath']!,
width: cardWidth,
);
Widget bottomCard;
if (bottomIndex < seasonalItems.length) {
bottomCard = SeasonalDiscountCard(
title: seasonalItems[bottomIndex]['title']!,
brand: seasonalItems[bottomIndex]['brand']!,
discount: seasonalItems[bottomIndex]['discount']!,
imagePath: seasonalItems[bottomIndex]['imagePath']!,
width: cardWidth,
);
} else {
bottomCard = SizedBox(width: cardWidth);
}
return Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
topCard,
const SizedBox(height: 16),
bottomCard,
],
),
);
},
),
);
}
Widget _buildCraftingSomethingSection() {
return Container(
height: 120,
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: DecorationImage(
image: AssetImage(Assets.images.image.path),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.darken,
),
),
),
child: const Center(
child: Text(
"Crafting something for you\nTell us your birthday and unlock special Dineout treats!",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
);
}
Widget _buildFirstPurchaseSection() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.isDarkMode?DarkAppColors.cardBackground: Color(0xFFE3F2FD),
borderRadius: BorderRadius.circular(15),
),
child: Row(
children: [
FirstPurchaseCard(
title: "McDonald's",
category: "Fast Food",
discount: "Up to 25% Off",
rating: 4.2,
imagePath:
Assets.images.wp1929534FastFoodWallpapers1.path,
),
const SizedBox(width: 16),
FirstPurchaseCard(
title: "Cafe Monarch",
category: "Cafe",
discount: "Up to 25% Off",
rating: 4.9,
imagePath: Assets.images.media.path,
),
// For testing with more cards:
// const SizedBox(width: 16),
// FirstPurchaseCard(
// title: "Another Cafe",
// category: "Cafe",
// discount: "15% Off",
// rating: 4.5,
// imagePath: Assets.images.media.path,
// ),
],
),
),
);
}
}
class FlashSaleCard extends StatelessWidget {
final String imagePath;
final String title;
final String location;
final String originalPrice;
final String discountedPrice;
final String discountPercent;
final String expiryTimeString;
final String categoryIconPath;
const FlashSaleCard({
super.key,
required this.imagePath,
required this.title,
required this.location,
required this.originalPrice,
required this.discountedPrice,
required this.discountPercent,
required this.expiryTimeString,
required this.categoryIconPath,
});
@override
Widget build(BuildContext context) {
final timer = RemainingTime()
..initializeFromExpiry(expiryTime: expiryTimeString);
return Container(
decoration: BoxDecoration(
color: AppColors.cardBackground,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 8, 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: SvgPicture.asset(
Assets.icons.timer.path,
color: AppColors.offerTimer,
height: 20,
),
),
const SizedBox(width: 10),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Limited time deal",
style: TextStyle(
color: AppColors.textPrimary,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
ValueListenableBuilder<int>(
valueListenable: timer.remainingSeconds,
builder: (context, _, __) => Text(
timer.formatTime(),
style: TextStyle(
color: AppColors.offerTimer,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(
height: 5,
)
],
),
],
),
),
Image.asset(
imagePath,
height: 110,
width: double.infinity,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SvgPicture.asset(
categoryIconPath,
color: AppColors.textSecondary,
width: 18,
),
const SizedBox(width: 8),
Expanded(
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: AppColors.textPrimary,
),
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
SvgPicture.asset(
Assets.icons.location.path,
color: AppColors.textSecondary,
width: 18,
),
const SizedBox(width: 4),
Text(
location,
style: TextStyle(
color: AppColors.offerCardDetail,
fontSize: 12),
),
],
),
const SizedBox(height: 8),
Row(
children: [
SvgPicture.asset(
Assets.icons.coin.path,
color: AppColors.textSecondary,
width: 18,
),
const SizedBox(width: 4),
Text.rich(
TextSpan(
style: TextStyle(
fontSize: 12,
color: AppColors.offerCardDetail,
),
children: [
TextSpan(
text: '$originalPrice\$',
style: const TextStyle(
decoration: TextDecoration.lineThrough,
),
),
TextSpan(
text: ' - $discountedPrice\$',
style: TextStyle(
color: AppColors.offerCardDetail,
fontWeight: FontWeight.normal,
fontSize: 14,
decoration: TextDecoration.none,
),
),
],
),
),
const SizedBox(
width: 10,
),
Text(
'($discountPercent% off)',
style: TextStyle(
color: AppColors.confirmButton,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: AppColors.confirmButton,
width: 1.0),
color: Colors.transparent,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Row(
children: [
SvgPicture.asset(
Assets.icons.cardPos.path,
color: AppColors.textSecondary,
height: 17,
),
const SizedBox(width: 4),
Text(
"Delivery",
style: TextStyle(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
],
),
),
),
const SizedBox(width: 8),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: AppColors.confirmButton,
width: 1.0),
color: Colors.transparent,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Row(
children: [
SvgPicture.asset(
Assets.icons.shoppingCart.path,
color: AppColors.textSecondary,
height: 17,
),
const SizedBox(width: 4),
Text(
"Pickup",
style: TextStyle(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
],
),
),
),
],
),
],
),
),
],
),
);
}
}
class SeasonalDiscountCard extends StatelessWidget {
final String title;
final String brand;
final String discount;
final String imagePath;
final double width;
const SeasonalDiscountCard({
super.key,
required this.title,
required this.brand,
required this.discount,
required this.imagePath,
required this.width,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: 110,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.divider),
),
child: Row(
children: [
Expanded(
flex: 2,
child: Container(
color: AppColors.cardBackground,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildDetailRow(
icon: Assets.icons.winter.path,
text: title,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppColors.textPrimary,
),
),
_buildDetailRow(
icon: Assets.icons.shop.path,
text: brand,
textStyle: TextStyle(
color: AppColors.textPrimary,
fontSize: 12,
),
),
_buildDetailRow(
icon: Assets.icons.icRoundLocalOffer.path,
text: discount,
textStyle: TextStyle(
color: AppColors.offerTimer,
fontWeight: FontWeight.normal,
fontSize: 14,
),
),
],
),
),
),
),
Expanded(
flex: 1,
child: Image.asset(
imagePath,
height: double.infinity,
fit: BoxFit.cover,
),
),
],
),
),
),
);
}
Widget _buildDetailRow({
required String icon,
required String text,
required TextStyle textStyle,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: SvgPicture.asset(
icon,
width: 20,
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
text,
style: textStyle,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
),
],
);
}
}
class FirstPurchaseCard extends StatelessWidget {
final String title;
final String category;
final String discount;
final double rating;
final String imagePath;
const FirstPurchaseCard({
super.key,
required this.title,
required this.category,
required this.discount,
required this.rating,
required this.imagePath,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 160,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: Image.asset(
imagePath,
height: 120,
width: double.infinity,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 5),
Text(
category,
style: TextStyle(
color: AppColors.textSecondary,
fontSize: 14),
),
const SizedBox(height: 5),
Text(
discount,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.textSecondary),
),
const SizedBox(height: 5),
Row(
children: [
SvgPicture.asset(Assets.icons.star.path,
width: 16,),
const SizedBox(width: 4),
Text(
rating.toString(),
style: TextStyle(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
],
),
);
}
}