qr scanner

null-safety-migration
suvaissov 2021-07-02 03:51:55 +06:00
parent d500e4daf7
commit 9b866298f5
13 changed files with 346 additions and 26 deletions

View File

@ -41,5 +41,9 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSCameraUsageDescription</key>
<string>Требуется доступ к камере для сканирования QR-кодов и штрих-кодов</string>
<key>io.flutter.embedded_views_preview</key>
<true/>
</dict>
</plist>

View File

@ -45,6 +45,27 @@ ThunkAction<AppState> authenticate(String email, String password) {
};
}
ThunkAction<AppState> authenticateByToken(String token) {
return (Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true)));
try {
AuthResponse result = await _api.authorization(token);
if ( result.operation!) {
_api.token = result.token!;
store.dispatch(SetUserStateAction(UserState(isLoading: false, auth: result)));
_navigation.replace(MainViewRoute);
_afterAuth(store);
} else {
_dialogService.showDialog(title: 'Внимание', buttonTitle: 'Ok', description: result.message!);
}
} catch (e) {
print(e);
} finally {
store.dispatch(SetUserStateAction(UserState(isLoading: false)));
}
};
}
Future<void> auth(Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true)));
try {

View File

@ -95,8 +95,8 @@ class Redux {
serializer: JsonSerializer<AppState>(AppState.fromJson), // Or use other serializers
);
final initialState = await persist.load();
final userStateInitial = UserState.initial(initialState!.userState!);
final AppState? initialState = await persist.load();
final userStateInitial = UserState.initial(initialState?.userState);
final navStateInitial = NavState.initial();
final sellStateInitial = SellState.initial();

View File

@ -91,6 +91,22 @@ class DictionaryService extends BaseService {
return list;
}
Future<List<Good>> getGoodsByEan( String code ) async {
List<Good> list = [];
try {
int? appCompanyId = Redux.store!.state.userState!.auth!.companyId;
String where = '( $GoodColumnAppCompanyId = ? and $GoodColumnEan like ? ) ';
List args = [appCompanyId, '%$code%'];
List<Map<String, dynamic>> elements = await _db.queryRowsWithWhere(GoodTableName, where, args);
elements.forEach((element) {
list.add(Good.fromMap(element));
});
} catch (e, stack) {
log.e("getGoodsByEan", e, stack);
}
return list;
}
bool _isNumericInt(String str) {
if(str == null) {
return false;

View File

@ -35,7 +35,7 @@ Route<dynamic> generateRoute(RouteSettings settings) {
case AddByBarcodeViewRoute:
return _getPageRoute(
routeName: settings.name!,
viewToShow: AddByBarcodeView(title: 'Scanner',),
viewToShow: AddByBarcodeView(),
);
case SettingPrinterBluetoothViewRoute:
return _getPageRoute(

View File

@ -2,15 +2,13 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:satu/views/work/tabs/component/products_app_bar.dart';
import 'package:satu/widgets/tools/app_barcode_scanner_widget.dart';
class AddByBarcodeView extends StatefulWidget {
final String? title;
final int? transactionId;
const AddByBarcodeView({
Key? key,
this.title,
this.transactionId,
Key? key
}) : super(key: key);
@override
@ -18,19 +16,27 @@ class AddByBarcodeView extends StatefulWidget {
}
class _AddByBarcodeViewState extends State<AddByBarcodeView> {
@override
void initState() {
super.initState();
}
String _code = '';
@override
Widget build(BuildContext context) {
print('barcode = image-${widget.transactionId}');
return Container(
child: Center(
child: Hero(
tag: 'text',
child: Text(widget.title ?? '')),
return Scaffold(
appBar: ProductsAppBar(
title: 'Сканер',
),
body: Column(
children: [
Expanded(
child: AppBarcodeScannerWidget.defaultStyle(
resultCallback: (String code) {
Navigator.pop(context, code);
// print(code);
// setState(() {
// _code = code;
// });
},
),
),
],
),
);
}

View File

@ -9,7 +9,9 @@ import 'package:satu/core/redux/actions/user_actions.dart';
import 'package:satu/core/redux/state/user_state.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/routes/route_names.dart';
import 'package:satu/shared/app_colors.dart';
import 'package:satu/shared/ui_helpers.dart';
import 'package:satu/widgets/buttons/busy_button.dart';
@ -102,6 +104,16 @@ class _LoginViewState extends State<LoginView> {
}
Future<void> scan() async {
String? result = await locator<NavigatorService>().push(AddByBarcodeViewRoute);
if(result != null) {
if( result.length == 60 ) {
Redux.store?.dispatch(authenticateByToken(result));
} else {
_dialogService.showDialog(description: 'Не верный формат QR кода');
}
}
// try {
// var options = ScanOptions(strings: {
// "cancel": 'Отмена',

View File

@ -33,10 +33,7 @@ class _ProductListItemState extends State<ProductListItem> {
void _onItemTapped(BuildContext context) {
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => new AddByBarcodeView(
title: widget.name,
transactionId: widget.transactionId,
)));
builder: (BuildContext context) => new AddByBarcodeView()));
}
@override

View File

@ -11,6 +11,7 @@ class ProductsAppBar extends StatelessWidget implements PreferredSizeWidget {
final int? childHeight;
final double elevation;
final Color? backgroundColor;
final bool drawerShow;
const ProductsAppBar(
{Key? key,
@ -19,6 +20,7 @@ class ProductsAppBar extends StatelessWidget implements PreferredSizeWidget {
this.child,
this.childHeight = 0,
this.elevation = 0.0,
this.drawerShow = false,
this.backgroundColor = Colors.transparent})
: super(key: key);
@ -44,7 +46,7 @@ class ProductsAppBar extends StatelessWidget implements PreferredSizeWidget {
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0.0,
leading: IconButton(
leading: drawerShow ? IconButton(
icon: Icon(
Icons.menu,
color: blackColor,
@ -55,7 +57,7 @@ class ProductsAppBar extends StatelessWidget implements PreferredSizeWidget {
.currentState!
.openDrawer();
},
),
) : null ,
actions: actions,
),
if (child != null && childHeight! > 0) child!,

View File

@ -2,10 +2,12 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:satu/core/entity/Goods.dart';
import 'package:satu/core/models/flow/product_dao.dart';
import 'package:satu/core/redux/actions/sell_actions.dart';
import 'package:satu/core/redux/state/sell_state.dart';
import 'package:satu/core/redux/store.dart';
import 'package:satu/core/services/dictionary_service.dart';
import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/utils/locator.dart';
import 'package:satu/routes/route_names.dart';
@ -27,6 +29,7 @@ class SellView extends StatelessWidget {
builder: (_, state) {
return Scaffold(
appBar: ProductsAppBar(
drawerShow: true,
title: 'Продажа',
child: ProductHeaderBar(
count: state.items!.length,
@ -103,7 +106,15 @@ class SellView extends StatelessWidget {
verticalSpaceMedium,
FloatingActionButton(
elevation: 2,
onPressed: () => locator<NavigatorService>().push(AddByBarcodeViewRoute),
onPressed: () async {
String? result = await locator<NavigatorService>().push(AddByBarcodeViewRoute);
if(result !=null) {
List<Good> goods = await locator<DictionaryService>().getGoodsByEan(result);
if(goods.isNotEmpty) {
Redux.store?.dispatch(addSellItem( good: goods.first));
}
}
},
child: Icon(Icons.qr_code_rounded, size: 35.sp, color: whiteColor),
),
],

View File

@ -0,0 +1,179 @@
import 'package:ai_barcode/ai_barcode.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
late String _label;
late Function(String result) _resultCallback;
///
/// AppBarcodeScannerWidget
class AppBarcodeScannerWidget extends StatefulWidget {
///
///
AppBarcodeScannerWidget.defaultStyle({
Function(String result)? resultCallback,
String label = 'Заголовок',
}) {
_resultCallback = resultCallback ?? (String result) {};
_label = label;
}
@override
_AppBarcodeState createState() => _AppBarcodeState();
}
class _AppBarcodeState extends State<AppBarcodeScannerWidget> {
@override
Widget build(BuildContext context) {
return _BarcodePermissionWidget();
}
}
class _BarcodePermissionWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _BarcodePermissionWidgetState();
}
}
class _BarcodePermissionWidgetState extends State<_BarcodePermissionWidget> {
bool _isGranted = false;
bool _useCameraScan = true;
String _inputValue = "";
@override
void initState() {
super.initState();
}
void _requestMobilePermission() async {
if (await Permission.camera.request().isGranted) {
setState(() {
_isGranted = true;
});
}
}
@override
Widget build(BuildContext context) {
TargetPlatform platform = Theme.of(context).platform;
if (!kIsWeb) {
if (platform == TargetPlatform.android ||
platform == TargetPlatform.iOS) {
_requestMobilePermission();
} else {
setState(() {
_isGranted = true;
});
}
} else {
setState(() {
_isGranted = true;
});
}
return Column(
children: <Widget>[
Expanded(
child: _isGranted
? _BarcodeScannerWidget()
: Center(
child: OutlinedButton(
onPressed: () {
_requestMobilePermission();
},
child: Text("Запроса на разрешения"),
),
),
),
// _useCameraScan
// ? OutlinedButton(
// onPressed: () {
// setState(() {
// _useCameraScan = false;
// });
// },
// child: Text("Использовать камеру $_label"),
// )
// : Row(
// children: [
// OutlineButton(
// onPressed: () {
// setState(() {
// _useCameraScan = true;
// });
// },
// child: Text("_useCameraScan = true $_label"),
// ),
// OutlineButton(
// onPressed: () {
// _resultCallback(_inputValue);
// },
// child: Text("_resultCallback"),
// ),
// ],
// ),
],
);
}
}
///ScannerWidget
class _BarcodeScannerWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AppBarcodeScannerWidgetState();
}
}
class _AppBarcodeScannerWidgetState extends State<_BarcodeScannerWidget> {
late ScannerController _scannerController;
@override
void initState() {
super.initState();
_scannerController = ScannerController(scannerResult: (result) {
_resultCallback(result);
}, scannerViewCreated: () {
TargetPlatform platform = Theme.of(context).platform;
if (TargetPlatform.iOS == platform) {
Future.delayed(Duration(seconds: 2), () {
_scannerController.startCamera();
_scannerController.startCameraPreview();
});
} else {
_scannerController.startCamera();
_scannerController.startCameraPreview();
}
});
}
@override
void dispose() {
super.dispose();
_scannerController.stopCameraPreview();
_scannerController.stopCamera();
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: _getScanWidgetByPlatform(),
)
],
);
}
Widget _getScanWidgetByPlatform() {
return PlatformAiBarcodeScannerWidget(
platformScannerController: _scannerController,
);
}
}

View File

@ -1,6 +1,27 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
ai_barcode:
dependency: "direct main"
description:
name: ai_barcode
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
ai_barcode_platform_interface:
dependency: transitive
description:
name: ai_barcode_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
ai_barcode_web:
dependency: transitive
description:
name: ai_barcode_web
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
async:
dependency: transitive
description:
@ -64,6 +85,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
cupertino_icons:
dependency: "direct main"
description:
@ -149,6 +177,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "7.1.3"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.15.0"
http:
dependency: "direct main"
description:
@ -282,6 +317,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.2"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.0"
platform:
dependency: transitive
description:
@ -476,6 +525,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
universal_html:
dependency: transitive
description:
name: universal_html
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
universal_io:
dependency: transitive
description:
name: universal_io
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
universal_platform:
dependency: transitive
description:
name: universal_platform
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0+1"
url_launcher:
dependency: "direct main"
description:

View File

@ -54,6 +54,8 @@ dependencies:
implicitly_animated_reorderable_list: ^0.4.0
uuid: ^3.0.4
charset_converter: ^2.0.0
ai_barcode: ^3.0.1
permission_handler: ^8.1.2
dev_dependencies:
flutter_test:
sdk: flutter