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,9 @@
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
abstract class CalculatorRemoteDataSource {
Future<CalculatePriceResponse> priceCalculate(PriceCalculateRequest request);
Future<LeadCreateResponse> createLead(LeadCreateRequest request);
}

View File

@@ -0,0 +1,64 @@
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
import 'package:dio/dio.dart';
import '../../../../constants/constants.dart';
import '../../../../core/error/exceptions.dart';
import '../../../../core/local_source/local_source.dart';
import '../../../../injector_container.dart';
import '../model/lead_create_response.dart';
import '../model/lead_create_request.dart';
import 'calculator_remote_data_source.dart';
class CalculatorRemoteDataSourceImpl extends CalculatorRemoteDataSource {
final Dio dio;
CalculatorRemoteDataSourceImpl(this.dio);
@override
Future<CalculatePriceResponse> priceCalculate(
PriceCalculateRequest request,
) async {
try {
final Response response = await dio.post(
Constants.baseUrl + Urls.calculatePrice,
options:
DioConstants.options
..headers = {
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
},
data: request,
);
if (response.statusCode == 200 || response.statusCode == 201) {
return CalculatePriceResponse.fromJson(response.data);
}
throw ServerException.fromJson(response.data);
} on DioException catch (e) {
throw ServerException.fromJson(e.response?.data);
} on FormatException {
throw ServerException(message: Validations.someThingWentWrong);
}
}
@override
Future<LeadCreateResponse> createLead(LeadCreateRequest request) async {
try {
final Response response = await dio.post(
Constants.baseUrl + Urls.lead,
options:
DioConstants.options
..headers = {
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
},
data: request,
);
if (response.statusCode == 200 || response.statusCode == 201) {
return LeadCreateResponse.fromJson(response.data);
}
throw ServerException.fromJson(response.data);
} on DioException catch (e) {
throw ServerException.fromJson(e.response?.data);
} on FormatException {
throw ServerException(message: Validations.someThingWentWrong);
}
}
}

View File

@@ -0,0 +1,15 @@
class CalculatePriceResponse {
CalculatePriceResponse({this.price});
CalculatePriceResponse.fromJson(dynamic json) {
price = double.tryParse(json['price'].toString());
}
double? price;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['price'] = price;
return map;
}
}

View File

@@ -0,0 +1,73 @@
class LeadCreateRequest {
LeadCreateRequest({
this.warehouseCode,
this.wharehouseUz,
this.wharehouseRu,
this.wharehouseZh,
this.nameUz,
this.nameRu,
this.nameZh,
this.userUcode,
this.phoneNumber,
this.gmail,
this.weight,
this.price,
this.status,
this.averageWeightKg,
this.m3,
});
LeadCreateRequest.fromJson(dynamic json) {
warehouseCode = json['warehouse_code'];
wharehouseUz = json['wharehouseUz'];
wharehouseRu = json['wharehouseRu'];
wharehouseZh = json['wharehouseZh'];
nameUz = json['nameUz'];
nameRu = json['nameRu'];
nameZh = json['nameZh'];
userUcode = json['user_ucode'];
phoneNumber = json['phone_number'];
gmail = json['gmail'];
weight = json['weight'];
price = json['price'];
status = json['status'];
averageWeightKg = json['average_weight_kg'];
m3 = json['status'];
}
int? warehouseCode;
String? wharehouseUz;
String? wharehouseRu;
String? wharehouseZh;
String? nameUz;
String? nameRu;
String? nameZh;
String? userUcode;
String? phoneNumber;
String? gmail;
String? weight;
String? price;
String? status;
String? averageWeightKg;
String? m3;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['warehouse_code'] = warehouseCode;
map['wharehouseUz'] = wharehouseUz;
map['wharehouseRu'] = wharehouseRu;
map['wharehouseZh'] = wharehouseZh;
map['nameUz'] = nameUz;
map['nameRu'] = nameRu;
map['nameZh'] = nameZh;
map['user_ucode'] = userUcode;
map['phone_number'] = phoneNumber;
map['gmail'] = gmail;
map['weight'] = weight;
map['price'] = price;
map['status'] = status;
map['average_weight_kg'] = averageWeightKg;
map['m3'] = m3;
return map;
}
}

