import 'package:fcs/domain/entities/fcs_shipment.dart'; import 'package:fcs/helpers/theme.dart'; import 'package:fcs/pages/carton/mix_carton/carton_selection_result.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.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 cartons); typedef OnContinue = Function(List cartons); class CartonSelectionWidget extends StatefulWidget { final FcsShipment shipment; final List 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 createState() => _CartonSelectionWidgetState(); } class _CartonSelectionWidgetState 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 model = context.read(); model.addDefaultCartons(widget.shipment.id!); model.addSelectedCarton(widget.cartons); _controller.text = model.query; _query = model.query; if (mounted) { setState(() {}); } } @override void didUpdateWidget(covariant CartonSelectionWidget oldWidget) { _init(); super.didUpdateWidget(oldWidget); } Future _loadMoreData() async { if (_isLoadMore) return; var model = context.read(); 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(); List searchResults = model.cartons; List 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), ), ); final selectedCartonBox = 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: selectedCartonList .map((e) => Padding( padding: const EdgeInsets.only(right: 20), child: Text(e.cartonNumber ?? "", 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: 5), LocalTitle(textKey: "box.select.cartion"), const SizedBox(height: 10), searchBox, selectedCartonBox ]) : const SizedBox()), Expanded( child: Padding( padding: const EdgeInsets.only(top: 10), child: CartonSelectionResult( controller: _scrollController, isLoadingMore: _isLoadMore, onLoadMore: _loadMoreData, onRefresh: () async { _init(); setState(() { _down = true; }); }, onTap: (a) async { setState(() { a.isSelected = !a.isSelected; }); context.read().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() .search(_query, imm: imm, shipmentId: widget.shipment.id!); } catch (e) { showMsgDialog(context, 'Error', e.toString()); } } }