add carton editor for mix carton

This commit is contained in:
tzw
2024-02-02 18:00:51 +06:30
parent 61f119c063
commit 891330a59e
18 changed files with 903 additions and 158 deletions

View File

@@ -322,6 +322,8 @@
"box.standard_carton_size":"Standard carton size",
"box.custom_size":"Custom size",
"box.package_size":"Package",
"box.select.cartion":"Select cartons",
"box.no_carton":"There is no cartons in this shipment.",
"Boxes End ================================================================":"",
"Delivery Start ================================================================":"",

View File

@@ -321,6 +321,8 @@
"box.standard_carton_size":"Standard carton size",
"box.custom_size":"Custom size",
"box.package_size":"Package",
"box.select.cartion":"Select cartons",
"box.no_carton":"There is no cartons in this shipment.",
"Boxes End ================================================================":"",
"Delivery Start ================================================================":"",

View File

@@ -29,6 +29,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'pages/carton/model/carton_selection_model.dart';
import 'pages/delivery/model/delivery_model.dart';
class App extends StatefulWidget {
@@ -62,6 +63,7 @@ class _AppState extends State<App> {
final CartonSizeModel cartonSizeModel = new CartonSizeModel();
final ProcessingModel processingModel = new ProcessingModel();
final PickupModel pickupModel = new PickupModel();
final CartonSelectionModel cartonSelectionModel = new CartonSelectionModel();
late AppTranslationsDelegate _newLocaleDelegate;
@@ -84,7 +86,8 @@ class _AppState extends State<App> {
..addModel(deliveryModel)
..addModel(cartonSizeModel)
..addModel(processingModel)
..addModel(pickupModel);
..addModel(pickupModel)
..addModel(cartonSelectionModel);
_newLocaleDelegate = AppTranslationsDelegate(
newLocale: Translation().supportedLocales().first);
@@ -133,6 +136,7 @@ class _AppState extends State<App> {
ChangeNotifierProvider.value(value: cartonSizeModel),
ChangeNotifierProvider.value(value: processingModel),
ChangeNotifierProvider.value(value: pickupModel),
ChangeNotifierProvider.value(value: cartonSelectionModel),
],
child: Consumer<LanguageModel>(
builder: (context, value, child) {

View File

@@ -95,6 +95,11 @@ const carton_from_shipments = "From shipments";
const carton_small_bag = "Small bag";
const carton_mix_box = "Mix box";
// carton Size
const standardCarton = "Standard carton size";
const customCarton = "Custom size";
const packageCartion = "Package";
//Mix types
const mix_delivery = "Mix Delivery";
const mix_pickup = "Mix Pickup";

View File

@@ -37,6 +37,7 @@ class Carton {
String? cartonSizeID;
String? cartonSizeName;
double cartonWeight;
bool isSelected;
int rate;
int weight;
@@ -158,7 +159,8 @@ class Carton {
this.mixBoxType,
this.mixCartons = const [],
this.mixCartonIDs = const [],
this.cartonWeight =0});
this.cartonWeight =0,
this.isSelected = false});
Map<String, dynamic> toMap() {
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();

View File

@@ -80,4 +80,10 @@ class FcsShipment {
fcsShipment.port != this.port ||
fcsShipment.destination != this.destination;
}
@override
bool operator ==(Object other) => other is FcsShipment && other.id == id;
@override
int get hashCode => id.hashCode;
}

View File

@@ -6,6 +6,7 @@ import 'package:fcs/pages/user_search/user_serach.dart';
import 'package:fcs/pages/widgets/display_text.dart';
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/local_app_bar.dart';
import 'package:fcs/pages/widgets/local_radio.dart';
import 'package:fcs/pages/widgets/local_radio_buttons.dart';
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:fcs/pages/widgets/local_title.dart';
@@ -124,11 +125,10 @@ class _CartonEditorState extends State<CartonEditor> {
onTap: () {
//for packages
if (isFromPackages) {
}
// for mix cartion
else {
Navigator.push(
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const MixCartonEditor()));
@@ -244,14 +244,10 @@ class _CartonEditorState extends State<CartonEditor> {
});
},
child: Row(children: <Widget>[
Radio(
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _billToValue,
LocalRadio(
value: 1,
onChanged: (value) {
groupValue: _billToValue,
onChanged: (p0) {
setState(() {
_billToValue = 1;
});
@@ -261,7 +257,7 @@ class _CartonEditorState extends State<CartonEditor> {
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.bill_to_sender',
fontSize: 14,
fontSize: 15,
color: _billToValue == 1 ? primaryColor : Colors.black),
),
)
@@ -275,14 +271,10 @@ class _CartonEditorState extends State<CartonEditor> {
});
},
child: Row(children: <Widget>[
Radio(
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _billToValue,
LocalRadio(
value: 2,
onChanged: (value) {
groupValue: _billToValue,
onChanged: (p0) {
setState(() {
_billToValue = 2;
});
@@ -292,7 +284,7 @@ class _CartonEditorState extends State<CartonEditor> {
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.bill_to.consignee',
fontSize: 14,
fontSize: 15,
color: _billToValue == 2 ? primaryColor : Colors.black),
),
)
@@ -327,7 +319,9 @@ class _CartonEditorState extends State<CartonEditor> {
Flexible(child: consigneeBox)
],
),
billRadioBox
const SizedBox(height: 5),
billRadioBox,
const SizedBox(height: 5),
],
)
: Container(),

View File

@@ -0,0 +1,116 @@
import 'package:fcs/pages/widgets/local_text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../../domain/entities/carton.dart';
import '../../../helpers/theme.dart';
import '../model/carton_selection_model.dart';
typedef OnAction = Future<void> Function();
class CartonSelectionResult extends StatelessWidget {
final bool isLoadingMore;
final OnAction onLoadMore;
final OnAction onRefresh;
final Function(Carton)? onTap;
final ScrollController controller;
const CartonSelectionResult(
{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<CartonSelectionModel>();
List<Carton> searchResults = model.cartons;
return searchResults.isEmpty
? Center(
child: LocalText(context, 'box.no_carton',
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) {
Carton carton = searchResults[index];
return Padding(
padding: const EdgeInsets.only(top: 5, bottom: 5),
child: InkWell(
onTap: () {
if (onTap != null) {
onTap!(carton);
}
},
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(carton.cartonNumber ?? "",
style: new TextStyle(
fontSize: 15.0,
color: Colors.black)),
new Text(
"${carton.cartonWeight.toStringAsFixed(2)} lb",
style: new TextStyle(
fontSize: 15.0,
color: Colors.grey),
),
],
),
),
),
carton.isSelected
? 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)),
)),
]);
}
}

View File

@@ -0,0 +1,246 @@
import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/carton/mix_cation/carton_selection_result.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../../domain/entities/carton.dart';
import '../../main/util.dart';
import '../../widgets/barcode_scanner.dart';
import '../../widgets/continue_button.dart';
import '../../widgets/local_title.dart';
import '../../widgets/previous_button.dart';
import '../model/carton_selection_model.dart';
typedef OnPrevious = Function(List<Carton> cartons);
typedef OnContinue = Function(List<Carton> cartons);
class CartonSelectionWidget extends StatefulWidget {
final FcsShipment shipment;
final List<Carton> cartons;
final OnPrevious? onPrevious;
final OnContinue? onContinue;
const CartonSelectionWidget({
Key? key,
required this.cartons,
this.onPrevious,
this.onContinue,
required this.shipment,
}) : super(key: key);
@override
State<CartonSelectionWidget> createState() => _CartonSelectionWidgetState();
}
class _CartonSelectionWidgetState extends State<CartonSelectionWidget> {
final TextEditingController _controller = TextEditingController();
String _query = "";
bool _isLoadMore = false;
final _scrollController = ScrollController();
@override
void initState() {
_init();
super.initState();
}
_init() {
var searchModel = context.read<CartonSelectionModel>();
searchModel.addDefaultCartons(widget.shipment.id!);
_controller.text = searchModel.query;
_query = searchModel.query;
if (mounted) {
setState(() {});
}
}
@override
void didUpdateWidget(covariant CartonSelectionWidget oldWidget) {
_init();
super.didUpdateWidget(oldWidget);
}
Future<void> _loadMoreData() async {
if (_isLoadMore) return;
var model = context.read<CartonSelectionModel>();
if (model.reachEnd || model.ended) return;
setState(() {
_isLoadMore = true;
});
if (_query != "") {
await model.loadMoreSearch(term: _query, shipmentId: widget.shipment.id!);
} else {
await model.loadMoreData(widget.shipment.id!);
}
setState(() {
_isLoadMore = false;
});
}
@override
Widget build(BuildContext context) {
var model = context.watch<CartonSelectionModel>();
List<Carton> searchResults = model.cartons;
List<Carton> selectedCartonList = model.selectedCartonList;
final continueBtn = ContinueButton(
onTap: () {
if (selectedCartonList.isEmpty || searchResults.isEmpty) {
showMsgDialog(context, 'Error', "Please select the cartons");
return false;
}
if (widget.onContinue != null) {
widget.onContinue!(selectedCartonList);
}
},
);
final previousBtn = PreviousButton(onTap: () {
if (widget.onPrevious != null) {
widget.onPrevious!(selectedCartonList);
}
});
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 carton 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),
LocalTitle(textKey: "box.select.cartion"),
const SizedBox(height: 10),
searchBox,
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 10),
child: CartonSelectionResult(
controller: _scrollController,
isLoadingMore: _isLoadMore,
onLoadMore: _loadMoreData,
onRefresh: () async {
_init();
},
onTap: (a) async {
setState(() {
a.isSelected = !a.isSelected;
});
context.read<CartonSelectionModel>().selectCarton(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());
}
}
}

