|
|
@ -1,6 +1,7 @@ |
|
|
|
import 'package:flutter/foundation.dart'; |
|
|
|
import 'package:flutter/foundation.dart'; |
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
import 'package:flutter_zxing/flutter_zxing.dart'; |
|
|
|
import 'package:flutter_zxing/flutter_zxing.dart'; |
|
|
|
|
|
|
|
import 'package:image_picker/image_picker.dart'; |
|
|
|
|
|
|
|
|
|
|
|
void main() { |
|
|
|
void main() { |
|
|
|
zx.setLogEnabled(kDebugMode); |
|
|
|
zx.setLogEnabled(kDebugMode); |
|
|
@ -34,6 +35,12 @@ class DemoPage extends StatefulWidget { |
|
|
|
class _DemoPageState extends State<DemoPage> { |
|
|
|
class _DemoPageState extends State<DemoPage> { |
|
|
|
Uint8List? createdCodeBytes; |
|
|
|
Uint8List? createdCodeBytes; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Code? result; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool showDebugInfo = true; |
|
|
|
|
|
|
|
int successScans = 0; |
|
|
|
|
|
|
|
int failedScans = 0; |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
Widget build(BuildContext context) { |
|
|
|
return DefaultTabController( |
|
|
|
return DefaultTabController( |
|
|
@ -52,26 +59,34 @@ class _DemoPageState extends State<DemoPage> { |
|
|
|
physics: const NeverScrollableScrollPhysics(), |
|
|
|
physics: const NeverScrollableScrollPhysics(), |
|
|
|
children: [ |
|
|
|
children: [ |
|
|
|
if (kIsWeb) |
|
|
|
if (kIsWeb) |
|
|
|
Center( |
|
|
|
const UnsupportedPlatformWidget() |
|
|
|
child: Text( |
|
|
|
else if (result != null) |
|
|
|
'Web is not supported yet.', |
|
|
|
ScanResultWidget( |
|
|
|
style: Theme.of(context).textTheme.headline6, |
|
|
|
result: result?.text, |
|
|
|
), |
|
|
|
onScanAgain: () => setState(() => result = null), |
|
|
|
) |
|
|
|
) |
|
|
|
else |
|
|
|
else |
|
|
|
|
|
|
|
Stack( |
|
|
|
|
|
|
|
children: [ |
|
|
|
ReaderWidget( |
|
|
|
ReaderWidget( |
|
|
|
onScan: (value) { |
|
|
|
onScan: _onScanSuccess, |
|
|
|
showMessage(context, 'Scanned: ${value.text ?? ''}'); |
|
|
|
onScanFailure: () => _onScanFailure(null), |
|
|
|
}, |
|
|
|
|
|
|
|
tryInverted: true, |
|
|
|
tryInverted: true, |
|
|
|
), |
|
|
|
), |
|
|
|
if (kIsWeb) |
|
|
|
ScanFromGalleryWidget( |
|
|
|
Center( |
|
|
|
onScan: _onScanSuccess, |
|
|
|
child: Text( |
|
|
|
onScanFailure: _onScanFailure, |
|
|
|
'Web is not supported yet.', |
|
|
|
|
|
|
|
style: Theme.of(context).textTheme.headline6, |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
) |
|
|
|
if (showDebugInfo) |
|
|
|
|
|
|
|
DebugInfoWidget( |
|
|
|
|
|
|
|
successScans: successScans, |
|
|
|
|
|
|
|
failedScans: failedScans, |
|
|
|
|
|
|
|
onReset: _onReset, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
if (kIsWeb) |
|
|
|
|
|
|
|
const UnsupportedPlatformWidget() |
|
|
|
else |
|
|
|
else |
|
|
|
ListView( |
|
|
|
ListView( |
|
|
|
children: [ |
|
|
|
children: [ |
|
|
@ -85,7 +100,7 @@ class _DemoPageState extends State<DemoPage> { |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
onError: (error) { |
|
|
|
onError: (error) { |
|
|
|
showMessage(context, 'Error: $error'); |
|
|
|
_showMessage(context, 'Error: $error'); |
|
|
|
}, |
|
|
|
}, |
|
|
|
), |
|
|
|
), |
|
|
|
if (createdCodeBytes != null) |
|
|
|
if (createdCodeBytes != null) |
|
|
@ -98,12 +113,162 @@ class _DemoPageState extends State<DemoPage> { |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
showMessage(BuildContext context, String message) { |
|
|
|
_onScanSuccess(value) { |
|
|
|
debugPrint(message); |
|
|
|
setState(() { |
|
|
|
|
|
|
|
successScans++; |
|
|
|
|
|
|
|
result = value; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_onScanFailure(String? error) { |
|
|
|
|
|
|
|
setState(() { |
|
|
|
|
|
|
|
failedScans++; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
if (error != null) { |
|
|
|
|
|
|
|
_showMessage(context, error); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_showMessage(BuildContext context, String message) { |
|
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar(); |
|
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar(); |
|
|
|
ScaffoldMessenger.of(context).showSnackBar( |
|
|
|
ScaffoldMessenger.of(context).showSnackBar( |
|
|
|
SnackBar( |
|
|
|
SnackBar(content: Text(message)), |
|
|
|
content: Text(message), |
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_onReset() { |
|
|
|
|
|
|
|
setState(() { |
|
|
|
|
|
|
|
successScans = 0; |
|
|
|
|
|
|
|
failedScans = 0; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScanResultWidget extends StatelessWidget { |
|
|
|
|
|
|
|
const ScanResultWidget({ |
|
|
|
|
|
|
|
Key? key, |
|
|
|
|
|
|
|
this.result, |
|
|
|
|
|
|
|
this.onScanAgain, |
|
|
|
|
|
|
|
}) : super(key: key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final String? result; |
|
|
|
|
|
|
|
final Function()? onScanAgain; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
|
|
|
return Center( |
|
|
|
|
|
|
|
child: Column( |
|
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
|
|
|
|
children: [ |
|
|
|
|
|
|
|
Text( |
|
|
|
|
|
|
|
result ?? '', |
|
|
|
|
|
|
|
style: Theme.of(context).textTheme.headline6, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
const SizedBox(height: 20), |
|
|
|
|
|
|
|
ElevatedButton( |
|
|
|
|
|
|
|
onPressed: onScanAgain, |
|
|
|
|
|
|
|
child: const Text('Scan Again'), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScanFromGalleryWidget extends StatelessWidget { |
|
|
|
|
|
|
|
const ScanFromGalleryWidget({ |
|
|
|
|
|
|
|
Key? key, |
|
|
|
|
|
|
|
this.onScan, |
|
|
|
|
|
|
|
this.onScanFailure, |
|
|
|
|
|
|
|
}) : super(key: key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final Function(Code?)? onScan; |
|
|
|
|
|
|
|
final Function(String)? onScanFailure; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
|
|
|
return Positioned( |
|
|
|
|
|
|
|
bottom: 20, |
|
|
|
|
|
|
|
right: 20, |
|
|
|
|
|
|
|
child: FloatingActionButton( |
|
|
|
|
|
|
|
onPressed: _onFromGalleryButtonTapped, |
|
|
|
|
|
|
|
child: const Icon(Icons.image), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _onFromGalleryButtonTapped() async { |
|
|
|
|
|
|
|
final XFile? file = |
|
|
|
|
|
|
|
await ImagePicker().pickImage(source: ImageSource.gallery); |
|
|
|
|
|
|
|
if (file != null) { |
|
|
|
|
|
|
|
final Code? result = await zx.readBarcodeImagePath( |
|
|
|
|
|
|
|
file, |
|
|
|
|
|
|
|
params: Params(tryInverted: true), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
if (result != null && result.isValid) { |
|
|
|
|
|
|
|
onScan?.call(result); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
onScanFailure?.call('Failed to read barcode from image'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DebugInfoWidget extends StatelessWidget { |
|
|
|
|
|
|
|
const DebugInfoWidget({ |
|
|
|
|
|
|
|
Key? key, |
|
|
|
|
|
|
|
required this.successScans, |
|
|
|
|
|
|
|
required this.failedScans, |
|
|
|
|
|
|
|
this.onReset, |
|
|
|
|
|
|
|
}) : super(key: key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final int successScans; |
|
|
|
|
|
|
|
final int failedScans; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final Function()? onReset; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
|
|
|
return Align( |
|
|
|
|
|
|
|
alignment: Alignment.topCenter, |
|
|
|
|
|
|
|
child: Padding( |
|
|
|
|
|
|
|
padding: const EdgeInsets.all(8.0), |
|
|
|
|
|
|
|
child: ClipRRect( |
|
|
|
|
|
|
|
borderRadius: BorderRadius.circular(10), |
|
|
|
|
|
|
|
child: Container( |
|
|
|
|
|
|
|
color: Colors.white.withOpacity(0.7), |
|
|
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), |
|
|
|
|
|
|
|
child: Column( |
|
|
|
|
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
|
|
|
|
children: [ |
|
|
|
|
|
|
|
Text( |
|
|
|
|
|
|
|
'Success: $successScans\nFailed: $failedScans', |
|
|
|
|
|
|
|
style: Theme.of(context).textTheme.headline6, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
TextButton( |
|
|
|
|
|
|
|
onPressed: onReset, |
|
|
|
|
|
|
|
child: const Text('Reset'), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UnsupportedPlatformWidget extends StatelessWidget { |
|
|
|
|
|
|
|
const UnsupportedPlatformWidget({ |
|
|
|
|
|
|
|
Key? key, |
|
|
|
|
|
|
|
}) : super(key: key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
|
|
|
|
|
return Center( |
|
|
|
|
|
|
|
child: Text( |
|
|
|
|
|
|
|
'This platform is not supported yet.', |
|
|
|
|
|
|
|
style: Theme.of(context).textTheme.headline6, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|