Browse Source

refactor: applying lint suggestions

No breaking changes.
pull/3/head
Hamed H 2 years ago
parent
commit
3515735025
  1. 239
      analysis_options.yaml
  2. 4
      example/pubspec.lock
  3. 47
      lib/flutter_zxing.dart
  4. 14
      lib/image_converter.dart
  5. 37
      lib/isolate_utils.dart
  6. 69
      lib/reader_widget.dart
  7. 25
      lib/scanner_overlay.dart
  8. 72
      lib/writer_widget.dart
  9. 4
      zxscanner/pubspec.lock

239
analysis_options.yaml

@ -1,6 +1,239 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer: analyzer:
exclude: [lib/generated_bindings.dart] language:
strict-casts: false
strict-raw-types: true
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# not allowing having TODO comments in the code
# todo: warning
# allow self-reference to deprecated members (we do this because otherwise we have
# to annotate every member in every test, assert, etc, when we deprecate something)
deprecated_member_use_from_same_package: ignore
# Turned off until null-safe rollout is complete.
unnecessary_null_comparison: warning
exclude:
- "bin/cache/**"
# Ignore protoc generated files
- "dev/conductor/lib/proto/*"
- "lib/generated_bindings.dart"
linter:
rules:
# This list is derived from the list of all available lints located at
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023
# - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023
- avoid_classes_with_only_static_members
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
# - avoid_final_parameters # incompatible with prefer_final_parameters
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
- avoid_js_rounded_ints
# - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it
- avoid_print
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # still violated by some pre-nnbd code that we haven't yet migrated
- avoid_returning_null_for_future
- avoid_returning_null_for_void
# - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # doesn't match the typical style of this repo
- cast_nullable_to_non_nullable
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
# - conditional_uri_does_not_exist # not yet tested
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
# - curly_braces_in_flow_control_structures # not required by flutter style
- depend_on_referenced_packages
- deprecated_consistency
# - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
- directives_ordering
# - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
- hash_and_equals
- implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type
# - join_return_with_assignment # not required by flutter style
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- library_private_types_in_public_api
# - lines_longer_than_80_chars # not required by flutter style
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_default_cases
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_logic_in_create_state
# - no_runtimeType_toString # ok in tests; we enable this only in packages/
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
- only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not required by flutter style
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
# - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
# - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018
# - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
- prefer_null_aware_operators
- prefer_relative_imports
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
# - require_trailing_commas # blocked on https://github.com/dart-lang/sdk/issues/47441
- secure_pubspec_urls
- sized_box_for_whitespace
# - sized_box_shrink_expand # not yet tested
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
# - sort_pub_dependencies # prevents separating pinned transitive dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives, especially with the way AnimationController works
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_constructor_name
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_late
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
# - use_colored_box # not yet tested
# - use_decorated_box # not yet tested
# - use_enums # not yet tested
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
- use_super_parameters
- use_test_throws_matchers
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks

4
example/pubspec.lock

@ -14,7 +14,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.9.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -141,7 +141,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.3.1" version: "0.3.2"
font_awesome_flutter: font_awesome_flutter:
dependency: "direct main" dependency: "direct main"
description: description:

47
lib/flutter_zxing.dart

