Initial commit

This commit is contained in:
jahongireshonqulov
2025-10-18 09:40:06 +05:00
commit 1bf3e41abe
352 changed files with 16315 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../router/app_routes.dart';
import '../../../data/models/comment_request.dart';
import '../../../domain/usecase/comment_usecase.dart';
part 'comment_event.dart';
part 'comment_state.dart';
class CommentBloc extends Bloc<CommentEvent, CommentState> {
CommentBloc(this.commentUseCase)
: super(const CommentState(rating: 0, isLoading: false)) {
on<RatingChangedEvent>(_ratingChange);
on<CommentEnterEvent>(_commentEnter);
on<SubmitEvent>(_commentSubmit);
}
final CommentUseCase commentUseCase;
void _ratingChange(RatingChangedEvent event, Emitter<CommentState> emit) {
emit(state.copyWith(rating: event.rating));
}
void _commentEnter(CommentEnterEvent event, Emitter<CommentState> emit) {
emit(state.copyWith(comment: event.comment));
}
Future<void> _commentSubmit(
SubmitEvent event,
Emitter<CommentState> emit,
) async {
emit(state.copyWith(isLoading: true));
final response = await commentUseCase(
CommentRequest(
orderNumber: event.orderId,
rate: state.rating.toInt(),
message: state.comment,
),
);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
emit(state.copyWith(isLoading: false));
Navigator.pop(rootNavigatorKey.currentContext!);
},
);
}
}

View File

@@ -0,0 +1,32 @@
part of 'comment_bloc.dart';
sealed class CommentEvent extends Equatable {
const CommentEvent();
}
final class RatingChangedEvent extends CommentEvent {
final double rating;
const RatingChangedEvent({required this.rating});
@override
List<Object?> get props => [rating];
}
final class CommentEnterEvent extends CommentEvent {
final String comment;
const CommentEnterEvent({required this.comment});
@override
List<Object?> get props => [comment];
}
final class SubmitEvent extends CommentEvent {
const SubmitEvent({required this.orderId});
final String orderId;
@override
List<Object?> get props => [orderId];
}

View File

@@ -0,0 +1,24 @@
part of 'comment_bloc.dart';
class CommentState extends Equatable {
const CommentState({
required this.rating,
required this.isLoading,
this.comment,
});
final double rating;
final bool isLoading;
final String? comment;
CommentState copyWith({double? rating, bool? isLoading, String? comment}) {
return CommentState(
rating: rating ?? this.rating,
isLoading: isLoading ?? this.isLoading,
comment: comment ?? this.comment,
);
}
@override
List<Object?> get props => [rating, isLoading, comment];
}

View File

@@ -0,0 +1,127 @@
import 'package:cargocalculaterapp/constants/constants.dart';
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/usecase/usecase.dart';
import '../../data/models/banner_response.dart';
import '../../domain/usecase/banners_usecase.dart';
import '../../domain/usecase/orders_list_usecase.dart';
part 'home_event.dart';
part 'home_state.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc(this.ordersListUseCase, this.bannersUseCase)
: super(
const HomeState(
paginationLoading: false,
isLoading: false,
page: 1,
selectedStatus: AppConst.all,
),
) {
on<GetAllOrdersListEvent>(_getAllOrders);
on<GetOrdersEvent>(_getOrders);
on<OrderStatusEvent>(_changeOrderStatus);
on<PaginationEvent>(_paginationLoading);
on<GetBannersEvent>(_getBanner);
on<RefreshEvent>(_refresh);
}
final OrdersListUseCase ordersListUseCase;
final BannersUseCase bannersUseCase;
final Map<String, dynamic> productsRequest = {};
void _getAllOrders(GetAllOrdersListEvent event, Emitter<HomeState> emit) {
productsRequest.remove(AppKeys.status);
productsRequest[AppKeys.page] = 1;
productsRequest[AppKeys.limit] = AppConst.limit;
add(const GetOrdersEvent());
}
Future<void> _getOrders(GetOrdersEvent event, Emitter<HomeState> emit) async {
emit(state.copyWith(isLoading: event.isLoading));
final response = await ordersListUseCase(productsRequest);
response.fold(
(l) {
emit(state.copyWith(isLoading: false, paginationLoading: false));
},
(r) {
emit(
state.copyWith(
isLoading: false,
paginationLoading: false,
ordersList: r,
page: productsRequest[AppKeys.page],
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
),
);
},
);
}
void _changeOrderStatus(OrderStatusEvent event, Emitter<HomeState> emit) {
if (event.status == state.selectedStatus) {
return;
}
emit(state.copyWith(selectedStatus: event.status));
productsRequest[AppKeys.page] = 1;
if (event.status == AppConst.all) {
productsRequest.remove(AppConst.status);
} else {
productsRequest[AppConst.status] = event.status;
}
add(const GetOrdersEvent());
}
Future<void> _paginationLoading(
PaginationEvent event,
Emitter<HomeState> emit,
) async {
productsRequest[AppKeys.page] = productsRequest[AppKeys.page] + 1;
emit(
state.copyWith(
paginationLoading: true,
page: productsRequest[AppKeys.page],
),
);
final response = await ordersListUseCase(productsRequest);
response.fold(
(l) {
emit(state.copyWith(isLoading: false, paginationLoading: false));
},
(r) {
emit(
state.copyWith(
isLoading: false,
paginationLoading: false,
ordersList: OrdersListResponse(
totalCount: r.totalCount,
data: state.ordersList?.data?..addAll(r.data ?? []),
),
page: productsRequest[AppKeys.page],
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
),
);
},
);
}
Future<void> _getBanner(
GetBannersEvent event,
Emitter<HomeState> emit,
) async {
final response = await bannersUseCase(const NoParams());
response.fold((l) {}, (r) {
emit(state.copyWith(banners: r));
});
}
void _refresh(RefreshEvent event, Emitter<HomeState> emit) {
productsRequest[AppKeys.page] = 1;
add(const GetOrdersEvent(isLoading: false));
add(const GetBannersEvent());
}
}

