feat: filters added to home page

This commit is contained in:
jahongireshonqulov
2025-10-24 14:54:08 +05:00
parent 41d8a38471
commit f2ab615b4e
30 changed files with 1159 additions and 86 deletions

View File

@@ -0,0 +1,3 @@
<svg width="19" height="13" viewBox="0 0 19 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 6.5L7.5 11.5L17.5 1.5" stroke="#4BA457" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 223 B

BIN
assets/icons/ic_clock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

View File

@@ -0,0 +1,4 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.5 1.5L1.5 19.5" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.5 1.5L19.5 19.5" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 323 B

View File

@@ -0,0 +1,3 @@
<svg width="26" height="25" viewBox="0 0 26 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.3532 -1.17809e-05L17.0302 24.1971L12.8532 13.1168L-4.19789e-05 10.2697L25.3532 -1.17809e-05Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 16.25L14.3335 12.5833C15.2502 11.25 15.8333 9.6667 15.8333 7.9167C15.8333 3.58336 12.25 0 7.91665 0C3.58334 0 0 3.58336 0 7.9167C0 12.25 3.58334 15.8334 7.91665 15.8334C9.66665 15.8334 11.2501 15.25 12.5835 14.3333L16.25 18L18 16.25ZM2.5 8C2.5 5.00001 4.91667 2.58334 7.91665 2.58334C10.9166 2.58334 13.3333 5.00001 13.3333 8C13.3333 11 10.9166 13.4167 7.91665 13.4167C4.91667 13.4167 2.5 11 2.5 8Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@@ -50,7 +50,35 @@
"ride": "Ride",
"chinese": "Chinese",
"dessert": "Dessert",
"more":"More"
"more":"More",
"orderDetails": "Order details",
"deliverNow": "Deliver now",
"schedule": "Schedule",
"enterNewAddress": "Enter a new address",
"nearby": "Nearby",
"currentLocation": "Current location",
"enable": "Enable",
"recentLocations": "Recent locations",
"allFilters": "All filters",
"sort": "Sort",
"pickedForYou": "Picked for you (default)",
"mostPopular": "Most popular",
"rating": "Rating",
"deliveryTime": "Delivery time",
"fromUberEats": "From Uber Eats",
"deals": "Deals",
"bestOverall": "Best overall",
"priceRange": "Price range",
"maxDeliveryFee": "Max. Delivery Fee",
"dietary": "Dietary",
"vegetarian": "Vegetarian",
"vegan": "Vegan",
"glutenFree": "Gluten-free",
"halal": "Halal",
"allergyFriendly": "Allergy friendly",
"atLeast":"At least",
"apply": "Apply"
}

View File

@@ -57,6 +57,35 @@
"ride": "Поездка",
"chinese": "Китайская",
"dessert": "Десерт",
"more":"Ещё"
"more":"Ещё",
"orderDetails": "Детали заказа",
"deliverNow": "Доставить сейчас",
"schedule": "Запланировать",
"enterNewAddress": "Введите новый адрес",
"nearby": "Рядом",
"currentLocation": "Текущее местоположение",
"enable": "Включить",
"recentLocations": "Недавние адреса",
"allFilters": "Все фильтры",
"sort": "Сортировка",
"pickedForYou": "Подобрано для вас (по умолчанию)",
"mostPopular": "Самые популярные",
"rating": "Рейтинг",
"deliveryTime": "Время доставки",
"fromUberEats": "От Uber Eats",
"deals": "Скидки",
"bestOverall": "Лучший выбор",
"priceRange": "Диапазон цен",
"maxDeliveryFee": "Макс. стоимость доставки",
"dietary": "Диета",
"vegetarian": "Вегетарианское",
"vegan": "Веганское",
"glutenFree": "Без глютена",
"halal": "Халяль",
"allergyFriendly": "Без аллергенов",
"atLeast":"Kак минимум",
"apply": "Применить"
}

View File

@@ -57,5 +57,34 @@
"ride": "Yol",
"chinese": "Xitoy",
"dessert": "Desert",
"more":"Ko'proq"
"more":"Ko'proq",
"orderDetails": "Buyurtma tafsilotlari",
"deliverNow": "Hozir yetkazish",
"schedule": "Rejalashtirish",
"enterNewAddress": "Yangi manzil kiriting",
"nearby": "Yaqin joylar",
"currentLocation": "Joriy joylashuv",
"enable": "Yoqqish",
"recentLocations": "Yaqinda ishlatilgan manzillar",
"allFilters": "Barcha filtrlar",
"sort": "Saralash",
"pickedForYou": "Siz uchun",
"mostPopular": "Eng ommabop",
"rating": "Reyting",
"deliveryTime": "Yetkazib berish vaqti",
"fromUberEats": "Uber Eats dan",
"deals": "Aksiyalar",
"bestOverall": "Eng yaxshisi",
"priceRange": "Narx oraligi",
"maxDeliveryFee": "Maks. yetkazish narxi",
"dietary": "Parhez",
"vegetarian": "Vegetarian",
"vegan": "Vegan",
"glutenFree": "Glutensiz",
"halal": "Halol",
"allergyFriendly": "Allergiyaga mos",
"atLeast":"Kаmida",
"apply": "Qollash"
}