View File

@@ -0,0 +1,118 @@
class LeadCreateResponse {
LeadCreateResponse({
this.userUcode,
this.phoneNumber,
this.gmail,
this.weight,
this.status,
this.nameUz,
this.nameRu,
this.nameZh,
this.wharehouseUz,
this.wharehouseRu,
this.wharehouseZh,
this.id,
this.createdAt,
this.updatedAt,
});
LeadCreateResponse.fromJson(dynamic json) {
userUcode = json['user_ucode'];
phoneNumber = json['phone_number'];
gmail = json['gmail'];
weight = json['weight'];
status = json['status'] != null ? Status.fromJson(json['status']) : null;
nameUz = json['nameUz'];
nameRu = json['nameRu'];
nameZh = json['nameZh'];
wharehouseUz = json['wharehouseUz'];
wharehouseRu = json['wharehouseRu'];
wharehouseZh = json['wharehouseZh'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
id = json['id'];
}
String? userUcode;
String? phoneNumber;
String? gmail;
String? weight;
Status? status;
String? nameUz;
String? nameRu;
String? nameZh;
String? wharehouseUz;
String? wharehouseRu;
String? wharehouseZh;
String? id;
String? createdAt;
String? updatedAt;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['user_ucode'] = userUcode;
map['phone_number'] = phoneNumber;
map['gmail'] = gmail;
map['weight'] = weight;
if (status != null) {
map['status'] = status?.toJson();
}
map['nameUz'] = nameUz;
map['nameRu'] = nameRu;
map['nameZh'] = nameZh;
map['wharehouseUz'] = wharehouseUz;
map['wharehouseRu'] = wharehouseRu;
map['wharehouseZh'] = wharehouseZh;
map['createdAt'] = createdAt;
map['updatedAt'] = updatedAt;
map['id'] = id;
return map;
}
}
class Status {
Status({this.code, this.translations});
Status.fromJson(dynamic json) {
code = json['code'];
translations =
json['translations'] != null
? Translations.fromJson(json['translations'])
: null;
}
String? code;
Translations? translations;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['code'] = code;
if (translations != null) {
map['translations'] = translations?.toJson();
}
return map;
}
}
class Translations {
Translations({this.uz, this.ru, this.zh});
Translations.fromJson(dynamic json) {
uz = json['uz'];
ru = json['ru'];
zh = json['zh'];
}
String? uz;
String? ru;
String? zh;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['uz'] = uz;
map['ru'] = ru;
map['zh'] = zh;
return map;
}
}

View File

@@ -0,0 +1,20 @@
class PriceCalculateRequest {
PriceCalculateRequest({
this.avg,
this.m3,});
PriceCalculateRequest.fromJson(dynamic json) {
avg = json['avg'];
m3 = json['m3'];
}
double? avg;
double? m3;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['avg'] = avg;
map['m3'] = m3;
return map;
}
}

View File

