pagination addedr
This commit is contained in:
parent
7285b8f104
commit
0ad7d44642
|
|
@ -12,11 +12,19 @@ part 'order_state.dart';
|
|||
class OrderBloc extends Bloc<OrderEvent, OrderState> {
|
||||
final GetOrderUseCase _getOrdersUseCase;
|
||||
OrderBloc(this._getOrdersUseCase)
|
||||
: super(const OrderInitial(
|
||||
orders: [],
|
||||
params: FilterProductParams(),
|
||||
)) {
|
||||
: super(
|
||||
OrderInitial(
|
||||
orders: const [],
|
||||
params: const FilterProductParams(),
|
||||
metaData: PaginationMetaData(
|
||||
pageSize: 10,
|
||||
limit: 0,
|
||||
total: 0,
|
||||
),
|
||||
),
|
||||
) {
|
||||
on<GetOrders>(_onGetOrders);
|
||||
on<GetMoreOrders>(_onLoadMoreOrders);
|
||||
}
|
||||
|
||||
FutureOr<void> _onGetOrders(
|
||||
|
|
@ -26,26 +34,73 @@ class OrderBloc extends Bloc<OrderEvent, OrderState> {
|
|||
try {
|
||||
emit(OrderLoading(
|
||||
orders: const [],
|
||||
metaData: state.metaData,
|
||||
params: event.params,
|
||||
));
|
||||
final result = await _getOrdersUseCase(event.params);
|
||||
result.fold(
|
||||
(failure) => emit(OrderError(
|
||||
orders: state.orders,
|
||||
metaData: state.metaData,
|
||||
failure: failure,
|
||||
params: event.params,
|
||||
)),
|
||||
(orders) => emit(OrderLoaded(
|
||||
orders: orders,
|
||||
(response) => emit(OrderLoaded(
|
||||
metaData: response.paginationMetaData,
|
||||
orders: response.orders,
|
||||
params: event.params,
|
||||
)),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(OrderError(
|
||||
orders: state.orders,
|
||||
metaData: state.metaData,
|
||||
failure: ExceptionFailure(),
|
||||
params: event.params,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _onLoadMoreOrders(GetMoreOrders event, Emitter<OrderState> emit) async {
|
||||
var state = this.state;
|
||||
var limit = state.metaData.limit;
|
||||
var total = state.metaData.total;
|
||||
var loadedProductsLength = state.orders.length;
|
||||
// check state and loaded products amount[loadedProductsLength] compare with
|
||||
// number of results total[total] results available in server
|
||||
if (state is OrderLoaded && (loadedProductsLength < total)) {
|
||||
try {
|
||||
emit(OrderLoading(
|
||||
orders: state.orders,
|
||||
metaData: state.metaData,
|
||||
params: state.params,
|
||||
));
|
||||
final result = await _getOrdersUseCase(FilterProductParams(limit: limit + 10));
|
||||
result.fold(
|
||||
(failure) => emit(OrderError(
|
||||
orders: state.orders,
|
||||
metaData: state.metaData,
|
||||
failure: failure,
|
||||
params: state.params,
|
||||
)),
|
||||
(response) {
|
||||
List<OrderEntity> products = state.orders;
|
||||
products.addAll(response.orders);
|
||||
emit(OrderLoaded(
|
||||
metaData: state.metaData,
|
||||
orders: products,
|
||||
params: state.params,
|
||||
));
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
emit(OrderError(
|
||||
orders: state.orders,
|
||||
metaData: state.metaData,
|
||||
failure: ExceptionFailure(),
|
||||
params: state.params,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,19 @@ part of 'order_bloc.dart';
|
|||
|
||||
abstract class OrderState extends Equatable {
|
||||
final List<OrderEntity> orders;
|
||||
final PaginationMetaData metaData;
|
||||
final FilterProductParams params;
|
||||
const OrderState({required this.orders, required this.params});
|
||||
const OrderState({
|
||||
required this.orders,
|
||||
required this.metaData,
|
||||
required this.params,
|
||||
});
|
||||
}
|
||||
|
||||
class OrderInitial extends OrderState {
|
||||
const OrderInitial({
|
||||
required super.orders,
|
||||
required super.metaData,
|
||||
required super.params,
|
||||
});
|
||||
@override
|
||||
|
|
@ -18,6 +24,7 @@ class OrderInitial extends OrderState {
|
|||
class OrderEmpty extends OrderState {
|
||||
const OrderEmpty({
|
||||
required super.orders,
|
||||
required super.metaData,
|
||||
required super.params,
|
||||
});
|
||||
@override
|
||||
|
|
@ -27,6 +34,7 @@ class OrderEmpty extends OrderState {
|
|||
class OrderLoading extends OrderState {
|
||||
const OrderLoading({
|
||||
required super.orders,
|
||||
required super.metaData,
|
||||
required super.params,
|
||||
});
|
||||
@override
|
||||
|
|
@ -36,6 +44,7 @@ class OrderLoading extends OrderState {
|
|||
class OrderLoaded extends OrderState {
|
||||
const OrderLoaded({
|
||||
required super.orders,
|
||||
required super.metaData,
|
||||
required super.params,
|
||||
});
|
||||
@override
|
||||
|
|
@ -46,6 +55,7 @@ class OrderError extends OrderState {
|
|||
final Failure failure;
|
||||
const OrderError({
|
||||
required super.orders,
|
||||
required super.metaData,
|
||||
required super.params,
|
||||
required this.failure,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../core/core.dart';
|
||||
|
|
@ -7,7 +5,7 @@ import '../../../domain/domain.dart';
|
|||
import '../../data.dart';
|
||||
|
||||
abstract class OrderRemoteDataSource {
|
||||
Future<List<OrderModel>> getOrders(FilterProductParams params, String token);
|
||||
Future<OrderResponseModel> getOrders(FilterProductParams params, String token);
|
||||
}
|
||||
|
||||
class OrderRemoteDataSourceImpl implements OrderRemoteDataSource {
|
||||
|
|
@ -15,9 +13,9 @@ class OrderRemoteDataSourceImpl implements OrderRemoteDataSource {
|
|||
OrderRemoteDataSourceImpl({required this.client});
|
||||
|
||||
@override
|
||||
Future<List<OrderModel>> getOrders(FilterProductParams params, String token) async {
|
||||
Future<OrderResponseModel> getOrders(FilterProductParams params, String token) async {
|
||||
final response = await client.get(
|
||||
Uri.parse('$baseUrl/Goods'),
|
||||
Uri.parse('$baseUrl/Goods?pageNumber=${params.offset}&pageSize=${params.limit}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': '*/*',
|
||||
|
|
@ -26,9 +24,7 @@ class OrderRemoteDataSourceImpl implements OrderRemoteDataSource {
|
|||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
List<dynamic> jsonData = json.decode(response.body);
|
||||
final list = jsonData.map((item) => OrderModel.fromJson(item)).toList();
|
||||
return list;
|
||||
return orderResponseModelFromJson(response.body);
|
||||
} else {
|
||||
throw ServerException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ export 'language.dart';
|
|||
export 'user/authentication_response_model.dart';
|
||||
export 'user/user_model.dart';
|
||||
export 'order/order_model.dart';
|
||||
export 'order/order_response_model.dart';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import '../../../domain/domain.dart';
|
||||
import 'order_model.dart';
|
||||
import 'pagination_data_model.dart';
|
||||
|
||||
OrderResponseModel orderResponseModelFromJson(String str) => OrderResponseModel.fromJson(json.decode(str));
|
||||
|
||||
// String orderResponseModelToJson(OrderResponseModel data) => json.encode(data.toJson());
|
||||
|
||||
class OrderResponseModel extends OrderResponse {
|
||||
OrderResponseModel({
|
||||
required PaginationMetaData meta,
|
||||
required List<OrderEntity> data,
|
||||
}) : super(orders: data, paginationMetaData: meta);
|
||||
|
||||
factory OrderResponseModel.fromJson(Map<String, dynamic> json) => OrderResponseModel(
|
||||
meta: PaginationMetaDataModel(
|
||||
page: json['PageNumber'],
|
||||
pageSize: json['PageSize'],
|
||||
total: json['TotalRecords'],
|
||||
),
|
||||
data: List<OrderModel>.from(json['Data'].map((x) => OrderModel.fromJson(x))),
|
||||
);
|
||||
|
||||
/* Map<String, dynamic> toJson() => {
|
||||
'meta': (paginationMetaData as PaginationMetaDataModel).toJson(),
|
||||
'Data': List<dynamic>.from((orders as List<OrderModel>).map((x) => x.toJson())),
|
||||
}; */
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:cargo/domain/entities/order/pagination_meta_data.dart';
|
||||
|
||||
class PaginationMetaDataModel extends PaginationMetaData {
|
||||
PaginationMetaDataModel({
|
||||
required int page,
|
||||
required super.pageSize,
|
||||
required super.total,
|
||||
}) : super(limit: page);
|
||||
|
||||
factory PaginationMetaDataModel.fromJson(Map<String, dynamic> json) => PaginationMetaDataModel(
|
||||
page: json['PageNumber'],
|
||||
pageSize: json['PageSize'],
|
||||
total: json['TotalRecords'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'PageNumber': limit,
|
||||
'PageSize': pageSize,
|
||||
'TotalRecords': total,
|
||||
};
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import 'package:dartz/dartz.dart';
|
|||
import '../../core/core.dart';
|
||||
import '../../domain/domain.dart';
|
||||
import '../data_sources/data_sources.dart';
|
||||
import '../models/order/order_response_model.dart';
|
||||
|
||||
class OrderRepositoryImpl extends OrderRepository {
|
||||
final OrderRemoteDataSource remoteDataSource;
|
||||
|
|
@ -16,7 +17,7 @@ class OrderRepositoryImpl extends OrderRepository {
|
|||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<OrderEntity>>> getOrders(FilterProductParams params) async {
|
||||
Future<Either<Failure, OrderResponseModel>> getOrders(FilterProductParams params) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure());
|
||||
}
|
||||
|
|
@ -27,8 +28,8 @@ class OrderRepositoryImpl extends OrderRepository {
|
|||
|
||||
try {
|
||||
final String token = await localDataSource.getToken();
|
||||
final orders = await remoteDataSource.getOrders(params, token);
|
||||
return Right(orders);
|
||||
final response = await remoteDataSource.getOrders(params, token);
|
||||
return Right(response);
|
||||
} on Failure catch (failure) {
|
||||
return Left(failure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
export 'user/user.dart';
|
||||
export 'order/order.dart';
|
||||
export 'order/filter_params_model.dart';
|
||||
export 'order/order_response.dart';
|
||||
export 'order/pagination_meta_data.dart';
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
class FilterProductParams {
|
||||
final int? limit;
|
||||
final int? pageSize;
|
||||
final int offset;
|
||||
final int limit;
|
||||
|
||||
const FilterProductParams({
|
||||
this.limit = 0,
|
||||
this.pageSize = 10,
|
||||
this.offset = 1,
|
||||
this.limit = 10,
|
||||
});
|
||||
|
||||
FilterProductParams copyWith({
|
||||
int? skip,
|
||||
int? offset,
|
||||
int? limit,
|
||||
int? pageSize,
|
||||
}) =>
|
||||
FilterProductParams(
|
||||
limit: skip ?? this.limit,
|
||||
pageSize: pageSize ?? this.pageSize,
|
||||
);
|
||||
}) {
|
||||
return FilterProductParams(
|
||||
offset: offset ?? this.offset,
|
||||
limit: limit ?? this.limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import 'pagination_meta_data.dart';
|
||||
import 'order.dart';
|
||||
|
||||
class OrderResponse {
|
||||
final List<OrderEntity> orders;
|
||||
final PaginationMetaData paginationMetaData;
|
||||
|
||||
OrderResponse({required this.orders, required this.paginationMetaData});
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
class PaginationMetaData {
|
||||
final int limit;
|
||||
final int pageSize;
|
||||
final int total;
|
||||
|
||||
PaginationMetaData({
|
||||
required this.limit,
|
||||
required this.pageSize,
|
||||
required this.total,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../data/models/order/order_response_model.dart';
|
||||
import '../domain.dart';
|
||||
|
||||
abstract class OrderRepository {
|
||||
Future<Either<Failure, List<OrderEntity>>> getOrders(FilterProductParams params);
|
||||
Future<Either<Failure, OrderResponseModel>> getOrders(FilterProductParams params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../core/core.dart';
|
||||
import '../../../data/models/order/order_response_model.dart';
|
||||
import '../../domain.dart';
|
||||
|
||||
class GetOrderUseCase implements UseCase<List<OrderEntity>, FilterProductParams> {
|
||||
class GetOrderUseCase implements UseCase<OrderResponseModel, FilterProductParams> {
|
||||
final OrderRepository repository;
|
||||
GetOrderUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<OrderEntity>>> call(FilterProductParams params) async {
|
||||
Future<Either<Failure, OrderResponseModel>> call(FilterProductParams params) async {
|
||||
return await repository.getOrders(params);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,79 +23,86 @@ class _OrdersScreenState extends State<OrdersScreen> {
|
|||
Widget build(BuildContext context) {
|
||||
App.init(context);
|
||||
|
||||
// Provide the OrderBloc
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.surface,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: OrderHeader(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: Space.all(1, 1),
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Sargytlarym',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
|
||||
context.read<OrderBloc>().add(const GetMoreOrders());
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: OrderHeader(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: Space.all(1, 1),
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Sargytlarym',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'öz ýüküňizi yzarlaň',
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
Text(
|
||||
'öz ýüküňizi yzarlaň',
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Use BlocBuilder to respond to state changes
|
||||
BlocBuilder<OrderBloc, OrderState>(
|
||||
builder: (context, state) {
|
||||
if (state is OrderLoading) {
|
||||
// Display a loading indicator while fetching orders
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
} else if (state is OrderError) {
|
||||
// Display an error message if there was a failure
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Failed to load orders: ${state.failure}',
|
||||
style: const TextStyle(color: Colors.red),
|
||||
BlocBuilder<OrderBloc, OrderState>(
|
||||
builder: (context, state) {
|
||||
if (state is OrderLoading && state.orders.isEmpty) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (state is OrderLoaded) {
|
||||
// Display the list of orders
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final order = state.orders[index];
|
||||
return OrderCard(order: order);
|
||||
},
|
||||
childCount: state.orders.length,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Default case (initial state)
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text('No orders available'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (state is OrderError && state.orders.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: RetryWidget(onRetry: () {
|
||||
context.read<OrderBloc>().add(GetOrders(state.params));
|
||||
}),
|
||||
),
|
||||
);
|
||||
} else if (state is OrderLoaded) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final order = state.orders[index];
|
||||
|
||||
if (index == state.orders.length - 1) {
|
||||
// Trigger loading more orders when reaching the bottom
|
||||
context.read<OrderBloc>().add(const GetMoreOrders());
|
||||
}
|
||||
|
||||
return OrderCard(order: order);
|
||||
},
|
||||
childCount: state.orders.length,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text('No orders available'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class RetryWidget extends StatelessWidget {
|
||||
final VoidCallback onRetry;
|
||||
final String message;
|
||||
|
||||
const RetryWidget({
|
||||
super.key,
|
||||
required this.onRetry,
|
||||
this.message = 'Something went wrong. Please try again.',
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: onRetry,
|
||||
child: const Text('Retry'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
export 'auth_error_dialog.dart';
|
||||
export 'bottom_navbar.dart';
|
||||
export 'button.dart';
|
||||
export 'dashed_line.dart';
|
||||
export 'error_dialog.dart';
|
||||
export 'info_card.dart';
|
||||
export 'lang_selection.dart';
|
||||
export 'location_card.dart';
|
||||
export 'order_card.dart';
|
||||
export 'order_header.dart';
|
||||
export 'retry_widget.dart';
|
||||
export 'successful_auth_dialog.dart';
|
||||
export 'vertical_line.dart';
|
||||
export 'dashed_line.dart';
|
||||
export 'lang_selection.dart';
|
||||
|
|
|
|||
Loading…
Reference in New Issue