View File

@@ -1,4 +1,5 @@
abstract class TimeDelayConst {
static const Duration durationMill150 = Duration(milliseconds: 150);
static const Duration durationMill300 = Duration(milliseconds: 300);
static const Duration durationMill800 = Duration(milliseconds: 800);

View File

@@ -31,9 +31,9 @@ extension GetItInjectableX on _i174.GetIt {
_i526.EnvironmentFilter? environmentFilter,
}) {
final gh = _i526.GetItHelper(this, environment, environmentFilter);
gh.factory<_i1007.HomeBloc>(() => _i1007.HomeBloc());
gh.factory<_i580.MainBloc>(() => _i580.MainBloc());
gh.factory<_i311.SplashBloc>(() => _i311.SplashBloc());
gh.factory<_i1007.HomeBloc>(() => _i1007.HomeBloc());
gh.singleton<_i306.StorageService>(() => _i306.StorageService());
gh.singleton<_i152.AppRoutes>(() => _i152.AppRoutes());
gh.factory<_i942.LanguageBloc>(

View File

@@ -327,6 +327,162 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'More'**
String get more;
/// No description provided for @orderDetails.
///
/// In en, this message translates to:
/// **'Order details'**
String get orderDetails;
/// No description provided for @deliverNow.
///
/// In en, this message translates to:
/// **'Deliver now'**
String get deliverNow;
/// No description provided for @schedule.
///
/// In en, this message translates to:
/// **'Schedule'**
String get schedule;
/// No description provided for @enterNewAddress.
///
/// In en, this message translates to:
/// **'Enter a new address'**
String get enterNewAddress;
/// No description provided for @nearby.
///
/// In en, this message translates to:
/// **'Nearby'**
String get nearby;
/// No description provided for @currentLocation.
///
/// In en, this message translates to:
/// **'Current location'**
String get currentLocation;
/// No description provided for @enable.
///
/// In en, this message translates to:
/// **'Enable'**
String get enable;
/// No description provided for @recentLocations.
///
/// In en, this message translates to:
/// **'Recent locations'**
String get recentLocations;
/// No description provided for @allFilters.
///
/// In en, this message translates to:
/// **'All filters'**
String get allFilters;
/// No description provided for @sort.
///
/// In en, this message translates to:
/// **'Sort'**
String get sort;
/// No description provided for @pickedForYou.
///
/// In en, this message translates to:
/// **'Picked for you (default)'**
String get pickedForYou;
/// No description provided for @mostPopular.
///
/// In en, this message translates to:
/// **'Most popular'**
String get mostPopular;
/// No description provided for @rating.
///
/// In en, this message translates to:
/// **'Rating'**
String get rating;
/// No description provided for @deliveryTime.
///
/// In en, this message translates to:
/// **'Delivery time'**
String get deliveryTime;
/// No description provided for @fromUberEats.
///
/// In en, this message translates to:
/// **'From Uber Eats'**
String get fromUberEats;
/// No description provided for @deals.
///
/// In en, this message translates to:
/// **'Deals'**
String get deals;
/// No description provided for @bestOverall.
///
/// In en, this message translates to:
/// **'Best overall'**
String get bestOverall;
/// No description provided for @priceRange.
///
/// In en, this message translates to:
/// **'Price range'**
String get priceRange;
/// No description provided for @maxDeliveryFee.
///
/// In en, this message translates to:
/// **'Max. Delivery Fee'**
String get maxDeliveryFee;
/// No description provided for @dietary.
///
/// In en, this message translates to:
/// **'Dietary'**
String get dietary;
/// No description provided for @vegetarian.
///
/// In en, this message translates to:
/// **'Vegetarian'**
String get vegetarian;
/// No description provided for @vegan.
///
/// In en, this message translates to:
/// **'Vegan'**
String get vegan;
/// No description provided for @glutenFree.
///
/// In en, this message translates to:
/// **'Gluten-free'**
String get glutenFree;
/// No description provided for @allergyFriendly.
///
/// In en, this message translates to:
/// **'Allergy friendly'**
String get allergyFriendly;
/// No description provided for @atLeast.
///
/// In en, this message translates to:
/// **'At least'**
String get atLeast;
/// No description provided for @apply.
///
/// In en, this message translates to:
/// **'Apply'**
String get apply;
}
class _AppLocalizationsDelegate

View File

@@ -126,4 +126,82 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get more => 'More';
@override
String get orderDetails => 'Order details';
@override
String get deliverNow => 'Deliver now';
@override
String get schedule => 'Schedule';
@override
String get enterNewAddress => 'Enter a new address';
@override
String get nearby => 'Nearby';
@override
String get currentLocation => 'Current location';
@override
String get enable => 'Enable';
@override
String get recentLocations => 'Recent locations';
@override
String get allFilters => 'All filters';
@override
String get sort => 'Sort';
@override
String get pickedForYou => 'Picked for you (default)';
@override
String get mostPopular => 'Most popular';
@override
String get rating => 'Rating';
@override
String get deliveryTime => 'Delivery time';
@override
String get fromUberEats => 'From Uber Eats';
@override
String get deals => 'Deals';
@override
String get bestOverall => 'Best overall';
@override
String get priceRange => 'Price range';
@override
String get maxDeliveryFee => 'Max. Delivery Fee';
@override
String get dietary => 'Dietary';
@override
String get vegetarian => 'Vegetarian';
@override
String get vegan => 'Vegan';
@override
String get glutenFree => 'Gluten-free';
@override
String get allergyFriendly => 'Allergy friendly';
@override
String get atLeast => 'At least';
@override
String get apply => 'Apply';
}

