proxibuy/lib/presentation/ui/screens/home/home_screen.dart

739 lines
30 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:proxibuy/core/gen/assets.gen.dart';
import 'package:proxibuy/core/routes/app_router.dart';
import 'package:proxibuy/core/utils/empty_space.dart';
import 'package:proxibuy/presentation/providers/cubit/them_mode_cubit.dart';
import 'package:proxibuy/presentation/providers/cubit/categories_cubit.dart';
import 'package:proxibuy/presentation/ui/theme/colors.dart';
import 'package:proxibuy/presentation/ui/theme/responsive.dart';
import 'package:proxibuy/presentation/ui/widgets/carousel/carousel_slider_widget.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<CategoriesCubit>().getAllCategories();
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Responsive(context)
.builder(mobile: mobile(context), desktop: desktop(context));
}
Scaffold desktop(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
controller: _scrollController,
physics: BouncingScrollPhysics(),
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 1600),
child: Column(
children: [
12.h,
titleDivider(context, title: 'what\'s on your mind?', top: 16),
BlocBuilder<CategoriesCubit, CategoriesState>(
builder: (context, state) {
if (state is CategoriesLoaded) {
return SizedBox(
width: double.infinity,
height: 40,
child: ListView.builder(
itemCount: state.categories.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 10),
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return Container(
width: 200,
height: 40,
margin: EdgeInsets.symmetric(horizontal: 6),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.surface,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.car_crash),
8.w,
Text(state.categories[index].name ?? '')
],
),
);
},
),
);
} else if (state is CategoriesError) {
return Text('Failed to load categories');
} else {
return CircularProgressIndicator();
}
},
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
titleDivider(context,
title: 'Top 10 Discount & Offers'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 320,
viewportFraction: 0.9,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
],
),
),
12.w,
Expanded(
child: flashSales(context),
)
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: seasonalDiscounts(context)),
12.w,
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
titleDivider(context, title: 'special discount'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 280,
viewportFraction: 0.9,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
],
)),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
titleDivider(context, title: 'special discount'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 280,
viewportFraction: 0.9,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
],
)),
12.w,
Expanded(child: firstPurchaseDiscount(context)),
],
),
64.h
],
),
),
),
),
);
}
Scaffold mobile(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Padding(
padding: const EdgeInsets.only(left: 2.0),
child: SelectableText(
"Proxibuy",
style: Theme.of(context).textTheme.headlineSmall,
),
),
actions: [
IconButton(
icon: Assets.icon.outline.notificationBing
.svg(color: Theme.of(context).colorScheme.onSurface),
onPressed: () {},
),
12.w,
IconButton(
icon: Icon(Icons.brightness_6),
onPressed: () {
context.read<ThemModeCubit>().changeTheme();
},
),
10.w
],
),
body: RefreshIndicator(
onRefresh: () async {
context.read<CategoriesCubit>().getAllCategories();
},
child: SingleChildScrollView(
controller: _scrollController,
physics:
BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
searchBar(context),
categories(context),
titleDivider(context, title: 'Top 10 Discount & Offers'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 180,
viewportFraction: 0.8,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
flashSales(context, top: 12),
titleDivider(context, title: 'special discount'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 180,
viewportFraction: 0.8,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
seasonalDiscounts(context, top: 12),
titleDivider(context, title: 'Crafting something for you'),
CarouselSliderWidget<String>(
items: ['1', '2', '3', '4'],
height: 180,
viewportFraction: 0.8,
onPageBuilder: (context, item, _) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://pixcap.com/cdn/library/templates/f1ee999f-4f9e-4b5d-83b5-51d04681a999/thumbnail/9158b87d-1079-4973-9514-ee4ada0adf54_transparent_1422_800.webp',
fit: BoxFit.cover,
width: double.infinity,
),
);
},
),
firstPurchaseDiscount(context),
12.h
],
),
),
),
);
}
Column firstPurchaseDiscount(BuildContext context) {
return Column(
children: [
titleDivider(context, title: 'First Purchase Discount'),
SizedBox(
width: double.infinity,
height: 280,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: 20,
padding: EdgeInsets.symmetric(horizontal: 16),
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return Container(
width: 180,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.only(
topLeft: index == 0 ? Radius.circular(16) : Radius.zero,
bottomLeft:
index == 0 ? Radius.circular(16) : Radius.zero,
topRight:
index == 20 - 1 ? Radius.circular(16) : Radius.zero,
bottomRight: index == 20 - 1
? Radius.circular(16)
: Radius.zero)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://s3-alpha-sig.figma.com/img/6ec2/c17a/beca4d2eb02da53e80d47f73d5f6b56d?Expires=1740960000&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=VvgWny6Sfh-HI0Yp2stzKejafivtWKDI9I5FKuSAQ8VqNLgiT4c5zB5m-PX174NFrSDvkocDlUIZ33gM~hrAafD8UreEvYfs30sB0ZrIPR7kSPcms~d9ArYJRsXSQ7w8-cv~RHWruGfbw462CrOdvJadQ3ZReRkK9CJsPCg2EcU9mr~hlcyx84QxGx0rw~FdBvHt99pzX0gDMWpr0kC9Om-eM45Fylmcb91d5AGh2sxtf0NXyNwt83jjfKC-VOZHt5wbjbZq3NQNGmrwWTwzLOauhjtgOsC32EbsPim3O~8CrKJAQ-TVOSXN4B15ygiFxZ1WX2z6l-LnjFghzv9Z3w__',
width: double.infinity,
height: 120,
fit: BoxFit.cover,
),
),
),
12.h,
SelectableText(
'McDonald\'s',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
12.h,
SelectableText('Fast food',
maxLines: 1,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
overflow: TextOverflow.ellipsis,
)),
8.h,
SelectableText('Up to 20% Off',
maxLines: 1,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
overflow: TextOverflow.ellipsis,
)),
8.h,
Row(
children: [
Icon(
Icons.star_border_outlined,
color: semanticGreen,
),
4.w,
Text('4.5'),
],
)
],
),
);
},
),
),
],
);
}
Column seasonalDiscounts(BuildContext context, {final double top = 32}) {
return Column(
children: [
titleDivider(context, title: 'Seasonal Discount', top: top),
SizedBox(
width: double.infinity,
height: 100 * 2 + 12,
child: GridView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 16),
physics: BouncingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 16,
crossAxisSpacing: 12,
childAspectRatio: 0.3,
crossAxisCount: 2),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
context.go(AppRouter.product);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface),
child: Row(
children: [
Image.network(
'https://s3-alpha-sig.figma.com/img/55dd/f119/e880815733401c0400dc01734b64e83a?Expires=1740960000&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=HtKhelOfFUI1gplOzBHtfDP4ljCPECuypXWNc88VVBfYaQ2knCk0qPFwd4mjcH7Trru2W8S4hpDUeo-ztViRi~83USuULrSE0558MtBC~3TjS5S~Jm7eaDQO3YtvnsRtPK2xDamOJU80Xnkq0YPPkB~EVb8jVKF-b8JF2jdjEwon6fcltZDb7jTFJU8L9pYQQAhFxRV1QC8dOwnTT1zknm9iDNRDVGSj~VKwPpQX7daYzAf7b6DuLF4sk7DmX2fDdgF54pcYZjc1UOoubraM-sf-RKnh2JD8Vl~XK9-drzg8~t9CT99dxxHYp6udCLlya~dHNhIYjd5R4anoDQQLPg__',
width: 100,
height: 100,
fit: BoxFit.cover,
),
8.w,
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
'boots',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(overflow: TextOverflow.ellipsis),
maxLines: 1,
),
12.h,
Row(
children: [
Icon(
Icons.shopping_bag_outlined,
size: 16,
),
8.w,
Expanded(
child: SelectableText(
'Columbia Sportswear',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
),
],
),
8.h,
Row(
children: [
Icon(
CupertinoIcons.ticket,
size: 16,
),
8.w,
Expanded(
child: SelectableText(
'22 - 35% off',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: semanticRed,
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
),
],
)
],
),
))
],
),
),
),
);
},
),
),
],
);
}
Column flashSales(BuildContext context, {double top = 32}) {
return Column(
children: [
titleDivider(context, title: 'Flash Sale', top: top),
SizedBox(
width: double.infinity,
height: 348,
child: ListView.builder(
itemCount: 20,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 10),
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
context.go(AppRouter.product);
},
child: Container(
width: 200,
margin: EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Theme.of(context).colorScheme.surface,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Icon(
Icons.timer_outlined,
color: semanticRed,
size: 32,
),
12.w,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
'Limited time deal',
style:
Theme.of(context).textTheme.titleMedium,
),
SelectableText('08: 35”:48',
style: Theme.of(context)
.textTheme
.titleSmall
?.copyWith(color: semanticRed))
],
)
],
),
),
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network(
'https://s3-alpha-sig.figma.com/img/7919/da3d/6e638d21e4b8cec7cda36bd4fdec2012?Expires=1740960000&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=OiH30jVJjAo~EZTdnnGakp0gZzdPFWPHrthl4iSPv8X7d-pGh35sycz42ZvNekboa2Hyp22O4mx8YUOn~SUVHuIKsxmRblfb-vlGfCwqg9xZL16R8pQV6V~v8FpAvFIQL1id6EgfuL4VcnADASZ7m2XjJ6CDNd~c~-k5N4MwcKUxvFNBFt7OhLNdB9bXYuAT~sfiQAai3B~JDaikS0SD48irIGaXeBi4KNAtLlFGrrGDK3KLbyfetvXwRF5ml7G0RBzEbR26WvwkgdX39B7GjCxjQzBvExI6V-JM76oc2YZiSbQIE89Qpvu2ErP77Kc6dW4aOTPGQ5RJXxPzj2vWDA__',
width: double.infinity,
height: 100,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
'Amul Cheese Slices',
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
12.h,
Row(
children: [
Icon(Icons.location_on_outlined),
8.w,
Expanded(
child: SelectableText(
'Fresno (750m away)',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
))
],
),
8.h,
Row(
children: [
Icon(Icons.monetization_on_outlined),
8.w,
Expanded(
child: SelectableText(
'70 \$ - 53 \$ (13% off)',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
))
],
),
8.h,
Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: semanticGreen,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(16))),
onPressed: () {},
child: Text(
'Reservation',
)),
)
],
),
)
],
)),
);
},
),
),
],
);
}
Column categories(BuildContext context) {
return Column(
children: [
titleDivider(context, title: 'what\'s on your mind?', top: 16),
BlocBuilder<CategoriesCubit, CategoriesState>(
builder: (context, state) {
if (state is CategoriesLoaded) {
return SizedBox(
width: double.infinity,
height: 64,
child: ListView.builder(
itemCount: state.categories.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 10),
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return Container(
width: 64,
height: 64,
margin: EdgeInsets.symmetric(horizontal: 6),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.surface,
),
child: Center(
child: Icon(Icons.abc),
),
);
},
),
);
} else if (state is CategoriesError) {
return Text('Failed to load categories');
} else {
return CircularProgressIndicator();
}
},
),
],
);
}
Widget searchBar(
BuildContext context,
) {
final defaultBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Theme.of(context).colorScheme.surface, width: 2));
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 800),
child: TextField(
textInputAction: TextInputAction.search,
onSubmitted: (value) {
print(value);
},
decoration: InputDecoration(
hintText: 'what are you looking for?',
suffixIcon: Padding(
padding: const EdgeInsets.all(12.0),
child: Assets.icon.outline.search.svg(
color: Theme.of(context).colorScheme.onSurface,
width: 16,
height: 16),
),
enabledBorder: defaultBorder,
border: defaultBorder),
),
)),
],
),
);
}
Column titleDivider(BuildContext context,
{required final String title,
final double top = 32,
final double bottom = 16}) {
return Column(
children: [
top.h,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
SelectableText(
title,
style: Theme.of(context).textTheme.titleMedium,
),
12.w,
Expanded(
child: Divider(
color: Theme.of(context).colorScheme.onSurface,
)),
],
),
),
bottom.h,
],
);
}
}