View File

@@ -0,0 +1,51 @@
part of 'home_bloc.dart';
sealed class HomeEvent extends Equatable {
const HomeEvent();
}
final class GetAllOrdersListEvent extends HomeEvent {
const GetAllOrdersListEvent();
@override
List<Object?> get props => [];
}
final class GetOrdersEvent extends HomeEvent {
const GetOrdersEvent({this.isLoading = true});
final bool isLoading;
@override
List<Object?> get props => [isLoading];
}
final class OrderStatusEvent extends HomeEvent {
final String status;
const OrderStatusEvent({required this.status});
@override
List<Object?> get props => [status];
}
final class PaginationEvent extends HomeEvent {
const PaginationEvent();
@override
List<Object?> get props => [];
}
final class GetBannersEvent extends HomeEvent {
const GetBannersEvent();
@override
List<Object?> get props => [];
}
final class RefreshEvent extends HomeEvent {
const RefreshEvent();
@override
List<Object?> get props => [];
}

View File

@@ -0,0 +1,52 @@
part of 'home_bloc.dart';
class HomeState extends Equatable {
const HomeState({
required this.isLoading,
required this.paginationLoading,
required this.page,
this.ordersList,
this.pageCount,
this.banners,
required this.selectedStatus,
});
final bool isLoading;
final bool paginationLoading;
final int page;
final int? pageCount;
final OrdersListResponse? ordersList;
final String selectedStatus;
final List<BannerResponse>? banners;
HomeState copyWith({
bool? isLoading,
bool? paginationLoading,
int? page,
int? pageCount,
OrdersListResponse? ordersList,
String? selectedStatus,
List<BannerResponse>? banners,
}) {
return HomeState(
isLoading: isLoading ?? this.isLoading,
page: page ?? this.page,
pageCount: pageCount ?? this.pageCount,
paginationLoading: paginationLoading ?? this.paginationLoading,
ordersList: ordersList ?? this.ordersList,
selectedStatus: selectedStatus ?? this.selectedStatus,
banners: banners ?? this.banners,
);
}
@override
List<Object?> get props => [
isLoading,
paginationLoading,
page,
ordersList,
pageCount,
selectedStatus,
banners,
];
}

View File

@@ -0,0 +1,102 @@
import 'package:cargocalculaterapp/constants/constants.dart';
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../data/models/notification_response.dart';
import '../../../domain/usecase/notification_read_usecase.dart';
import '../../../domain/usecase/notification_usecase.dart';
part 'notification_event.dart';
part 'notification_state.dart';
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
NotificationBloc(this.notificationUseCase, this.notificationReadUseCase)
: super(
const NotificationState(
isLoading: false,
currentPage: 1,
paginationLoading: false,
),
) {
on<GetNotificationsEvent>(_onGetNotifications);
on<NotificationPaginationEvent>(_notificationPagination);
on<ReadNotificationsEvent>(_notificationRead);
}
final NotificationUseCase notificationUseCase;
final NotificationReadUseCase notificationReadUseCase;
final Map<String, dynamic> notificationRequest = {};
Future<void> _onGetNotifications(
GetNotificationsEvent event,
Emitter<NotificationState> emit,
) async {
emit(state.copyWith(isLoading: true));
notificationRequest[AppKeys.page] = 1;
notificationRequest[AppKeys.limit] = AppConst.limit;
final response = await notificationUseCase(notificationRequest);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
add(ReadNotificationsEvent(notifications: r.notifications ?? []));
emit(
state.copyWith(
isLoading: false,
currentPage: 1,
notifications: r.notifications,
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
),
);
},
);
}
Future<void> _notificationPagination(
NotificationPaginationEvent event,
Emitter<NotificationState> emit,
) async {
notificationRequest[AppKeys.page] = event.page;
emit(state.copyWith(paginationLoading: true, currentPage: event.page));
final response = await notificationUseCase(notificationRequest);
response.fold(
(l) {
emit(state.copyWith(paginationLoading: false));
},
(r) {
add(ReadNotificationsEvent(notifications: r.notifications ?? []));
final List<Notifications> notifications = [];
notifications.addAll(List.of(state.notifications ?? []));
notifications.addAll(List.of(r.notifications ?? []));
emit(
state.copyWith(
paginationLoading: false,
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
notifications: notifications,
),
);
},
);
}
Future<void> _notificationRead(
ReadNotificationsEvent event,
Emitter<NotificationState> emit,
) async {
final List<String> ids = [];
for (var element in event.notifications) {
if (!(element.isRead ?? false)) {
ids.add(element.id ?? "");
}
}
if (ids.isNotEmpty) {
await notificationReadUseCase(
ReadNotificationRequest(notificationIds: ids),
);
}
}
}

View File