View File

@@ -127,4 +127,82 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get more => 'Ещё';
@override
String get orderDetails => 'Детали заказа';
@override
String get deliverNow => 'Доставить сейчас';
@override
String get schedule => 'Запланировать';
@override
String get enterNewAddress => 'Введите новый адрес';
@override
String get nearby => 'Рядом';
@override
String get currentLocation => 'Текущее местоположение';
@override
String get enable => 'Включить';
@override
String get recentLocations => 'Недавние адреса';
@override
String get allFilters => 'Все фильтры';
@override
String get sort => 'Сортировка';
@override
String get pickedForYou => 'Подобрано для вас (по умолчанию)';
@override
String get mostPopular => 'Самые популярные';
@override
String get rating => 'Рейтинг';
@override
String get deliveryTime => 'Время доставки';
@override
String get fromUberEats => 'От Uber Eats';
@override
String get deals => 'Скидки';
@override
String get bestOverall => 'Лучший выбор';
@override
String get priceRange => 'Диапазон цен';
@override
String get maxDeliveryFee => 'Макс. стоимость доставки';
@override
String get dietary => 'Диета';
@override
String get vegetarian => 'Вегетарианское';
@override
String get vegan => 'Веганское';
@override
String get glutenFree => 'Без глютена';
@override
String get allergyFriendly => 'Без аллергенов';
@override
String get atLeast => 'Kак минимум';
@override
String get apply => 'Применить';
}

View File

@@ -127,4 +127,82 @@ class AppLocalizationsUz extends AppLocalizations {
@override
String get more => 'Ko\'proq';
@override
String get orderDetails => 'Buyurtma tafsilotlari';
@override
String get deliverNow => 'Hozir yetkazish';
@override
String get schedule => 'Rejalashtirish';
@override
String get enterNewAddress => 'Yangi manzil kiriting';
@override
String get nearby => 'Yaqin joylar';
@override
String get currentLocation => 'Joriy joylashuv';
@override
String get enable => 'Yoqqish';
@override
String get recentLocations => 'Yaqinda ishlatilgan manzillar';
@override
String get allFilters => 'Barcha filtrlar';
@override
String get sort => 'Saralash';
@override
String get pickedForYou => 'Siz uchun';
@override
String get mostPopular => 'Eng ommabop';
@override
String get rating => 'Reyting';
@override
String get deliveryTime => 'Yetkazib berish vaqti';
@override
String get fromUberEats => 'Uber Eats dan';
@override
String get deals => 'Aksiyalar';
@override
String get bestOverall => 'Eng yaxshisi';
@override
String get priceRange => 'Narx oraligi';
@override
String get maxDeliveryFee => 'Maks. yetkazish narxi';
@override
String get dietary => 'Parhez';
@override
String get vegetarian => 'Vegetarian';
@override
String get vegan => 'Vegan';
@override
String get glutenFree => 'Glutensiz';
@override
String get allergyFriendly => 'Allergiyaga mos';
@override
String get atLeast => 'Kаmida';
@override
String get apply => 'Qollash';
}

View File

