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

@@ -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
}) = _HomeState;
@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,55 +11,167 @@ class WDeliveryHeader extends StatelessWidget {
context.loc.petSupplies,
context.loc.more,
];
return Column(
children: [
15.verticalSpace,
Row(
spacing: 12,
/*
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: [
WCategoriesHeaderItem(
onTap: () {},
text: context.loc.american,
imageUrl: AppImages.imgAmerican,
),
WCategoriesHeaderItem(
onTap: () {},
text: context.loc.grocery,
imageUrl: AppImages.imgGrocery,
Column(
children: [
15.verticalSpace,
Row(
spacing: 12,
children: [
WCategoriesHeaderItem(
onTap: () {},
text: context.loc.american,
imageUrl: AppImages.imgAmerican,
),
WCategoriesHeaderItem(
onTap: () {},
text: context.loc.grocery,
imageUrl: AppImages.imgGrocery,
),
],
),
8.verticalSpace,
GridView.builder(
shrinkWrap: true,
itemCount: _titles.length,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
mainAxisSpacing: 10,
mainAxisExtent: 118,
// childAspectRatio: 78 / 100,
),
itemBuilder: (context, index) => WCategoryItem(
onTap: () {
if (index == 3) {
CategoriesPage().show(context);
}
},
imgUrl: index != 3 ? _images[index] : null,
text: _titles[index],
child: index == 3
? SizedBox(
height: 55,
width: 55,
child: Icon(Icons.more_horiz),
)
: null,
),
),
],
).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(),
],
),
8.verticalSpace,
GridView.builder(
shrinkWrap: true,
itemCount: _titles.length,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
mainAxisSpacing: 10,
childAspectRatio: 78 / 100,
),
itemBuilder: (context, index) => WCategoryItem(
onTap: () {
if (index == 3) {
CategoriesPage().show(context);
}
},
imgUrl: index != 3 ? _images[index] : null,
text: _titles[index],
child: index == 3
? SizedBox(height: 55, width: 55, child: Icon(Icons.more_horiz))
: null,
),
),
8.verticalSpace,
WDivider()
],
).paddingSymmetric(horizontal: 15);
);
},
);
}
}

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),
),
);
}
}