@@ -0,0 +1,30 @@
part of 'notification_bloc.dart';
sealed class NotificationEvent extends Equatable {
const NotificationEvent();
}
final class GetNotificationsEvent extends NotificationEvent {
const GetNotificationsEvent();
@override
List<Object?> get props => [];
}
final class NotificationPaginationEvent extends NotificationEvent {
const NotificationPaginationEvent({required this.page});
final int page;
@override
List<Object?> get props => [page];
}
final class ReadNotificationsEvent extends NotificationEvent {
final List<Notifications> notifications;
const ReadNotificationsEvent({required this.notifications});
@override
List<Object?> get props => [notifications];
}

View File

@@ -0,0 +1,42 @@
part of 'notification_bloc.dart';
class NotificationState extends Equatable {
const NotificationState({
required this.isLoading,
required this.paginationLoading,
required this.currentPage,
this.pageCount,
this.notifications,
});
final bool isLoading;
final bool paginationLoading;
final int currentPage;
final int? pageCount;
final List<Notifications>? notifications;
NotificationState copyWith({
bool? isLoading,
bool? paginationLoading,
int? currentPage,
int? pageCount,
List<Notifications>? notifications,
}) {
return NotificationState(
isLoading: isLoading ?? this.isLoading,
paginationLoading: paginationLoading ?? this.paginationLoading,
currentPage: currentPage ?? this.currentPage,
pageCount: pageCount ?? this.pageCount,
notifications: notifications ?? this.notifications,
);
}
@override
List<Object?> get props => [
isLoading,
paginationLoading,
currentPage,
pageCount,
notifications,
];
}

View File

@@ -0,0 +1,34 @@
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../domain/usecase/order_single_usecase.dart';
part 'order_single_event.dart';
part 'order_single_state.dart';
class OrderSingleBloc extends Bloc<OrderSingleEvent, OrderSingleState> {
OrderSingleBloc(this.orderSingleUseCase)
: super(const OrderSingleState(isLoading: false)) {
on<GetOrderSingleEvent>(_getOrderInfo);
}
final OrderSingleUseCase orderSingleUseCase;
Future<void> _getOrderInfo(
GetOrderSingleEvent event,
Emitter<OrderSingleState> emit,
) async {
emit(state.copyWith(isLoading: true));
final response = await orderSingleUseCase(event.orderId);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
emit(state.copyWith(isLoading: false, orderSingleResponse: r));
},
);
}
}

View File

@@ -0,0 +1,14 @@
part of 'order_single_bloc.dart';
sealed class OrderSingleEvent extends Equatable {
const OrderSingleEvent();
}
class GetOrderSingleEvent extends OrderSingleEvent {
const GetOrderSingleEvent({required this.orderId});
final String orderId;
@override
List<Object?> get props => [orderId];
}

View File

