1220 lines
38 KiB
Dart
1220 lines
38 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/customBottomSheet.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: Colors.black.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: Colors.white,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.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: 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) {
|
|
// You can use the _filters map to conditionally show/hide sections
|
|
// Example: if (_filters['Flash Sale']!) ... [ ... Flash Sale Section ... ]
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
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: Colors.white,
|
|
title: Row(
|
|
children: [
|
|
SvgPicture.asset(Assets.icons.lBALogo.path, height: 32),
|
|
const SizedBox(width: 8),
|
|
const Text(
|
|
"Proxibuy",
|
|
style: TextStyle(
|
|
color: LightAppColors.hintTitle,
|
|
fontWeight: FontWeight.normal,
|
|
fontSize: 24,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: SvgPicture.asset(Assets.icons.notificationBing.path),
|
|
onPressed: () {},
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFilterButton() {
|
|
return Container(
|
|
width: 48,
|
|
height: 48,
|
|
decoration: BoxDecoration(
|
|
color: const Color.fromARGB(255, 14, 63, 102),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: IconButton(
|
|
icon: SvgPicture.asset(Assets.icons.sort.path, color: Colors.white),
|
|
onPressed: _showFilterMenu,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildCustomCheckbox(bool isChecked) {
|
|
return AnimatedContainer(
|
|
duration: const Duration(milliseconds: 200),
|
|
curve: Curves.easeInOut,
|
|
width: 24,
|
|
height: 24,
|
|
decoration: BoxDecoration(
|
|
color: isChecked ? LightAppColors.primary : Colors.transparent,
|
|
border: isChecked
|
|
? null
|
|
: Border.all(color: 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
|
|
? const Icon(
|
|
Icons.check,
|
|
color: Colors.white,
|
|
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
|
|
? const Color.fromRGBO(186, 222, 251, 1)
|
|
: const Color(0xFFF3F4F6);
|
|
final iconColor = isFirstIcon
|
|
? const Color.fromARGB(255, 14, 63, 102)
|
|
: Colors.grey.shade600;
|
|
|
|
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: Colors.grey.shade300,
|
|
activeColor: LightAppColors.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: [
|
|
Colors.black.withOpacity(0.6),
|
|
Colors.black.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: Colors.white.withOpacity(0.8),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: const Text(
|
|
"65% OFF",
|
|
style: TextStyle(
|
|
color: Colors.red,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
"NEW COLLECTION",
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
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: Colors.white,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
time,
|
|
style: const TextStyle(
|
|
color: Colors.black,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTimeSeparator() {
|
|
return const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 4.0),
|
|
child: Text(
|
|
":",
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
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: const TextStyle(fontSize: 18, fontWeight: FontWeight.normal),
|
|
),
|
|
const SizedBox(width: 8),
|
|
const Expanded(
|
|
child: Divider(color: Colors.grey, thickness: 1),
|
|
),
|
|
if (showSeeAll) ...[
|
|
const SizedBox(width: 8),
|
|
InkWell(
|
|
onTap: onSeeAllTap,
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
'See all',
|
|
style: TextStyle(
|
|
color: LightAppColors.primary,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
const SizedBox(width: 7),
|
|
SvgPicture.asset(
|
|
Assets.icons.arrowRight.path,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
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); // Placeholder
|
|
}
|
|
|
|
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: const 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(expiryTimeString: expiryTimeString);
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: LightAppColors.cardBackground,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: Colors.grey.shade200),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.1),
|
|
spreadRadius: 1,
|
|
blurRadius: 5,
|
|
offset: const Offset(0, 3),
|
|
),
|
|
],
|
|
),
|
|
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: LightAppColors.offerTimer,
|
|
height: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
"Limited time deal",
|
|
style: TextStyle(
|
|
color: Colors.black87,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
ValueListenableBuilder<int>(
|
|
valueListenable: timer.remainingSeconds,
|
|
builder: (context, _, __) => Text(
|
|
timer.formatTime(),
|
|
style: const TextStyle(
|
|
color: LightAppColors.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: Colors.grey.shade700,
|
|
width: 18,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
SvgPicture.asset(
|
|
Assets.icons.location.path,
|
|
color: Colors.grey.shade700,
|
|
width: 18,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
location,
|
|
style: const TextStyle(
|
|
color: LightAppColors.offerCardDetail, fontSize: 12),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
SvgPicture.asset(
|
|
Assets.icons.coin.path,
|
|
color: Colors.grey.shade700,
|
|
width: 18,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text.rich(
|
|
TextSpan(
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: LightAppColors.offerCardDetail,
|
|
),
|
|
children: [
|
|
TextSpan(
|
|
text: '$originalPrice\$',
|
|
style: const TextStyle(
|
|
decoration: TextDecoration.lineThrough,
|
|
),
|
|
),
|
|
TextSpan(
|
|
text: ' - $discountedPrice\$',
|
|
style: const TextStyle(
|
|
color: LightAppColors.offerCardDetail,
|
|
fontWeight: FontWeight.normal,
|
|
fontSize: 14,
|
|
decoration: TextDecoration.none,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(
|
|
width: 10,
|
|
),
|
|
Text(
|
|
'($discountPercent% off)',
|
|
style: const TextStyle(
|
|
color: Colors.green,
|
|
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: const Color.fromARGB(255, 76, 175, 80), 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: const Color.fromARGB(255, 95, 95, 95),
|
|
height: 17,
|
|
),
|
|
const SizedBox(width: 4),
|
|
const Text(
|
|
"Delivery",
|
|
style: TextStyle(
|
|
color: Color.fromARGB(255, 95, 95, 95),
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(25),
|
|
border: Border.all(color: const Color.fromARGB(255, 76, 175, 80), 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: const Color.fromARGB(255, 95, 95, 95),
|
|
height: 17,
|
|
),
|
|
const SizedBox(width: 4),
|
|
const Text(
|
|
"Pickup",
|
|
style: TextStyle(
|
|
color: Color.fromARGB(255, 95, 95, 95),
|
|
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: Colors.grey.shade200),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
flex: 2,
|
|
child: Container(
|
|
color: const Color(0xFFF8F9FA),
|
|
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: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
_buildDetailRow(
|
|
icon: Assets.icons.shop.path,
|
|
text: brand,
|
|
textStyle: const TextStyle(
|
|
color: Colors.black,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
_buildDetailRow(
|
|
icon: Assets.icons.icRoundLocalOffer.path,
|
|
text: discount,
|
|
textStyle: const TextStyle(
|
|
color: Colors.red,
|
|
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: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 15,
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
category,
|
|
style: const TextStyle(
|
|
color: LightAppColors.nearbyPopuphint, fontSize: 14),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
discount,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w500,
|
|
color: LightAppColors.nearbyPopuphint),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Row(
|
|
children: [
|
|
SvgPicture.asset(Assets.icons.star.path, width: 16),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
rating.toString(),
|
|
style: const TextStyle(
|
|
color: LightAppColors.nearbyPopuphint,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |