diff --git a/analysis_options.yaml b/analysis_options.yaml index 4d9f4ef..e634759 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -24,4 +24,7 @@ linter: always_put_required_named_parameters_first: true # Util classes are awesome! - avoid_classes_with_only_static_members: true \ No newline at end of file + avoid_classes_with_only_static_members: true + + # Avoid returning an awaited expression when the expression type is assignable to the function's return type. + unnecessary_await_in_return: false \ No newline at end of file diff --git a/lib/core/models/auth/auth_response.dart b/lib/core/models/auth/auth_response.dart index 77cd583..522f868 100644 --- a/lib/core/models/auth/auth_response.dart +++ b/lib/core/models/auth/auth_response.dart @@ -1,3 +1,5 @@ +import 'package:satu/core/utils/utils_parse.dart'; + /// user_id : 10 /// company_id : 281 /// kassa_id : 3 @@ -19,13 +21,13 @@ class AuthResponse { static AuthResponse fromMap(dynamic map) { final AuthResponse authResponseBean = AuthResponse(); - authResponseBean.userId = map['user_id'] as int; - authResponseBean.companyId = map['company_id'] as int; - authResponseBean.kassaId = map['kassa_id'] as int; - authResponseBean.token = map['token']?.toString(); - authResponseBean.authAt = map['auth_at']?.toString(); - authResponseBean.shard = map['shard'] as int; - authResponseBean.message = map['message']?.toString(); + authResponseBean.userId = cast(map['user_id']); + authResponseBean.companyId = cast(map['company_id']); + authResponseBean.kassaId = cast(map['kassa_id']); + authResponseBean.token = cast(map['token']); + authResponseBean.authAt = cast(map['auth_at']); + authResponseBean.shard = cast(map['shard']); + authResponseBean.message = cast(map['message']); authResponseBean.operation = map['operation'] as bool; return authResponseBean; } diff --git a/lib/core/models/dialog_models.dart b/lib/core/models/dialog_models.dart index c2f6592..4e74c56 100644 --- a/lib/core/models/dialog_models.dart +++ b/lib/core/models/dialog_models.dart @@ -1,30 +1,35 @@ import 'package:flutter/foundation.dart'; class DialogRequest { - final String? title; - final String? description; - final String? buttonTitle; - final String? cancelTitle; - final String? formatType; - DialogRequest( - {@required this.title, - @required this.description, - @required this.buttonTitle, - this.cancelTitle, - this.formatType}); + { + required this.title, + required this.description, + required this.buttonTitle, + this.cancelTitle, + this.requestPrice, + this.requestCount + }); + final String title; + final String description; + final String buttonTitle; + final String? cancelTitle; + + final String? requestPrice; + final String? requestCount; + + } class DialogResponse { - //final String fieldOne; - //final String fieldTwo; - final String? responseText; - final bool? confirmed; - DialogResponse({ - //this.fieldOne, - //this.fieldTwo, - this.responseText, - this.confirmed, + required this.confirmed, + this.responsePrice, + this.responseCount, }); + final String? responsePrice; + final String? responseCount; + final bool confirmed; + + } diff --git a/lib/core/redux/actions/sell_actions.dart b/lib/core/redux/actions/sell_actions.dart index e545df2..2027042 100644 --- a/lib/core/redux/actions/sell_actions.dart +++ b/lib/core/redux/actions/sell_actions.dart @@ -28,7 +28,8 @@ final Logger log = getLogger('SetSellStateAction'); final DbService _dbService = locator(); -ThunkAction counterSellItem({required int transactionId, required num counter}) { +ThunkAction counterOrEditSellItem( + {required int transactionId, required num counter, num? price}) { return (Store store) async { log.i('counterSellItem'); int? appCompanyId = store.state.userState!.auth!.companyId; @@ -36,19 +37,33 @@ ThunkAction counterSellItem({required int transactionId, required num Transaction? transaction; - if (uuid != null ) { - List> set = await _dbService.queryRowsWithWhere( - transactionTableName, - '$transactionColumnAppCompanyId = ? and $transactionColumnStatus = ? and ${transactionColumnType} = ? and ${transactionColumnId} = ?', - [appCompanyId, transactionStatusPrepare, transactionTypeSell, transactionId], - orderBy: '$transactionColumnCreatedAt desc'); + if (uuid != null) { + final List> set = + await _dbService.queryRowsWithWhere( + transactionTableName, + '$transactionColumnAppCompanyId = ? ' + ' and $transactionColumnStatus = ? ' + ' and $transactionColumnType = ? ' + ' and $transactionColumnId = ?', + [ + appCompanyId, + transactionStatusPrepare, + transactionTypeSell, + transactionId + ], + orderBy: '$transactionColumnCreatedAt desc'); if (set.isNotEmpty) { transaction = Transaction.fromMap(set.first); } } if (transaction != null) { - ProductDao item = ProductDao.fromMap(jsonDecode(transaction.data!)); - item.count = (item.count ?? 0) + counter; + final ProductDao item = ProductDao.fromMap(jsonDecode(transaction.data!)); + if (price != null) { + item.price = price; + item.count = counter; + } else { + item.count = (item.count ?? 0) + counter; + } transaction.data = jsonEncode(item.toMap()); _dbService.update(transactionTableName, transaction.toMap()); } @@ -60,21 +75,24 @@ ThunkAction counterSellItem({required int transactionId, required num ThunkAction addSellItem({required Good good, String? excise}) { return (Store store) async { log.i('addSellItem'); - int? appCompanyId = store.state.userState!.auth!.companyId; + final int? appCompanyId = store.state.userState!.auth!.companyId; String? uuid = store.state.sellState!.transactionState!.uuid; Transaction? transaction; - if (uuid != null ) { + if (uuid != null) { List> set = await _dbService.queryRowsWithWhere( transactionTableName, - '$transactionColumnAppCompanyId = ? and $transactionColumnStatus = ? and ${transactionColumnType} = ?', + '$transactionColumnAppCompanyId = ? ' + ' and $transactionColumnStatus = ? ' + ' and $transactionColumnType = ?', [appCompanyId, transactionStatusPrepare, transactionTypeSell], orderBy: '$transactionColumnCreatedAt desc'); if (set.isNotEmpty) { for (Map map in set) { Transaction _transaction = Transaction.fromMap(map); - ProductDao _product = ProductDao.fromMap(jsonDecode(_transaction.data!)); + ProductDao _product = + ProductDao.fromMap(jsonDecode(_transaction.data!)); if (_product.id == good.id && _product.excise == excise) { transaction = _transaction; break; @@ -100,8 +118,8 @@ ThunkAction addSellItem({required Good good, String? excise}) { ..excise = excise; //category add logic if (good.categoryId != null) { - List> set = - await _dbService.queryRowsWithWhere(categoryTableName, 'id = ?', [good.categoryId]); + List> set = await _dbService + .queryRowsWithWhere(categoryTableName, 'id = ?', [good.categoryId]); if (set.isNotEmpty) { Category category = Category.fromMap(set.first); item.categoryId = category.id; @@ -131,7 +149,6 @@ ThunkAction addSellItem({required Good good, String? excise}) { ThunkAction removeSellItem({required int transactionId}) { return (Store store) async { - int? appCompanyId = store.state.userState!.auth!.companyId; String? uuid = store.state.sellState!.transactionState!.uuid; @@ -153,7 +170,7 @@ Future removeAllSellData(Store store) async { '$transactionColumnAppCompanyId = ? ' ' and $transactionColumnStatus = ? ' ' and ${transactionColumnType} = ?' - ' and ${transactionColumnUuid} = ?', + ' and ${transactionColumnUuid} = ?', [appCompanyId, transactionStatusPrepare, transactionTypeSell, uuid]); await loadSellData(store); } catch (e, stack) { @@ -179,7 +196,8 @@ Future loadSellData(Store store) async { productDao.transactionId = transaction.id; list.add(productDao); } - store.dispatch(SetSellStateAction(SellState(items: list, transactionState: TransactionState()..uuid = uuid))); + store.dispatch(SetSellStateAction(SellState( + items: list, transactionState: TransactionState()..uuid = uuid))); } catch (e, stack) { log.e('loadSellData', e, stack); } diff --git a/lib/core/services/dialog_service.dart b/lib/core/services/dialog_service.dart index ecc4750..f6b8913 100644 --- a/lib/core/services/dialog_service.dart +++ b/lib/core/services/dialog_service.dart @@ -4,7 +4,8 @@ import 'package:flutter/cupertino.dart'; import 'package:satu/core/models/dialog_models.dart'; class DialogService { - final GlobalKey _dialogNavigationKey = GlobalKey(); + final GlobalKey _dialogNavigationKey = + GlobalKey(); late Function(DialogRequest)? _showDialogListener; late Function(DialogRequest)? _showDialogInputListener; Completer? _dialogCompleter; @@ -22,14 +23,14 @@ class DialogService { /// Calls the dialog listener and returns a Future that will wait for dialogComplete. Future showDialog({ - String title = 'Aman Касса', + String title = 'SATU', String? description, String buttonTitle = 'Ok', }) { _dialogCompleter = Completer(); _showDialogListener!(DialogRequest( title: title, - description: description, + description: description ?? '', buttonTitle: buttonTitle, )); return _dialogCompleter!.future; @@ -43,26 +44,29 @@ class DialogService { String cancelTitle = 'Cancel'}) { _dialogCompleter = Completer(); _showDialogListener!(DialogRequest( - title: title, - description: description, + title: title ?? '', + description: description ?? '', buttonTitle: confirmationTitle, cancelTitle: cancelTitle)); return _dialogCompleter!.future; } - Future showConfirmationDialogInput( - {String title = ' Aman Касса', - String? description, - String confirmationTitle = 'Ok', - String cancelTitle = 'Cancel', - String? formatType}) { + Future showConfirmationDialogInput({ + required String title, + required String requestPrice, + required String requestCount, + String? description, + String confirmationTitle = 'ПОДТВЕРДИТЬ', + String cancelTitle = 'Отмена', + }) { _dialogCompleter = Completer(); _showDialogInputListener!(DialogRequest( title: title, - description: description, + description: description ?? '', buttonTitle: confirmationTitle, cancelTitle: cancelTitle, - formatType: formatType)); + requestPrice: requestPrice, + requestCount: requestCount)); return _dialogCompleter!.future; } diff --git a/lib/core/utils/utils_parse.dart b/lib/core/utils/utils_parse.dart index 6c2035d..52d21eb 100644 --- a/lib/core/utils/utils_parse.dart +++ b/lib/core/utils/utils_parse.dart @@ -3,4 +3,16 @@ List? parseListString(Iterable? json){ return List.from(json); } -T? cast(x) => x is T ? x : null; \ No newline at end of file +T? cast(x) => x is T ? x : null; + +bool isNumeric(String? s) { + if (s == null) { + return false; + } + return double.tryParse(s) != null; +} + +String formatDecimal(double value) { + if (value % 1 == 0) return value.toStringAsFixed(0).toString(); + return value.toString(); +} \ No newline at end of file diff --git a/lib/shared/shared_styles.dart b/lib/shared/shared_styles.dart index cef8562..7f0bea7 100644 --- a/lib/shared/shared_styles.dart +++ b/lib/shared/shared_styles.dart @@ -3,38 +3,37 @@ import 'package:flutter/material.dart'; import 'app_colors.dart'; // Box Decorations -BoxDecoration fieldDecoration = BoxDecoration(color: whiteColor); +BoxDecoration fieldDecoration = const BoxDecoration(color: whiteColor); BoxDecoration disabledFieldDecoration = BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.grey[100]); -const LinearGradient primaryGradient = LinearGradient( +LinearGradient primaryGradient = const LinearGradient( colors: [ primaryGrStartColor, primaryGrEndColor, ], - begin: const FractionalOffset(0.0, 0.0), - end: const FractionalOffset(1.0, 0.0), - stops: [0.0, 1.0], - tileMode: TileMode.clamp); + begin: FractionalOffset(0.0, 0.0), + end: FractionalOffset(1.0, 0.0), + stops: [0.0, 1.0]); // Field Variables const double fieldHeight = 55; const double smallFieldHeight = 40; const double inputFieldBottomMargin = 30; const double inputFieldSmallBottomMargin = 0; -const EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 8.0); -const EdgeInsets largeFieldPadding = +EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 8.0); +EdgeInsets largeFieldPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 15); // Text Variables - - const TextStyle dropDownTradeTypeTextStyle = TextStyle(color: Colors.black54, fontWeight: FontWeight.bold, fontSize: 24); // Box Shadow -const BoxShadow buttonShadowBox = - BoxShadow(blurRadius: 10, color: shadowColor, offset: Offset(0, 4)); -const BoxShadow cardShadowBox = - BoxShadow(blurRadius: 3, color: Color.fromRGBO(0, 0, 0, 0.15), offset: Offset(0, 1)); +BoxShadow buttonShadowBox = + const BoxShadow(blurRadius: 10, color: shadowColor, offset: Offset(0, 4)); +BoxShadow inputShadowBox = +const BoxShadow(blurRadius: 1, color: shadowColor, offset: Offset(0, 2)); +BoxShadow cardShadowBox = const BoxShadow( + blurRadius: 3, color: Color.fromRGBO(0, 0, 0, 0.15), offset: Offset(0, 1)); diff --git a/lib/views/work/tabs/component/product_list_item.dart b/lib/views/work/tabs/component/product_list_item.dart index 6f550da..4ea3b3d 100644 --- a/lib/views/work/tabs/component/product_list_item.dart +++ b/lib/views/work/tabs/component/product_list_item.dart @@ -1,11 +1,15 @@ +import 'dart:ffi'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:satu/core/models/dialog_models.dart'; import 'package:satu/core/redux/actions/sell_actions.dart'; import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; +import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/routes/route_names.dart'; import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/shared_styles.dart'; @@ -16,6 +20,17 @@ import 'package:satu/widgets/ui/product_title_widget.dart'; import 'dialog_edit_product.dart'; class ProductListItem extends StatefulWidget { + const ProductListItem( + {Key? key, + this.name = '', + this.ean, + this.categoryName, + this.price, + this.count, + this.isOdd, + this.transactionId}) + : super(key: key); + final String name; final String? ean; final String? categoryName; @@ -24,10 +39,6 @@ class ProductListItem extends StatefulWidget { final bool? isOdd; final int? transactionId; - const ProductListItem( - {Key? key, this.name = '', this.ean, this.categoryName, this.price, this.count, this.isOdd, this.transactionId}) - : super(key: key); - @override _ProductListItemState createState() => _ProductListItemState(); } @@ -38,7 +49,7 @@ class _ProductListItemState extends State { void _onItemTapped(BuildContext context) { Navigator.of(context).push(MaterialPageRoute( - builder: (BuildContext context) => AddByBarcodeView())); + builder: (BuildContext context) => const AddByBarcodeView())); } @override @@ -61,13 +72,17 @@ class _ProductListItemState extends State { context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text("Внимание"), - content: Text("Удалить товар \"${this.widget.name}\" - ${widget.count} ед. ?"), + title: const Text('Внимание'), + content: Text('Удалить товар ' + '"${widget.name}"' + ' - ${widget.count} ед. ?'), actions: [ - TextButton(onPressed: () => Navigator.of(context).pop(true), child: const Text("Удалить")), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Удалить')), TextButton( onPressed: () => Navigator.of(context).pop(false), - child: const Text("Отмена"), + child: const Text('Отмена'), ), ], ); @@ -75,19 +90,25 @@ class _ProductListItemState extends State { ); }, onDismissed: (direction) { - Redux.store!.dispatch(removeSellItem(transactionId: this.widget.transactionId!)); + Redux.store! + .dispatch(removeSellItem(transactionId: widget.transactionId!)); }, - key: Key(widget.name ), + key: Key(widget.name), child: ListTile( //onTap: () => _onItemTapped(context), onTap: () {}, - contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), + contentPadding: + const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), title: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: ProductTitleWidget(name: widget.name, ean: widget.ean, categoryName: widget.categoryName, ), + child: ProductTitleWidget( + name: widget.name, + ean: widget.ean, + categoryName: widget.categoryName, + ), ), SizedBox( width: 100.w, @@ -98,7 +119,10 @@ class _ProductListItemState extends State { padding: const EdgeInsets.only(bottom: 3.0), child: Text( '${widget.price} ₸', - style: TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w500, color: textColor), + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + color: textColor), ), ), Row( @@ -106,17 +130,21 @@ class _ProductListItemState extends State { children: [ Material( color: Colors.transparent, - borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), + borderRadius: + BorderRadius.circular(ScreenUtil().radius(5)), child: InkWell( onTap: () { - Redux.store! - .dispatch(counterSellItem(transactionId: widget.transactionId!, counter: 1.0)); + Redux.store!.dispatch(counterOrEditSellItem( + transactionId: widget.transactionId!, + counter: 1.0)); }, child: Container( decoration: BoxDecoration( //color: whiteColor, - borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), - border: Border.all(width: 1.0.sp, color: successColor)), + borderRadius: BorderRadius.circular( + ScreenUtil().radius(5)), + border: Border.all( + width: 1.0.sp, color: successColor)), child: Icon( Icons.add, color: successColor, @@ -130,11 +158,12 @@ class _ProductListItemState extends State { margin: EdgeInsets.symmetric(horizontal: 5.w), decoration: BoxDecoration( color: whiteColor, - borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), + borderRadius: + BorderRadius.circular(ScreenUtil().radius(5)), boxShadow: [cardShadowBox]), child: InkWell( - onTap: (){ - _dialogService.showConfirmationDialogInput(description: 'asd'); + onTap: () { + editProductModal(); }, child: Padding( padding: EdgeInsets.symmetric(vertical: 6.0.w), @@ -142,7 +171,8 @@ class _ProductListItemState extends State { width: 45.w, child: Text( '${widget.count} шт', - style: TextStyle(fontSize: 8.sp, color: placeholderColor), + style: TextStyle( + fontSize: 10.sp, color: placeholderColor), textAlign: TextAlign.center, ), ), @@ -151,23 +181,31 @@ class _ProductListItemState extends State { ), Material( color: Colors.transparent, - borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), + borderRadius: + BorderRadius.circular(ScreenUtil().radius(5)), child: InkWell( onTap: () { if (widget.count! > 1.0) { - Redux.store! - .dispatch(counterSellItem(transactionId: this.widget.transactionId!, counter: -1.0)); + Redux.store!.dispatch(counterOrEditSellItem( + transactionId: widget.transactionId!, + counter: -1.0)); } }, child: Container( decoration: BoxDecoration( //color: whiteColor, - borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), + borderRadius: BorderRadius.circular( + ScreenUtil().radius(5)), border: Border.all( - width: 1.0.sp, color: widget.count! <= 1.0 ? disableColor : dangerColor)), + width: 1.0.sp, + color: widget.count! <= 1.0 + ? disableColor + : dangerColor)), child: Icon( Icons.remove, - color: widget.count! <= 1.0 ? disableColor : dangerColor, + color: widget.count! <= 1.0 + ? disableColor + : dangerColor, size: 20.0.sp, ), ), @@ -184,6 +222,24 @@ class _ProductListItemState extends State { ), ); } + + Future editProductModal() async { + final DialogResponse response = + await _dialogService.showConfirmationDialogInput( + title: widget.name, + requestCount: formatDecimal(widget.count!.toDouble()), + requestPrice: formatDecimal(widget.price!.toDouble())); + if (response.confirmed) { + if (isNumeric(response.responsePrice) && + isNumeric(response.responseCount)) { + Redux.store!.dispatch(counterOrEditSellItem( + transactionId: widget.transactionId!, + counter: num.parse(response.responseCount!), + price: num.parse(response.responsePrice!), + )); + } else { + _dialogService.showDialog(description: 'Не верный формат'); + } + } + } } - - diff --git a/lib/widgets/buttons/busy_button.dart b/lib/widgets/buttons/busy_button.dart index 1fa3169..ba7ca3b 100644 --- a/lib/widgets/buttons/busy_button.dart +++ b/lib/widgets/buttons/busy_button.dart @@ -37,20 +37,20 @@ class _BusyButtonState extends State { type: MaterialType.transparency, child: InkWell( onTap: () { - if(!(widget.busy || !widget.enabled)) - widget.onPressed(); + if (!(widget.busy || !widget.enabled)) widget.onPressed(); }, child: AnimatedContainer( height: 40.h, duration: const Duration(milliseconds: 300), alignment: Alignment.center, - //margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0 ), child: !widget.busy ? Text( widget.title, textAlign: TextAlign.center, style: TextStyle( - fontWeight: FontWeight.w400, color: blackColor, fontSize: 12.sp ), + fontWeight: FontWeight.w400, + color: blackColor, + fontSize: 14.sp), //minFontSize: 2, maxLines: 1, ) diff --git a/lib/widgets/dialog/dialog_manager.dart b/lib/widgets/dialog/dialog_manager.dart index 13c0083..5b104c3 100644 --- a/lib/widgets/dialog/dialog_manager.dart +++ b/lib/widgets/dialog/dialog_manager.dart @@ -1,9 +1,16 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; import 'package:satu/core/models/dialog_models.dart'; import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/utils/locator.dart'; +import 'package:satu/shared/app_colors.dart'; +import 'package:satu/shared/ui_helpers.dart'; +import 'package:satu/widgets/buttons/busy_button.dart'; +import 'package:satu/widgets/fields/input_field.dart'; +import 'package:satu/widgets/fields/input_field_rounded.dart'; class DialogManager extends StatefulWidget { final Widget child; @@ -15,18 +22,21 @@ class DialogManager extends StatefulWidget { class _DialogManagerState extends State { final DialogService _dialogService = locator(); - late TextEditingController _controller; + late TextEditingController _controllerPrice; + late TextEditingController _controllerCount; @override void initState() { super.initState(); - _controller = new TextEditingController(); + _controllerPrice = TextEditingController(); + _controllerCount = TextEditingController(); _dialogService.registerDialogListener(_showDialog, _showDialogInput); } @override void dispose() { - _controller.dispose(); + _controllerPrice.dispose(); + _controllerCount.dispose(); super.dispose(); } @@ -36,7 +46,6 @@ class _DialogManagerState extends State { } void _showDialog(DialogRequest request) { - var isConfirmationDialog = request.cancelTitle != null; showDialog( context: context, @@ -49,13 +58,13 @@ class _DialogManagerState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - request.title!, + request.title, style: const TextStyle(fontWeight: FontWeight.bold), ), //Divider(), ], ), - content: Text(request.description!), + content: Text(request.description), actions: [ if (isConfirmationDialog) TextButton( @@ -70,95 +79,95 @@ class _DialogManagerState extends State { _dialogService .dialogComplete(DialogResponse(confirmed: true)); }, - child: Text(request.buttonTitle!), + child: Text(request.buttonTitle), ), ], )); } void _showDialogInput(DialogRequest request) { - var isConfirmationDialog = request.cancelTitle != null; + final bool isConfirmationDialog = request.cancelTitle != null; - _controller.clear(); + _controllerPrice.text = request.requestPrice ?? ''; + _controllerCount.text = request.requestCount ?? ''; - var maskFormatter = new MaskTextInputFormatter( - mask: '+% (###) ###-##-##', - filter: {"#": RegExp(r'[0-9]'), "%": RegExp(r'[7]')}); - - var dialogController = showDialog( + final dialogController = showDialog( context: context, builder: (context) => AlertDialog( + backgroundColor: backgroundColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5.0), ), actionsPadding: const EdgeInsets.only(right: 15, bottom: 5), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - request.title!, - style: TextStyle(fontWeight: FontWeight.bold), - ), - //Divider(), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - //Text(request.description), - TextField( - autofocus: true, - decoration: InputDecoration( - labelText: request.description, - hintText: request.formatType == "phone" - ? "+7 (123) 456-78-90" - : ""), - controller: _controller, - onSubmitted: (value) { - _dialogService - .dialogComplete(DialogResponse(confirmed: false)); - }, - keyboardType: TextInputType.phone, - inputFormatters: [ - if (request.formatType == "phone") maskFormatter, - if (request.formatType == null) - FilteringTextInputFormatter.allow(RegExp("^[0-9.]*")), - ], - ) - ], - ), - actions: [ - if (isConfirmationDialog) - RaisedButton( - //color: redColor, - child: Text( - request.cancelTitle!, - style: TextStyle(fontSize: 18), + content: SizedBox( + width: ScreenUtil().setWidth(ScreenUtil().screenWidth * 0.9), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + request.title, + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: ScreenUtil().setSp(14)), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - onPressed: () { - _dialogService - .dialogComplete(DialogResponse(confirmed: false)); - }, - ), - SizedBox( - width: 5, + verticalSpaceSmall, + const Divider(), + verticalSpaceSmall, + InputFieldRounded( + controller: _controllerPrice, + placeholder: '', + suffixText: '₸', + labelText: 'Стоимость товара', + textInputType: + const TextInputType.numberWithOptions(decimal: true), + ), + InputFieldRounded( + textInputType: + const TextInputType.numberWithOptions(decimal: true), + controller: _controllerCount, + placeholder: '', + suffixText: 'шт', + labelText: 'Количество товара', + ), + verticalSpaceSmall, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (isConfirmationDialog) + Expanded( + child: TextButton( + onPressed: () { + _dialogService.dialogComplete( + DialogResponse(confirmed: false)); + }, + //color: redColor, + child: Text( + request.cancelTitle!, + style: TextStyle( + fontSize: 14.sp, color: placeholderColor), + ), + ), + ), + Expanded( + child: BusyButton( + title: request.buttonTitle, + onPressed: () { + final String _price = _controllerPrice.text; + final String _count = _controllerCount.text; + _dialogService.dialogComplete(DialogResponse( + confirmed: true, + responsePrice: _price.replaceAll(',', '.'), + responseCount: _count.replaceAll(',', '.'))); + }, + ), + ), + ], + ) + ], ), - RaisedButton( - //color: primaryColor, - child: Text( - request.buttonTitle!, - style: TextStyle(fontSize: 18), - ), - onPressed: () { - String _result = _controller.text; - if (request.formatType == "phone") { - _result = maskFormatter.getUnmaskedText(); - } - _dialogService.dialogComplete( - DialogResponse(confirmed: true, responseText: _result)); - }, - ), - ], + ), )); dialogController.whenComplete(() { //hook when press overlay and response not completed diff --git a/lib/widgets/fields/input_field_rounded.dart b/lib/widgets/fields/input_field_rounded.dart new file mode 100644 index 0000000..203d237 --- /dev/null +++ b/lib/widgets/fields/input_field_rounded.dart @@ -0,0 +1,195 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:satu/shared/app_colors.dart'; +import 'package:satu/shared/shared_styles.dart'; +import 'package:satu/shared/ui_helpers.dart'; + +import 'note_text.dart'; + +class InputFieldRounded extends StatefulWidget { + final TextEditingController controller; + final TextInputType textInputType; + final bool password; + final bool search; + final bool isReadOnly; + final String placeholder; + final String? validationMessage; + final Function? enterPressed; + final bool smallVersion; + final FocusNode? fieldFocusNode; + final FocusNode? nextFocusNode; + final TextInputAction textInputAction; + final bool multiline; + final String? additionalNote; + final Function(String)? onChanged; + final TextInputFormatter? formatter; + final String? initialValue; + final String? labelText; + final String? suffixText; + + InputFieldRounded( + {required this.controller, + required this.placeholder, + this.enterPressed, + this.fieldFocusNode, + this.nextFocusNode, + this.additionalNote, + this.onChanged, + this.formatter, + this.suffixText, + this.initialValue, + this.validationMessage, + this.textInputAction = TextInputAction.next, + this.textInputType = TextInputType.text, + this.password = false, + this.search = false, + this.isReadOnly = false, + this.multiline = false, + this.smallVersion = false, + this.labelText}); + + @override + _InputFieldRoundedState createState() => _InputFieldRoundedState(); +} + +class _InputFieldRoundedState extends State { + late bool isPassword; + late bool isSearch; + double fieldHeight = 40; + + @override + void initState() { + super.initState(); + isPassword = widget.password; + isSearch = widget.search; + if (widget.search == true) { + widget.fieldFocusNode!.addListener(() { + if (widget.fieldFocusNode!.hasFocus) { + setState(() { + isSearch = !isSearch; + }); + } + }); + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.labelText != null) + NoteText( + widget.labelText ?? '', + fontSize: ScreenUtil().setSp(14), + ), + if (widget.labelText != null) verticalSpace(5), + Container( + constraints: BoxConstraints( + minHeight: widget.smallVersion ? 40.h : fieldHeight), + alignment: Alignment.centerLeft, + padding: fieldPadding, + decoration: + widget.isReadOnly ? disabledFieldDecoration : BoxDecoration( color: whiteColor , borderRadius: BorderRadius.circular(6.0), boxShadow: [ + inputShadowBox + ]), + child: Row( + children: [ + GestureDetector( + onTap: () { + if (isSearch) { + widget.fieldFocusNode!.requestFocus(); + } else { + setState(() { + isSearch = !isSearch; + }); + FocusScope.of(context) + .requestFocus(new FocusNode()); //remove focus + WidgetsBinding.instance!.addPostFrameCallback( + (_) => widget.controller.clear()); // clear content + } + }, + child: widget.search + ? Container( + width: fieldHeight, + height: fieldHeight, + alignment: Alignment.center, + child: Icon(isSearch ? Icons.search : Icons.search_off, + color: placeholderColor)) + : Container(), + ), + Expanded( + child: TextFormField( + style: TextStyle( + color: textColor, + fontSize: widget.smallVersion + ? ScreenUtil().setSp(12) + : ScreenUtil().setSp(15)), + controller: widget.controller, + keyboardType: widget.textInputType, + focusNode: widget.fieldFocusNode, + textInputAction: widget.textInputAction, + maxLines: widget.multiline ? null : 1, + onChanged: widget.onChanged, + initialValue: widget.initialValue, + inputFormatters: + widget.formatter != null ? [widget.formatter!] : null, + onEditingComplete: () { + if (widget.enterPressed != null) { + FocusScope.of(context).requestFocus(FocusNode()); + widget.enterPressed!(); + } + }, + onFieldSubmitted: (value) { + if (widget.nextFocusNode != null) { + widget.nextFocusNode!.requestFocus(); + } + }, + obscureText: isPassword, + readOnly: widget.isReadOnly, + decoration: InputDecoration( + hintText: widget.placeholder, + filled: true, + fillColor: whiteColor, + border: InputBorder.none, + suffixText: widget.suffixText, + hintStyle: TextStyle( + fontSize: widget.smallVersion + ? ScreenUtil().setSp(12) + : ScreenUtil().setSp(15), + color: placeholderColor)), + ), + ), + GestureDetector( + onTap: () => setState(() { + isPassword = !isPassword; + }), + child: widget.password + ? Container( + width: fieldHeight, + height: fieldHeight, + alignment: Alignment.center, + child: Icon( + isPassword + ? Icons.visibility + : Icons.visibility_off, + color: textColor)) + : Container(), + ), + ], + ), + ), + if (widget.validationMessage != null) + NoteText( + widget.validationMessage ?? '', + color: dangerColor, + ), + if (widget.additionalNote != null) verticalSpace(5), + if (widget.additionalNote != null) + NoteText(widget.additionalNote ?? ''), + verticalSpaceSmall + ], + ); + } +} diff --git a/lib/widgets/fields/note_text.dart b/lib/widgets/fields/note_text.dart index c1fc495..ab695fa 100644 --- a/lib/widgets/fields/note_text.dart +++ b/lib/widgets/fields/note_text.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:satu/shared/app_colors.dart'; class NoteText extends StatelessWidget { + const NoteText(this.text, {this.textAlign, this.color, this.fontSize}); final String text; final TextAlign? textAlign; final Color? color; final double? fontSize; - const NoteText(this.text, {this.textAlign, this.color, this.fontSize}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/ui/product_title_widget.dart b/lib/widgets/ui/product_title_widget.dart index 58b0bad..473abe2 100644 --- a/lib/widgets/ui/product_title_widget.dart +++ b/lib/widgets/ui/product_title_widget.dart @@ -18,20 +18,20 @@ class ProductTitleWidget extends StatelessWidget { children: [ Text( name, - style: TextStyle(fontSize: 12.sp, color: textColor), + style: TextStyle(fontSize: 14.sp, color: textColor), overflow: TextOverflow.ellipsis, maxLines: 2, ), verticalSpaceTiny, if (ean != null) Text( - 'Штрих-код: ${ean}', - style: TextStyle(color: placeholderColor, fontSize: 8.sp), + 'Штрих-код: $ean', + style: TextStyle(color: placeholderColor, fontSize: 10.sp), ), if (categoryName != null) Text( - 'Категория: ${categoryName}', - style: TextStyle(color: placeholderColor, fontSize: 8.sp), + 'Категория: $categoryName', + style: TextStyle(color: placeholderColor, fontSize: 10.sp), ) ], );