@ -13,11 +13,13 @@ import 'generated_bindings.dart';
import 'isolate_utils.dart'; import 'isolate_utils.dart';
export 'generated_bindings.dart'; export 'generated_bindings.dart';
export 'reader_widget.dart';
export 'writer_widget.dart';
export 'image_converter.dart'; export 'image_converter.dart';
export 'reader_widget.dart';
export 'scanner_overlay.dart'; export 'scanner_overlay.dart';
export 'writer_widget.dart';
// TODO: fix below warning from lint
// ignore: avoid_classes_with_only_static_members
/// The main class for reading barcodes from images or camera. /// The main class for reading barcodes from images or camera.
class FlutterZxing { class FlutterZxing {
static const MethodChannel _channel = MethodChannel('flutter_zxing'); static const MethodChannel _channel = MethodChannel('flutter_zxing');
@ -27,7 +29,7 @@ class FlutterZxing {
return version; return version;
} }
static final bindings = GeneratedBindings(dylib); static final GeneratedBindings bindings = GeneratedBindings(dylib);
static IsolateUtils? isolateUtils; static IsolateUtils? isolateUtils;
@ -39,7 +41,7 @@ class FlutterZxing {
static String version() => bindings.version().cast<Utf8>().toDartString(); static String version() => bindings.version().cast<Utf8>().toDartString();
/// Starts reading barcode from the camera /// Starts reading barcode from the camera
static Future startCameraProcessing() async { static Future<void> startCameraProcessing() async {
// Spawn a new isolate // Spawn a new isolate
isolateUtils = IsolateUtils(); isolateUtils = IsolateUtils();
await isolateUtils?.startReadingBarcode(); await isolateUtils?.startReadingBarcode();
@ -66,7 +68,7 @@ class FlutterZxing {
int cropHeight = 0, int cropHeight = 0,
}) async { }) async {
final Uint8List imageBytes = await path.readAsBytes(); final Uint8List imageBytes = await path.readAsBytes();
imglib.Image? image = imglib.decodeImage(imageBytes); final imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return null; return null;
} }
@ -91,7 +93,7 @@ class FlutterZxing {
(await NetworkAssetBundle(Uri.parse(url)).load(url)) (await NetworkAssetBundle(Uri.parse(url)).load(url))
.buffer .buffer
.asUint8List(); .asUint8List();
imglib.Image? image = imglib.decodeImage(imageBytes); final imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return null; return null;
} }
@ -113,9 +115,9 @@ class FlutterZxing {
static List<CodeResult> readBarcodes(Uint8List bytes, int format, int width, static List<CodeResult> readBarcodes(Uint8List bytes, int format, int width,
int height, int cropWidth, int cropHeight) { int height, int cropWidth, int cropHeight) {
final result = bindings.readBarcodes( final CodeResults result = bindings.readBarcodes(
bytes.allocatePointer(), format, width, height, cropWidth, cropHeight); bytes.allocatePointer(), format, width, height, cropWidth, cropHeight);
List<CodeResult> results = []; final List<CodeResult> results = <CodeResult>[];
for (int i = 0; i < result.count; i++) { for (int i = 0; i < result.count; i++) {
results.add(result.results.elementAt(i).ref); results.add(result.results.elementAt(i).ref);
} }
@ -124,24 +126,31 @@ class FlutterZxing {
static EncodeResult encodeBarcode(String contents, int width, int height, static EncodeResult encodeBarcode(String contents, int width, int height,
int format, int margin, int eccLevel) { int format, int margin, int eccLevel) {
var result = bindings.encodeBarcode(contents.toNativeUtf8().cast<Char>(), final EncodeResult result = bindings.encodeBarcode(
width, height, format, margin, eccLevel); contents.toNativeUtf8().cast<Char>(),
width,
height,
format,
margin,
eccLevel);
return result; return result;
} }
static Future<CodeResult> processCameraImage(CameraImage image, static Future<CodeResult> processCameraImage(CameraImage image,
{int format = Format.Any, double cropPercent = 0.5}) async { {int format = Format.Any, double cropPercent = 0.5}) async {
var isolateData = IsolateData(image, format, cropPercent); final IsolateData isolateData = IsolateData(image, format, cropPercent);
CodeResult result = await _inference(isolateData); final CodeResult result = await _inference(isolateData);
return result; return result;
} }
/// Runs inference in another isolate /// Runs inference in another isolate
static Future<CodeResult> _inference(IsolateData isolateData) async { static Future<CodeResult> _inference(IsolateData isolateData) async {
ReceivePort responsePort = ReceivePort(); final ReceivePort responsePort = ReceivePort();
isolateUtils?.sendPort isolateUtils?.sendPort
?.send(isolateData..responsePort = responsePort.sendPort); ?.send(isolateData..responsePort = responsePort.sendPort);
var results = await responsePort.first; // TODO: fix below warning from lint
// ignore: always_specify_types
final results = await responsePort.first;
return results; return results;
} }
@ -153,7 +162,7 @@ DynamicLibrary _openDynamicLibrary() {
if (Platform.isAndroid) { if (Platform.isAndroid) {
return DynamicLibrary.open('libflutter_zxing.so'); return DynamicLibrary.open('libflutter_zxing.so');
} else if (Platform.isWindows) { } else if (Platform.isWindows) {
return DynamicLibrary.open("flutter_zxing_windows_plugin.dll"); return DynamicLibrary.open('flutter_zxing_windows_plugin.dll');
} }
return DynamicLibrary.process(); return DynamicLibrary.process();
} }
@ -163,8 +172,8 @@ DynamicLibrary dylib = _openDynamicLibrary();
extension Uint8ListBlobConversion on Uint8List { extension Uint8ListBlobConversion on Uint8List {
/// Allocates a pointer filled with the Uint8List data. /// Allocates a pointer filled with the Uint8List data.
Pointer<Char> allocatePointer() { Pointer<Char> allocatePointer() {
final blob = calloc<Int8>(length); final Pointer<Int8> blob = calloc<Int8>(length);
final blobBytes = blob.asTypedList(length); final Int8List blobBytes = blob.asTypedList(length);
blobBytes.setAll(0, this); blobBytes.setAll(0, this);
return blob.cast<Char>(); return blob.cast<Char>();
} }
@ -189,7 +198,7 @@ extension EncodeExt on EncodeResult {
extension CodeFormat on Format { extension CodeFormat on Format {
String get name => _formatNames[this] ?? 'Unknown'; String get name => _formatNames[this] ?? 'Unknown';
static final writerFormats = [ static final List<int> writerFormats = <int>[
Format.QRCode, Format.QRCode,
Format.DataMatrix, Format.DataMatrix,
Format.Aztec, Format.Aztec,
@ -209,7 +218,7 @@ extension CodeFormat on Format {
]; ];
} }
final _formatNames = { final Map<int, String> _formatNames = <int, String>{
Format.None: 'None', Format.None: 'None',
Format.Aztec: 'Aztec', Format.Aztec: 'Aztec',
Format.Codabar: 'CodaBar', Format.Codabar: 'CodaBar',

14
lib/image_converter.dart

@ -17,7 +17,7 @@ Future<Uint8List> convertImage(CameraImage image) async {
} }
return img.getBytes(format: imglib.Format.luminance); return img.getBytes(format: imglib.Format.luminance);
} catch (e) { } catch (e) {
debugPrint(">>>>>>>>>>>> ERROR: $e"); debugPrint('>>>>>>>>>>>> ERROR: $e');
} }
return Uint8List(0); return Uint8List(0);
} }
@ -33,21 +33,25 @@ imglib.Image convertBGRA8888(CameraImage image) {
// ignore: unused_element // ignore: unused_element
imglib.Image convertYUV420(CameraImage image) { imglib.Image convertYUV420(CameraImage image) {
var img = imglib.Image(image.width, image.height); // Create Image buffer final imglib.Image img =
imglib.Image(image.width, image.height); // Create Image buffer
Plane plane = image.planes[0]; final Plane plane = image.planes[0];
const int shift = (0xFF << 24); const int shift = 0xFF << 24;
// Fill image buffer with plane[0] from YUV420_888 // Fill image buffer with plane[0] from YUV420_888
for (int x = 0; x < image.width; x++) { for (int x = 0; x < image.width; x++) {
for (int planeOffset = 0; for (int planeOffset = 0;
planeOffset < image.height * image.width; planeOffset < image.height * image.width;
planeOffset += image.width) { planeOffset += image.width) {
// TODO: fix below warning from lint
// ignore: always_specify_types
final pixelColor = plane.bytes[planeOffset + x]; final pixelColor = plane.bytes[planeOffset + x];
// color: 0x FF FF FF FF // color: 0x FF FF FF FF
// A B G R // A B G R
// Calculate pixel color // Calculate pixel color
var newVal = shift | (pixelColor << 16) | (pixelColor << 8) | pixelColor; final int newVal =
shift | (pixelColor << 16) | (pixelColor << 8) | pixelColor;
img.data[planeOffset + x] = newVal; img.data[planeOffset + x] = newVal;
} }

37
lib/isolate_utils.dart

@ -1,5 +1,6 @@
import 'dart:isolate'; import 'dart:isolate';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
@ -9,26 +10,25 @@ import 'flutter_zxing.dart';
/// Bundles data to pass between Isolate /// Bundles data to pass between Isolate
class IsolateData { class IsolateData {
CameraImage cameraImage;
int format;
double cropPercent;
SendPort? responsePort;
IsolateData( IsolateData(
this.cameraImage, this.cameraImage,
this.format, this.format,
this.cropPercent, this.cropPercent,
); );
CameraImage cameraImage;
int format;
double cropPercent;
SendPort? responsePort;
} }
/// Manages separate Isolate instance for inference /// Manages separate Isolate instance for inference
class IsolateUtils { class IsolateUtils {
static const String kDebugName = "ZxingIsolate"; static const String kDebugName = 'ZxingIsolate';
// ignore: unused_field // ignore: unused_field
Isolate? _isolate; Isolate? _isolate;
final _receivePort = ReceivePort(); final ReceivePort _receivePort = ReceivePort();
SendPort? _sendPort; SendPort? _sendPort;
SendPort? get sendPort => _sendPort; SendPort? get sendPort => _sendPort;
@ -49,21 +49,26 @@ class IsolateUtils {
_sendPort = null; _sendPort = null;
} }
static void readBarcodeEntryPoint(SendPort sendPort) async { static Future<void> readBarcodeEntryPoint(SendPort sendPort) async {
final port = ReceivePort(); final ReceivePort port = ReceivePort();
sendPort.send(port.sendPort); sendPort.send(port.sendPort);
await for (final IsolateData? isolateData in port) { await for (final IsolateData? isolateData in port) {
if (isolateData != null) { if (isolateData != null) {
try { try {
final image = isolateData.cameraImage; final CameraImage image = isolateData.cameraImage;
final cropPercent = isolateData.cropPercent; final double cropPercent = isolateData.cropPercent;
final bytes = await convertImage(image); final Uint8List bytes = await convertImage(image);
final cropSize = final int cropSize =
(min(image.width, image.height) * cropPercent).round(); (min(image.width, image.height) * cropPercent).round();
final result = FlutterZxing.readBarcode(bytes, isolateData.format, final CodeResult result = FlutterZxing.readBarcode(
image.width, image.height, cropSize, cropSize); bytes,
isolateData.format,
image.width,
image.height,
cropSize,
cropSize);
isolateData.responsePort?.send(result); isolateData.responsePort?.send(result);
} catch (e) { } catch (e) {

69
lib/reader_widget.dart

@ -12,7 +12,7 @@ import 'isolate_utils.dart';
class ReaderWidget extends StatefulWidget { class ReaderWidget extends StatefulWidget {
const ReaderWidget({ const ReaderWidget({
Key? key, super.key,
required this.onScan, required this.onScan,
this.onControllerCreated, this.onControllerCreated,
this.codeFormat = Format.Any, this.codeFormat = Format.Any,
@ -24,7 +24,7 @@ class ReaderWidget extends StatefulWidget {
this.scanDelay = const Duration(milliseconds: 1000), // 1000ms delay this.scanDelay = const Duration(milliseconds: 1000), // 1000ms delay
this.cropPercent = 0.5, // 50% of the screen this.cropPercent = 0.5, // 50% of the screen
this.resolution = ResolutionPreset.high, this.resolution = ResolutionPreset.high,
}) : super(key: key); });
final Function(CodeResult) onScan; final Function(CodeResult) onScan;
final Function(CameraController?)? onControllerCreated; final Function(CameraController?)? onControllerCreated;
@ -46,7 +46,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
with TickerProviderStateMixin { with TickerProviderStateMixin {
List<CameraDescription>? cameras; List<CameraDescription>? cameras;
CameraController? controller; CameraController? controller;
var _cameraOn = false; bool _cameraOn = false;
double _zoom = 1.0; double _zoom = 1.0;
double _scaleFactor = 1.0; double _scaleFactor = 1.0;
@ -68,11 +68,11 @@ class _ReaderWidgetState extends State<ReaderWidget>
initStateAsync(); initStateAsync();
} }
void initStateAsync() async { Future<void> initStateAsync() async {
// Spawn a new isolate // Spawn a new isolate
await FlutterZxing.startCameraProcessing(); await FlutterZxing.startCameraProcessing();
availableCameras().then((cameras) { availableCameras().then((List<CameraDescription> cameras) {
setState(() { setState(() {
this.cameras = cameras; this.cameras = cameras;
if (cameras.isNotEmpty) { if (cameras.isNotEmpty) {
@ -81,7 +81,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
}); });
}); });
SystemChannels.lifecycle.setMessageHandler((message) async { SystemChannels.lifecycle.setMessageHandler((String? message) async {
debugPrint(message); debugPrint(message);
final CameraController? cameraController = controller; final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) { if (cameraController == null || !cameraController.value.isInitialized) {
@ -110,7 +110,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
super.dispose(); super.dispose();
} }
void onNewCameraSelected(CameraDescription cameraDescription) async { Future<void> onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) { if (controller != null) {
await controller!.dispose(); await controller!.dispose();
} }
@ -137,7 +137,9 @@ class _ReaderWidgetState extends State<ReaderWidget>
} }
cameraController.addListener(() { cameraController.addListener(() {
if (mounted) setState(() {}); if (mounted) {
setState(() {});
}
}); });
if (mounted) { if (mounted) {
@ -148,11 +150,11 @@ class _ReaderWidgetState extends State<ReaderWidget>
widget.onControllerCreated?.call(cameraController); widget.onControllerCreated?.call(cameraController);
} }
processCameraImage(CameraImage image) async { Future<void> processCameraImage(CameraImage image) async {
if (!_isProcessing) { if (!_isProcessing) {
_isProcessing = true; _isProcessing = true;
try { try {
CodeResult result = await FlutterZxing.processCameraImage( final CodeResult result = await FlutterZxing.processCameraImage(
image, image,
format: widget.codeFormat, format: widget.codeFormat,
cropPercent: widget.cropPercent, cropPercent: widget.cropPercent,
@ -163,28 +165,30 @@ class _ReaderWidgetState extends State<ReaderWidget>
} }
widget.onScan(result); widget.onScan(result);
setState(() {}); setState(() {});
await Future.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
} }
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
debugPrint(e.message); debugPrint(e.message);
} catch (e) { } catch (e) {
debugPrint(e.toString()); debugPrint(e.toString());
} }
await Future.delayed(widget.scanDelay); await Future<void>.delayed(widget.scanDelay);
_isProcessing = false; _isProcessing = false;
} }
return null; return;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final size = MediaQuery.of(context).size; final Size size = MediaQuery.of(context).size;
final cropSize = min(size.width, size.height) * widget.cropPercent; final double cropSize = min(size.width, size.height) * widget.cropPercent;
return Stack( return Stack(
children: [ children: <Widget>[
// Camera preview // Camera preview
Center(child: _cameraPreviewWidget(cropSize)), Center(
child: _cameraPreviewWidget(cropSize),
),
], ],
); );
} }
@ -192,23 +196,22 @@ class _ReaderWidgetState extends State<ReaderWidget>
// Display the preview from the camera. // Display the preview from the camera.
Widget _cameraPreviewWidget(double cropSize) { Widget _cameraPreviewWidget(double cropSize) {
final CameraController? cameraController = controller; final CameraController? cameraController = controller;
if (cameras != null && cameras?.isEmpty == true) { if (cameras != null && (cameras?.isEmpty ?? false)) {
return const Text('No cameras found'); return const Text('No cameras found');
} else if (cameraController == null || } else if (cameraController == null ||
!cameraController.value.isInitialized || !cameraController.value.isInitialized ||
!_cameraOn) { !_cameraOn) {
return const CircularProgressIndicator(); return const CircularProgressIndicator();
} else { } else {
final size = MediaQuery.of(context).size; final Size size = MediaQuery.of(context).size;
var cameraMaxSize = max(size.width, size.height); final double cameraMaxSize = max(size.width, size.height);
return Stack( return Stack(
children: [ children: <Widget>[
SizedBox( SizedBox(
width: cameraMaxSize, width: cameraMaxSize,
height: cameraMaxSize, height: cameraMaxSize,
child: ClipRRect( child: ClipRRect(
child: OverflowBox( child: OverflowBox(
alignment: Alignment.center,
child: FittedBox( child: FittedBox(
fit: BoxFit.cover, fit: BoxFit.cover,
child: SizedBox( child: SizedBox(
@ -237,10 +240,10 @@ class _ReaderWidgetState extends State<ReaderWidget>
), ),
if (widget.allowPinchZoom) if (widget.allowPinchZoom)
GestureDetector( GestureDetector(
onScaleStart: (details) { onScaleStart: (ScaleStartDetails details) {
_zoom = _scaleFactor; _zoom = _scaleFactor;
}, },
onScaleUpdate: (details) { onScaleUpdate: (ScaleUpdateDetails details) {
_scaleFactor = _scaleFactor =
(_zoom * details.scale).clamp(_minZoomLevel, _maxZoomLevel); (_zoom * details.scale).clamp(_minZoomLevel, _maxZoomLevel);
cameraController.setZoomLevel(_scaleFactor); cameraController.setZoomLevel(_scaleFactor);
@ -253,14 +256,10 @@ class _ReaderWidgetState extends State<ReaderWidget>
child: FloatingActionButton( child: FloatingActionButton(
onPressed: () { onPressed: () {
FlashMode mode = cameraController.value.flashMode; FlashMode mode = cameraController.value.flashMode;
switch (mode) { if (mode == FlashMode.torch) {
case FlashMode.torch: mode = FlashMode.off;
mode = FlashMode.off; } else {
break; mode = FlashMode.torch;
case FlashMode.off:
mode = FlashMode.torch;
break;
default:
} }
cameraController.setFlashMode(mode); cameraController.setFlashMode(mode);
setState(() {}); setState(() {});
@ -281,8 +280,10 @@ class _ReaderWidgetState extends State<ReaderWidget>
return Icons.flash_on; return Icons.flash_on;
case FlashMode.off: case FlashMode.off:
return Icons.flash_off; return Icons.flash_off;
default: case FlashMode.always:
return Icons.flash_off; return Icons.flash_on;
case FlashMode.auto:
return Icons.flash_auto;
} }
} }
} }

25
lib/scanner_overlay.dart

@ -54,31 +54,32 @@ class ScannerOverlay extends ShapeBorder {
@override @override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
final width = rect.width; final double width = rect.width;
final borderWidthSize = width / 2; final double borderWidthSize = width / 2;
final height = rect.height; final double height = rect.height;
final borderOffset = borderWidth / 2; final double borderOffset = borderWidth / 2;
final newBorderLength = borderLength > cutOutSize / 2 + borderWidth * 2 final double newBorderLength =
? borderWidthSize / 2 borderLength > cutOutSize / 2 + borderWidth * 2
: borderLength; ? borderWidthSize / 2
final newCutOutSize = : borderLength;
final double newCutOutSize =
cutOutSize < width ? cutOutSize : width - borderOffset; cutOutSize < width ? cutOutSize : width - borderOffset;
final backgroundPaint = Paint() final Paint backgroundPaint = Paint()
..color = overlayColor ..color = overlayColor
..style = PaintingStyle.fill; ..style = PaintingStyle.fill;
final borderPaint = Paint() final Paint borderPaint = Paint()
..color = borderColor ..color = borderColor
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeWidth = borderWidth; ..strokeWidth = borderWidth;
final boxPaint = Paint() final Paint boxPaint = Paint()
..color = borderColor ..color = borderColor
..style = PaintingStyle.fill ..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut; ..blendMode = BlendMode.dstOut;
final cutOutRect = Rect.fromLTWH( final Rect cutOutRect = Rect.fromLTWH(
rect.left + width / 2 - newCutOutSize / 2 + borderOffset, rect.left + width / 2 - newCutOutSize / 2 + borderOffset,
rect.top + height / 2 - newCutOutSize / 2 + borderOffset, rect.top + height / 2 - newCutOutSize / 2 + borderOffset,
newCutOutSize - borderOffset * 2, newCutOutSize - borderOffset * 2,

72
lib/writer_widget.dart

@ -7,10 +7,10 @@ import 'flutter_zxing.dart';
class WriterWidget extends StatefulWidget { class WriterWidget extends StatefulWidget {
const WriterWidget({ const WriterWidget({
Key? key, super.key,
this.onSuccess, this.onSuccess,
this.onError, this.onError,
}) : super(key: key); });
final Function(EncodeResult, Uint8List?)? onSuccess; final Function(EncodeResult, Uint8List?)? onSuccess;
final Function(String)? onError; final Function(String)? onError;
@ -24,18 +24,18 @@ class _WriterWidgetState extends State<WriterWidget>
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _textController = TextEditingController(); final TextEditingController _textController = TextEditingController();
final TextEditingController _widthController = final TextEditingController _widthController =
TextEditingController(text: "300"); TextEditingController(text: '300');
final TextEditingController _heightController = final TextEditingController _heightController =
TextEditingController(text: "300"); TextEditingController(text: '300');
final TextEditingController _marginController = final TextEditingController _marginController =
TextEditingController(text: "10"); TextEditingController(text: '10');
final TextEditingController _eccController = TextEditingController(text: "0"); final TextEditingController _eccController = TextEditingController(text: '0');
bool isAndroid() => Theme.of(context).platform == TargetPlatform.android; bool isAndroid() => Theme.of(context).platform == TargetPlatform.android;
final _maxTextLength = 2000; final int _maxTextLength = 2000;
final _supportedFormats = CodeFormat.writerFormats; final List<int> _supportedFormats = CodeFormat.writerFormats;
var _codeFormat = Format.QRCode; int _codeFormat = Format.QRCode;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -47,7 +47,7 @@ class _WriterWidgetState extends State<WriterWidget>
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: <Widget>[
const SizedBox(height: 20), const SizedBox(height: 20),
// Input multiline text // Input multiline text
TextFormField( TextFormField(
@ -55,7 +55,7 @@ class _WriterWidgetState extends State<WriterWidget>
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
maxLines: null, maxLines: null,
maxLength: _maxTextLength, maxLength: _maxTextLength,
onChanged: (value) { onChanged: (String value) {
setState(() {}); setState(() {});
}, },
decoration: InputDecoration( decoration: InputDecoration(
@ -65,8 +65,8 @@ class _WriterWidgetState extends State<WriterWidget>
counterText: counterText:
'${_textController.value.text.length} / $_maxTextLength', '${_textController.value.text.length} / $_maxTextLength',
), ),
validator: (value) { validator: (String? value) {
if (value?.isEmpty == true) { if (value?.isEmpty ?? false) {
return 'Please enter some text'; return 'Please enter some text';
} }
return null; return null;
@ -77,12 +77,12 @@ class _WriterWidgetState extends State<WriterWidget>
DropdownButtonFormField<int>( DropdownButtonFormField<int>(
value: _codeFormat, value: _codeFormat,
items: _supportedFormats items: _supportedFormats
.map((format) => DropdownMenuItem( .map((int format) => DropdownMenuItem<int>(
value: format, value: format,
child: Text(FlutterZxing.formatName(format)), child: Text(FlutterZxing.formatName(format)),
)) ))
.toList(), .toList(),
onChanged: (format) { onChanged: (int? format) {
setState(() { setState(() {
_codeFormat = format ?? Format.QRCode; _codeFormat = format ?? Format.QRCode;
}); });
@ -90,7 +90,7 @@ class _WriterWidgetState extends State<WriterWidget>
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Row( Row(
children: [ children: <Widget>[
Flexible( Flexible(
child: TextFormField( child: TextFormField(
controller: _widthController, controller: _widthController,
@ -98,8 +98,8 @@ class _WriterWidgetState extends State<WriterWidget>
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Width', labelText: 'Width',
), ),
validator: (value) { validator: (String? value) {
final width = int.tryParse(value ?? ''); final int? width = int.tryParse(value ?? '');
if (width == null) { if (width == null) {
return 'Invalid number'; return 'Invalid number';
} }
@ -115,8 +115,8 @@ class _WriterWidgetState extends State<WriterWidget>
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Height', labelText: 'Height',
), ),
validator: (value) { validator: (String? value) {
final width = int.tryParse(value ?? ''); final int? width = int.tryParse(value ?? '');
if (width == null) { if (width == null) {
return 'Invalid number'; return 'Invalid number';
} }
@ -128,7 +128,7 @@ class _WriterWidgetState extends State<WriterWidget>
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Row( Row(
children: [ children: <Widget>[
Flexible( Flexible(
child: TextFormField( child: TextFormField(
controller: _marginController, controller: _marginController,
@ -136,8 +136,8 @@ class _WriterWidgetState extends State<WriterWidget>
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Margin', labelText: 'Margin',
), ),
validator: (value) { validator: (String? value) {
final width = int.tryParse(value ?? ''); final int? width = int.tryParse(value ?? '');
if (width == null) { if (width == null) {
return 'Invalid number'; return 'Invalid number';
} }
@ -153,8 +153,8 @@ class _WriterWidgetState extends State<WriterWidget>
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'ECC Level', labelText: 'ECC Level',
), ),
validator: (value) { validator: (String? value) {
final width = int.tryParse(value ?? ''); final int? width = int.tryParse(value ?? '');
if (width == null) { if (width == null) {
return 'Invalid number'; return 'Invalid number';
} }
@ -182,18 +182,24 @@ class _WriterWidgetState extends State<WriterWidget>
if (_formKey.currentState?.validate() ?? false) { if (_formKey.currentState?.validate() ?? false) {
_formKey.currentState?.save(); _formKey.currentState?.save();
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
final text = _textController.value.text; final String text = _textController.value.text;
final width = int.parse(_widthController.value.text); final int width = int.parse(_widthController.value.text);
final height = int.parse(_heightController.value.text); final int height = int.parse(_heightController.value.text);
final margin = int.parse(_marginController.value.text); final int margin = int.parse(_marginController.value.text);
final ecc = int.parse(_eccController.value.text); final int ecc = int.parse(_eccController.value.text);
var result = FlutterZxing.encodeBarcode( final EncodeResult result = FlutterZxing.encodeBarcode(
text, width, height, _codeFormat, margin, ecc); text, width, height, _codeFormat, margin, ecc);
String? error; String? error;
if (result.isValidBool) { if (result.isValidBool) {
try { try {
final img = imglib.Image.fromBytes(width, height, result.bytes); final imglib.Image img = imglib.Image.fromBytes(
final encodedBytes = Uint8List.fromList(imglib.encodeJpg(img)); width,
height,
result.bytes,
);
final Uint8List encodedBytes = Uint8List.fromList(
imglib.encodeJpg(img),
);
widget.onSuccess?.call(result, encodedBytes); widget.onSuccess?.call(result, encodedBytes);
} catch (e) { } catch (e) {
error = e.toString(); error = e.toString();

4
zxscanner/pubspec.lock

@ -35,7 +35,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.9.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -300,7 +300,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.3.1" version: "0.3.2"
font_awesome_flutter: font_awesome_flutter:
dependency: "direct main" dependency: "direct main"
description: description:

Loading…
Cancel
Save