@@ -3,6 +3,8 @@ import '../../food_delivery_client.dart';
abstract class AppColors {
static const Color cTransparent = Colors.transparent;
static const Color cRed = Colors.red;
static const Color cYellow = Colors.yellow;
static const Color cFFFFFF = Color(0xFFFFFFFF);
static const Color c000000 = Color(0xFF000000);

View File

@@ -23,5 +23,24 @@ abstract class AppIcons {
static const String icLocation = "$baseUrl/ic_location.svg";
static const String icArrowBottom = "$baseUrl/ic_arrow_btm.svg";
static const String icPicked = "$baseUrl/ic_picked.svg";
static const String icMostPopular = "$baseUrl/ic_popular.svg";
static const String icStar = "$baseUrl/ic_rating.svg";
static const String icDeliveryTime = "$baseUrl/ic_delivery_time.svg";
static const String icDeals = "$baseUrl/ic_deals.svg";
static const String icClose = "$baseUrl/ic_close.svg";
static const String icCurrentLocation = "$baseUrl/ic_current_loc.svg";
static const String icEdit = "$baseUrl/ic_edit.svg";
static const String icSearch = "$baseUrl/ic_search.svg";
static const String icCheck= "$baseUrl/ic_check.svg";
static const String icCheck1= "$baseUrl/ic_check1.svg";
///.png icons
static const String icBestOverall = "$baseUrl/ic_best.png";
static const String icVegetarian = "$baseUrl/ic_vegetarian.png";
static const String icVegen = "$baseUrl/ic_vegen.png";
static const String icGlutenFree = "$baseUrl/ic_gluten_free.png";
static const String icAllergyFriendly = "$baseUrl/ic_allergy_friendly.png";
static const String icClock = "$baseUrl/ic_clock.png";
}

View File

@@ -63,6 +63,20 @@ abstract class AppTextStyles {
fontWeight: FontWeight.w500,
);
static const TextStyle size20Medium = TextStyle(
color: _defaultColor,
fontSize: SizesCons.size_20,
fontFamily: _fontMedium,
fontWeight: FontWeight.w500,
);
static const TextStyle size16Bold= TextStyle(
color: _defaultColor,
fontSize: SizesCons.size_16,
fontFamily: _fontBold,
fontWeight: FontWeight.w700,
);
static const TextStyle size17Bold = TextStyle(
color: _defaultColor,
fontSize: SizesCons.size_17,

View File

@@ -1,6 +1,8 @@
import '../../food_delivery_client.dart';
abstract class AppUtils {
static const SizedBox kSizedBox = SizedBox.shrink();
static const Radius kRadius = Radius.zero;
static const Radius kRadius8 = Radius.circular(8);
static const Radius kRadius12 = Radius.circular(12);
@@ -79,6 +81,10 @@ abstract class AppUtils {
static const BorderRadius kBorderRadius40 = BorderRadius.all(
Radius.circular(40),
);
static const BorderRadius kBorderRadiusTop20 = BorderRadius.only(
topLeft: kRadius20,
topRight: kRadius20,
);
static const BorderRadius kBorderRadiusTop20Bottom20 = BorderRadius.only(
bottomRight: kRadius20,
topRight: kRadius20,

View File

@@ -0,0 +1,61 @@
import '../../../../food_delivery_client.dart';
class AppButton extends StatelessWidget {
const AppButton({
super.key,
required this.name,
required this.onPressed,
this.margin,
this.backgroundColor,
this.borderRadius,
this.height,
this.textColor,
this.width,
this.action,
this.trailing,
this.mainAxisAlignment,
});
final String name;
final VoidCallback onPressed;
final EdgeInsets? margin;
final Color? backgroundColor;
final Color? textColor;
final double? borderRadius;
final double? width;
final double? height;
final Widget? action;
final Widget? trailing;
final MainAxisAlignment? mainAxisAlignment;
@override
Widget build(BuildContext context) {
return Bounceable(
onTap: onPressed,
duration: TimeDelayConst.durationMill150,
child: Container(
width: width ?? double.infinity,
height: height ?? 55,
margin: margin,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: backgroundColor ?? AppColors.c000000,
borderRadius: BorderRadius.circular(borderRadius ?? 0),
),
child: Row(
mainAxisAlignment: mainAxisAlignment ?? MainAxisAlignment.center,
children: [
action ?? AppUtils.kSizedBox,
Text(name, style: AppTextStyles.size16Bold.copyWith(
color: AppColors.cFFFFFF
)),
trailing ?? AppUtils.kSizedBox,
],
),
),
);
}
}

View File

@@ -0,0 +1,37 @@
import '../../../../../../food_delivery_client.dart';
class WCustomModalBottomSheet extends StatelessWidget {
const WCustomModalBottomSheet({super.key, required this.child});
final Widget child;
@override
Widget build(BuildContext context) {
return Material(
color: AppColors.cFFFFFF,
borderRadius: AppUtils.kBorderRadiusTop20,
child: SafeArea(
child: SizedBox(
width: context.w,
child: Column(
children: [
10.verticalSpace,
SizedBox(
height: 6,
width: 100,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius6,
color: AppColors.cE6E6E6.newWithOpacity(.6),
),
),
),
10.verticalSpace,
child,
],
),
),
),
);
}
}

View File

@@ -3,3 +3,5 @@ export 'w_food_item.dart';
export 'w_divider.dart';
export 'w_see_all_raw.dart';
export 'w_stories_list_item.dart';
export 'w_custom_modal_bottom_sheet.dart';
export 'app_button.dart';

View File

@@ -9,3 +9,6 @@ export 'presentation/pages/home_page/widgets/w_offers_carouseL_slider.dart';
export 'presentation/pages/home_page/widgets/w_popular_near_you.dart';
export 'presentation/pages/home_page/widgets/w_todays_offers.dart';
export 'package:food_delivery_client/feature/home/presentation/pages/home_page/widgets/pick_it_for_free.dart';
export 'package:flutter_svg/flutter_svg.dart';
export 'package:food_delivery_client/feature/home/presentation/pages/categories_page/categories_page.dart';
export 'package:food_delivery_client/feature/home/presentation/pages/home_page/widgets/w_rating_btm_sheet.dart';

View File

@@ -5,13 +5,33 @@ part 'home_event.dart';
part 'home_state.dart';
part 'home_bloc.freezed.dart';
@injectable
class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc() : super(const HomeState()) {
on<_Changed>(_onChanged);
on<_Filtered>(_onFiltered);
on<_Rated>(_onRated);
}
void _onChanged(_Changed event, Emitter<HomeState> emit) {
emit(state.copyWith(currentIndex: event.index));
}
void _onFiltered(_Filtered event, Emitter<HomeState> emit) {
final oldFilters = List.from(state.filters);
if (oldFilters.contains(event.filter)) {
final newFilters = oldFilters..remove(event.filter);
emit(state.copyWith(filters: newFilters));
} else {
final newFilters = oldFilters..add(event.filter);
emit(state.copyWith(filters: newFilters));
}
}
void _onRated(_Rated event, Emitter<HomeState> emit){
emit(state.copyWith(rating: event.filter));
}
}

View File

@@ -55,12 +55,14 @@ extension HomeEventPatterns on HomeEvent {
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Started value)? started,TResult Function( _Changed value)? changed,required TResult orElse(),}){
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Started value)? started,TResult Function( _Changed value)? changed,TResult Function( _Filtered value)? filtered,TResult Function( _Rated value)? rated,required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return changed(_that);case _Filtered() when filtered != null:
return filtered(_that);case _Rated() when rated != null:
return rated(_that);case _:
return orElse();
}
@@ -78,12 +80,14 @@ return changed(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Started value) started,required TResult Function( _Changed value) changed,}){
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Started value) started,required TResult Function( _Changed value) changed,required TResult Function( _Filtered value) filtered,required TResult Function( _Rated value) rated,}){
final _that = this;
switch (_that) {
case _Started():
return started(_that);case _Changed():
return changed(_that);case _:
return changed(_that);case _Filtered():
return filtered(_that);case _Rated():
return rated(_that);case _:
throw StateError('Unexpected subclass');
}
@@ -100,12 +104,14 @@ return changed(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Started value)? started,TResult? Function( _Changed value)? changed,}){
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Started value)? started,TResult? Function( _Changed value)? changed,TResult? Function( _Filtered value)? filtered,TResult? Function( _Rated value)? rated,}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return changed(_that);case _Filtered() when filtered != null:
return filtered(_that);case _Rated() when rated != null:
return rated(_that);case _:
return null;
}
@@ -122,11 +128,13 @@ return changed(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? started,TResult Function( int index)? changed,required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? started,TResult Function( int index)? changed,TResult Function( String filter)? filtered,TResult Function( List<String> filter)? rated,required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed(_that.index);case _:
return changed(_that.index);case _Filtered() when filtered != null:
return filtered(_that.filter);case _Rated() when rated != null:
return rated(_that.filter);case _:
return orElse();
}
@@ -144,11 +152,13 @@ return changed(_that.index);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() started,required TResult Function( int index) changed,}) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() started,required TResult Function( int index) changed,required TResult Function( String filter) filtered,required TResult Function( List<String> filter) rated,}) {final _that = this;
switch (_that) {
case _Started():
return started();case _Changed():
return changed(_that.index);case _:
return changed(_that.index);case _Filtered():
return filtered(_that.filter);case _Rated():
return rated(_that.filter);case _:
throw StateError('Unexpected subclass');
}
@@ -165,11 +175,13 @@ return changed(_that.index);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? started,TResult? Function( int index)? changed,}) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? started,TResult? Function( int index)? changed,TResult? Function( String filter)? filtered,TResult? Function( List<String> filter)? rated,}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed(_that.index);case _:
return changed(_that.index);case _Filtered() when filtered != null:
return filtered(_that.filter);case _Rated() when rated != null:
return rated(_that.filter);case _:
return null;
}
@@ -273,12 +285,150 @@ as int,
}
}
/// @nodoc
class _Filtered implements HomeEvent {
const _Filtered(this.filter);
final String filter;
/// Create a copy of HomeEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$FilteredCopyWith<_Filtered> get copyWith => __$FilteredCopyWithImpl<_Filtered>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Filtered&&(identical(other.filter, filter) || other.filter == filter));
}
@override
int get hashCode => Object.hash(runtimeType,filter);
@override
String toString() {
return 'HomeEvent.filtered(filter: $filter)';
}
}
/// @nodoc
abstract mixin class _$FilteredCopyWith<$Res> implements $HomeEventCopyWith<$Res> {
factory _$FilteredCopyWith(_Filtered value, $Res Function(_Filtered) _then) = __$FilteredCopyWithImpl;
@useResult
$Res call({
String filter
});
}
/// @nodoc
class __$FilteredCopyWithImpl<$Res>
implements _$FilteredCopyWith<$Res> {
__$FilteredCopyWithImpl(this._self, this._then);
final _Filtered _self;
final $Res Function(_Filtered) _then;
/// Create a copy of HomeEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? filter = null,}) {
return _then(_Filtered(
null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _Rated implements HomeEvent {
const _Rated(final List<String> filter): _filter = filter;
final List<String> _filter;
List<String> get filter {
if (_filter is EqualUnmodifiableListView) return _filter;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_filter);
}
/// Create a copy of HomeEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$RatedCopyWith<_Rated> get copyWith => __$RatedCopyWithImpl<_Rated>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Rated&&const DeepCollectionEquality().equals(other._filter, _filter));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_filter));
@override
String toString() {
return 'HomeEvent.rated(filter: $filter)';
}
}
/// @nodoc
abstract mixin class _$RatedCopyWith<$Res> implements $HomeEventCopyWith<$Res> {
factory _$RatedCopyWith(_Rated value, $Res Function(_Rated) _then) = __$RatedCopyWithImpl;
@useResult
$Res call({
List<String> filter
});
}
/// @nodoc
class __$RatedCopyWithImpl<$Res>
implements _$RatedCopyWith<$Res> {
__$RatedCopyWithImpl(this._self, this._then);
final _Rated _self;
final $Res Function(_Rated) _then;
/// Create a copy of HomeEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? filter = null,}) {
return _then(_Rated(
null == filter ? _self._filter : filter // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
/// @nodoc
mixin _$HomeState {
int get currentIndex;
int get currentIndex; List get filters; List<String> get rating;
/// Create a copy of HomeState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -289,16 +439,16 @@ $HomeStateCopyWith<HomeState> get copyWith => _$HomeStateCopyWithImpl<HomeState>
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&const DeepCollectionEquality().equals(other.filters, filters)&&const DeepCollectionEquality().equals(other.rating, rating));
}
@override
int get hashCode => Object.hash(runtimeType,currentIndex);
int get hashCode => Object.hash(runtimeType,currentIndex,const DeepCollectionEquality().hash(filters),const DeepCollectionEquality().hash(rating));
@override
String toString() {
return 'HomeState(currentIndex: $currentIndex)';
return 'HomeState(currentIndex: $currentIndex, filters: $filters, rating: $rating)';
}
@@ -309,7 +459,7 @@ abstract mixin class $HomeStateCopyWith<$Res> {
factory $HomeStateCopyWith(HomeState value, $Res Function(HomeState) _then) = _$HomeStateCopyWithImpl;
@useResult
$Res call({
int currentIndex
int currentIndex, List filters, List<String> rating
});
@@ -326,10 +476,12 @@ class _$HomeStateCopyWithImpl<$Res>
/// Create a copy of HomeState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? filters = null,Object? rating = null,}) {
return _then(_self.copyWith(
currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable
as int,
as int,filters: null == filters ? _self.filters : filters // ignore: cast_nullable_to_non_nullable
as List,rating: null == rating ? _self.rating : rating // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
@@ -414,10 +566,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int currentIndex)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int currentIndex, List filters, List<String> rating)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _HomeState() when $default != null:
return $default(_that.currentIndex);case _:
return $default(_that.currentIndex,_that.filters,_that.rating);case _:
return orElse();
}
@@ -435,10 +587,10 @@ return $default(_that.currentIndex);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int currentIndex) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int currentIndex, List filters, List<String> rating) $default,) {final _that = this;
switch (_that) {
case _HomeState():
return $default(_that.currentIndex);case _:
return $default(_that.currentIndex,_that.filters,_that.rating);case _:
throw StateError('Unexpected subclass');
}
@@ -455,10 +607,10 @@ return $default(_that.currentIndex);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int currentIndex)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int currentIndex, List filters, List<String> rating)? $default,) {final _that = this;
switch (_that) {
case _HomeState() when $default != null:
return $default(_that.currentIndex);case _:
return $default(_that.currentIndex,_that.filters,_that.rating);case _:
return null;
}
@@ -470,10 +622,24 @@ return $default(_that.currentIndex);case _:
class _HomeState implements HomeState {
const _HomeState({this.currentIndex = 0});
const _HomeState({this.currentIndex = 0, final List filters = const [], final List<String> rating = const []}): _filters = filters,_rating = rating;
@override@JsonKey() final int currentIndex;
final List _filters;
@override@JsonKey() List get filters {
if (_filters is EqualUnmodifiableListView) return _filters;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_filters);
}
final List<String> _rating;
@override@JsonKey() List<String> get rating {
if (_rating is EqualUnmodifiableListView) return _rating;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_rating);
}
/// Create a copy of HomeState
/// with the given fields replaced by the non-null parameter values.
@@ -485,16 +651,16 @@ _$HomeStateCopyWith<_HomeState> get copyWith => __$HomeStateCopyWithImpl<_HomeSt
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&const DeepCollectionEquality().equals(other._filters, _filters)&&const DeepCollectionEquality().equals(other._rating, _rating));
}
@override
int get hashCode => Object.hash(runtimeType,currentIndex);
int get hashCode => Object.hash(runtimeType,currentIndex,const DeepCollectionEquality().hash(_filters),const DeepCollectionEquality().hash(_rating));
@override
String toString() {
return 'HomeState(currentIndex: $currentIndex)';
return 'HomeState(currentIndex: $currentIndex, filters: $filters, rating: $rating)';
}
@@ -505,7 +671,7 @@ abstract mixin class _$HomeStateCopyWith<$Res> implements $HomeStateCopyWith<$Re
factory _$HomeStateCopyWith(_HomeState value, $Res Function(_HomeState) _then) = __$HomeStateCopyWithImpl;
@override @useResult
$Res call({
int currentIndex
int currentIndex, List filters, List<String> rating
});
@@ -522,10 +688,12 @@ class __$HomeStateCopyWithImpl<$Res>
/// Create a copy of HomeState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? filters = null,Object? rating = null,}) {
return _then(_HomeState(
currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable
as int,
as int,filters: null == filters ? _self._filters : filters // ignore: cast_nullable_to_non_nullable
as List,rating: null == rating ? _self._rating : rating // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}

View File

@@ -4,5 +4,7 @@ part of 'home_bloc.dart';
class HomeEvent with _$HomeEvent {
const factory HomeEvent.started() = _Started;
const factory HomeEvent.changed(int index) = _Changed;
const factory HomeEvent.filtered(String filter) = _Filtered;
const factory HomeEvent.rated(List<String> filter) = _Rated;
}

View File

@@ -3,6 +3,9 @@ part of 'home_bloc.dart';
@freezed
abstract class HomeState with _$HomeState {
const factory HomeState({
@Default(0) int currentIndex
@Default(0) int currentIndex,
@Default([]) List filters,
@Default([]) List<String> rating
}) = _HomeState;
}

View File

@@ -1,5 +1,3 @@
import 'package:food_delivery_client/feature/home/presentation/pages/categories_page/categories_page.dart';
import '../../../../../../food_delivery_client.dart';
class WDeliveryHeader extends StatelessWidget {
@@ -13,7 +11,29 @@ class WDeliveryHeader extends StatelessWidget {
context.loc.petSupplies,
context.loc.more,
];
/*
Siz uchun
Eng ommabop,
Reyting
Yetkazib berish vaqti,
Aksiyalar
*/
List titles = [
'',
context.loc.pickedForYou,
context.loc.mostPopular,
context.loc.rating,
context.loc.deliveryTime,
context.loc.deals,
];
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
15.verticalSpace,
Row(
@@ -38,12 +58,14 @@ class WDeliveryHeader extends StatelessWidget {
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
mainAxisSpacing: 10,
childAspectRatio: 78 / 100,
mainAxisExtent: 118,
// childAspectRatio: 78 / 100,
),
itemBuilder: (context, index) => WCategoryItem(
onTap: () {
@@ -54,14 +76,102 @@ class WDeliveryHeader extends StatelessWidget {
imgUrl: index != 3 ? _images[index] : null,
text: _titles[index],
child: index == 3
? SizedBox(height: 55, width: 55, child: Icon(Icons.more_horiz))
? SizedBox(
height: 55,
width: 55,
child: Icon(Icons.more_horiz),
)
: null,
),
),
8.verticalSpace,
WDivider()
],
).paddingSymmetric(horizontal: 15);
).paddingSymmetric(horizontal: 15),
15.verticalSpace,
SizedBox(
height: 40,
child: ListView.separated(
padding: EdgeInsets.symmetric(horizontal: 15),
scrollDirection: Axis.horizontal,
itemCount: titles.length,
physics: const AlwaysScrollableScrollPhysics(),
separatorBuilder: (context, index) => 10.horizontalSpace,
itemBuilder: (context, index) => InkWell(
onTap: () async {
if (index != 3 && index != 0) {
context.read<HomeBloc>().add(
HomeEvent.filtered(titles[index]),
);
}
if (index == 3) {
final response = await WRatingBottomSheet(
savedRatings: state.rating ?? [],
).show(context);
if (response != null) {
context.read<HomeBloc>().add(
HomeEvent.rated(response ?? []),
);
}
}
},
borderRadius: AppUtils.kBorderRadius25,
child: Ink(
padding: EdgeInsets.zero,
decoration: BoxDecoration(
color: AppColors.cEEEEEE,
borderRadius: AppUtils.kBorderRadius25,
),
child: Row(
children: [
if (index != 0 && state.filters.contains(titles[index]))
SvgPicture.asset(
AppIcons.icCheck1,
).paddingOnly(right: 5),
if (index == 0)
Badge(
isLabelVisible:
state.filters.isNotEmpty ||
state.rating.isNotEmpty,
backgroundColor: AppColors.c34A853,
child: SvgPicture.asset(AppIcons.icFilter),
).paddingSymmetric(vertical: 6),
if (index == 3 && state.rating.isNotEmpty)
SizedBox(
height: 25,
width: 25,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.c34A853,
borderRadius: AppUtils.kBorderRadius16,
),
child: Center(
child: Text(
"${state.rating.length}",
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cFFFFFF,
),
),
),
),
).paddingOnly(right: 8),
Text(titles[index], style: AppTextStyles.size14Medium),
if (index == 3) Icon(Icons.keyboard_arrow_down),
],
).paddingSymmetric(vertical: 0, horizontal: 15),
),
),
),
),
10.verticalSpace,
WDivider(),
],
);
},
);
}
}