View File

@@ -1,12 +1,21 @@
// ignore_for_file: deprecated_member_use
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 '../../widgets/local_text.dart';
import '../../widgets/progress.dart';
import '../../widgets/step_widget.dart';
import '../model/carton_selection_model.dart';
import 'carton_selection_widget.dart';
import 'type_widget.dart';
class MixCartonEditor extends StatefulWidget {
@@ -26,12 +35,21 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
LocalStep(lable: 'Cartons', stepType: StepType.CARTONS),
LocalStep(lable: 'Submit', stepType: StepType.SUBMIT)
];
List<Carton> _cartions = [];
bool _isLoading = false;
int currentStep = 0;
double _length = 0;
double _width = 0;
double _height = 0;
FcsShipment? _shipment;
String _cartionSizeType = standardCarton;
CartonSize? _standardSize;
bool _isLoading = false;
@override
void initState() {
context.read<CartonSelectionModel>().clearSelection();
super.initState();
}
@@ -98,57 +116,48 @@ class _MixCartonEditorState extends State<MixCartonEditor> {
Widget getContent(int index) {
var step = steps[index];
if (step.stepType == StepType.TYPE) {
return Expanded(child: TypeWidget(
return Expanded(
child: TypeWidget(
shipment: _shipment,
cartonSizeType: _cartionSizeType,
standardSize: _standardSize,
length: _length,
width: _width,
height: _height,
onPrevious: () {
Navigator.pop(context);
},
// warehouse: _warehouse,
// onSelectWarehouse: (w) {
// setState(() {
// _warehouse = w;
// currentStep += 1;
// });
// },
onContinue: (shipment, cartonSizeType,
{standardSize, length, width, height}) {
setState(() {
_shipment = shipment;
_cartionSizeType = cartonSizeType;
_standardSize = standardSize;
_length = length ?? 0;
_width = width ?? 0;
_height = height ?? 0;
currentStep += 1;
});
},
));
} else if (step.stepType == StepType.CARTONS) {
return Expanded(
child: Text("cartons"),
// child: StockAdjustmentProducts(
// products: products,
// onAdd: (ps) {
// setState(() {
// products = List.from(ps);
// });
// },
// onRemove: (p) {
// setState(() {
// products.removeWhere((e) => e.id == p.id);
// });
// },
// onRemoveAll: (ps) {
// for (var e in ps) {
// setState(() {
// products.removeWhere((p) => p.id == e.id);
// });
// }
// },
// onContinue: (ps) {
// if (products.isEmpty) {
// showMsgDialog(context, 'Error', "Please select product");
// return false;
// }
// setState(() {
// products = List.from(ps);
// currentStep += 1;
// });
// },
// onPrevious: (ps) {
// setState(() {
// products = List.from(ps);
// currentStep -= 1;
// });
// },
// ),
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(

View File

@@ -1,10 +1,15 @@
import 'package:fcs/pages/widgets/local_radio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:provider/provider.dart';
import '../../../domain/constants.dart';
import '../../../domain/entities/carton_size.dart';
import '../../../domain/entities/fcs_shipment.dart';
import '../../../helpers/theme.dart';
import '../../carton_size/model/carton_size_model.dart';
import '../../fcs_shipment/model/fcs_shipment_model.dart';
import '../../main/util.dart';
import '../../widgets/box_size_picker.dart';
import '../../widgets/continue_button.dart';
import '../../widgets/local_dropdown.dart';
import '../../widgets/local_text.dart';
@@ -13,15 +18,29 @@ import '../../widgets/previous_button.dart';
typedef OnPrevious = Function();
typedef OnContinue = Function(FcsShipment shipment, String cartonSizeType,
{CartonSize? standardSize, double? length, double? width, double? height});
class TypeWidget extends StatefulWidget {
final OnPrevious? onPrevious;
final bool isTransferFrom;
final bool isTransferTo;
final OnContinue? onContinue;
final FcsShipment? shipment;
final String cartonSizeType;
final CartonSize? standardSize;
final double? length;
final double? width;
final double? height;
const TypeWidget(
{Key? key,
this.onPrevious,
this.isTransferFrom = false,
this.isTransferTo = false})
this.onContinue,
this.shipment,
required this.cartonSizeType,
this.standardSize,
this.length,
this.width,
this.height})
: super(key: key);
@override
@@ -30,14 +49,14 @@ class TypeWidget extends StatefulWidget {
class _TypeWidgetState extends State<TypeWidget> {
FcsShipment? _shipment;
List<FcsShipment> shipments = [];
List<String> standardSizeList = [
'Large - 20”x20”x20”',
'Medium - 15”x15”x15”',
'Small - 10”x10”x10”'
];
int _cartinSizeValue = 1;
late String selectedValue;
String _cartionSizeType = standardCarton;
List<FcsShipment> _shipments = [];
CartonSize? _selectStandardSize;
TextEditingController _widthController = new TextEditingController();
TextEditingController _heightController = new TextEditingController();
TextEditingController _lengthController = new TextEditingController();
@override
void initState() {
@@ -46,10 +65,22 @@ class _TypeWidgetState extends State<TypeWidget> {
}
_init() async {
selectedValue = standardSizeList[1];
_shipment = widget.shipment;
_cartionSizeType = widget.cartonSizeType;
List<CartonSize> cartonSizes = context.read<CartonSizeModel>().cartonSizes;
_selectStandardSize = widget.standardSize ?? cartonSizes.first;
_lengthController.text =
widget.length == null ? "0" : widget.length.toString();
_widthController.text =
widget.width == null ? "0" : widget.width.toString();
_heightController.text =
widget.height == null ? "0" : widget.height.toString();
var fcsShipments =
await context.read<FcsShipmentModel>().getActiveFcsShipments();
shipments = fcsShipments;
_shipments = fcsShipments;
if (mounted) {
setState(() {});
@@ -58,11 +89,42 @@ class _TypeWidgetState extends State<TypeWidget> {
@override
Widget build(BuildContext context) {
List<CartonSize> cartonSizes = context.watch<CartonSizeModel>().cartonSizes;
bool isStandardSize = _cartionSizeType == standardCarton;
bool isCustomSize = _cartionSizeType == customCarton;
bool isNoneDefinedSize = _cartionSizeType == packageCartion;
final continueBtn = ContinueButton(onTap: () {
double l = double.tryParse(_lengthController.text) ?? 0;
double w = double.tryParse(_widthController.text) ?? 0;
double h = double.tryParse(_heightController.text) ?? 0;
if (_shipment == null) {
showMsgDialog(context, "Error", "Please select shipment");
return;
}
if (isStandardSize &&
_selectStandardSize == null &&
!isCustomSize &&
!isNoneDefinedSize) {
showMsgDialog(
context, "Error", "Please select the standard cartion size");
return;
}
if (isCustomSize &&
!isStandardSize &&
!isNoneDefinedSize &&
(l == 0 || w == 0 || h == 0)) {
showMsgDialog(context, "Error", "Please add the cartion size");
return;
}
if (widget.onContinue != null) {
widget.onContinue!(_shipment!, _cartionSizeType,
standardSize: _selectStandardSize, length: l, width: w, height: h);
}
});
final previousBtn = PreviousButton(onTap: () {
@@ -72,52 +134,83 @@ class _TypeWidgetState extends State<TypeWidget> {
});
final standardSizeBox = Padding(
padding: const EdgeInsets.only(left: 35.0),
child: DropdownButton<String>(
isDense: true,
value: selectedValue,
style: TextStyle(color: Colors.black, fontSize: 14),
underline: Container(
height: 1,
color: Colors.grey,
padding: const EdgeInsets.only(left: 34.0, top: 8),
child: IgnorePointer(
ignoring: !isStandardSize,
child: DropdownButton<CartonSize>(
isDense: true,
value: _selectStandardSize,
style: TextStyle(color: Colors.black, fontSize: 14),
underline: Container(height: 1, color: Colors.grey),
onChanged: (newValue) {
setState(() {
_selectStandardSize = newValue!;
});
},
isExpanded: true,
items:
cartonSizes.map<DropdownMenuItem<CartonSize>>((CartonSize value) {
return DropdownMenuItem<CartonSize>(
value: value,
child: Row(
children: [
Text("${value.name} - ",
style: TextStyle(
color: isStandardSize ? Colors.black : labelColor)),
Text(
"${value.length.toInt()}”x${value.width.toInt()}”x${value.height.toInt()}",
style: TextStyle(color: labelColor)),
],
),
);
}).toList(),
),
onChanged: (newValue) {
setState(() {
selectedValue = newValue!;
});
},
isExpanded: true,
items: standardSizeList.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.toString(),
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.black)),
);
}).toList(),
),
);
final lengthBox = BoxSizePicker(
lableKey: 'box.length',
controller: _lengthController,
enable: isCustomSize);
final widthBox = BoxSizePicker(
lableKey: 'box.width',
controller: _widthController,
enable: isCustomSize);
final heightBox = BoxSizePicker(
lableKey: 'box.height',
controller: _heightController,
enable: isCustomSize);
final customSizeBox = Padding(
padding: const EdgeInsets.only(left: 34.0),
child: Row(
children: [
Flexible(child: lengthBox),
Flexible(child: widthBox),
Flexible(child: heightBox)
],
),
);
final cartonSizedBox = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// standard carton size
InkWell(
onTap: () {
setState(() {
_cartinSizeValue = 1;
_cartionSizeType = standardCarton;
});
},
child: Row(children: <Widget>[
Radio(
visualDensity:
const VisualDensity(horizontal: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _cartinSizeValue,
value: 1,
onChanged: (value) {
LocalRadio(
value: standardCarton,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartinSizeValue = 1;
_cartionSizeType = standardCarton;
});
},
),
@@ -125,32 +218,28 @@ class _TypeWidgetState extends State<TypeWidget> {
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.standard_carton_size',
fontSize: 14,
color: _cartinSizeValue == 1 ? primaryColor : Colors.black),
fontSize: 15,
color: isStandardSize ? primaryColor : labelColor),
),
)
]),
),
standardSizeBox,
const SizedBox(height: 10),
const SizedBox(height: 20),
// custom size
InkWell(
onTap: () {
setState(() {
_cartinSizeValue = 2;
_cartionSizeType = customCarton;
});
},
child: Row(children: <Widget>[
Radio(
visualDensity:
const VisualDensity(horizontal: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _cartinSizeValue,
value: 2,
onChanged: (value) {
LocalRadio(
value: customCarton,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartinSizeValue = 2;
_cartionSizeType = customCarton;
});
},
),
@@ -158,30 +247,28 @@ class _TypeWidgetState extends State<TypeWidget> {
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.custom_size',
fontSize: 14,
color: _cartinSizeValue == 2 ? primaryColor : Colors.black),
fontSize: 15,
color: isCustomSize ? primaryColor : Colors.black54),
),
)
]),
),
customSizeBox,
const SizedBox(height: 10),
// not defined size
InkWell(
onTap: () {
setState(() {
_cartinSizeValue = 3;
_cartionSizeType = packageCartion;
});
},
child: Row(children: <Widget>[
Radio(
visualDensity:
const VisualDensity(horizontal: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _cartinSizeValue,
value: 3,
onChanged: (value) {
LocalRadio(
value: packageCartion,
groupValue: _cartionSizeType,
onChanged: (p0) {
setState(() {
_cartinSizeValue = 3;
_cartionSizeType = packageCartion;
});
},
),
@@ -189,12 +276,21 @@ class _TypeWidgetState extends State<TypeWidget> {
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: LocalText(context, 'box.package_size',
fontSize: 14,
color: _cartinSizeValue == 3 ? primaryColor : Colors.black),
fontSize: 15,
color: isNoneDefinedSize ? primaryColor : Colors.black54),
),
)
]),
),
Padding(
padding: const EdgeInsets.only(left: 34.0),
child: Text(
"No defined size",
style: TextStyle(
fontSize: 14,
color: isNoneDefinedSize ? Colors.black : labelColor),
),
)
],
);
@@ -210,7 +306,7 @@ class _TypeWidgetState extends State<TypeWidget> {
iconData: Ionicons.ios_airplane,
display: (u) => u.shipmentNumber,
selectedValue: _shipment,
values: shipments,
values: _shipments,
));
return Column(
@@ -220,9 +316,13 @@ class _TypeWidgetState extends State<TypeWidget> {
child: ListView(
padding: EdgeInsets.only(left: 10, right: 10),
children: [
const SizedBox(height: 8),
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
],
)),

View File

@@ -0,0 +1,142 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/entities/carton.dart';
import 'package:logging/logging.dart';
import '../../../domain/constants.dart';
import '../../main/model/base_model.dart';
class CartonSelectionModel extends BaseModel {
final log = Logger("CartonSelectionModel");
// for search
String query = "";
int offset = 0;
bool reachEnd = false;
List<Carton> cartons = [];
// for default carton
DocumentSnapshot? _lastDocument;
bool ended = false;
List<Carton> selectedCartonList = [];
Timer? t;
search(String term, {bool imm = false, required String shipmentId}) async {
query = term;
cartons.clear();
offset = 0;
reachEnd = false;
t?.cancel();
t = Timer(Duration(milliseconds: imm ? 0 : 800), () async {
await loadMoreSearch(term: term, shipmentId: shipmentId);
});
}
Future<void> loadMoreSearch(
{required String term, required String shipmentId}) async {
if (term == "") {
await _refresh(shipmentId);
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();
}
addDefaultCartons(String shipmentId) async {
cartons.clear();
await _refresh(shipmentId);
}
selectCarton(Carton a) {
if (a.isSelected) {
selectedCartonList.add(a);
} else {
selectedCartonList.remove(a);
}
}
Future<void> _refresh(String shipmentId) async {
cartons.clear();
_lastDocument = null;
ended = false;
await loadMoreData(shipmentId);
notifyListeners();
}
Future<void> loadMoreData(String shipmentId) async {
int rowPerPage = 20;
try {
String path = "/$cartons_collection";
Query query = FirebaseFirestore.instance
.collection(path)
.where("fcs_shipment_id", isEqualTo: shipmentId)
// .where("status", isEqualTo: carton_processing_status)
// .where("carton_type", isEqualTo: carton_mix_box)
.where("is_deleted", isEqualTo: false)
.orderBy("created_at", 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<Carton> list = querySnap.docs.map((documentSnapshot) {
var p = Carton.fromMap(documentSnapshot.data() as Map<String, dynamic>,
documentSnapshot.id);
return p;
}).toList();
for (var p in list) {
selectedCartonList.contains(p)
? p.isSelected = true
: p.isSelected = false;
}
cartons.addAll(list);
if (list.length < rowPerPage) ended = true;
notifyListeners();
} catch (e) {
log.warning("error:$e");
}
}
clearSelection() {
selectedCartonList.clear();
cartons.clear();
query = "";
}
}

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import '../../helpers/theme.dart';
import 'input_text.dart';
import 'local_text.dart';
class BoxSizePicker extends StatelessWidget {
final TextEditingController? controller;
final String lableKey;
final bool enable;
const BoxSizePicker(
{super.key, this.controller, required this.lableKey, this.enable = true});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 4),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocalText(context, lableKey,
color: enable ? Colors.black : labelColor, fontSize: 15),
Text("inches",
style: TextStyle(color: labelColor, fontSize: 12))
],
),
),
Flexible(
child: InputText(
isDense: true,
enabled: enable,
textAlign: TextAlign.end,
controller: controller,
withBorder: true,
textInputType: TextInputType.number,
constraints: BoxConstraints(maxWidth: 50, maxHeight: 40),
contentPadding: EdgeInsets.all(7),
borderColor: enable ? primaryColor : labelColor))
],
),
);
}
}