@@ -0,0 +1,21 @@
part of 'order_single_bloc.dart';
class OrderSingleState extends Equatable {
const OrderSingleState({required this.isLoading, this.orderSingleResponse});
final bool isLoading;
final OrderSingleResponse? orderSingleResponse;
OrderSingleState copyWith({
bool? isLoading,
OrderSingleResponse? orderSingleResponse,
}) {
return OrderSingleState(
isLoading: isLoading ?? this.isLoading,
orderSingleResponse: orderSingleResponse ?? this.orderSingleResponse,
);
}
@override
List<Object?> get props => [isLoading, orderSingleResponse];
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
mixin BannerMixin {
late PageController pageController;
void initController() {
pageController = PageController(viewportFraction: 0.94);
}
void disposeController() {
pageController.dispose();
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/cupertino.dart';
mixin CommentMixin {
late TextEditingController commentController;
void initControllers() {
commentController = TextEditingController();
}
void disposeControllers() {
commentController.dispose();
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
mixin HomeMixin {
late ScrollController scrollController;
void initControllers() {
scrollController = ScrollController();
}
void disposeControllers() {
scrollController.dispose();
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/cupertino.dart';
mixin ImagePageMixin {
late PageController pageController;
void initController(int initialPage) {
pageController = PageController(initialPage: initialPage + 1);
}
void disposeController() {
pageController.dispose();
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
mixin NotificationMixin {
late ScrollController scrollController;
void initControllers() {
scrollController = ScrollController();
}
void disposeControllers() {
scrollController.dispose();
}
}

View File

@@ -0,0 +1,5 @@
class CommentArgument {
final String orderId;
CommentArgument({required this.orderId});
}

View File

@@ -0,0 +1,6 @@
class ImagePageArgument {
final List<String>? images;
final int? currentPage;
ImagePageArgument({this.images, this.currentPage});
}

View File

@@ -0,0 +1,6 @@
class OrderInfoArgument{
final String orderId;
final String orderNumber;
OrderInfoArgument({required this.orderId, required this.orderNumber});
}

View File

@@ -0,0 +1,97 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../core/widgets/rating/rating_bar_widget.dart';
import '../../../../../core/widgets/text_filds/custom_text_field_name.dart';
import '../../bloc/comment/comment_bloc.dart';
import '../../mixin/comment_mixin.dart';
import '../arguments/comment_argument.dart';
class CommentPage extends StatefulWidget {
const CommentPage({super.key, required this.argument});
final CommentArgument? argument;
@override
State<CommentPage> createState() => _CommentPageState();
}
class _CommentPageState extends State<CommentPage> with CommentMixin {
@override
void initState() {
initControllers();
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<CommentBloc, CommentState>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(title: Text(AppLocalization.current.left_comment)),
body: ModalProgressHUD(
inAsyncCall: state.isLoading,
child: ListView(
padding: AppUtils.kPaddingAll16,
children: [
Text(
AppLocalization.current.comment_desc,
style: context.text.authDesc,
),
AppUtils.kBoxHeight16,
RatingBarWidget(
rating: state.rating,
separator: AppUtils.kBoxWith12,
iconsSize: 24,
onRatingChanged: (rating) {
context.read<CommentBloc>().add(
RatingChangedEvent(rating: rating),
);
},
),
AppUtils.kBoxHeight24,
CustomTextFieldName(
name: AppLocalization.current.comment,
hint: AppLocalization.current.comment_text,
minLines: 4,
controller: commentController,
onchange: (comment) {
context.read<CommentBloc>().add(
CommentEnterEvent(comment: comment),
);
},
),
AppUtils.kBoxHeight16,
Padding(
padding: AppUtils.kPaddingHor34,
child: ElevatedButton(
onPressed:
state.rating > 0 && (state.comment?.isNotEmpty ?? false)
? () {
context.read<CommentBloc>().add(
SubmitEvent(
orderId: widget.argument?.orderId ?? "",
),
);
}
: null,
child: Text(AppLocalization.current.send),
),
),
],
),
),
);
},
);
}
@override
void dispose() {
disposeControllers();
super.dispose();
}
}

View File

@@ -0,0 +1,139 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/local_source/local_source.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/core/widgets/loading/custom_loading.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/banners_widget.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/empty_order_widget.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/order_list_shimmer.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/orders_list_widget.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/status_list_widget.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:cargocalculaterapp/router/name_routes.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../injector_container.dart';
import '../bloc/home_bloc.dart';
import '../mixin/home_mixin.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with HomeMixin {
@override
void initState() {
context.read<HomeBloc>().add(const GetAllOrdersListEvent());
context.read<HomeBloc>().add(const GetBannersEvent());
initControllers();
_scrollListener();
super.initState();
}
void _scrollListener() {
scrollController.addListener(() {
final state = context.read<HomeBloc>().state;
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200 &&
!state.paginationLoading &&
state.page <= (state.pageCount ?? 1)) {
context.read<HomeBloc>().add(const PaginationEvent());
}
});
}
@override
Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return Scaffold(
backgroundColor: context.color.grayBackground,
appBar: AppBar(
title: Row(
children: [
GestureDetector(
onTap: () {
Clipboard.setData(
ClipboardData(text: sl<LocalSource>().getUCode()),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalization.current.text_copied),
),
);
},
child: Text(
sl<LocalSource>().getUCode(),
style: context.text.secondaryText14,
),
),
const Spacer(),
Text(AppLocalization.current.orders),
const Spacer(),
],
),
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
Navigator.pushNamed(context, Routes.notification);
},
icon: SvgPicture.asset(
"assets/svg/ic_notification.svg",
colorFilter: ColorFilter.mode(
context.color.textColor,
BlendMode.srcIn,
),
),
),
AppUtils.kBoxWith12,
],
),
body: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
slivers: [
StatusListWidget(selectedStatus: state.selectedStatus),
CupertinoSliverRefreshControl(
onRefresh: () async {
context.read<HomeBloc>().add(const RefreshEvent());
await Future<void>.delayed(
const Duration(milliseconds: 1500),
);
},
),
if (state.banners?.isNotEmpty ?? false)
BannersWidget(banners: state.banners),
state.isLoading
? const OrderListShimmer()
: (state.ordersList?.data?.length ?? 0) > 0
? OrdersListWidget(data: state.ordersList?.data)
: const EmptyOrderWidget(),
if (state.paginationLoading)
const SliverPadding(
padding: AppUtils.kPaddingAll24,
sliver: SliverToBoxAdapter(
child: Center(child: CustomLoadingWidget()),
),
),
],
),
);
},
);
}
@override
void dispose() {
disposeControllers();
scrollController.removeListener(_scrollListener);
super.dispose();
}
}

View File

@@ -0,0 +1,46 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import '../../../../../constants/constants.dart';
import '../../mixin/image_page_mixin.dart';
import '../arguments/image_page_argument.dart';
class ImagePage extends StatefulWidget {
const ImagePage({super.key, required this.argument});
final ImagePageArgument? argument;
@override
State<ImagePage> createState() => _ImagePageState();
}
class _ImagePageState extends State<ImagePage> with ImagePageMixin {
@override
void initState() {
initController(widget.argument?.currentPage ?? 0);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: PageView.builder(
controller: pageController,
itemCount: widget.argument?.images?.length ?? 0,
itemBuilder: (_, index) => InteractiveViewer(
scaleEnabled: true,
panEnabled: true,
constrained: true,
maxScale: 14,
clipBehavior: Clip.antiAlias,
child: CachedNetworkImage(
imageUrl: "${Constants.baseUrl}${widget.argument?.images?[index]}",
height: double.infinity,
width: double.infinity,
),
),
),
);
}
}

View File

