product edit modal finished

null-safety-migration
suvaissov 2021-07-19 12:33:35 +06:00
parent 94823fcd56
commit 91e43dbcf4
13 changed files with 497 additions and 194 deletions

View File

@ -24,4 +24,7 @@ linter:
always_put_required_named_parameters_first: true always_put_required_named_parameters_first: true
# Util classes are awesome! # Util classes are awesome!
avoid_classes_with_only_static_members: true 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

View File

@ -1,3 +1,5 @@
import 'package:satu/core/utils/utils_parse.dart';
/// user_id : 10 /// user_id : 10
/// company_id : 281 /// company_id : 281
/// kassa_id : 3 /// kassa_id : 3
@ -19,13 +21,13 @@ class AuthResponse {
static AuthResponse fromMap(dynamic map) { static AuthResponse fromMap(dynamic map) {
final AuthResponse authResponseBean = AuthResponse(); final AuthResponse authResponseBean = AuthResponse();
authResponseBean.userId = map['user_id'] as int; authResponseBean.userId = cast<int>(map['user_id']);
authResponseBean.companyId = map['company_id'] as int; authResponseBean.companyId = cast<int>(map['company_id']);
authResponseBean.kassaId = map['kassa_id'] as int; authResponseBean.kassaId = cast<int>(map['kassa_id']);
authResponseBean.token = map['token']?.toString(); authResponseBean.token = cast<String>(map['token']);
authResponseBean.authAt = map['auth_at']?.toString(); authResponseBean.authAt = cast<String>(map['auth_at']);
authResponseBean.shard = map['shard'] as int; authResponseBean.shard = cast<int>(map['shard']);
authResponseBean.message = map['message']?.toString(); authResponseBean.message = cast<String>(map['message']);
authResponseBean.operation = map['operation'] as bool; authResponseBean.operation = map['operation'] as bool;
return authResponseBean; return authResponseBean;
} }

View File

@ -1,30 +1,35 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class DialogRequest { class DialogRequest {
final String? title;
final String? description;
final String? buttonTitle;
final String? cancelTitle;
final String? formatType;
DialogRequest( DialogRequest(
{@required this.title, {
@required this.description, required this.title,
@required this.buttonTitle, required this.description,
this.cancelTitle, required this.buttonTitle,
this.formatType}); 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 { class DialogResponse {
//final String fieldOne;
//final String fieldTwo;
final String? responseText;
final bool? confirmed;
DialogResponse({ DialogResponse({
//this.fieldOne, required this.confirmed,
//this.fieldTwo, this.responsePrice,
this.responseText, this.responseCount,
this.confirmed,
}); });
final String? responsePrice;
final String? responseCount;
final bool confirmed;
} }

View File

@ -28,7 +28,8 @@ final Logger log = getLogger('SetSellStateAction');
final DbService _dbService = locator<DbService>(); final DbService _dbService = locator<DbService>();
ThunkAction<AppState> counterSellItem({required int transactionId, required num counter}) { ThunkAction<AppState> counterOrEditSellItem(
{required int transactionId, required num counter, num? price}) {
return (Store<AppState> store) async { return (Store<AppState> store) async {
log.i('counterSellItem'); log.i('counterSellItem');
int? appCompanyId = store.state.userState!.auth!.companyId; int? appCompanyId = store.state.userState!.auth!.companyId;
@ -36,19 +37,33 @@ ThunkAction<AppState> counterSellItem({required int transactionId, required num
Transaction? transaction; Transaction? transaction;
if (uuid != null ) { if (uuid != null) {
List<Map<String, dynamic>> set = await _dbService.queryRowsWithWhere( final List<Map<String, dynamic>> set =
transactionTableName, await _dbService.queryRowsWithWhere(
'$transactionColumnAppCompanyId = ? and $transactionColumnStatus = ? and ${transactionColumnType} = ? and ${transactionColumnId} = ?', transactionTableName,
[appCompanyId, transactionStatusPrepare, transactionTypeSell, transactionId], '$transactionColumnAppCompanyId = ? '
orderBy: '$transactionColumnCreatedAt desc'); ' and $transactionColumnStatus = ? '
' and $transactionColumnType = ? '
' and $transactionColumnId = ?',
[
appCompanyId,
transactionStatusPrepare,
transactionTypeSell,
transactionId
],
orderBy: '$transactionColumnCreatedAt desc');
if (set.isNotEmpty) { if (set.isNotEmpty) {
transaction = Transaction.fromMap(set.first); transaction = Transaction.fromMap(set.first);
} }
} }
if (transaction != null) { if (transaction != null) {
ProductDao item = ProductDao.fromMap(jsonDecode(transaction.data!)); final ProductDao item = ProductDao.fromMap(jsonDecode(transaction.data!));
item.count = (item.count ?? 0) + counter; if (price != null) {
item.price = price;
item.count = counter;
} else {
item.count = (item.count ?? 0) + counter;
}
transaction.data = jsonEncode(item.toMap()); transaction.data = jsonEncode(item.toMap());
_dbService.update(transactionTableName, transaction.toMap()); _dbService.update(transactionTableName, transaction.toMap());
} }
@ -60,21 +75,24 @@ ThunkAction<AppState> counterSellItem({required int transactionId, required num
ThunkAction<AppState> addSellItem({required Good good, String? excise}) { ThunkAction<AppState> addSellItem({required Good good, String? excise}) {
return (Store<AppState> store) async { return (Store<AppState> store) async {
log.i('addSellItem'); 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; String? uuid = store.state.sellState!.transactionState!.uuid;
Transaction? transaction; Transaction? transaction;
if (uuid != null ) { if (uuid != null) {
List<Map<String, dynamic>> set = await _dbService.queryRowsWithWhere( List<Map<String, dynamic>> set = await _dbService.queryRowsWithWhere(
transactionTableName, transactionTableName,
'$transactionColumnAppCompanyId = ? and $transactionColumnStatus = ? and ${transactionColumnType} = ?', '$transactionColumnAppCompanyId = ? '
' and $transactionColumnStatus = ? '
' and $transactionColumnType = ?',
[appCompanyId, transactionStatusPrepare, transactionTypeSell], [appCompanyId, transactionStatusPrepare, transactionTypeSell],
orderBy: '$transactionColumnCreatedAt desc'); orderBy: '$transactionColumnCreatedAt desc');
if (set.isNotEmpty) { if (set.isNotEmpty) {
for (Map<String, dynamic> map in set) { for (Map<String, dynamic> map in set) {
Transaction _transaction = Transaction.fromMap(map); 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) { if (_product.id == good.id && _product.excise == excise) {
transaction = _transaction; transaction = _transaction;
break; break;
@ -100,8 +118,8 @@ ThunkAction<AppState> addSellItem({required Good good, String? excise}) {
..excise = excise; ..excise = excise;
//category add logic //category add logic
if (good.categoryId != null) { if (good.categoryId != null) {
List<Map<String, dynamic>> set = List<Map<String, dynamic>> set = await _dbService
await _dbService.queryRowsWithWhere(categoryTableName, 'id = ?', [good.categoryId]); .queryRowsWithWhere(categoryTableName, 'id = ?', [good.categoryId]);
if (set.isNotEmpty) { if (set.isNotEmpty) {
Category category = Category.fromMap(set.first); Category category = Category.fromMap(set.first);
item.categoryId = category.id; item.categoryId = category.id;
@ -131,7 +149,6 @@ ThunkAction<AppState> addSellItem({required Good good, String? excise}) {
ThunkAction<AppState> removeSellItem({required int transactionId}) { ThunkAction<AppState> removeSellItem({required int transactionId}) {
return (Store<AppState> store) async { return (Store<AppState> store) async {
int? appCompanyId = store.state.userState!.auth!.companyId; int? appCompanyId = store.state.userState!.auth!.companyId;
String? uuid = store.state.sellState!.transactionState!.uuid; String? uuid = store.state.sellState!.transactionState!.uuid;
@ -153,7 +170,7 @@ Future<void> removeAllSellData(Store<AppState> store) async {
'$transactionColumnAppCompanyId = ? ' '$transactionColumnAppCompanyId = ? '
' and $transactionColumnStatus = ? ' ' and $transactionColumnStatus = ? '
' and ${transactionColumnType} = ?' ' and ${transactionColumnType} = ?'
' and ${transactionColumnUuid} = ?', ' and ${transactionColumnUuid} = ?',
[appCompanyId, transactionStatusPrepare, transactionTypeSell, uuid]); [appCompanyId, transactionStatusPrepare, transactionTypeSell, uuid]);
await loadSellData(store); await loadSellData(store);
} catch (e, stack) { } catch (e, stack) {
@ -179,7 +196,8 @@ Future<void> loadSellData(Store<AppState> store) async {
productDao.transactionId = transaction.id; productDao.transactionId = transaction.id;
list.add(productDao); 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) { } catch (e, stack) {
log.e('loadSellData', e, stack); log.e('loadSellData', e, stack);
} }

View File

@ -4,7 +4,8 @@ import 'package:flutter/cupertino.dart';
import 'package:satu/core/models/dialog_models.dart'; import 'package:satu/core/models/dialog_models.dart';
class DialogService { class DialogService {
final GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _dialogNavigationKey =
GlobalKey<NavigatorState>();
late Function(DialogRequest)? _showDialogListener; late Function(DialogRequest)? _showDialogListener;
late Function(DialogRequest)? _showDialogInputListener; late Function(DialogRequest)? _showDialogInputListener;
Completer<DialogResponse>? _dialogCompleter; Completer<DialogResponse>? _dialogCompleter;
@ -22,14 +23,14 @@ class DialogService {
/// Calls the dialog listener and returns a Future that will wait for dialogComplete. /// Calls the dialog listener and returns a Future that will wait for dialogComplete.
Future<DialogResponse> showDialog({ Future<DialogResponse> showDialog({
String title = 'Aman Касса', String title = 'SATU',
String? description, String? description,
String buttonTitle = 'Ok', String buttonTitle = 'Ok',
}) { }) {
_dialogCompleter = Completer<DialogResponse>(); _dialogCompleter = Completer<DialogResponse>();
_showDialogListener!(DialogRequest( _showDialogListener!(DialogRequest(
title: title, title: title,
description: description, description: description ?? '',
buttonTitle: buttonTitle, buttonTitle: buttonTitle,
)); ));
return _dialogCompleter!.future; return _dialogCompleter!.future;
@ -43,26 +44,29 @@ class DialogService {
String cancelTitle = 'Cancel'}) { String cancelTitle = 'Cancel'}) {
_dialogCompleter = Completer<DialogResponse>(); _dialogCompleter = Completer<DialogResponse>();
_showDialogListener!(DialogRequest( _showDialogListener!(DialogRequest(
title: title, title: title ?? '',
description: description, description: description ?? '',
buttonTitle: confirmationTitle, buttonTitle: confirmationTitle,
cancelTitle: cancelTitle)); cancelTitle: cancelTitle));
return _dialogCompleter!.future; return _dialogCompleter!.future;
} }
Future<DialogResponse> showConfirmationDialogInput( Future<DialogResponse> showConfirmationDialogInput({
{String title = ' Aman Касса', required String title,
String? description, required String requestPrice,
String confirmationTitle = 'Ok', required String requestCount,
String cancelTitle = 'Cancel', String? description,
String? formatType}) { String confirmationTitle = 'ПОДТВЕРДИТЬ',
String cancelTitle = 'Отмена',
}) {
_dialogCompleter = Completer<DialogResponse>(); _dialogCompleter = Completer<DialogResponse>();
_showDialogInputListener!(DialogRequest( _showDialogInputListener!(DialogRequest(
title: title, title: title,
description: description, description: description ?? '',
buttonTitle: confirmationTitle, buttonTitle: confirmationTitle,
cancelTitle: cancelTitle, cancelTitle: cancelTitle,
formatType: formatType)); requestPrice: requestPrice,
requestCount: requestCount));
return _dialogCompleter!.future; return _dialogCompleter!.future;
} }

View File

@ -3,4 +3,16 @@ List<String>? parseListString(Iterable<dynamic>? json){
return List<String>.from(json); return List<String>.from(json);
} }
T? cast<T>(x) => x is T ? x : null; T? cast<T>(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();
}

View File

@ -3,38 +3,37 @@ import 'package:flutter/material.dart';
import 'app_colors.dart'; import 'app_colors.dart';
// Box Decorations // Box Decorations
BoxDecoration fieldDecoration = BoxDecoration(color: whiteColor); BoxDecoration fieldDecoration = const BoxDecoration(color: whiteColor);
BoxDecoration disabledFieldDecoration = BoxDecoration( BoxDecoration disabledFieldDecoration = BoxDecoration(
borderRadius: BorderRadius.circular(5), color: Colors.grey[100]); borderRadius: BorderRadius.circular(5), color: Colors.grey[100]);
const LinearGradient primaryGradient = LinearGradient( LinearGradient primaryGradient = const LinearGradient(
colors: [ colors: [
primaryGrStartColor, primaryGrStartColor,
primaryGrEndColor, primaryGrEndColor,
], ],
begin: const FractionalOffset(0.0, 0.0), begin: FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 0.0), end: FractionalOffset(1.0, 0.0),
stops: [0.0, 1.0], stops: [0.0, 1.0]);
tileMode: TileMode.clamp);
// Field Variables // Field Variables
const double fieldHeight = 55; const double fieldHeight = 55;
const double smallFieldHeight = 40; const double smallFieldHeight = 40;
const double inputFieldBottomMargin = 30; const double inputFieldBottomMargin = 30;
const double inputFieldSmallBottomMargin = 0; const double inputFieldSmallBottomMargin = 0;
const EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 8.0); EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 8.0);
const EdgeInsets largeFieldPadding = EdgeInsets largeFieldPadding =
const EdgeInsets.symmetric(horizontal: 15, vertical: 15); const EdgeInsets.symmetric(horizontal: 15, vertical: 15);
// Text Variables // Text Variables
const TextStyle dropDownTradeTypeTextStyle = const TextStyle dropDownTradeTypeTextStyle =
TextStyle(color: Colors.black54, fontWeight: FontWeight.bold, fontSize: 24); TextStyle(color: Colors.black54, fontWeight: FontWeight.bold, fontSize: 24);
// Box Shadow // Box Shadow
const BoxShadow buttonShadowBox = BoxShadow buttonShadowBox =
BoxShadow(blurRadius: 10, color: shadowColor, offset: Offset(0, 4)); const BoxShadow(blurRadius: 10, color: shadowColor, offset: Offset(0, 4));
const BoxShadow cardShadowBox = BoxShadow inputShadowBox =
BoxShadow(blurRadius: 3, color: Color.fromRGBO(0, 0, 0, 0.15), offset: Offset(0, 1)); 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));

View File

@ -1,11 +1,15 @@
import 'dart:ffi';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.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/actions/sell_actions.dart';
import 'package:satu/core/redux/store.dart'; import 'package:satu/core/redux/store.dart';
import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/dialog_service.dart';
import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/utils/locator.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/routes/route_names.dart';
import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/app_colors.dart';
import 'package:satu/shared/shared_styles.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'; import 'dialog_edit_product.dart';
class ProductListItem extends StatefulWidget { 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 name;
final String? ean; final String? ean;
final String? categoryName; final String? categoryName;
@ -24,10 +39,6 @@ class ProductListItem extends StatefulWidget {
final bool? isOdd; final bool? isOdd;
final int? transactionId; 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 @override
_ProductListItemState createState() => _ProductListItemState(); _ProductListItemState createState() => _ProductListItemState();
} }
@ -38,7 +49,7 @@ class _ProductListItemState extends State<ProductListItem> {
void _onItemTapped(BuildContext context) { void _onItemTapped(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => AddByBarcodeView())); builder: (BuildContext context) => const AddByBarcodeView()));
} }
@override @override
@ -61,13 +72,17 @@ class _ProductListItemState extends State<ProductListItem> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: const Text("Внимание"), title: const Text('Внимание'),
content: Text("Удалить товар \"${this.widget.name}\" - ${widget.count} ед. ?"), content: Text('Удалить товар '
'"${widget.name}"'
' - ${widget.count} ед. ?'),
actions: <Widget>[ actions: <Widget>[
TextButton(onPressed: () => Navigator.of(context).pop(true), child: const Text("Удалить")), TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Удалить')),
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(false), onPressed: () => Navigator.of(context).pop(false),
child: const Text("Отмена"), child: const Text('Отмена'),
), ),
], ],
); );
@ -75,19 +90,25 @@ class _ProductListItemState extends State<ProductListItem> {
); );
}, },
onDismissed: (direction) { 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( child: ListTile(
//onTap: () => _onItemTapped(context), //onTap: () => _onItemTapped(context),
onTap: () {}, onTap: () {},
contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), contentPadding:
const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0),
title: Row( title: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: ProductTitleWidget(name: widget.name, ean: widget.ean, categoryName: widget.categoryName, ), child: ProductTitleWidget(
name: widget.name,
ean: widget.ean,
categoryName: widget.categoryName,
),
), ),
SizedBox( SizedBox(
width: 100.w, width: 100.w,
@ -98,7 +119,10 @@ class _ProductListItemState extends State<ProductListItem> {
padding: const EdgeInsets.only(bottom: 3.0), padding: const EdgeInsets.only(bottom: 3.0),
child: Text( child: Text(
'${widget.price}', '${widget.price}',
style: TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w500, color: textColor), style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: textColor),
), ),
), ),
Row( Row(
@ -106,17 +130,21 @@ class _ProductListItemState extends State<ProductListItem> {
children: [ children: [
Material( Material(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), borderRadius:
BorderRadius.circular(ScreenUtil().radius(5)),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
Redux.store! Redux.store!.dispatch(counterOrEditSellItem(
.dispatch(counterSellItem(transactionId: widget.transactionId!, counter: 1.0)); transactionId: widget.transactionId!,
counter: 1.0));
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
//color: whiteColor, //color: whiteColor,
borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), borderRadius: BorderRadius.circular(
border: Border.all(width: 1.0.sp, color: successColor)), ScreenUtil().radius(5)),
border: Border.all(
width: 1.0.sp, color: successColor)),
child: Icon( child: Icon(
Icons.add, Icons.add,
color: successColor, color: successColor,
@ -130,11 +158,12 @@ class _ProductListItemState extends State<ProductListItem> {
margin: EdgeInsets.symmetric(horizontal: 5.w), margin: EdgeInsets.symmetric(horizontal: 5.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: whiteColor, color: whiteColor,
borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), borderRadius:
BorderRadius.circular(ScreenUtil().radius(5)),
boxShadow: [cardShadowBox]), boxShadow: [cardShadowBox]),
child: InkWell( child: InkWell(
onTap: (){ onTap: () {
_dialogService.showConfirmationDialogInput(description: 'asd'); editProductModal();
}, },
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: 6.0.w), padding: EdgeInsets.symmetric(vertical: 6.0.w),
@ -142,7 +171,8 @@ class _ProductListItemState extends State<ProductListItem> {
width: 45.w, width: 45.w,
child: Text( child: Text(
'${widget.count} шт', '${widget.count} шт',
style: TextStyle(fontSize: 8.sp, color: placeholderColor), style: TextStyle(
fontSize: 10.sp, color: placeholderColor),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@ -151,23 +181,31 @@ class _ProductListItemState extends State<ProductListItem> {
), ),
Material( Material(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), borderRadius:
BorderRadius.circular(ScreenUtil().radius(5)),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (widget.count! > 1.0) { if (widget.count! > 1.0) {
Redux.store! Redux.store!.dispatch(counterOrEditSellItem(
.dispatch(counterSellItem(transactionId: this.widget.transactionId!, counter: -1.0)); transactionId: widget.transactionId!,
counter: -1.0));
} }
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
//color: whiteColor, //color: whiteColor,
borderRadius: BorderRadius.circular(ScreenUtil().radius(5)), borderRadius: BorderRadius.circular(
ScreenUtil().radius(5)),
border: Border.all( 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( child: Icon(
Icons.remove, Icons.remove,
color: widget.count! <= 1.0 ? disableColor : dangerColor, color: widget.count! <= 1.0
? disableColor
: dangerColor,
size: 20.0.sp, size: 20.0.sp,
), ),
), ),
@ -184,6 +222,24 @@ class _ProductListItemState extends State<ProductListItem> {
), ),
); );
} }
Future<void> 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: 'Не верный формат');
}
}
}
} }

View File

@ -37,20 +37,20 @@ class _BusyButtonState extends State<BusyButton> {
type: MaterialType.transparency, type: MaterialType.transparency,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if(!(widget.busy || !widget.enabled)) if (!(widget.busy || !widget.enabled)) widget.onPressed();
widget.onPressed();
}, },
child: AnimatedContainer( child: AnimatedContainer(
height: 40.h, height: 40.h,
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
alignment: Alignment.center, alignment: Alignment.center,
//margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0 ),
child: !widget.busy child: !widget.busy
? Text( ? Text(
widget.title, widget.title,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w400, color: blackColor, fontSize: 12.sp ), fontWeight: FontWeight.w400,
color: blackColor,
fontSize: 14.sp),
//minFontSize: 2, //minFontSize: 2,
maxLines: 1, maxLines: 1,
) )

View File

@ -1,9 +1,16 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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:mask_text_input_formatter/mask_text_input_formatter.dart';
import 'package:satu/core/models/dialog_models.dart'; import 'package:satu/core/models/dialog_models.dart';
import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/dialog_service.dart';
import 'package:satu/core/utils/locator.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 { class DialogManager extends StatefulWidget {
final Widget child; final Widget child;
@ -15,18 +22,21 @@ class DialogManager extends StatefulWidget {
class _DialogManagerState extends State<DialogManager> { class _DialogManagerState extends State<DialogManager> {
final DialogService _dialogService = locator<DialogService>(); final DialogService _dialogService = locator<DialogService>();
late TextEditingController _controller; late TextEditingController _controllerPrice;
late TextEditingController _controllerCount;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = new TextEditingController(); _controllerPrice = TextEditingController();
_controllerCount = TextEditingController();
_dialogService.registerDialogListener(_showDialog, _showDialogInput); _dialogService.registerDialogListener(_showDialog, _showDialogInput);
} }
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controllerPrice.dispose();
_controllerCount.dispose();
super.dispose(); super.dispose();
} }
@ -36,7 +46,6 @@ class _DialogManagerState extends State<DialogManager> {
} }
void _showDialog(DialogRequest request) { void _showDialog(DialogRequest request) {
var isConfirmationDialog = request.cancelTitle != null; var isConfirmationDialog = request.cancelTitle != null;
showDialog<String>( showDialog<String>(
context: context, context: context,
@ -49,13 +58,13 @@ class _DialogManagerState extends State<DialogManager> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
request.title!, request.title,
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
//Divider(), //Divider(),
], ],
), ),
content: Text(request.description!), content: Text(request.description),
actions: <Widget>[ actions: <Widget>[
if (isConfirmationDialog) if (isConfirmationDialog)
TextButton( TextButton(
@ -70,95 +79,95 @@ class _DialogManagerState extends State<DialogManager> {
_dialogService _dialogService
.dialogComplete(DialogResponse(confirmed: true)); .dialogComplete(DialogResponse(confirmed: true));
}, },
child: Text(request.buttonTitle!), child: Text(request.buttonTitle),
), ),
], ],
)); ));
} }
void _showDialogInput(DialogRequest request) { 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( final dialogController = showDialog(
mask: '+% (###) ###-##-##',
filter: {"#": RegExp(r'[0-9]'), "%": RegExp(r'[7]')});
var dialogController = showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), borderRadius: BorderRadius.circular(5.0),
), ),
actionsPadding: const EdgeInsets.only(right: 15, bottom: 5), actionsPadding: const EdgeInsets.only(right: 15, bottom: 5),
title: Column( content: SizedBox(
crossAxisAlignment: CrossAxisAlignment.start, width: ScreenUtil().setWidth(ScreenUtil().screenWidth * 0.9),
children: <Widget>[ child: Column(
Text( mainAxisSize: MainAxisSize.min,
request.title!, children: <Widget>[
style: TextStyle(fontWeight: FontWeight.bold), Text(
), request.title,
//Divider(), style: TextStyle(
], fontWeight: FontWeight.w400,
), fontSize: ScreenUtil().setSp(14)),
content: Column( maxLines: 2,
mainAxisSize: MainAxisSize.min, overflow: TextOverflow.ellipsis,
children: <Widget>[
//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: <TextInputFormatter>[
if (request.formatType == "phone") maskFormatter,
if (request.formatType == null)
FilteringTextInputFormatter.allow(RegExp("^[0-9.]*")),
],
)
],
),
actions: <Widget>[
if (isConfirmationDialog)
RaisedButton(
//color: redColor,
child: Text(
request.cancelTitle!,
style: TextStyle(fontSize: 18),
), ),
onPressed: () { verticalSpaceSmall,
_dialogService const Divider(),
.dialogComplete(DialogResponse(confirmed: false)); verticalSpaceSmall,
}, InputFieldRounded(
), controller: _controllerPrice,
SizedBox( placeholder: '',
width: 5, 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(() { dialogController.whenComplete(() {
//hook when press overlay and response not completed //hook when press overlay and response not completed

View File

@ -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<InputFieldRounded> {
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: <Widget>[
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: <Widget>[
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
],
);
}
}

View File

@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/app_colors.dart';
class NoteText extends StatelessWidget { class NoteText extends StatelessWidget {
const NoteText(this.text, {this.textAlign, this.color, this.fontSize});
final String text; final String text;
final TextAlign? textAlign; final TextAlign? textAlign;
final Color? color; final Color? color;
final double? fontSize; final double? fontSize;
const NoteText(this.text, {this.textAlign, this.color, this.fontSize});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -18,20 +18,20 @@ class ProductTitleWidget extends StatelessWidget {
children: [ children: [
Text( Text(
name, name,
style: TextStyle(fontSize: 12.sp, color: textColor), style: TextStyle(fontSize: 14.sp, color: textColor),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
), ),
verticalSpaceTiny, verticalSpaceTiny,
if (ean != null) if (ean != null)
Text( Text(
'Штрих-код: ${ean}', 'Штрих-код: $ean',
style: TextStyle(color: placeholderColor, fontSize: 8.sp), style: TextStyle(color: placeholderColor, fontSize: 10.sp),
), ),
if (categoryName != null) if (categoryName != null)
Text( Text(
'Категория: ${categoryName}', 'Категория: $categoryName',
style: TextStyle(color: placeholderColor, fontSize: 8.sp), style: TextStyle(color: placeholderColor, fontSize: 10.sp),
) )
], ],
); );