View File

@@ -28,10 +28,9 @@ class DisplayText extends StatelessWidget {
var languageModel = Provider.of<LanguageModel>(context);
var labelStyle = languageModel.isEng
? TextStyle(
color: Colors.black54,
)
: TextStyle(color: Colors.black54, fontFamily: "Myanmar3");
? TextStyle(color: Colors.black54, fontSize: 15)
: TextStyle(
color: Colors.black54, fontFamily: "Myanmar3", fontSize: 15);
var textStyle = languageModel.isEng
? TextStyle(color: Colors.black)
: TextStyle(color: Colors.black, fontFamily: "Myanmar3");

View File

@@ -16,6 +16,9 @@ class InputText extends StatelessWidget {
final bool autoFocus;
final TextAlign textAlign;
final bool enabled;
final BoxConstraints? constraints;
final EdgeInsetsGeometry? contentPadding;
final bool? isDense;
const InputText(
{Key? key,
@@ -29,7 +32,10 @@ class InputText extends StatelessWidget {
this.autoFocus = false,
this.textInputType,
this.enabled = true,
this.textAlign = TextAlign.start})
this.textAlign = TextAlign.start,
this.constraints,
this.contentPadding,
this.isDense})
: super(key: key);
@override
Widget build(BuildContext context) {
@@ -47,6 +53,9 @@ class InputText extends StatelessWidget {
keyboardType: textInputType,
textAlign: textAlign,
decoration: new InputDecoration(
isDense: isDense,
constraints: constraints,
contentPadding: contentPadding,
// hintText: '',
hintStyle: TextStyle(
height: 1.5,
@@ -65,16 +74,28 @@ class InputText extends StatelessWidget {
),
enabledBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0)),
focusedBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0),
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(color: primaryColor, width: 1.0)),
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0)),
disabledBorder: withBorder
? OutlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0),
)
: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? primaryColor, width: 1.0)),
),
validator: validator),
);

