add carton editor for package
This commit is contained in:
@@ -329,6 +329,11 @@
|
||||
"box.crete.carton":"Create carton",
|
||||
"box.carton.type":"Carton Type",
|
||||
"box.select.delivery":"Select delivery type",
|
||||
"box.select.package":"Select packages",
|
||||
"box.no_package":"There is no packages.",
|
||||
"box.input_cargo_weight":"Input cargo weight (lb)",
|
||||
"box.input_surcharge_item":"Input surcharge items",
|
||||
"box.select.cargo_type":"Select surcharge item",
|
||||
"Boxes End ================================================================":"",
|
||||
|
||||
"Delivery Start ================================================================":"",
|
||||
|
||||
@@ -328,6 +328,11 @@
|
||||
"box.crete.carton":"Create carton",
|
||||
"box.carton.type":"Carton Type",
|
||||
"box.select.delivery":"Select delivery type",
|
||||
"box.select.package":"Select packages",
|
||||
"box.no_package":"There is no packages.",
|
||||
"box.input_cargo_weight":"Input cargo weight (lb)",
|
||||
"box.input_surcharge_item":"Input surcharge items",
|
||||
"box.select.cargo_type":"Select surcharge item",
|
||||
"Boxes End ================================================================":"",
|
||||
|
||||
"Delivery Start ================================================================":"",
|
||||
|
||||
@@ -30,6 +30,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'pages/carton/model/carton_selection_model.dart';
|
||||
import 'pages/carton/model/package_selection_model.dart';
|
||||
import 'pages/delivery/model/delivery_model.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
@@ -64,6 +65,7 @@ class _AppState extends State<App> {
|
||||
final ProcessingModel processingModel = new ProcessingModel();
|
||||
final PickupModel pickupModel = new PickupModel();
|
||||
final CartonSelectionModel cartonSelectionModel = new CartonSelectionModel();
|
||||
final PackageSelectionModel packageSelectionModel = new PackageSelectionModel();
|
||||
|
||||
late AppTranslationsDelegate _newLocaleDelegate;
|
||||
|
||||
@@ -87,7 +89,8 @@ class _AppState extends State<App> {
|
||||
..addModel(cartonSizeModel)
|
||||
..addModel(processingModel)
|
||||
..addModel(pickupModel)
|
||||
..addModel(cartonSelectionModel);
|
||||
..addModel(cartonSelectionModel)
|
||||
..addModel(packageSelectionModel);
|
||||
|
||||
_newLocaleDelegate = AppTranslationsDelegate(
|
||||
newLocale: Translation().supportedLocales().first);
|
||||
@@ -137,6 +140,7 @@ class _AppState extends State<App> {
|
||||
ChangeNotifierProvider.value(value: processingModel),
|
||||
ChangeNotifierProvider.value(value: pickupModel),
|
||||
ChangeNotifierProvider.value(value: cartonSelectionModel),
|
||||
ChangeNotifierProvider.value(value: packageSelectionModel),
|
||||
],
|
||||
child: Consumer<LanguageModel>(
|
||||
builder: (context, value, child) {
|
||||
|
||||
@@ -133,3 +133,7 @@ const invoice_paid_status = "paid";
|
||||
const payment_pending_status = "pending";
|
||||
const payment_confirmed_status = "confirmed";
|
||||
const payment_canceled_status = "canceled";
|
||||
|
||||
//Delivery types
|
||||
const delivery_caton = "Delivery carton";
|
||||
const pickup_carton = "Pick-up carton";
|
||||
|
||||
@@ -14,6 +14,7 @@ class Package {
|
||||
DateTime? currentStatusDate;
|
||||
List<String> photoUrls;
|
||||
List<ShipmentStatus> shipmentHistory;
|
||||
List<String> cartonIds;
|
||||
String? desc;
|
||||
|
||||
String? status;
|
||||
@@ -69,7 +70,8 @@ class Package {
|
||||
this.deliveryAddress,
|
||||
this.isChecked = false,
|
||||
this.photoFiles = const [],
|
||||
this.senderPhoneNumber});
|
||||
this.senderPhoneNumber,
|
||||
this.cartonIds = const []});
|
||||
|
||||
factory Package.fromMap(Map<String, dynamic> map, String docID) {
|
||||
var _currentStatusDate = (map['status_date'] as Timestamp);
|
||||
@@ -82,6 +84,9 @@ class Package {
|
||||
var da = map['delivery_address'];
|
||||
var _da = da != null ? DeliveryAddress.fromMap(da, da["id"]) : null;
|
||||
|
||||
List<String> cartonIds =
|
||||
map['carton_ids'] == null ? [] : List.from(map['carton_ids']);
|
||||
|
||||
return Package(
|
||||
id: docID,
|
||||
userID: map['user_id'],
|
||||
@@ -99,7 +104,8 @@ class Package {
|
||||
deliveryAddress: _da,
|
||||
currentStatusDate: _currentStatusDate.toDate().toLocal(),
|
||||
photoUrls: _photoUrls,
|
||||
shipmentHistory: _shipmentStatus);
|
||||
shipmentHistory: _shipmentStatus,
|
||||
cartonIds: cartonIds);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
||||
356
lib/pages/carton/cargo_widget.dart
Normal file
356
lib/pages/carton/cargo_widget.dart
Normal file
@@ -0,0 +1,356 @@
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../domain/entities/cargo_type.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../rates/model/shipment_rate_model.dart';
|
||||
import '../widgets/continue_button.dart';
|
||||
import '../widgets/display_text.dart';
|
||||
import '../widgets/local_title.dart';
|
||||
import '../widgets/previous_button.dart';
|
||||
import 'custom_duty_addition.dart';
|
||||
|
||||
typedef OnPrevious = Function(
|
||||
List<CargoType> cargoTypes, List<CargoType> customDuties);
|
||||
typedef OnContinue = Function(
|
||||
List<CargoType> cargoTypes, List<CargoType> customDuties);
|
||||
|
||||
class CargoWidget extends StatefulWidget {
|
||||
final User sender;
|
||||
final User consignee;
|
||||
final List<CargoType> cargoTypes;
|
||||
final List<CargoType> customDuties;
|
||||
final OnPrevious? onPrevious;
|
||||
final OnContinue? onContinue;
|
||||
|
||||
const CargoWidget({
|
||||
Key? key,
|
||||
required this.cargoTypes,
|
||||
required this.customDuties,
|
||||
this.onPrevious,
|
||||
this.onContinue,
|
||||
required this.sender,
|
||||
required this.consignee,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CargoWidget> createState() => _CargoWidgetState();
|
||||
}
|
||||
|
||||
class _CargoWidgetState extends State<CargoWidget> {
|
||||
List<CargoType> _cargoTypes = [];
|
||||
List<CargoType> _customDuties = [];
|
||||
TextEditingController _totalCtl = TextEditingController();
|
||||
List<TextEditingController> _cargoTypeControllers = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_init() {
|
||||
var model = context.read<ShipmentRateModel>();
|
||||
_cargoTypes = model.rate.cargoTypes.map((e) => e.clone()).toList();
|
||||
|
||||
if (widget.cargoTypes.isNotEmpty) {
|
||||
} else {
|
||||
_cargoTypes.forEach((e) {
|
||||
var editor = new TextEditingController();
|
||||
editor.text = '';
|
||||
editor.addListener(inputChangeListener);
|
||||
_cargoTypeControllers.add(editor);
|
||||
});
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
bool isFieldEmpty(int index) {
|
||||
return _cargoTypeControllers[index].text.isEmpty;
|
||||
}
|
||||
|
||||
List<int> getEmptyFields() {
|
||||
List<int> emptyFields = [];
|
||||
for (int i = 0; i < _cargoTypeControllers.length; i++) {
|
||||
if (isFieldEmpty(i)) {
|
||||
emptyFields.add(i);
|
||||
}
|
||||
}
|
||||
return emptyFields;
|
||||
}
|
||||
|
||||
inputChangeListener() {
|
||||
List<int> emptyFields = getEmptyFields();
|
||||
print("emptyFields:$emptyFields");
|
||||
|
||||
// if (emptyFields.isNotEmpty && emptyFields.length == 1) {
|
||||
// // _cargoTypeControllers[emptyFields.first].text =
|
||||
// }
|
||||
|
||||
if (emptyFields.isEmpty) {
|
||||
_cargoTypes.asMap().entries.forEach((e) {
|
||||
_cargoTypes[e.key].weight =
|
||||
double.tryParse(_cargoTypeControllers[e.key].text) ?? 0;
|
||||
});
|
||||
double total = _cargoTypes.fold(0, (sum, value) => sum + value.weight);
|
||||
setState(() {
|
||||
_totalCtl.text = total.toString();
|
||||
});
|
||||
} else {
|
||||
// if (emptyFields.length == 1) {
|
||||
// print("_totalCtl.text:${_totalCtl.text}");
|
||||
|
||||
// if (_totalCtl.text.isNotEmpty) {
|
||||
// double t = double.tryParse(_totalCtl.text) ?? 0;
|
||||
|
||||
// _cargoTypes.asMap().entries.forEach((e) {
|
||||
// _cargoTypes[e.key].weight =
|
||||
// double.tryParse(_cargoTypeControllers[e.key].text) ?? 0;
|
||||
// });
|
||||
// double result =
|
||||
// _cargoTypes.fold(0, (sum, value) => sum + value.weight);
|
||||
|
||||
// double remaining = t - result;
|
||||
// setState(() {
|
||||
// _cargoTypeControllers[emptyFields.first].text =
|
||||
// remaining.toString();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final senderBox = DisplayText(
|
||||
text: widget.sender.name,
|
||||
labelTextKey: "box.sender.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_right,
|
||||
subText: Text(widget.sender.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final consigneeBox = DisplayText(
|
||||
text: widget.consignee.name,
|
||||
labelTextKey: "box.consignee.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_left,
|
||||
subText: Text(widget.consignee.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final userRow = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: senderBox,
|
||||
flex: 2,
|
||||
),
|
||||
Flexible(child: consigneeBox)
|
||||
],
|
||||
);
|
||||
|
||||
final cargosBox = Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
runSpacing: 15,
|
||||
children: _cargoTypes.asMap().entries.map((e) {
|
||||
var key = e.key;
|
||||
var c = e.value;
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.3,
|
||||
child: Row(
|
||||
children: [
|
||||
InkResponse(
|
||||
radius: 25,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_cargoTypeControllers[key].clear();
|
||||
});
|
||||
},
|
||||
child: Icon(MaterialIcons.clear)),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: inputTextFieldWidget(context,
|
||||
lableText: c.name ?? "",
|
||||
controller: _cargoTypeControllers[key]
|
||||
// onChanged: (newValue) {
|
||||
// setState(() {
|
||||
// _cargoTypes[key].weight = double.tryParse(newValue) ?? 0;
|
||||
// });
|
||||
// double total =
|
||||
// _cargoTypes.fold(0, (sum, value) => sum + value.weight);
|
||||
// setState(() {
|
||||
// _totalCtl.text = total.toString();
|
||||
// });
|
||||
// },
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList());
|
||||
|
||||
final totalBox = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.3,
|
||||
child: Row(
|
||||
children: [
|
||||
InkResponse(
|
||||
radius: 25,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_totalCtl.clear();
|
||||
});
|
||||
},
|
||||
child: Icon(MaterialIcons.clear)),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: inputTextFieldWidget(context,
|
||||
lableText: "Total", controller: _totalCtl),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
);
|
||||
|
||||
final subchargeItemTitleBox = LocalTitle(
|
||||
textKey: "box.input_surcharge_item",
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.add_circle,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () async {
|
||||
List<CargoType>? customList = await Navigator.push<List<CargoType>>(
|
||||
context,
|
||||
CupertinoPageRoute(
|
||||
builder: (context) =>
|
||||
CustomDutyAddition(customDuties: _customDuties)));
|
||||
if (customList == null) return;
|
||||
|
||||
_customDuties = List.from(customList);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
final subChargeItemsBox = Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
runSpacing: 15,
|
||||
children: _customDuties.asMap().entries.map((e) {
|
||||
var key = e.key;
|
||||
var c = e.value;
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.3,
|
||||
child: Row(
|
||||
children: [
|
||||
InkResponse(
|
||||
radius: 25,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_customDuties.removeAt(key);
|
||||
});
|
||||
},
|
||||
child: Icon(Feather.minus_circle)),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: inputTextFieldWidget(
|
||||
context,
|
||||
lableText: c.name ?? "",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList());
|
||||
|
||||
final continueBtn = ContinueButton(
|
||||
onTap: () {
|
||||
// if (selectedPackageList.isEmpty || searchResults.isEmpty) {
|
||||
// showMsgDialog(context, 'Error', "Please select the packages");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (widget.onContinue != null) {
|
||||
// widget.onContinue!(selectedPackageList);
|
||||
// }
|
||||
},
|
||||
);
|
||||
|
||||
final previousBtn = PreviousButton(onTap: () {
|
||||
if (widget.onPrevious != null) {
|
||||
widget.onPrevious!(_cargoTypes, _customDuties);
|
||||
}
|
||||
});
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
userRow,
|
||||
LocalTitle(textKey: "box.input_cargo_weight", topPadding: 10),
|
||||
cargosBox,
|
||||
const SizedBox(height: 15),
|
||||
Divider(),
|
||||
const SizedBox(height: 5),
|
||||
totalBox,
|
||||
subchargeItemTitleBox,
|
||||
subChargeItemsBox,
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
widget.onContinue != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
previousBtn,
|
||||
continueBtn,
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
const SizedBox(height: 20)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget inputTextFieldWidget(BuildContext context,
|
||||
{required String lableText,
|
||||
TextEditingController? controller,
|
||||
Function(String)? onChanged}) {
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
style: textStyle,
|
||||
cursorColor: primaryColor,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: onChanged,
|
||||
decoration: new InputDecoration(
|
||||
contentPadding: EdgeInsets.all(0),
|
||||
labelText: lableText,
|
||||
labelStyle: newLabelStyle(color: Colors.black54, fontSize: 17),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
disabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: primaryColor, width: 1.0)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class _CargoTableState extends State<CargoTable> {
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
c.isCutomDuty!
|
||||
c.isCutomDuty
|
||||
? GestureDetector(
|
||||
onTap: () async {
|
||||
String? _t = await showDialog(
|
||||
|
||||
@@ -3,25 +3,31 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../domain/constants.dart';
|
||||
import '../../../domain/entities/carton.dart';
|
||||
import '../../../domain/entities/carton_size.dart';
|
||||
import '../../../domain/entities/fcs_shipment.dart';
|
||||
import '../../../domain/vo/local_step.dart';
|
||||
import '../../../helpers/theme.dart';
|
||||
import '../../domain/entities/cargo_type.dart';
|
||||
import '../../domain/entities/package.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/local_text.dart';
|
||||
import '../widgets/progress.dart';
|
||||
import '../widgets/step_widget.dart';
|
||||
import 'cargo_widget.dart';
|
||||
import 'carton_size_widget.dart';
|
||||
import 'mix_carton/mix_carton_submit.dart';
|
||||
import 'model/package_selection_model.dart';
|
||||
import 'package_selection_widget.dart';
|
||||
|
||||
class CartonEditorForPackage extends StatefulWidget {
|
||||
final User sender;
|
||||
final User consignee;
|
||||
const CartonEditorForPackage({Key? key, required this.sender, required this.consignee}) : super(key: key);
|
||||
const CartonEditorForPackage(
|
||||
{Key? key, required this.sender, required this.consignee})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<CartonEditorForPackage> createState() => _CartonEditorForPackageState();
|
||||
@@ -36,18 +42,27 @@ class _CartonEditorForPackageState extends State<CartonEditorForPackage> {
|
||||
LocalStep(lable: 'Cargos', stepType: StepType.CARGOS),
|
||||
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
|
||||
];
|
||||
List<Carton> _cartions = [];
|
||||
List<Package> _packages = [];
|
||||
List<CargoType> _cargoTypes = [];
|
||||
List<CargoType> _customDuties = [];
|
||||
|
||||
int currentStep = 0;
|
||||
double _length = 0;
|
||||
double _width = 0;
|
||||
double _height = 0;
|
||||
|
||||
String _selectedDeliveryType = delivery_caton;
|
||||
FcsShipment? _shipment;
|
||||
String _cartonSizeType = standardCarton;
|
||||
CartonSize? _standardSize;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
context.read<PackageSelectionModel>().clearSelection();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
@@ -113,6 +128,7 @@ class _CartonEditorForPackageState extends State<CartonEditorForPackage> {
|
||||
if (step.stepType == StepType.SIZE) {
|
||||
return Expanded(
|
||||
child: CartonSizeWidget(
|
||||
deliveryType: _selectedDeliveryType,
|
||||
sender: widget.sender,
|
||||
consignee: widget.consignee,
|
||||
shipment: _shipment,
|
||||
@@ -124,9 +140,10 @@ class _CartonEditorForPackageState extends State<CartonEditorForPackage> {
|
||||
onPrevious: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onContinue: (shipment, cartonSizeType,
|
||||
onContinue: (deliveryType, shipment, cartonSizeType,
|
||||
{standardSize, length, width, height}) {
|
||||
setState(() {
|
||||
_selectedDeliveryType = deliveryType;
|
||||
_shipment = shipment;
|
||||
_cartonSizeType = cartonSizeType;
|
||||
_standardSize = standardSize;
|
||||
@@ -139,64 +156,69 @@ class _CartonEditorForPackageState extends State<CartonEditorForPackage> {
|
||||
));
|
||||
} else if (step.stepType == StepType.PACKAGES) {
|
||||
return Expanded(
|
||||
child: Text("PACKAGES"),
|
||||
// child: CartonSelectionWidget(
|
||||
// shipment: _shipment!,
|
||||
// cartons: _cartions,
|
||||
// onContinue: (cartons) {
|
||||
// setState(() {
|
||||
// _cartions = List.from(cartons);
|
||||
// currentStep += 1;
|
||||
// });
|
||||
// },
|
||||
// onPrevious: (cartons) {
|
||||
// setState(() {
|
||||
// _cartions = List.from(cartons);
|
||||
// currentStep -= 1;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
);
|
||||
} else if (step.stepType == StepType.CARGOS) {
|
||||
return Expanded(
|
||||
child: Text("cargos"),
|
||||
// child: CartonSelectionWidget(
|
||||
// shipment: _shipment!,
|
||||
// cartons: _cartions,
|
||||
// onContinue: (cartons) {
|
||||
// setState(() {
|
||||
// _cartions = List.from(cartons);
|
||||
// currentStep += 1;
|
||||
// });
|
||||
// },
|
||||
// onPrevious: (cartons) {
|
||||
// setState(() {
|
||||
// _cartions = List.from(cartons);
|
||||
// currentStep -= 1;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
);
|
||||
} else {
|
||||
return Expanded(
|
||||
child: MixCartonSubmit(
|
||||
cartonSizeType: _cartonSizeType,
|
||||
standardSize: _standardSize,
|
||||
length: _length,
|
||||
width: _width,
|
||||
height: _height,
|
||||
child: PackageSelectionWidget(
|
||||
sender: widget.sender,
|
||||
consignee: widget.consignee,
|
||||
shipment: _shipment!,
|
||||
cartons: _cartions,
|
||||
onCreate: () {
|
||||
_create();
|
||||
},
|
||||
onPrevious: () {
|
||||
packages: _packages,
|
||||
onContinue: (packages) {
|
||||
setState(() {
|
||||
_packages = List.from(packages);
|
||||
currentStep += 1;
|
||||
});
|
||||
},
|
||||
onPrevious: (packages) {
|
||||
setState(() {
|
||||
_packages = List.from(packages);
|
||||
currentStep -= 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (step.stepType == StepType.CARGOS) {
|
||||
return Expanded(
|
||||
child: CargoWidget(
|
||||
sender: widget.sender,
|
||||
consignee: widget.consignee,
|
||||
cargoTypes: _cargoTypes,
|
||||
customDuties: _customDuties,
|
||||
onContinue: (cargoTypes, customDuties) {
|
||||
setState(() {
|
||||
_cargoTypes = List.from(cargoTypes);
|
||||
_customDuties = List.from(customDuties);
|
||||
currentStep += 1;
|
||||
});
|
||||
},
|
||||
onPrevious: (cargoTypes, customDuties) {
|
||||
setState(() {
|
||||
_cargoTypes = List.from(cargoTypes);
|
||||
_customDuties = List.from(customDuties);
|
||||
currentStep -= 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Expanded(
|
||||
child: Text("Submit"),
|
||||
// child: MixCartonSubmit(
|
||||
// cartonSizeType: _cartonSizeType,
|
||||
// standardSize: _standardSize,
|
||||
// length: _length,
|
||||
// width: _width,
|
||||
// height: _height,
|
||||
// shipment: _shipment!,
|
||||
// cartons: _packages,
|
||||
// onCreate: () {
|
||||
// _create();
|
||||
// },
|
||||
// onPrevious: () {
|
||||
// setState(() {
|
||||
// currentStep -= 1;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,14 @@ import '../widgets/box_size_picker.dart';
|
||||
import '../widgets/continue_button.dart';
|
||||
import '../widgets/display_text.dart';
|
||||
import '../widgets/local_dropdown.dart';
|
||||
import '../widgets/local_radio_buttons.dart';
|
||||
import '../widgets/local_text.dart';
|
||||
import '../widgets/local_title.dart';
|
||||
import '../widgets/previous_button.dart';
|
||||
|
||||
typedef OnPrevious = Function();
|
||||
|
||||
typedef OnContinue = Function(FcsShipment shipment, String cartonSizeType,
|
||||
typedef OnContinue = Function(String deliveryType,FcsShipment shipment, String cartonSizeType,
|
||||
{CartonSize? standardSize, double? length, double? width, double? height});
|
||||
|
||||
class CartonSizeWidget extends StatefulWidget {
|
||||
@@ -28,6 +29,7 @@ class CartonSizeWidget extends StatefulWidget {
|
||||
final OnContinue? onContinue;
|
||||
final User sender;
|
||||
final User consignee;
|
||||
final String deliveryType;
|
||||
final FcsShipment? shipment;
|
||||
final String cartonSizeType;
|
||||
final CartonSize? standardSize;
|
||||
@@ -46,7 +48,8 @@ class CartonSizeWidget extends StatefulWidget {
|
||||
this.width,
|
||||
this.height,
|
||||
required this.sender,
|
||||
required this.consignee})
|
||||
required this.consignee,
|
||||
required this.deliveryType})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -54,11 +57,14 @@ class CartonSizeWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
List<String> _deliveryTypes = [delivery_caton, pickup_carton];
|
||||
|
||||
FcsShipment? _shipment;
|
||||
String _cartionSizeType = standardCarton;
|
||||
|
||||
List<FcsShipment> _shipments = [];
|
||||
CartonSize? _selectStandardSize;
|
||||
late String _selectedDeliveryType;
|
||||
|
||||
TextEditingController _widthController = new TextEditingController();
|
||||
TextEditingController _heightController = new TextEditingController();
|
||||
@@ -71,6 +77,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
}
|
||||
|
||||
_init() async {
|
||||
_selectedDeliveryType = widget.deliveryType;
|
||||
_shipment = widget.shipment;
|
||||
_cartionSizeType = widget.cartonSizeType;
|
||||
|
||||
@@ -104,17 +111,21 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
text: widget.sender.name,
|
||||
labelTextKey: "box.sender.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_right,
|
||||
subText: Text(widget.sender.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final consigneeBox = DisplayText(
|
||||
text: widget.consignee.name,
|
||||
labelTextKey: "box.consignee.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_left,
|
||||
subText: Text(widget.consignee.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final userRow = Row(
|
||||
children: [
|
||||
Flexible(
|
||||
Expanded(
|
||||
child: senderBox,
|
||||
flex: 2,
|
||||
),
|
||||
@@ -122,6 +133,15 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
],
|
||||
);
|
||||
|
||||
final deliveryTypeBox = LocalRadioButtons(
|
||||
values: _deliveryTypes,
|
||||
selectedValue: _selectedDeliveryType,
|
||||
callback: (String? v) {
|
||||
setState(() {
|
||||
_selectedDeliveryType = v!;
|
||||
});
|
||||
});
|
||||
|
||||
final continueBtn = ContinueButton(onTap: () {
|
||||
double l = double.tryParse(_lengthController.text) ?? 0;
|
||||
double w = double.tryParse(_widthController.text) ?? 0;
|
||||
@@ -137,7 +157,7 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
!isCustomSize &&
|
||||
!isNoneDefinedSize) {
|
||||
showMsgDialog(
|
||||
context, "Error", "Please select the standard cartion size");
|
||||
context, "Error", "Please select the standard carton size");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,12 +165,12 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
!isStandardSize &&
|
||||
!isNoneDefinedSize &&
|
||||
(l == 0 || w == 0 || h == 0)) {
|
||||
showMsgDialog(context, "Error", "Please add the cartion size");
|
||||
showMsgDialog(context, "Error", "Please add the carton size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.onContinue != null) {
|
||||
widget.onContinue!(_shipment!, _cartionSizeType,
|
||||
widget.onContinue!(_selectedDeliveryType,_shipment!, _cartionSizeType,
|
||||
standardSize: _selectStandardSize, length: l, width: w, height: h);
|
||||
}
|
||||
});
|
||||
@@ -346,13 +366,18 @@ class _CartonSizeWidgetState extends State<CartonSizeWidget> {
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
userRow,
|
||||
LocalTitle(textKey: "box.select.delivery", topPadding: 10),
|
||||
const SizedBox(height: 5),
|
||||
deliveryTypeBox,
|
||||
const SizedBox(height: 5),
|
||||
LocalTitle(textKey: "box.select_carton_size"),
|
||||
const SizedBox(height: 8),
|
||||
cartonSizedBox,
|
||||
const SizedBox(height: 8),
|
||||
LocalTitle(textKey: "box.select_shipment"),
|
||||
const SizedBox(height: 5),
|
||||
fcsShipmentsBox
|
||||
fcsShipmentsBox,
|
||||
const SizedBox(height: 30)
|
||||
],
|
||||
)),
|
||||
Padding(
|
||||
|
||||
127
lib/pages/carton/custom_duty_addition.dart
Normal file
127
lib/pages/carton/custom_duty_addition.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:fcs/pages/main/util.dart';
|
||||
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
|
||||
import 'package:fcs/pages/widgets/local_app_bar.dart';
|
||||
import 'package:fcs/pages/widgets/local_title.dart';
|
||||
import 'package:fcs/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../widgets/local_button.dart';
|
||||
|
||||
class CustomDutyAddition extends StatefulWidget {
|
||||
final List<CargoType> customDuties;
|
||||
|
||||
const CustomDutyAddition({super.key, required this.customDuties});
|
||||
@override
|
||||
_CustomDutyAdditionState createState() => _CustomDutyAdditionState();
|
||||
}
|
||||
|
||||
class _CustomDutyAdditionState extends State<CustomDutyAddition> {
|
||||
bool _isLoading = false;
|
||||
List<CargoType> customDuties = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_init() {
|
||||
var shipmentRateModel =
|
||||
Provider.of<ShipmentRateModel>(context, listen: false);
|
||||
customDuties =
|
||||
shipmentRateModel.rate.customDuties.map((e) => e.clone()).toList();
|
||||
|
||||
for (var p in customDuties) {
|
||||
if (widget.customDuties.any((e) => e.id == p.id)) {
|
||||
p.isChecked = true;
|
||||
} else {
|
||||
p.isChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> getCargoRowList(List<CargoType> _c) {
|
||||
return _c.map((c) {
|
||||
return Container(
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.only(left: 10.0, right: 5.0, top: 3.0, bottom: 3.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
c.isChecked = !c.isChecked;
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Checkbox(
|
||||
value: c.isChecked,
|
||||
activeColor: primaryColor,
|
||||
onChanged: (bool? check) {
|
||||
setState(() {
|
||||
c.isChecked = check ?? false;
|
||||
});
|
||||
}),
|
||||
new Text(c.name ?? '', style: textStyle),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
final saveBtn = Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: LocalButton(
|
||||
textKey: "box.cargo.select.btn",
|
||||
callBack: () {
|
||||
List<CargoType> _cargos =
|
||||
customDuties.where((c) => c.isChecked).toList();
|
||||
if (_cargos.isEmpty) {
|
||||
showMsgDialog(context, 'Error', "Please select the cargo type");
|
||||
return;
|
||||
}
|
||||
Navigator.pop(context, _cargos);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: LocalAppBar(
|
||||
labelKey: 'box.select.cargo_type',
|
||||
backgroundColor: Colors.white,
|
||||
labelColor: primaryColor,
|
||||
arrowColor: primaryColor),
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
LocalTitle(
|
||||
textKey: "box.select.cargo.title",
|
||||
),
|
||||
Column(
|
||||
children: getCargoRowList(customDuties),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
saveBtn,
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
|
||||
LocalStep(lable: 'Cartons', stepType: StepType.CARTONS),
|
||||
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
|
||||
];
|
||||
List<Carton> _cartions = [];
|
||||
List<Carton> _cartons = [];
|
||||
|
||||
int currentStep = 0;
|
||||
double _length = 0;
|
||||
@@ -146,16 +146,16 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
|
||||
return Expanded(
|
||||
child: CartonSelectionWidget(
|
||||
shipment: _shipment!,
|
||||
cartons: _cartions,
|
||||
cartons: _cartons,
|
||||
onContinue: (cartons) {
|
||||
setState(() {
|
||||
_cartions = List.from(cartons);
|
||||
_cartons = List.from(cartons);
|
||||
currentStep += 1;
|
||||
});
|
||||
},
|
||||
onPrevious: (cartons) {
|
||||
setState(() {
|
||||
_cartions = List.from(cartons);
|
||||
_cartons = List.from(cartons);
|
||||
currentStep -= 1;
|
||||
});
|
||||
},
|
||||
@@ -170,7 +170,7 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
|
||||
width: _width,
|
||||
height: _height,
|
||||
shipment: _shipment!,
|
||||
cartons: _cartions,
|
||||
cartons: _cartons,
|
||||
onCreate: () {
|
||||
_create();
|
||||
},
|
||||
|
||||
@@ -109,7 +109,7 @@ class _TypeWidgetState extends State<TypeWidget> {
|
||||
!isCustomSize &&
|
||||
!isNoneDefinedSize) {
|
||||
showMsgDialog(
|
||||
context, "Error", "Please select the standard cartion size");
|
||||
context, "Error", "Please select the standard carton size");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class _TypeWidgetState extends State<TypeWidget> {
|
||||
!isStandardSize &&
|
||||
!isNoneDefinedSize &&
|
||||
(l == 0 || w == 0 || h == 0)) {
|
||||
showMsgDialog(context, "Error", "Please add the cartion size");
|
||||
showMsgDialog(context, "Error", "Please add the carton size");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class CartonSelectionModel extends BaseModel {
|
||||
String path = "/$cartons_collection";
|
||||
Query query = FirebaseFirestore.instance
|
||||
.collection(path)
|
||||
.where("fcs_shipment_id", isEqualTo: shipmentId)
|
||||
// .where("fcs_shipment_id", isEqualTo: shipmentId)
|
||||
// .where("status", isEqualTo: carton_processing_status)
|
||||
// .where("carton_type", isEqualTo: carton_mix_box)
|
||||
.where("is_deleted", isEqualTo: false)
|
||||
|
||||
170
lib/pages/carton/model/package_selection_model.dart
Normal file
170
lib/pages/carton/model/package_selection_model.dart
Normal file
@@ -0,0 +1,170 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../../../domain/constants.dart';
|
||||
import '../../../domain/entities/package.dart';
|
||||
import '../../main/model/base_model.dart';
|
||||
|
||||
class PackageSelectionModel extends BaseModel {
|
||||
final log = Logger("PackageSelectionModel");
|
||||
// for search
|
||||
String query = "";
|
||||
int offset = 0;
|
||||
bool reachEnd = false;
|
||||
List<Package> packages = [];
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
// for default package
|
||||
DocumentSnapshot? _lastDocument;
|
||||
bool ended = false;
|
||||
|
||||
List<Package> selectedPackageList = [];
|
||||
|
||||
Timer? t;
|
||||
search(String term,
|
||||
{bool imm = false,
|
||||
required String shipmentId,
|
||||
required String senderId,
|
||||
required String consigneeId}) async {
|
||||
query = term;
|
||||
packages.clear();
|
||||
offset = 0;
|
||||
reachEnd = false;
|
||||
t?.cancel();
|
||||
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
|
||||
await loadMoreSearch(
|
||||
term: term,
|
||||
shipmentId: shipmentId,
|
||||
consigneeId: consigneeId,
|
||||
senderId: senderId);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loadMoreSearch(
|
||||
{required String term,
|
||||
required String shipmentId,
|
||||
required String senderId,
|
||||
required String consigneeId}) async {
|
||||
if (term == "") {
|
||||
await _refresh(
|
||||
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
|
||||
return;
|
||||
}
|
||||
// int rowPerPage = 21;
|
||||
// List<Carton> list = [];
|
||||
// SearchPara searchPara = SearchPara(filters: [], term: term);
|
||||
// isLoading = true;
|
||||
|
||||
// var path =
|
||||
// "/search/$cartons_collection/${searchPara.escapeTerm}/$rowPerPage/$offset/${searchPara.escapeFilters}";
|
||||
|
||||
// var result = await requestAPI(path, "GET",
|
||||
// token: await getToken(), url: Config.instance.searchURL);
|
||||
|
||||
// if (result != null) {
|
||||
// for (var row in result) {
|
||||
// var item = ArtistExt.fromMapForSearch(row);
|
||||
// list.add(item);
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (var p in list) {
|
||||
// selectedArtistList.contains(p)
|
||||
// ? p.isSelected = true
|
||||
// : p.isSelected = false;
|
||||
// }
|
||||
|
||||
// artists.addAll(list);
|
||||
// offset += rowPerPage;
|
||||
// if (list.length < rowPerPage) {
|
||||
// reachEnd = true;
|
||||
// }
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
addDefaultPackages(
|
||||
{required String shipmentId,
|
||||
required String senderId,
|
||||
required String consigneeId}) async {
|
||||
packages.clear();
|
||||
await _refresh(
|
||||
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
|
||||
}
|
||||
|
||||
selectPackage(Package a) {
|
||||
if (a.isChecked) {
|
||||
selectedPackageList.add(a);
|
||||
} else {
|
||||
selectedPackageList.remove(a);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refresh(
|
||||
{required String shipmentId,
|
||||
required String senderId,
|
||||
required String consigneeId}) async {
|
||||
packages.clear();
|
||||
_lastDocument = null;
|
||||
ended = false;
|
||||
await loadMoreData(
|
||||
shipmentId: shipmentId, senderId: senderId, consigneeId: consigneeId);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadMoreData(
|
||||
{required String shipmentId,
|
||||
required String senderId,
|
||||
required String consigneeId}) async {
|
||||
int rowPerPage = 20;
|
||||
|
||||
try {
|
||||
isLoading = true;
|
||||
String path = "/$packages_collection";
|
||||
Query query = FirebaseFirestore.instance
|
||||
.collection(path)
|
||||
// .where("fcs_shipment_id", isEqualTo: shipmentId)
|
||||
// .where("status", isEqualTo: package_processed_status)
|
||||
.where("user_id", whereIn: [senderId, consigneeId])
|
||||
.where("is_deleted", isEqualTo: false)
|
||||
.orderBy("created_date", descending: true);
|
||||
|
||||
if (_lastDocument != null) {
|
||||
query = query.startAfterDocument(_lastDocument!);
|
||||
}
|
||||
|
||||
QuerySnapshot querySnap = await query.limit(rowPerPage).get();
|
||||
|
||||
if (querySnap.docs.isEmpty) return;
|
||||
_lastDocument = querySnap.docs[querySnap.docs.length - 1];
|
||||
|
||||
List<Package> list = querySnap.docs.map((documentSnapshot) {
|
||||
var p = Package.fromMap(documentSnapshot.data() as Map<String, dynamic>,
|
||||
documentSnapshot.id);
|
||||
return p;
|
||||
}).toList();
|
||||
|
||||
for (var p in list) {
|
||||
selectedPackageList.contains(p)
|
||||
? p.isChecked = true
|
||||
: p.isChecked = false;
|
||||
}
|
||||
|
||||
packages.addAll(list);
|
||||
if (list.length < rowPerPage) ended = true;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
log.warning("error:$e");
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
selectedPackageList.clear();
|
||||
packages.clear();
|
||||
query = "";
|
||||
}
|
||||
}
|
||||
120
lib/pages/carton/package_selection_result.dart
Normal file
120
lib/pages/carton/package_selection_result.dart
Normal file
@@ -0,0 +1,120 @@
|
||||
import 'package:fcs/pages/widgets/local_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../helpers/theme.dart';
|
||||
import '../../domain/entities/package.dart';
|
||||
import 'model/package_selection_model.dart';
|
||||
|
||||
typedef OnAction = Future<void> Function();
|
||||
final NumberFormat numberFormatter = NumberFormat("#,###");
|
||||
|
||||
class PackageSelectionResult extends StatelessWidget {
|
||||
final bool isLoadingMore;
|
||||
final OnAction onLoadMore;
|
||||
final OnAction onRefresh;
|
||||
final Function(Package)? onTap;
|
||||
final ScrollController controller;
|
||||
|
||||
const PackageSelectionResult(
|
||||
{super.key,
|
||||
required this.isLoadingMore,
|
||||
required this.onLoadMore,
|
||||
required this.onRefresh,
|
||||
this.onTap,
|
||||
required this.controller});
|
||||
|
||||
bool _scrollNotification(ScrollNotification scrollInfo) {
|
||||
if (!isLoadingMore &&
|
||||
scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
|
||||
onLoadMore();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var model = context.watch<PackageSelectionModel>();
|
||||
List<Package> searchResults = model.packages;
|
||||
|
||||
return searchResults.isEmpty && !model.isLoading
|
||||
? Center(
|
||||
child: LocalText(context, 'box.no_package',
|
||||
color: Colors.black, fontSize: 15))
|
||||
: Column(children: [
|
||||
Expanded(
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: _scrollNotification,
|
||||
child: RefreshIndicator(
|
||||
color: primaryColor,
|
||||
onRefresh: () => onRefresh(),
|
||||
child: ListView.builder(
|
||||
controller: controller,
|
||||
shrinkWrap: true,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
Package package = searchResults[index];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 5, bottom: 5),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap!(package);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5)),
|
||||
border:
|
||||
Border.all(color: Colors.grey.shade300)),
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8.0),
|
||||
child: new Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Text(package.trackingID ?? "",
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.black)),
|
||||
new Text(
|
||||
package.cartonIds.isEmpty
|
||||
? "-"
|
||||
: "${numberFormatter.format(package.cartonIds.length)} Boxes",
|
||||
style: new TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
package.isChecked
|
||||
? Icon(Icons.check, color: primaryColor)
|
||||
: const SizedBox()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: searchResults.length)),
|
||||
)),
|
||||
Container(
|
||||
height: isLoadingMore ? 50.0 : 0,
|
||||
color: Colors.transparent,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(primaryColor)),
|
||||
)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
289
lib/pages/carton/package_selection_widget.dart
Normal file
289
lib/pages/carton/package_selection_widget.dart
Normal file
@@ -0,0 +1,289 @@
|
||||
import 'package:fcs/domain/entities/fcs_shipment.dart';
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../domain/entities/package.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/barcode_scanner.dart';
|
||||
import '../widgets/continue_button.dart';
|
||||
import '../widgets/display_text.dart';
|
||||
import '../widgets/local_title.dart';
|
||||
import '../widgets/previous_button.dart';
|
||||
import 'model/carton_selection_model.dart';
|
||||
import 'model/package_selection_model.dart';
|
||||
import 'package_selection_result.dart';
|
||||
|
||||
typedef OnPrevious = Function(List<Package> packages);
|
||||
typedef OnContinue = Function(List<Package> packages);
|
||||
|
||||
class PackageSelectionWidget extends StatefulWidget {
|
||||
final User sender;
|
||||
final User consignee;
|
||||
final FcsShipment shipment;
|
||||
final List<Package> packages;
|
||||
final OnPrevious? onPrevious;
|
||||
final OnContinue? onContinue;
|
||||
|
||||
const PackageSelectionWidget({
|
||||
Key? key,
|
||||
required this.packages,
|
||||
this.onPrevious,
|
||||
this.onContinue,
|
||||
required this.shipment,
|
||||
required this.sender,
|
||||
required this.consignee,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PackageSelectionWidget> createState() => _PackageSelectionWidgetState();
|
||||
}
|
||||
|
||||
class _PackageSelectionWidgetState extends State<PackageSelectionWidget> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
String _query = "";
|
||||
bool _isLoadMore = false;
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_init() {
|
||||
var searchModel = context.read<PackageSelectionModel>();
|
||||
searchModel.addDefaultPackages(
|
||||
shipmentId: widget.shipment.id!,
|
||||
consigneeId: widget.consignee.id!,
|
||||
senderId: widget.sender.id!);
|
||||
|
||||
_controller.text = searchModel.query;
|
||||
_query = searchModel.query;
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant PackageSelectionWidget oldWidget) {
|
||||
_init();
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
Future<void> _loadMoreData() async {
|
||||
if (_isLoadMore) return;
|
||||
var model = context.read<PackageSelectionModel>();
|
||||
if (model.reachEnd || model.ended) return;
|
||||
setState(() {
|
||||
_isLoadMore = true;
|
||||
});
|
||||
if (_query != "") {
|
||||
await model.loadMoreSearch(
|
||||
term: _query,
|
||||
shipmentId: widget.shipment.id!,
|
||||
consigneeId: widget.consignee.id!,
|
||||
senderId: widget.sender.id!);
|
||||
} else {
|
||||
await model.loadMoreData(
|
||||
shipmentId: widget.shipment.id!,
|
||||
consigneeId: widget.consignee.id!,
|
||||
senderId: widget.sender.id!);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoadMore = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var model = context.watch<PackageSelectionModel>();
|
||||
List<Package> searchResults = model.packages;
|
||||
List<Package> selectedPackageList = model.selectedPackageList;
|
||||
|
||||
final senderBox = DisplayText(
|
||||
text: widget.sender.name,
|
||||
labelTextKey: "box.sender.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_right,
|
||||
subText: Text(widget.sender.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final consigneeBox = DisplayText(
|
||||
text: widget.consignee.name,
|
||||
labelTextKey: "box.consignee.title",
|
||||
iconData: MaterialCommunityIcons.account_arrow_left,
|
||||
subText: Text(widget.consignee.fcsID!,
|
||||
style: TextStyle(fontSize: 13, color: labelColor)),
|
||||
);
|
||||
|
||||
final userRow = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: senderBox,
|
||||
flex: 2,
|
||||
),
|
||||
Flexible(child: consigneeBox)
|
||||
],
|
||||
);
|
||||
|
||||
final continueBtn = ContinueButton(
|
||||
onTap: () {
|
||||
if (selectedPackageList.isEmpty || searchResults.isEmpty) {
|
||||
showMsgDialog(context, 'Error', "Please select the packages");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (widget.onContinue != null) {
|
||||
widget.onContinue!(selectedPackageList);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
final previousBtn = PreviousButton(onTap: () {
|
||||
if (widget.onPrevious != null) {
|
||||
widget.onPrevious!(selectedPackageList);
|
||||
}
|
||||
});
|
||||
|
||||
final searchBox = SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
cursorColor: primaryColor,
|
||||
onSubmitted: (value) {
|
||||
setState(() {
|
||||
_query = value;
|
||||
});
|
||||
_search(imm: true);
|
||||
},
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_query = v;
|
||||
});
|
||||
_search();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: labelColor),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
borderSide:
|
||||
BorderSide(color: labelColor.withOpacity(0.3), width: 0)),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||
borderSide: BorderSide(color: labelColor),
|
||||
),
|
||||
hintText: "Search by tracking number",
|
||||
hintStyle: const TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal),
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkResponse(
|
||||
radius: 20,
|
||||
onTap: () {
|
||||
_scan(context);
|
||||
},
|
||||
child: const Icon(Icons.qr_code_scanner,
|
||||
color: Colors.black87)),
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_controller.clear();
|
||||
_query = "";
|
||||
});
|
||||
_search();
|
||||
},
|
||||
icon: const Icon(Icons.close, color: Colors.black87)),
|
||||
],
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
filled: true),
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
userRow,
|
||||
LocalTitle(textKey: "box.select.package", topPadding: 10),
|
||||
const SizedBox(height: 10),
|
||||
searchBox,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: PackageSelectionResult(
|
||||
controller: _scrollController,
|
||||
isLoadingMore: _isLoadMore,
|
||||
onLoadMore: _loadMoreData,
|
||||
onRefresh: () async {
|
||||
_init();
|
||||
},
|
||||
onTap: (a) async {
|
||||
setState(() {
|
||||
a.isChecked = !a.isChecked;
|
||||
});
|
||||
context.read<PackageSelectionModel>().selectPackage(a);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
widget.onContinue != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
previousBtn,
|
||||
continueBtn,
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
const SizedBox(height: 20)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_scan(BuildContext context) async {
|
||||
try {
|
||||
String? barcode = await scanBarcode();
|
||||
if (barcode != null) {
|
||||
setState(() {
|
||||
_controller.text = barcode;
|
||||
_query = barcode;
|
||||
});
|
||||
await _search();
|
||||
}
|
||||
} catch (e) {
|
||||
showMsgDialog(context, 'Error', e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
_search({bool imm = false}) async {
|
||||
try {
|
||||
await context
|
||||
.read<CartonSelectionModel>()
|
||||
.search(_query, imm: imm, shipmentId: widget.shipment.id!);
|
||||
} catch (e) {
|
||||
showMsgDialog(context, 'Error', e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@ import 'package:flutter/material.dart';
|
||||
class LocalTitle extends StatelessWidget {
|
||||
final String? textKey;
|
||||
final Widget? trailing;
|
||||
final double topPadding;
|
||||
final List<String>? translationVariables;
|
||||
|
||||
const LocalTitle(
|
||||
{Key? key, this.textKey, this.trailing, this.translationVariables})
|
||||
{Key? key, this.textKey, this.trailing, this.translationVariables,this.topPadding = 18})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -17,7 +18,7 @@ class LocalTitle extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 18),
|
||||
padding: EdgeInsets.only(top: topPadding),
|
||||
child: Row(
|
||||
children: [
|
||||
LocalText(context, textKey!,
|
||||
|
||||
Reference in New Issue
Block a user