View File

@@ -9,6 +9,8 @@ class WHomeHeader extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return DecoratedBox(
decoration: BoxDecoration(color: AppColors.cFFFFFF),
child: Column(

View File

@@ -0,0 +1,133 @@
import '../../../../../../food_delivery_client.dart';
class WRatingBottomSheet extends StatefulWidget {
const WRatingBottomSheet({super.key, required this.savedRatings});
final List<String> savedRatings;
Future<List<String>?> show(BuildContext context) {
return showModalBottomSheet<List<String>>(
context: context,
builder: (context) => Wrap(children: [this]),
);
}
@override
State<WRatingBottomSheet> createState() => _WRatingBottomSheetState();
}
class _WRatingBottomSheetState extends State<WRatingBottomSheet> {
List<String> rating = [];
void updateRating(String value) {
final list = List<String>.from(rating);
if (list.contains(value)) {
list.remove(value);
setState(() {
rating = list;
});
} else {
list.add(value);
setState(() {
rating = list;
});
}
}
@override
void initState() {
rating = widget.savedRatings;
super.initState();
}
@override
Widget build(BuildContext context) {
final List<String> value = [
"${context.loc.atLeast} 4,9",
"${context.loc.atLeast} 4,7",
"${context.loc.atLeast} 4,5",
];
return WCustomModalBottomSheet(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(context.loc.rating, style: AppTextStyles.size20Medium),
IconButton(
onPressed: () {
context.pop(null);
},
icon: SvgPicture.asset(AppIcons.icClose, height: 15),
),
],
),
15.verticalSpace,
Wrap(
spacing: 10,
children: List.generate(value.length, (index) {
return WRatingButton(
onTap: () {
updateRating(value[index]);
},
isActive: rating.contains(value[index]),
rating: value[index],
);
}),
),
25.verticalSpace,
AppButton(
name: context.loc.apply,
onPressed: () {
context.pop(rating);
},
),
20.verticalSpace,
],
).paddingSymmetric(horizontal: 15),
);
}
}
class WRatingButton extends StatelessWidget {
const WRatingButton({
super.key,
required this.onTap,
required this.isActive,
required this.rating,
});
final VoidCallback onTap;
final bool isActive;
final String rating;
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: AppUtils.kBorderRadius25,
onTap: onTap,
child: Ink(
decoration: BoxDecoration(
color: isActive
? AppColors.c34A853
: AppColors.cE6E6E6.newWithOpacity(.6),
borderRadius: AppUtils.kBorderRadius25,
),
child: Row(
spacing: 4,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: AppColors.cYellow),
Text(
rating,
style: AppTextStyles.size14Medium.copyWith(
color: isActive ? AppColors.cFFFFFF : AppColors.c000000,
),
),
],
).paddingSymmetric(vertical: 6, horizontal: 10),
),
);
}
}

View File

@@ -19,3 +19,4 @@ export 'package:freezed_annotation/freezed_annotation.dart';
export 'package:shared_preferences/shared_preferences.dart';
export 'package:cached_network_image/cached_network_image.dart';
export 'package:carousel_slider/carousel_slider.dart';
export 'package:flutter_bounceable/flutter_bounceable.dart';