diff --git a/assets/icons/LBA Logo.svg b/assets/icons/LBA Logo.svg
new file mode 100644
index 0000000..a0b325d
--- /dev/null
+++ b/assets/icons/LBA Logo.svg
@@ -0,0 +1,13 @@
+
diff --git a/assets/icons/Winter.svg b/assets/icons/Winter.svg
new file mode 100644
index 0000000..999c14c
--- /dev/null
+++ b/assets/icons/Winter.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/notification-bing.svg b/assets/icons/notification-bing.svg
new file mode 100644
index 0000000..21ac461
--- /dev/null
+++ b/assets/icons/notification-bing.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/stash_stars-light.svg b/assets/icons/stash_stars-light.svg
new file mode 100644
index 0000000..01a32c3
--- /dev/null
+++ b/assets/icons/stash_stars-light.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart
index dc393c6..69d0cf9 100644
--- a/lib/gen/assets.gen.dart
+++ b/lib/gen/assets.gen.dart
@@ -15,6 +15,9 @@ import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsIconsGen {
const $AssetsIconsGen();
+ /// File path: assets/icons/LBA Logo.svg
+ SvgGenImage get lBALogo => const SvgGenImage('assets/icons/LBA Logo.svg');
+
/// File path: assets/icons/Line 1.svg
SvgGenImage get line1 => const SvgGenImage('assets/icons/Line 1.svg');
@@ -46,6 +49,9 @@ class $AssetsIconsGen {
/// File path: assets/icons/Shop2.svg
SvgGenImage get shop2 => const SvgGenImage('assets/icons/Shop2.svg');
+ /// File path: assets/icons/Winter.svg
+ SvgGenImage get winter => const SvgGenImage('assets/icons/Winter.svg');
+
/// File path: assets/icons/arrow-down-black.svg
SvgGenImage get arrowDownBlack =>
const SvgGenImage('assets/icons/arrow-down-black.svg');
@@ -165,6 +171,10 @@ class $AssetsIconsGen {
/// File path: assets/icons/next.svg
SvgGenImage get next => const SvgGenImage('assets/icons/next.svg');
+ /// File path: assets/icons/notification-bing.svg
+ SvgGenImage get notificationBing =>
+ const SvgGenImage('assets/icons/notification-bing.svg');
+
/// File path: assets/icons/ph_cheese.svg
SvgGenImage get phCheese => const SvgGenImage('assets/icons/ph_cheese.svg');
@@ -224,6 +234,10 @@ class $AssetsIconsGen {
/// File path: assets/icons/star.svg
SvgGenImage get star => const SvgGenImage('assets/icons/star.svg');
+ /// File path: assets/icons/stash_stars-light.svg
+ SvgGenImage get stashStarsLight =>
+ const SvgGenImage('assets/icons/stash_stars-light.svg');
+
/// File path: assets/icons/tick.svg
SvgGenImage get tick => const SvgGenImage('assets/icons/tick.svg');
@@ -240,6 +254,7 @@ class $AssetsIconsGen {
/// List of all assets
List get values => [
+ lBALogo,
line1,
line4,
mDSPublicTWButton,
@@ -249,6 +264,7 @@ class $AssetsIconsGen {
shape,
shop,
shop2,
+ winter,
arrowDownBlack,
arrowDown,
arrowLeft,
@@ -284,6 +300,7 @@ class $AssetsIconsGen {
nearby,
nearby2,
next,
+ notificationBing,
phCheese,
profile2,
profile,
@@ -302,6 +319,7 @@ class $AssetsIconsGen {
starFill,
starHalf,
star,
+ stashStarsLight,
tick,
timerPause,
timerStart,
diff --git a/lib/main.dart b/lib/main.dart
index 979e0bd..a8ddfcb 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -3,6 +3,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'features/auth/presentation/pages/onboarding_page.dart';
+import 'widgets/animated_splash_screen.dart';
import 'injection_container.dart';
void main() {
@@ -21,7 +22,7 @@ class MyApp extends StatelessWidget {
title: 'LBA',
theme: ThemeData(
fontFamily: 'Roboto',
- scaffoldBackgroundColor: Colors.white,
+ scaffoldBackgroundColor: Colors.white,
primaryColor: const Color.fromARGB(255, 14, 63, 102),
buttonTheme: const ButtonThemeData(
buttonColor: Color.fromARGB(255, 14, 63, 102),
@@ -29,9 +30,7 @@ class MyApp extends StatelessWidget {
appBarTheme: const AppBarTheme(
backgroundColor: Color.fromARGB(255, 14, 63, 102),
),
- dialogTheme: DialogTheme(
- backgroundColor: Colors.white,
- ),
+ dialogTheme: DialogTheme(backgroundColor: Colors.white),
dropdownMenuTheme: DropdownMenuThemeData(
menuStyle: MenuStyle(
backgroundColor: MaterialStatePropertyAll(Colors.white),
@@ -44,11 +43,23 @@ class MyApp extends StatelessWidget {
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
- borderSide: BorderSide(color: Color.fromARGB(255, 14, 63, 102), width: 2),
+ borderSide: BorderSide(
+ color: Color.fromARGB(255, 14, 63, 102),
+ width: 2,
+ ),
),
),
+ pageTransitionsTheme: const PageTransitionsTheme(
+ builders: {
+ TargetPlatform.android: CupertinoPageTransitionsBuilder(),
+ TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
+ },
+ ),
+ ),
+ home: CoolSplashScreen(
+ nextScreen: const OnboardingPage(),
+ duration: const Duration(seconds: 6),
),
- home: const OnboardingPage(),
),
);
}
diff --git a/lib/res/colors.dart b/lib/res/colors.dart
index f9ac123..ed57c64 100644
--- a/lib/res/colors.dart
+++ b/lib/res/colors.dart
@@ -17,4 +17,8 @@ class LightAppColors{
static const fillOrder = Color.fromARGB(255, 200, 230, 201);
static const fillOrderText = Color.fromARGB(255, 32, 74, 34);
static const allReviewOpener = Color.fromARGB(255, 183, 28, 28);
+ static const hintTitle = Color.fromARGB(255, 73, 69, 79);
+ static const cardBackground = Color.fromARGB(255, 242, 242, 241);
+ static const offerTimer = Color.fromARGB(255, 244, 67, 54);
+ static const offerCardDetail = Color.fromARGB(255, 73, 69, 79);
}
\ No newline at end of file
diff --git a/lib/screens/mains/discover/discover.dart b/lib/screens/mains/discover/discover.dart
index 8ccf133..d628b7a 100644
--- a/lib/screens/mains/discover/discover.dart
+++ b/lib/screens/mains/discover/discover.dart
@@ -1,10 +1,1025 @@
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 StatelessWidget {
+class Discover extends StatefulWidget {
const Discover({super.key});
+ @override
+ State createState() => _DiscoverState();
+}
+
+class _DiscoverState extends State with TickerProviderStateMixin {
+ final PageController _pageController = PageController();
+ double _currentPage = 0;
+
+ late AnimationController _staggeredController;
+ late List> _staggeredAnimations;
+
+ final List categoryIcons = [
+ Assets.icons.stashStarsLight.path,
+ Assets.icons.shoppingCart.path,
+ Assets.icons.elementEqual.path,
+ Assets.icons.shop.path,
+ Assets.icons.game.path,
+ Assets.icons.receiptDiscount.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(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(
+ begin: const Offset(0.0, 0.3),
+ end: Offset.zero,
+ ).animate(_staggeredAnimations[index]),
+ child: child,
+ ),
+ );
+ }
+
@override
Widget build(BuildContext context) {
- return const Placeholder();
+ 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"), 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"), 2),
+ const SizedBox(height: 12),
+ _buildAnimatedSection(_buildSeasonalDiscountSection(), 3),
+ const SizedBox(height: 24),
+ _buildAnimatedSection(_buildSectionTitle("Crafting something for you"), 4),
+ const SizedBox(height: 12),
+ _buildAnimatedSection(_buildCraftingSomethingSection(), 5),
+ const SizedBox(height: 24),
+ _buildAnimatedSection(_buildSectionTitle("First Purchase Discount"), 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: () {
+ CustomBottomSheet.show(context, [
+ "Food & Dining",
+ "Entertainment & Leisure",
+ "Health & Fitness",
+ "Travel & Transportation",
+ ]);
+ },
+ padding: const EdgeInsets.all(8),
+ ),
+ );
+ }
+
+ Widget _buildCategoryIcons() {
+ return SizedBox(
+ height: 60,
+ 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: 60,
+ height: 60,
+ padding: const EdgeInsets.all(16),
+ 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) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Row(
+ children: [
+ Text(
+ title,
+ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.normal),
+ ),
+ const SizedBox(width: 8),
+ const Expanded(child: Divider(color: Colors.grey, thickness: 1)),
+ ],
+ ),
+ );
+ }
+
+ 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.topDealsAndStores.path),
+ fit: BoxFit.cover,
+ ),
+ ),
+ );
+ }
+
+ Widget _buildSeasonalDiscountSection() {
+ final List