diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f3ddb..ede5ebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.1.6 +- Adds analyzer configuration +- Fixes linting issues +- Migrate to Dart 2.x friendly syntax +- Tidy some initialization logic / code +- Bump copyright + # 1.1.5 - Add image data export functions (see `test/painter_tests.dart` for an example) diff --git a/LICENSE b/LICENSE index 7fdd0b9..1ebce27 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ QR.Flutter -Copyright (c) 2018 the QR.Flutter authors. +Copyright (c) 2019 the QR.Flutter authors. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 1331e4f..8f28dd1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ You can install the package by adding the following lines to your `pubspec.yaml` ```yaml dependencies: - qr_flutter: ^1.1.5 + qr_flutter: ^1.1.6 ``` After adding the dependency to your `pubspec.yaml` you can run: `flutter packages get` or update your packages using diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..c1bf808 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,102 @@ +analyzer: + strong-mode: + implicit-dynamic: false + 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 + # allow having TODOs in the code + todo: ignore + exclude: + - '.idea/**' + - 'android/**' + - 'assets/**' + - 'build/**' + - 'ios/**' + - 'res/**' + - 'example/**' + +linter: + rules: + - always_declare_return_types + - always_put_control_body_on_new_line + - always_require_non_null_named_parameters + - always_specify_types + - annotate_overrides + - avoid_as + - avoid_empty_else + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_slow_async_io + - avoid_types_as_parameter_names + - avoid_unused_constructor_parameters + - avoid_void_async + - await_only_futures + - camel_case_types + - cancel_subscriptions + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - iterable_contains_unrelated_type + - library_names + - library_prefixes + - list_remove_unrelated_type + - no_adjacent_strings_in_list + - no_duplicate_case_values + - non_constant_identifier_names + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_contains + - prefer_equal_for_default_values + - prefer_final_fields + - prefer_final_locals + - prefer_foreach + - prefer_generic_function_type_aliases + - prefer_initializing_formals + - prefer_is_empty + - prefer_is_not_empty + - prefer_iterable_whereType + - prefer_single_quotes + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - recursive_getters + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + - super_goes_last + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_getters_setters + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_this + - unrelated_type_equality_checks + - use_rethrow_when_possible + - valid_regexps \ No newline at end of file diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..a337fba --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,102 @@ +analyzer: + strong-mode: + implicit-dynamic: false + 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 + # allow having TODOs in the code + todo: ignore + exclude: + - '.idea/**' + - 'android/**' + - 'assets/**' + - 'build/**' + - 'ios/**' + - 'res/**' + - 'example/**' + +linter: + rules: + - always_declare_return_types + - always_put_control_body_on_new_line + - always_require_non_null_named_parameters + - always_specify_types + - annotate_overrides + - avoid_as + - avoid_empty_else + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_slow_async_io + - avoid_types_as_parameter_names + - avoid_unused_constructor_parameters + - avoid_void_async + - await_only_futures + - camel_case_types + - cancel_subscriptions + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - iterable_contains_unrelated_type + - library_names + - library_prefixes + - list_remove_unrelated_type + - no_adjacent_strings_in_list + - no_duplicate_case_values + - non_constant_identifier_names + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_contains + - prefer_equal_for_default_values + - prefer_final_fields + - prefer_final_locals + - prefer_foreach + - prefer_generic_function_type_aliases + - prefer_initializing_formals + - prefer_is_empty + - prefer_is_not_empty + - prefer_iterable_whereType + - prefer_single_quotes + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - recursive_getters + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + - super_goes_last + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_getters_setters + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_this + - unrelated_type_equality_checks + - use_rethrow_when_possible + - valid_regexps \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 996440d..09d0e6c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,18 +2,19 @@ import 'package:flutter/material.dart'; import 'screens/main.screen.dart'; -void main() => runApp(new App()); +void main() => runApp(App()); class App extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return new MaterialApp( + return MaterialApp( title: 'QR code demo', - theme: new ThemeData( + theme: ThemeData( primarySwatch: Colors.blue, ), - home: new MainScreen(), + home: MainScreen(), + debugShowCheckedModeBanner: false, ); } } diff --git a/example/lib/screens/main.screen.dart b/example/lib/screens/main.screen.dart index c5ab308..a95f51c 100644 --- a/example/lib/screens/main.screen.dart +++ b/example/lib/screens/main.screen.dart @@ -3,7 +3,7 @@ import 'package:qr_flutter/qr_flutter.dart'; class MainScreen extends StatefulWidget { @override - _MainScreenState createState() => new _MainScreenState(); + _MainScreenState createState() => _MainScreenState(); } class _MainScreenState extends State { @@ -11,57 +11,56 @@ class _MainScreenState extends State { static const double _topSectionBottomPadding = 20.0; static const double _topSectionHeight = 50.0; - String _dataString = "Hello from this QR code!"; + String _dataString = 'Hello from this QR code!'; String _inputErrorText; - final TextEditingController _textController = new TextEditingController(); + final TextEditingController _textController = TextEditingController(); @override Widget build(BuildContext context) { - return new Scaffold( + return Scaffold( body: _contentWidget(), resizeToAvoidBottomPadding: true, ); } @override - didUpdateWidget(Widget oldWidget) { + void didUpdateWidget(MainScreen oldWidget) { super.didUpdateWidget(oldWidget); setState(() {}); } - _contentWidget() { - final bodyHeight = MediaQuery.of(context).size.height - - MediaQuery.of(context).viewInsets.bottom; - return new Container( + Widget _contentWidget() { + return Container( color: const Color(0xFFFFFFFF), - child: new Column( + child: Column( children: [ - new Padding( + Padding( padding: const EdgeInsets.only( top: _topSectionTopPadding, - left: 20.0, - right: 10.0, + left: 30.0, + right: 20.0, bottom: _topSectionBottomPadding, ), - child: new Container( + child: Container( height: _topSectionHeight, - child: new Row( + child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - new Expanded( - child: new TextField( + Expanded( + child: TextField( + autofocus: true, controller: _textController, - decoration: new InputDecoration( - hintText: "Enter a custom message", + decoration: InputDecoration( + hintText: 'Enter a custom message', errorText: _inputErrorText, ), ), ), - new Padding( + Padding( padding: const EdgeInsets.only(left: 10.0), - child: new FlatButton( - child: new Text("SUBMIT"), + child: FlatButton( + child: const Text('SUBMIT'), onPressed: () { setState(() { _dataString = _textController.text; @@ -74,17 +73,22 @@ class _MainScreenState extends State { ), ), ), - new Expanded( - child: new Center( - child: new QrImage( - data: _dataString, - onError: (ex) { - print("[QR] ERROR - $ex"); - setState(() { - _inputErrorText = - "Error! Maybe your input value is too long?"; - }); - }, + Expanded( + child: Center( + child: Padding( + padding: const EdgeInsets.all(25.0), + child: QrImage( + data: _dataString, + gapless: false, + foregroundColor: const Color(0xFF111111), + onError: (dynamic ex) { + print('[QR] ERROR - $ex'); + setState(() { + _inputErrorText = + 'Error! Maybe your input value is too long?'; + }); + }, + ), ), ), ), diff --git a/lib/qr_flutter.dart b/lib/qr_flutter.dart index 6e3e7d4..a98fcaf 100644 --- a/lib/qr_flutter.dart +++ b/lib/qr_flutter.dart @@ -1,6 +1,6 @@ /* * QR.Flutter - * Copyright (c) 2018 the QR.Flutter authors. + * Copyright (c) 2019 the QR.Flutter authors. * See LICENSE for distribution and usage details. */ export 'src/qr_image.dart'; diff --git a/lib/src/qr_image.dart b/lib/src/qr_image.dart index 5ac7527..620801b 100644 --- a/lib/src/qr_image.dart +++ b/lib/src/qr_image.dart @@ -1,6 +1,6 @@ /* * QR.Flutter - * Copyright (c) 2018 the QR.Flutter authors. + * Copyright (c) 2019 the QR.Flutter authors. * See LICENSE for distribution and usage details. */ import 'package:flutter/foundation.dart'; @@ -20,7 +20,7 @@ class QrImage extends StatelessWidget { int errorCorrectionLevel = QrErrorCorrectLevel.L, this.onError, this.gapless = false, - }) : _painter = new QrPainter( + }) : _painter = QrPainter( data: data, color: foregroundColor, version: version, @@ -38,15 +38,15 @@ class QrImage extends StatelessWidget { @override Widget build(BuildContext context) { return LayoutBuilder( - builder: (context, constraints) { - double widgetSize = size ?? constraints.biggest.shortestSide; - return new Container( + builder: (BuildContext context, BoxConstraints constraints) { + final double widgetSize = size ?? constraints.biggest.shortestSide; + return Container( width: widgetSize, height: widgetSize, color: backgroundColor, - child: new Padding( - padding: this.padding, - child: new CustomPaint( + child: Padding( + padding: padding, + child: CustomPaint( painter: _painter, ), ), diff --git a/lib/src/qr_painter.dart b/lib/src/qr_painter.dart index 7df8c25..a7973cc 100644 --- a/lib/src/qr_painter.dart +++ b/lib/src/qr_painter.dart @@ -1,18 +1,30 @@ /* * QR.Flutter - * Copyright (c) 2018 the QR.Flutter authors. + * Copyright (c) 2019 the QR.Flutter authors. * See LICENSE for distribution and usage details. */ +import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:qr/qr.dart'; -typedef void QrError(dynamic error); +typedef QrError = void Function(dynamic error); class QrPainter extends CustomPainter { - // properties + QrPainter({ + @required String data, + @required this.version, + this.errorCorrectionLevel = QrErrorCorrectLevel.L, + this.color = const Color(0xff000000), + this.emptyColor, + this.onError, + this.gapless = false, + }) : _qr = QrCode(version, errorCorrectionLevel) { + _init(data); + } + final int version; // the qr code version final int errorCorrectionLevel; // the qr code error correction level final Color color; // the color of the dark squares @@ -20,35 +32,29 @@ class QrPainter extends CustomPainter { final QrError onError; final bool gapless; - QrPainter( - {@required String data, - @required this.version, - this.errorCorrectionLevel = QrErrorCorrectLevel.L, - this.color = const Color(0xff000000), - this.emptyColor, - this.onError, - this.gapless = false}) - : this._qr = new QrCode(version, errorCorrectionLevel) { - _p.color = this.color; + final QrCode _qr; // our qr code data + final Paint _paint = Paint()..style = PaintingStyle.fill; + bool _hasError = false; + + void _init(String data) { + _paint.color = color; // configure and make the QR code data try { _qr.addData(data); _qr.make(); } catch (ex) { - if (this.onError != null) { + if (onError != null) { _hasError = true; this.onError(ex); } } } - final QrCode _qr; // our qr code data - final _p = new Paint()..style = PaintingStyle.fill; - bool _hasError = false; - @override void paint(Canvas canvas, Size size) { - if (_hasError) return; + if (_hasError) { + return; + } if (size.shortestSide == 0) { print( "[QR] WARN: width or height is zero. You should set a 'size' value or nest this painter in a Widget that defines a non-zero size"); @@ -58,14 +64,14 @@ class QrPainter extends CustomPainter { canvas.drawColor(emptyColor, BlendMode.color); } - final squareSize = size.shortestSide / _qr.moduleCount; - final pxAdjustValue = gapless ? 1 : 0; + final double squareSize = size.shortestSide / _qr.moduleCount.toDouble(); + final int pxAdjustValue = gapless ? 1 : 0; for (int x = 0; x < _qr.moduleCount; x++) { for (int y = 0; y < _qr.moduleCount; y++) { if (_qr.isDark(y, x)) { - final squareRect = new Rect.fromLTWH(x * squareSize, y * squareSize, + final Rect squareRect = Rect.fromLTWH(x * squareSize, y * squareSize, squareSize + pxAdjustValue, squareSize + pxAdjustValue); - canvas.drawRect(squareRect, _p); + canvas.drawRect(squareRect, _paint); } } } @@ -74,24 +80,25 @@ class QrPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) { if (oldDelegate is QrPainter) { - return this.color != oldDelegate.color || - this.errorCorrectionLevel != oldDelegate.errorCorrectionLevel || - this.version != oldDelegate.version || - this._qr != oldDelegate._qr; + return color != oldDelegate.color || + errorCorrectionLevel != oldDelegate.errorCorrectionLevel || + version != oldDelegate.version || + _qr != oldDelegate._qr; } return false; } ui.Picture toPicture(double size) { - ui.PictureRecorder recorder = new ui.PictureRecorder(); - Canvas canvas = new Canvas(recorder); + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); paint(canvas, Size(size, size)); return recorder.endRecording(); } Future toImageData(double size, {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { - final uiImage = toPicture(size).toImage(size.toInt(), size.toInt()); + final ui.Image uiImage = + toPicture(size).toImage(size.toInt(), size.toInt()); return await uiImage.toByteData(format: format); } } diff --git a/pubspec.yaml b/pubspec.yaml index 2b7275b..224766d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,12 +2,12 @@ name: qr_flutter description: > QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widget or custom painter. -version: 1.1.5 +version: 1.1.6 author: Luke Freeman homepage: https://github.com/lukef/qr.flutter environment: - sdk: ">=2.0.0-dev.58.0 <3.0.0" + sdk: ">=2.0.0 <3.0.0" dependencies: flutter: diff --git a/test/painter_tests.dart b/test/painter_tests.dart index 19e23a8..b65403b 100644 --- a/test/painter_tests.dart +++ b/test/painter_tests.dart @@ -1,25 +1,26 @@ import 'dart:io'; import 'dart:ui'; -import 'package:qr/qr.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:qr/qr.dart'; import 'package:qr_flutter/src/qr_painter.dart'; void main() { testWidgets('Painter generates an image', (WidgetTester tester) async { await tester.runAsync(() async { - QrPainter painter = QrPainter( - data: "This is a test image", - color: Color(0xff222222), - emptyColor: Color(0xffffffff), + final QrPainter painter = QrPainter( + data: 'This is a test image', + color: const Color(0xff222222), + emptyColor: const Color(0xffffffff), version: 4, gapless: true, errorCorrectionLevel: QrErrorCorrectLevel.L, ); - final imageData = await painter.toImageData(300.0); - File file = File("./test_image.png"); + final ByteData imageData = await painter.toImageData(300.0); + File file = File('./test_image.png'); file = await file.writeAsBytes(imageData.buffer.asUint8List()); - int len = await file.length(); + final int len = await file.length(); expect(len, greaterThan(0)); }); });