@@ -0,0 +1,37 @@
import 'package:cargocalculaterapp/core/error/failure.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
import 'package:cargocalculaterapp/features/calculator/domain/repository/calculator_repository.dart';
import 'package:dartz/dartz.dart';
import '../data_source/calculator_remote_data_source.dart';
class CalculatorRepositoryImpl extends CalculatorRepository {
final CalculatorRemoteDataSource calculatorRemoteDataSource;
CalculatorRepositoryImpl(this.calculatorRemoteDataSource);
@override
Future<Either<Failure, CalculatePriceResponse>> calculatePrice(
PriceCalculateRequest request,
) async {
try {
final response = await calculatorRemoteDataSource.priceCalculate(request);
return Right(response);
} catch (e) {
return Left(ServerFailure(message: e.toString()));
}
}
@override
Future<Either<Failure, LeadCreateResponse>> createLead(LeadCreateRequest request) async {
try {
final response = await calculatorRemoteDataSource.createLead(request);
return Right(response);
} catch (e) {
return Left(ServerFailure(message: e.toString()));
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
import 'package:dartz/dartz.dart';
import '../../../../core/error/failure.dart';
abstract class CalculatorRepository {
Future<Either<Failure, CalculatePriceResponse>> calculatePrice(
PriceCalculateRequest request,
);
Future<Either<Failure, LeadCreateResponse>> createLead(
LeadCreateRequest request,
);
}

View File

@@ -0,0 +1,21 @@
import 'package:cargocalculaterapp/core/error/failure.dart';
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
import 'package:dartz/dartz.dart';
import '../repository/calculator_repository.dart';
class CalculatePriceUseCase
extends UseCase<CalculatePriceResponse, PriceCalculateRequest> {
final CalculatorRepository repository;
CalculatePriceUseCase(this.repository);
@override
Future<Either<Failure, CalculatePriceResponse>> call(
PriceCalculateRequest params,
) async {
return await repository.calculatePrice(params);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:cargocalculaterapp/core/error/failure.dart';
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
import 'package:dartz/dartz.dart';
import '../repository/calculator_repository.dart';
class CreateLeadUseCase
extends UseCase<LeadCreateResponse, LeadCreateRequest> {
final CalculatorRepository repository;
CreateLeadUseCase(this.repository);
@override
Future<Either<Failure, LeadCreateResponse>> call(
LeadCreateRequest params,
) async {
return await repository.createLead(params);
}
}

View File

@@ -0,0 +1,17 @@
class CalculatorInfoArgument {
final String productName;
final String weraHouse;
final double weight;
final double size;
final double averageWeight;
final double deliveryPrice;
CalculatorInfoArgument({
required this.productName,
required this.weraHouse,
required this.weight,
required this.size,
required this.averageWeight,
required this.deliveryPrice,
});
}

View File

@@ -0,0 +1,76 @@
import 'package:cargocalculaterapp/router/app_routes.dart';
import 'package:cargocalculaterapp/router/name_routes.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../data/model/price_calculate_request.dart';
import '../../domain/usecase/calculate_price_usecase.dart';
import '../arguments/calculator_info_argument.dart';
part 'calculator_event.dart';
part 'calculator_state.dart';
class CalculatorBloc extends Bloc<CalculatorEvent, CalculatorState> {
CalculatorBloc(this.calculatePriceUseCase)
: super(const CalculatorState(isLoading: false)) {
on<WeightSizeEnterEvent>(_weightSizeEntered);
on<WareHouseSelectEvent>(_wareHouseSelect);
on<CalculateEvent>(_calculate);
}
final CalculatePriceUseCase calculatePriceUseCase;
void _weightSizeEntered(
WeightSizeEnterEvent event,
Emitter<CalculatorState> emit,
) {
emit(
state.copyWith(
size: double.tryParse(event.size.replaceAll(",", "")),
weight: double.tryParse(event.weight.replaceAll(",", "")),
),
);
}
void _wareHouseSelect(
WareHouseSelectEvent event,
Emitter<CalculatorState> emit,
) {
emit(state.copyWith(selectedWarehouse: event.wareHouse));
}
Future<void> _calculate(
CalculateEvent event,
Emitter<CalculatorState> emit,
) async {
emit(state.copyWith(isLoading: true));
final response = await calculatePriceUseCase(
PriceCalculateRequest(
m3: state.size,
avg: (state.weight ?? 0) / (state.size ?? 1),
),
);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
emit(state.copyWith(isLoading: false));
Navigator.pushNamed(
rootNavigatorKey.currentContext!,
Routes.calculationInfo,
arguments: CalculatorInfoArgument(
productName: event.productName,
deliveryPrice: r.price ?? 0,
size: state.size ?? 0,
averageWeight: (state.weight ?? 0) / (state.size ?? 1),
weight: state.weight ?? 0,
weraHouse: state.selectedWarehouse ?? "",
),
);
},
);
}
}

View File

@@ -0,0 +1,33 @@
part of 'calculator_bloc.dart';
sealed class CalculatorEvent extends Equatable {
const CalculatorEvent();
}
final class WeightSizeEnterEvent extends CalculatorEvent {
final String weight;
final String size;
const WeightSizeEnterEvent({required this.weight, required this.size});
@override
List<Object?> get props => [weight, size];
}
final class WareHouseSelectEvent extends CalculatorEvent {
final String wareHouse;
const WareHouseSelectEvent({required this.wareHouse});
@override
List<Object?> get props => [wareHouse];
}
final class CalculateEvent extends CalculatorEvent {
const CalculateEvent({required this.productName});
final String productName;
@override
List<Object?> get props => [productName];
}

View File

@@ -0,0 +1,120 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../../constants/constants.dart';
import '../../../../../generated/l10n.dart';
import '../../../../../router/app_routes.dart';
import '../../../../../router/name_routes.dart';
import '../../../data/model/lead_create_request.dart';
import '../../../domain/usecase/create_lead_usecase.dart';
import '../../arguments/calculator_info_argument.dart';
import '../../pages/calculator_info/dialog/order_dialog.dart';
part 'calculator_info_event.dart';
part 'calculator_info_state.dart';
class CalculatorInfoBloc
extends Bloc<CalculatorInfoEvent, CalculatorInfoState> {
CalculatorInfoBloc(this.createLeadUseCase)
: super(const CalculatorInfoState(isLoading: false)) {
on<CreateLeadEvent>(_createLead);
on<ShowSuccessDialogEvent>(_showSuccessDialog);
}
final CreateLeadUseCase createLeadUseCase;
Future<void> _createLead(
CreateLeadEvent event,
Emitter<CalculatorInfoState> emit,
) async {
emit(state.copyWith(isLoading: true));
final local = Localizations.localeOf(
rootNavigatorKey.currentContext!,
).languageCode;
final response = await createLeadUseCase(
LeadCreateRequest(
status: "NOT_CONTACTED",
m3: "${event.argument.size}",
averageWeightKg: "${event.argument.averageWeight}",
weight: "${event.argument.weight}",
nameRu: local == "ru" ? event.argument.productName : "",
nameUz: local == "uz" ? event.argument.productName : "",
nameZh: local == "zh" ? event.argument.productName : "",
price: "${event.argument.deliveryPrice}",
warehouseCode: int.tryParse(event.argument.weraHouse),
wharehouseRu: AppConst.warehouseOptions[event.argument.weraHouse]?.ru,
wharehouseUz: AppConst.warehouseOptions[event.argument.weraHouse]?.uz,
wharehouseZh: AppConst.warehouseOptions[event.argument.weraHouse]?.zh,
),
);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
emit(state.copyWith(isLoading: false));
add(ShowSuccessDialogEvent(isCall: event.isCall));
},
);
}
void _showSuccessDialog(
ShowSuccessDialogEvent event,
Emitter<CalculatorInfoState> emit,
) {
if (event.isCall) {
showCupertinoModalPopup(
context: rootNavigatorKey.currentContext!,
builder: (context) => CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
child: Text(
"+998 99-110-22-22",
style: TextStyle(color: context.color.primaryColor),
),
onPressed: () async {
final Uri url = Uri(scheme: 'tel', path: "+998991102222");
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
Navigator.pop(rootNavigatorKey.currentContext!, true);
},
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(rootNavigatorKey.currentContext!);
},
child: Text(
AppLocalization.current.cancel,
style: TextStyle(color: context.color.textColor),
),
),
),
).then((value) {
if (value is bool) {
Navigator.pushNamedAndRemoveUntil(
rootNavigatorKey.currentContext!,
Routes.main,
(route) => false,
);
}
});
} else {
showDialog(
context: rootNavigatorKey.currentContext!,
builder: (context) => OrderDialog(isCall: event.isCall),
).then((value) {
Navigator.pushNamedAndRemoveUntil(
rootNavigatorKey.currentContext!,
Routes.main,
(route) => false,
);
});
}
}
}