View File

@@ -37,12 +37,14 @@ class LocalDropdown<T> extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.only(right: 18.0),
child: labelKey!=null? LocalText(
context,
labelKey!,
color: Colors.black54,
fontSize: 16,
): const SizedBox(),
child: labelKey != null
? LocalText(
context,
labelKey!,
color: Colors.black54,
fontSize: 16,
)
: const SizedBox(),
),
DropdownButton<T>(
isDense: true,

View File

@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import '../../helpers/theme.dart';
class LocalRadio<T> extends StatelessWidget {
final Function(T?)? onChanged;
final T value;
final T? groupValue;
const LocalRadio(
{super.key, this.onChanged, required this.value, this.groupValue});
@override
Widget build(BuildContext context) {
return Radio<T>(
fillColor: MaterialStateProperty.resolveWith((states) {
// active
if (states.contains(MaterialState.selected)) {
return primaryColor;
}
// inactive
return labelColor;
}),
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: groupValue,
value: value,
onChanged: onChanged);
}
}

View File

@@ -33,6 +33,14 @@ class LocalRadioButtons<T> extends StatelessWidget {
onTap: () => callback!(e),
child: Row(children: <Widget>[
Radio<T>(
fillColor: MaterialStateProperty.resolveWith((states) {
// active
if (states.contains(MaterialState.selected)) {
return primaryColor;
}
// inactive
return labelColor;
}),
activeColor: primaryColor,
groupValue: selectedValue,
value: e,
@@ -42,7 +50,7 @@ class LocalRadioButtons<T> extends StatelessWidget {
),
Text(e.toString(),
style: TextStyle(
fontSize: 14,
fontSize: 15,
color:
e == selectedValue ? primaryColor : Colors.black)),
]),
@@ -67,7 +75,14 @@ class LocalRadioButtons<T> extends StatelessWidget {
e == selectedValue ? primaryColor : Colors.grey,
),
),
Text(e.toString()),
Text(
e.toString(),
style: TextStyle(
fontSize: 15,
color: e == selectedValue
? primaryColor
: Colors.black),
),
]),
)
: Container())