@@ -0,0 +1,120 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../../core/widgets/loading/custom_loading.dart';
import '../../bloc/notification/notification_bloc.dart';
import '../../mixin/notification_mixin.dart';
class NotificationPage extends StatefulWidget {
const NotificationPage({super.key});
@override
State<NotificationPage> createState() => _NotificationPageState();
}
class _NotificationPageState extends State<NotificationPage>
with NotificationMixin {
@override
void initState() {
context.read<NotificationBloc>().add(const GetNotificationsEvent());
initControllers();
super.initState();
_scrollListener();
}
void _scrollListener() {
scrollController.addListener(() {
final state = context.read<NotificationBloc>().state;
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200 &&
!state.paginationLoading &&
state.currentPage <= (state.pageCount ?? 1)) {
context.read<NotificationBloc>().add(
NotificationPaginationEvent(page: state.currentPage + 1),
);
}
});
}
@override
Widget build(BuildContext context) {
return BlocBuilder<NotificationBloc, NotificationState>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalization.current.notification),
elevation: 0.2,
),
body: ModalProgressHUD(
inAsyncCall: state.paginationLoading,
child: CustomScrollView(
controller: scrollController,
slivers: [
SliverPadding(
padding: AppUtils.kPaddingAll16,
sliver: SliverList.separated(
itemBuilder: (context, index) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Ink(
width: 36,
height: 36,
decoration: BoxDecoration(
color: context.color.iconBackground,
borderRadius: AppUtils.kBorderRadius24,
),
padding: AppUtils.kPaddingAll8,
child: SvgPicture.asset(
"assets/svg/ic_check_box.svg",
),
),
AppUtils.kBoxWith8,
Expanded(
child: Text(
Functions.getTranslatedNameModel(
state.notifications?[index].text,
context,
),
style: context.text.authDesc,
),
),
],
),
separatorBuilder: (_, _) => Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Divider(
thickness: 1,
height: 1,
color: context.color.lightBorder,
),
),
itemCount: state.notifications?.length ?? 0,
),
),
if (state.paginationLoading)
const SliverPadding(
padding: AppUtils.kPaddingAll24,
sliver: SliverToBoxAdapter(
child: Center(child: CustomLoadingWidget()),
),
),
],
),
),
);
},
);
}
@override
void dispose() {
disposeControllers();
super.dispose();
}
}

View File

@@ -0,0 +1,390 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/core/widgets/loading/custom_loading.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/arguments/image_page_argument.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/delivery_info_widget.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/order_info_widget.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/status_item_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import '../../../../../constants/constants.dart';
import '../../../../../core/functions/base_finctions.dart';
import '../../../../../generated/l10n.dart';
import '../../../../../router/name_routes.dart';
import '../../bloc/order_single/order_single_bloc.dart';
import '../arguments/comment_argument.dart';
import '../arguments/order_info_argument.dart';
class OrderInfoPage extends StatefulWidget {
const OrderInfoPage({super.key, required this.argument});
final OrderInfoArgument? argument;
@override
State<OrderInfoPage> createState() => _OrderInfoPageState();
}
class _OrderInfoPageState extends State<OrderInfoPage> {
@override
void initState() {
context.read<OrderSingleBloc>().add(
GetOrderSingleEvent(orderId: widget.argument?.orderId ?? ""),
);
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<OrderSingleBloc, OrderSingleState>(
builder: (context, state) {
final status = _checkStatus(
state.orderSingleResponse?.status?.code ?? "",
);
return Scaffold(
backgroundColor: context.color.grayBackground,
appBar: AppBar(title: Text("#${widget.argument?.orderNumber ?? ""}")),
body: state.isLoading
? const Center(child: CustomLoadingWidget())
: ListView(
padding: AppUtils.kPaddingVer24Hor16,
children: [
Container(
padding: AppUtils.kPaddingAll16,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
AppLocalization.current.order_delivery_info,
style: context.text.orderTitle,
),
AppUtils.kBoxHeight16,
DeliveryInfoWidget(
title: AppLocalization.current.order_number,
name: state.orderSingleResponse?.orderNumber ?? "",
icon: "assets/svg/ic_box_gray.svg",
),
DeliveryInfoWidget(
title: AppLocalization.current.warehouse,
name:
Localizations.localeOf(context).languageCode ==
"uz"
? (state.orderSingleResponse?.wharehouseUz ??
"")
: Localizations.localeOf(
context,
).languageCode ==
"ru"
? (state.orderSingleResponse?.wharehouseRu ??
"")
: (state.orderSingleResponse?.wharehouseZh ??
""),
icon: "assets/svg/ic_building.svg",
),
DeliveryInfoWidget(
title: AppLocalization.current.delivery_price,
name:
state.orderSingleResponse?.deliveryPrice ?? "",
icon: "assets/svg/ic_chek.svg",
),
DeliveryInfoWidget(
title:
AppLocalization.current.delivery_time_estimated,
name: DateFormat('dd-MM-yyyy').format(
DateTime.tryParse(
state.orderSingleResponse?.arrivalDate ??
"",
) ??
DateTime.now(),
),
icon: "assets/svg/ic_calendar.svg",
),
Divider(
height: 1,
thickness: 1,
color: Colors.black.withAlpha(20),
),
AppUtils.kBoxHeight16,
StatusItemWidget(
isCompleted: status >= 1,
dividerCompleted: status > 1,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.waiting],
context,
),
number: "1",
),
StatusItemWidget(
isCompleted: status >= 2,
dividerCompleted: status > 2,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.partReceived],
context,
),
number: "2",
),
StatusItemWidget(
isCompleted: status >= 3,
dividerCompleted: status > 3,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.fullReceived],
context,
),
number: "3",
),
StatusItemWidget(
isCompleted: status >= 4,
dividerCompleted: status > 4,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.shipped],
context,
),
number: "4",
),
StatusItemWidget(
dividerCompleted: status > 5,
isCompleted: status >= 5,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.leftChine],
context,
),
number: "5",
),
StatusItemWidget(
isCompleted: status >= 6,
dividerCompleted: status > 6,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.enteredUzbekistan],
context,
),
number: "6",
),
StatusItemWidget(
isCompleted: status >= 7,
dividerCompleted: status > 7,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.tashkentWarehouse],
context,
),
number: "7",
),
StatusItemWidget(
isCompleted: status >= 8,
dividerCompleted: status > 8,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.customsClearance],
context,
),
number: "8",
),
StatusItemWidget(
isCompleted: status >= 9,
dividerCompleted: status > 9,
title: Functions.getTranslatedItem(
AppConst.statusList[AppConst.delivered],
context,
),
showConnector: false,
number: "9",
),
],
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16),
padding: AppUtils.kPaddingAll16,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
AppLocalization.current.order_details,
style: context.text.orderTitle,
),
AppUtils.kBoxHeight12,
OrderInfoWidget(
title: AppLocalization.current.order_name,
name:
Localizations.localeOf(context).languageCode ==
"uz"
? (state.orderSingleResponse?.nameUz ?? "")
: Localizations.localeOf(
context,
).languageCode ==
"ru"
? (state.orderSingleResponse?.nameRu ?? "")
: (state.orderSingleResponse?.nameZh ?? ""),
),
OrderInfoWidget(
title: AppLocalization.current.weight,
name: state.orderSingleResponse?.weight ?? "",
),
OrderInfoWidget(
title: AppLocalization.current.size,
name: state.orderSingleResponse?.m3 ?? "",
),
OrderInfoWidget(
title: AppLocalization.current.average_weight,
name:
state.orderSingleResponse?.averageWeightKg ??
"",
),
AppUtils.kBoxHeight8,
if ((state.orderSingleResponse?.images?.length ?? 0) >
0)
Text(
AppLocalization.current.order_photo,
style: context.text.orderTitle,
),
if ((state.orderSingleResponse?.images?.length ?? 0) >
0)
AppUtils.kBoxHeight8,
if ((state.orderSingleResponse?.images?.length ?? 0) >
0)
SizedBox(
height: 72,
child: ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => GestureDetector(
onTap: () {
Navigator.pushNamed(
context,
Routes.image,
arguments: ImagePageArgument(
currentPage: index,
images:
state.orderSingleResponse?.images,
),
);
},
child: ClipRRect(
borderRadius: AppUtils.kBorderRadius16,
child: CachedNetworkImage(
imageUrl:
"${Constants.baseUrl}${state.orderSingleResponse?.images?[index]}",
width: 72,
height: 72,
fit: BoxFit.cover,
errorWidget: (context, _, _) {
return const SizedBox(
width: 72,
height: 72,
child: Center(
child: Icon(Icons.warning),
),
);
},
),
),
),
separatorBuilder: (_, _) => AppUtils.kBoxWidth8,
itemCount:
state.orderSingleResponse?.images?.length ??
0,
),
),
],
),
),
if (status == 9)
Padding(
padding: AppUtils.kPaddingHor34,
child: InkWell(
onTap: () {
Navigator.pushNamed(
context,
Routes.comment,
arguments: CommentArgument(
orderId: widget.argument?.orderNumber ?? "",
),
);
},
borderRadius: AppUtils.kBorderRadius24,
child: Ink(
decoration: BoxDecoration(
color: context.color.scaffoldBackgroundColor,
borderRadius: AppUtils.kBorderRadius24,
border: Border.all(
color: context.color.primaryColor,
),
),
height: 56,
padding: AppUtils.kPaddingHor16,
child: Center(
child: Text(
AppLocalization.current.left_comment,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: context.color.textColor,
),
),
),
),
),
),
],
),
);
},
);
}
int _checkStatus(String status) {
switch (status) {
case AppConst.waiting:
{
return 1;
}
case AppConst.partReceived:
{
return 2;
}
case AppConst.fullReceived:
{
return 3;
}
case AppConst.shipped:
{
return 4;
}
case AppConst.leftChine:
{
return 5;
}
case AppConst.enteredUzbekistan:
{
return 6;
}
case AppConst.tashkentWarehouse:
{
return 7;
}
case AppConst.customsClearance:
{
return 8;
}
case AppConst.delivered:
{
return 9;
}
default:
{
return 1;
}
}
}
}