View File

@@ -0,0 +1,24 @@
part of 'calculator_info_bloc.dart';
sealed class CalculatorInfoEvent extends Equatable {
const CalculatorInfoEvent();
}
final class CreateLeadEvent extends CalculatorInfoEvent {
const CreateLeadEvent({required this.argument, required this.isCall});
final bool isCall;
final CalculatorInfoArgument argument;
@override
List<Object?> get props => [argument, isCall];
}
final class ShowSuccessDialogEvent extends CalculatorInfoEvent {
const ShowSuccessDialogEvent({required this.isCall});
final bool isCall;
@override
List<Object?> get props => [isCall];
}

View File

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

View File

@@ -0,0 +1,32 @@
part of 'calculator_bloc.dart';
class CalculatorState extends Equatable {
const CalculatorState({
required this.isLoading,
this.weight,
this.size,
this.selectedWarehouse,
});
final bool isLoading;
final double? weight;
final double? size;
final String? selectedWarehouse;
CalculatorState copyWith({
bool? isLoading,
double? weight,
double? size,
String? selectedWarehouse,
}) {
return CalculatorState(
isLoading: isLoading ?? this.isLoading,
weight: weight ?? this.weight,
size: size ?? this.size,
selectedWarehouse: selectedWarehouse ?? this.selectedWarehouse,
);
}
@override
List<Object?> get props => [isLoading, weight, size, selectedWarehouse];
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/cupertino.dart';
mixin CalculatorMixin{
late TextEditingController weightController;
late TextEditingController sizeController;
late TextEditingController averageWeightController;
late TextEditingController productNameController;
void initControllers(){
weightController=TextEditingController();
sizeController=TextEditingController();
averageWeightController=TextEditingController();
productNameController=TextEditingController();
}
void disposeController(){
weightController.dispose();
sizeController.dispose();
averageWeightController.dispose();
productNameController.dispose();
}
}

View File

@@ -0,0 +1,155 @@
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/features/calculator/presentation/pages/calculator_info/widget/calculator_info_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../../constants/constants.dart';
import '../../../../../generated/l10n.dart';
import '../../arguments/calculator_info_argument.dart';
import '../../bloc/calculator_info/calculator_info_bloc.dart';
class CalculatorInfoPage extends StatelessWidget {
const CalculatorInfoPage({super.key, required this.argument});
final CalculatorInfoArgument? argument;
@override
Widget build(BuildContext context) {
return BlocBuilder<CalculatorInfoBloc, CalculatorInfoState>(
builder: (context, state) {
return Scaffold(
backgroundColor: context.color.grayBackground,
appBar: AppBar(title: Text(AppLocalization.current.calculator)),
body: ModalProgressHUD(
inAsyncCall: state.isLoading,
child: ListView(
padding: AppUtils.kPaddingAll16,
children: [
Container(
margin: const EdgeInsets.only(bottom: 32),
padding: AppUtils.kPaddingAll16,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CalculatorInfoWidget(
title: AppLocalization.current.product_name,
name: argument?.productName ?? "-",
),
CalculatorInfoWidget(
title: AppLocalization.current.warehouse,
name: Functions.getTranslatedItem(
AppConst.warehouseOptions[argument?.weraHouse],
context,
),
),
CalculatorInfoWidget(
title: AppLocalization.current.weight,
name: "${argument?.weight} kg",
),
CalculatorInfoWidget(
title: AppLocalization.current.size,
name: "${argument?.size}",
),
CalculatorInfoWidget(
title: AppLocalization.current.average_weight,
name: "${argument?.averageWeight}",
),
CalculatorInfoWidget(
title: AppLocalization.current.delivery_price,
name: "${argument?.deliveryPrice}",
),
],
),
),
Row(
children: [
Expanded(
child: InkWell(
onTap: () {
if (argument != null) {
context.read<CalculatorInfoBloc>().add(
CreateLeadEvent(
isCall: true,
argument: argument!,
),
);
}
},
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: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/svg/ic_phone.svg",
colorFilter: ColorFilter.mode(
context.color.textColor,
BlendMode.srcIn,
),
),
AppUtils.kBoxWidth4,
Text(
AppLocalization.current.phone_call,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: context.color.textColor,
),
),
],
),
),
),
),
AppUtils.kBoxWidth16,
Expanded(
child: ElevatedButton(
onPressed: () {
if (argument != null) {
context.read<CalculatorInfoBloc>().add(
CreateLeadEvent(
isCall: false,
argument: argument!,
),
);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/svg/ic_list_1.svg"),
AppUtils.kBoxWidth4,
Text(AppLocalization.current.send),
],
),
),
),
],
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,45 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class OrderDialog extends StatelessWidget {
const OrderDialog({super.key, required this.isCall});
final bool isCall;
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: context.color.scaffoldBackgroundColor,
insetPadding: AppUtils.kPaddingAll16,
child: Padding(
padding: AppUtils.kPaddingVer24Hor16,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset("assets/svg/ic_done.svg"),
AppUtils.kBoxHeight20,
Text(
isCall
? AppLocalization.current.done_phone
: AppLocalization.current.done_order,
style: context.text.authDesc.copyWith(
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
AppUtils.kBoxHeight20,
ElevatedButton(
onPressed: () {
Navigator.pop(context, true);
},
child: Text(AppLocalization.current.done_ready),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:flutter/material.dart';
import '../../../../../../core/utils/app_utils.dart';
class CalculatorInfoWidget extends StatelessWidget {
const CalculatorInfoWidget({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,146 @@
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/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../core/widgets/drop_down/custom_drop_down.dart';
import '../../../../core/widgets/text_filds/custom_text_field_name.dart';
import '../bloc/calculator_bloc.dart';
import '../mixin/calculator_mixin.dart';
class CalculatorPage extends StatefulWidget {
const CalculatorPage({super.key});
@override
State<CalculatorPage> createState() => _CalculatorPageState();
}
class _CalculatorPageState extends State<CalculatorPage> with CalculatorMixin {
@override
void initState() {
initControllers();
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<CalculatorBloc, CalculatorState>(
listener: (context, state) {
if ((state.weight ?? 0) > 0 && (state.size ?? 0) > 0) {
averageWeightController.text =
"${(state.weight ?? 0) / (state.size ?? 1)}";
}
},
builder: (context, state) {
return Scaffold(
backgroundColor: context.color.grayBackground,
appBar: AppBar(title: Text(AppLocalization.current.calculator)),
body: ModalProgressHUD(
inAsyncCall: state.isLoading,
child: ListView(
padding: AppUtils.kPaddingAll16,
children: [
CustomTextFieldName(
hint: AppLocalization.current.weight,
name: AppLocalization.current.weight_in_kg,
isRequired: true,
format: [
FilteringTextInputFormatter.allow(
RegExp(r'^\d*[.,]?\d{0,}$'), // accepts both "." and ","
),
],
controller: weightController,
inputType: const TextInputType.numberWithOptions(
decimal: true,
),
onchange: (value) {
context.read<CalculatorBloc>().add(
WeightSizeEnterEvent(
weight: value,
size: sizeController.text,
),
);
},
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.size,
name: AppLocalization.current.size_in_m3,
isRequired: true,
controller: sizeController,
format: [
FilteringTextInputFormatter.allow(
RegExp(r'^\d*[.,]?\d{0,}$'), // accepts both "." and ","
),
],
inputType: const TextInputType.numberWithOptions(
decimal: true,
),
onchange: (value) {
context.read<CalculatorBloc>().add(
WeightSizeEnterEvent(
weight: weightController.text,
size: value,
),
);
},
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.average_weight,
name: AppLocalization.current.average_weight_in,
controller: averageWeightController,
readOnly: true,
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.product_name,
isRequired: true,
name: AppLocalization.current.product_name,
controller: productNameController,
inputType: TextInputType.name,
),
AppUtils.kBoxHeight16,
CustomDropDown(
isRequired: false,
value: state.selectedWarehouse,
onChange: (value) {
context.read<CalculatorBloc>().add(
WareHouseSelectEvent(wareHouse: value ?? ""),
);
},
name: AppLocalization.current.select_warehouse,
),
AppUtils.kBoxHeight16,
ElevatedButton(
onPressed: ((state.weight ?? 0) > 0 && (state.size ?? 0) > 0)
? () {
context.read<CalculatorBloc>().add(
CalculateEvent(
productName: productNameController.text,
),
);
}
: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/svg/ic_calculator_1.svg"),
AppUtils.kBoxWith8,
Text(AppLocalization.current.calculate),
],
),
),
],
),
),
);
},
);
}
}

View File

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