import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.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/package_selection_model.dart'; import 'package_selection_result.dart'; typedef OnPrevious = Function(List packages); typedef OnContinue = Function(List packages); class PackageSelectionWidget extends StatefulWidget { final User sender; final User consignee; final FcsShipment shipment; final List 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 createState() => _PackageSelectionWidgetState(); } class _PackageSelectionWidgetState extends State { final TextEditingController _controller = TextEditingController(); String _query = ""; bool _isLoadMore = false; final _scrollController = ScrollController(); bool _down = true; @override void initState() { _init(); super.initState(); _scrollController.addListener(() { setState(() { _down = _scrollController.position.userScrollDirection == ScrollDirection.forward; }); }); } _init() { var searchModel = context.read(); _controller.text = searchModel.query; _query = searchModel.query; searchModel.refresh( shipmentId: widget.shipment.id!, consigneeId: widget.consignee.id!, senderId: widget.sender.id!, term: _query); searchModel.addSelectedPackage(widget.packages); if (mounted) { setState(() {}); } } // @override // void didUpdateWidget(covariant PackageSelectionWidget oldWidget) { // _init(); // super.didUpdateWidget(oldWidget); // } Future _loadMoreData() async { if (_isLoadMore) return; var model = context.read(); if (model.ended) return; setState(() { _isLoadMore = true; }); await model.loadMoreData( shipmentId: widget.shipment.id!, consigneeId: widget.consignee.id!, senderId: widget.sender.id!, term: _query); setState(() { _isLoadMore = false; }); } @override Widget build(BuildContext context) { var model = context.watch(); List searchResults = model.packages; List selectedPackageList = model.selectedPackages; 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), ), ); final selectedPackageBox = Align( alignment: Alignment.topLeft, child: SingleChildScrollView( scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), padding: EdgeInsets.only(top: 8), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: selectedPackageList .map((e) => Padding( padding: const EdgeInsets.only(right: 20), child: Text(e.trackingID ?? "", style: TextStyle(color: Colors.black, fontSize: 15)), )) .toList())), ); return Column( children: [ Expanded( child: Padding( padding: EdgeInsets.only(left: 10, right: 10), child: Column( children: [ AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (Widget child, Animation animation) => FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axis: Axis.vertical, child: child, ), ), child: _down ? Column(children: [ const SizedBox(height: 8), userRow, LocalTitle( textKey: "box.select.package", topPadding: 5), const SizedBox(height: 10), searchBox, selectedPackageBox, ]) : const SizedBox(), ), Expanded( child: Padding( padding: const EdgeInsets.only(top: 10), child: PackageSelectionResult( controller: _scrollController, isLoadingMore: _isLoadMore, onLoadMore: _loadMoreData, onRefresh: () async { _init(); setState(() { _down = true; }); }, onTap: (a) async { setState(() { a.isChecked = !a.isChecked; }); context.read().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().search(_query, imm: imm, shipmentId: widget.shipment.id!, senderId: widget.sender.id!, consigneeId: widget.consignee.id!); } catch (e) { showMsgDialog(context, 'Error', e.toString()); } } }