View File

@@ -0,0 +1,32 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class DeliveryInfoWidget extends StatelessWidget {
const DeliveryInfoWidget({
super.key,
required this.icon,
required this.title,
required this.name,
});
final String icon;
final String title;
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
children: [
SvgPicture.asset(icon, width: 12, height: 12),
AppUtils.kBoxWidth4,
Expanded(child: Text(title, style: context.text.orderListTitle)),
Text(name, style: context.text.profileCategory),
],
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:flutter/material.dart';
class OrderInfoWidget extends StatelessWidget {
const OrderInfoWidget({super.key, required this.title, required this.name});
final String title;
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(title, style: context.text.orderListTitle)),
AppUtils.kBoxWidth8,
Expanded(
child: Text(
name,
style: context.text.profileCategory,
textAlign: TextAlign.end,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,73 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:flutter/material.dart';
class StatusItemWidget extends StatelessWidget {
final bool showConnector;
final bool isCompleted;
final bool dividerCompleted;
final String title;
final String number;
const StatusItemWidget({
super.key,
this.showConnector = true,
this.dividerCompleted = false,
required this.isCompleted,
required this.title,
required this.number,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Container(
margin: const EdgeInsets.only(top: 4, bottom: 4, right: 12),
width: 32,
height: 32,
decoration: BoxDecoration(
color:
isCompleted
? context.color.primaryColor
: context.color.statusIconBackground,
border: Border.all(color: context.color.primaryColor),
borderRadius: AppUtils.kBorderRadius48,
),
padding: AppUtils.kPaddingAll6,
child:
isCompleted
? const Center(
child: Icon(
Icons.check_rounded,
color: Colors.white,
size: 16,
),
)
: Center(
child: Text(number, style: context.text.statusNumber),
),
),
Text(
title,
textAlign: TextAlign.start,
style: context.text.statusDesc,
),
],
),
if (showConnector)
Container(
margin: const EdgeInsets.only(left: 15),
height: 16,
width: 2,
color: context.color.primaryColor,
),
],
);
}
}

View File

@@ -0,0 +1,101 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import '../../../../../constants/constants.dart';
import '../../../../../core/utils/app_utils.dart';
import '../../../data/models/banner_response.dart';
import '../../mixin/banner_mixin.dart';
class BannersWidget extends StatefulWidget {
const BannersWidget({super.key, required this.banners});
final List<BannerResponse>? banners;
@override
State<BannersWidget> createState() => _BannersWidgetState();
}
class _BannersWidgetState extends State<BannersWidget> with BannerMixin {
Timer? timer;
@override
void initState() {
initController();
if ((widget.banners?.length ?? 0) > 1) {
animationTimer();
}
super.initState();
}
@override
void didUpdateWidget(covariant BannersWidget oldWidget) {
if ((widget.banners?.length ?? 0) > 1) {
animationTimer();
} else if ((widget.banners?.length ?? 0) == 1) {
if (timer?.isActive ?? false) {
timer?.cancel();
}
}
super.didUpdateWidget(oldWidget);
}
void animationTimer() {
if (timer?.isActive ?? false) {
timer?.cancel();
}
timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) async {
pageController.nextPage(
duration: const Duration(milliseconds: 800),
curve: Curves.fastOutSlowIn,
);
});
}
@override
void dispose() {
timer?.cancel();
pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(bottom: 16),
child: SizedBox(
height: 180,
width: double.infinity,
child: PageView.builder(
controller: pageController,
itemCount: (widget.banners?.length ?? 0) > 1
? null
: widget.banners?.length,
scrollDirection: Axis.horizontal,
physics: widget.banners?.length != 1
? null
: const NeverScrollableScrollPhysics(),
itemBuilder: (_, index) => GestureDetector(
onTap: () {},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ClipRRect(
borderRadius: AppUtils.kBorderRadius16,
child: CachedNetworkImage(
imageUrl:
"${Constants.baseUrl}${widget.banners?[index % (widget.banners?.length ?? 1)].image}",
height: 180,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,55 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import '../../../../../core/utils/app_utils.dart';
class EmptyOrderWidget extends StatelessWidget {
const EmptyOrderWidget({super.key});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
width: double.infinity,
// padding: const EdgeInsets.symmetric(vertical: 42, horizontal: 32),
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: AppUtils.kBorderRadiusTop16,
child: SvgPicture.asset(
"assets/svg/ic_empty_box.svg",
width: MediaQuery.sizeOf(context).width - 34,
),
),
Text(
AppLocalization.current.no_order,
style: context.text.titleBig,
textAlign: TextAlign.center,
),
AppUtils.kBoxHeight12,
Padding(
padding: AppUtils.kPaddingHor24,
child: Text(
AppLocalization.current.no_order_desc,
style: context.text.profileCategory.copyWith(
color: context.color.lightSecondary,
),
textAlign: TextAlign.center,
),
),
AppUtils.kBoxHeight48,
],
),
),
);
}
}

View File

@@ -0,0 +1,66 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/widgets/shimmer/shimmer_widget.dart';
import 'package:flutter/material.dart';
import '../../../../../core/utils/app_utils.dart';
class OrderListShimmer extends StatelessWidget {
const OrderListShimmer({super.key});
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: AppUtils.kPaddingAll16,
sliver: SliverList.separated(
itemBuilder: (context, index) {
return Ink(
padding: AppUtils.kPaddingAll16,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius12,
color: context.color.scaffoldBackgroundColor,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShimmerWidget(width: 60, height: 20),
ShimmerWidget(width: 100, height: 20),
],
),
AppUtils.kBoxHeight8,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShimmerWidget(width: 40, height: 20),
ShimmerWidget(width: 70, height: 20),
],
),
AppUtils.kBoxHeight4,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShimmerWidget(width: 100, height: 20),
ShimmerWidget(width: 60, height: 20),
],
),
AppUtils.kBoxHeight4,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShimmerWidget(width: 80, height: 20),
ShimmerWidget(width: 140, height: 20),
],
),
],
),
);
},
separatorBuilder: (_, __) => AppUtils.kBoxHeight12,
itemCount: 6,
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:flutter/material.dart';
class OrderTextWidget extends StatelessWidget {
const OrderTextWidget({
super.key,
required this.title,
required this.name,
this.crossAxisAlignment = CrossAxisAlignment.start,
});
final String title;
final String name;
final CrossAxisAlignment crossAxisAlignment;
@override
Widget build(BuildContext context) {
return Expanded(
child: Column(
crossAxisAlignment: crossAxisAlignment,
mainAxisSize: MainAxisSize.min,
children: [
Text(title, style: context.text.orderListTitle),
AppUtils.kBoxHeight6,
Text(name, style: context.text.categoryName),
],
),
);
}
}

View File

@@ -0,0 +1,109 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/status_widget.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:cargocalculaterapp/router/name_routes.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../../data/models/orders_list_response.dart';
import '../arguments/order_info_argument.dart';
import 'order_text_widget.dart';
class OrdersListWidget extends StatelessWidget {
const OrdersListWidget({super.key, required this.data});
final List<Data>? data;
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
sliver: SliverList.separated(
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.pushNamed(
context,
Routes.orderInfo,
arguments: OrderInfoArgument(
orderId: data?[index].id ?? "",
orderNumber: data?[index].orderNumber ?? "",
),
);
},
borderRadius: AppUtils.kBorderRadius16,
child: Ink(
padding: AppUtils.kPaddingAll16,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
OrderTextWidget(
title: AppLocalization.current.order_number,
name: data?[index].orderNumber ?? "",
),
StatusWidget(status: data?[index].status?.code ?? ""),
],
),
AppUtils.kBoxHeight16,
Row(
children: [
OrderTextWidget(
title: AppLocalization.current.warehouse,
name:
Localizations.localeOf(context).languageCode == "uz"
? (data?[index].wharehouseUz ?? "")
: Localizations.localeOf(context).languageCode ==
"ru"
? (data?[index].wharehouseRu ?? "")
: (data?[index].wharehouseZh ?? ""),
),
OrderTextWidget(
crossAxisAlignment: CrossAxisAlignment.end,
title: AppLocalization.current.order_name,
name:
Localizations.localeOf(context).languageCode == "uz"
? (data?[index].nameUz ?? "")
: Localizations.localeOf(context).languageCode ==
"ru"
? (data?[index].nameRu ?? "")
: (data?[index].nameZh ?? ""),
),
],
),
AppUtils.kBoxHeight16,
Row(
children: [
OrderTextWidget(
title: AppLocalization.current.delivery_price,
name: (data?[index].deliveryPrice ?? ""),
),
OrderTextWidget(
crossAxisAlignment: CrossAxisAlignment.end,
title: AppLocalization.current.delivery_time,
name: DateFormat('dd-MM-yyyy').format(
DateTime.tryParse(data?[index].arrivalDate ?? "") ??
DateTime.now(),
),
),
],
),
],
),
),
);
},
separatorBuilder: (_, __) => AppUtils.kBoxHeight12,
itemCount: data?.length ?? 0,
),
);
}
}

View File

@@ -0,0 +1,98 @@
import 'package:cargocalculaterapp/constants/constants.dart';
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/features/home/presentation/bloc/home_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class StatusListWidget extends StatelessWidget {
const StatusListWidget({super.key, required this.selectedStatus});
final String selectedStatus;
@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: _StatusHeaderDelegate(selectedStatus: selectedStatus),
);
}
}
class _StatusHeaderDelegate extends SliverPersistentHeaderDelegate {
final String selectedStatus;
_StatusHeaderDelegate({required this.selectedStatus});
@override
double get minExtent => 72;
@override
double get maxExtent => 72;
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return Material(
color: context.color.grayBackground,
child: SizedBox(
height: 72,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
final key = AppConst.statusList.keys.toList()[index];
return InkWell(
borderRadius: AppUtils.kBorderRadius16,
onTap: () {
context.read<HomeBloc>().add(OrderStatusEvent(status: key));
},
child: Ink(
padding: AppUtils.kPaddingHor16,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color:
key == selectedStatus
? context.color.primaryColor
: context.color.statusBackground,
border: Border.all(
color:
key == selectedStatus
? context.color.primaryColor
: context.color.lightSecondary,
),
),
child: Center(
child: Text(
Functions.getTranslatedItem(
AppConst.statusList.values.toList()[index],
context,
),
textAlign: TextAlign.center,
style:
key == selectedStatus
? context.text.statusText
: context.text.statusText.copyWith(
color: context.color.textColor,
),
),
),
),
);
},
separatorBuilder: (_, __) => AppUtils.kBoxWidth16,
itemCount: AppConst.statusList.length,
),
),
);
}
@override
bool shouldRebuild(covariant _StatusHeaderDelegate oldDelegate) {
return true;
}
}

View File

@@ -0,0 +1,89 @@
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
import 'package:cargocalculaterapp/core/theme/app_text_styles.dart';
import 'package:cargocalculaterapp/core/theme/colors/app_colors.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:flutter/material.dart';
import '../../../../../constants/constants.dart';
class StatusWidget extends StatelessWidget {
const StatusWidget({super.key, required this.status});
final String status;
@override
Widget build(BuildContext context) {
switch (status) {
case AppConst.waiting:
{
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: const BoxDecoration(
borderRadius: AppUtils.kBorderRadius24,
color: ThemeColors.warningSurface,
),
child: Text(
Functions.getTranslatedItem(
AppConst.statusList[AppConst.waiting],
context,
),
style: AppTextStyles.status.copyWith(color: ThemeColors.warning),
),
);
}
case AppConst.shipped:
{
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: const BoxDecoration(
borderRadius: AppUtils.kBorderRadius24,
color: ThemeColors.green173Light,
),
child: Text(
Functions.getTranslatedItem(
AppConst.statusList[AppConst.shipped],
context,
),
style: AppTextStyles.status,
),
);
}
case AppConst.leftChine:
{
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: const BoxDecoration(
borderRadius: AppUtils.kBorderRadius24,
color: ThemeColors.green191Light,
),
child: Text(
Functions.getTranslatedItem(
AppConst.statusList[AppConst.leftChine],
context,
),
style: AppTextStyles.status.copyWith(
color: ThemeColors.green191text,
),
),
);
}
default:
{
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: const BoxDecoration(
borderRadius: AppUtils.kBorderRadius24,
color: ThemeColors.green173Light,
),
child: Text(
Functions.getTranslatedItem(
AppConst.statusList[status],
context,
),
style: AppTextStyles.status,
),
);
}
}
}
}