diff --git a/AUTHORS b/AUTHORS index 6a6707a..6c56fc7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,4 +5,5 @@ Charles Crete: @cretezy Dmitry: @kelegorm Pedro Massango: @pedromassango Luke Pighetti: @lukepighetti -Jonas Zebari: @jonas-zebari \ No newline at end of file +Jonas Zebari: @jonas-zebari +Ivan Semkin: @vanyasem \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b3ec5..b35b156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 4.1.0 +- Bump `qr` dependency (from `^3.0.0` to `^3.0.1`). + # 4.0.1 - Bump `qr` dependency (from `^2.0.0` to `^3.0.0`). - **BREAKING**: Rename `QrImage` to `QrImageView` diff --git a/README.md b/README.md index 3c8f84b..49869e1 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You should add the following to your `pubspec.yaml` file: ```yaml dependencies: - qr_flutter: ^4.0.0 + qr_flutter: ^4.1.0 ``` **Note**: If you're using the Flutter `master` channel, if you encounter build issues, or want to try the latest and greatest then you should use the `master` branch and not a specific release version. To do so, use the following configuration in your `pubspec.yaml`: @@ -37,7 +37,7 @@ dependencies: dependencies: qr_flutter: git: - url: git://github.com/lukef/qr.flutter.git + url: https://github.com/theyakka/qr.flutter.git ``` Keep in mind the `master` branch could be unstable. @@ -55,8 +55,8 @@ import 'package:qr_flutter/qr_flutter.dart'; Next, to render a basic QR code you can use the following code (or something like it): ```dart -QrImage( - data: "1234567890", +QrImageView( + data: '1234567890', version: QrVersions.auto, size: 200.0, ), @@ -64,21 +64,22 @@ QrImage( Depending on your data requirements you may want to tweak the QR code output. The following options are available: -| Property | Type | Description | -|----|----|----| -| `version` | int | `QrVersions.auto` or a value between 1 and 40. See http://www.qrcode.com/en/about/version.html for limitations and details. | -| `errorCorrectionLevel` | int | A value defined on `QrErrorCorrectLevel`. e.g.: `QrErrorCorrectLevel.L`. | -| `size` | double | The (square) size of the image. If not given, will auto size using shortest size constraint. | -| `padding` | EdgeInsets | Padding surrounding the QR code data. | -| `backgroundColor` | Color | The background color (default is none). | -| `foregroundColor` | Color | The foreground color (default is black). | -| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true). | -| `errorStateBuilder` | QrErrorBuilder | Allows you to show an error state `Widget` in the event there is an error rendering the QR code (e.g.: version is too low, input is too long, etc). | -| `constrainErrorBounds` | bool | If true, the error `Widget` will be constrained to the square that the QR code was going to be drawn in. If false, the error state `Widget` will grow/shrink to whatever size it needs. | -| `embeddedImage` | ImageProvider | An `ImageProvider` that defines an image to be overlaid in the center of the QR code. | -| `embeddedImageStyle` | QrEmbeddedImageStyle | Properties to style the embedded image. | -| `embeddedImageEmitsError` | bool | If true, any failure to load the embedded image will trigger the `errorStateBuilder` or render an empty `Container`. If false, the QR code will be rendered and the embedded image will be ignored. | -|`semanticsLabel`|String|`semanticsLabel` will be used by screen readers to describe the content of the QR code.| +| Property | Type | Description | +|---------------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `version` | int | `QrVersions.auto` or a value between 1 and 40. See http://www.qrcode.com/en/about/version.html for limitations and details. | +| `errorCorrectionLevel` | int | A value defined on `QrErrorCorrectLevel`. e.g.: `QrErrorCorrectLevel.L`. | +| `size` | double | The (square) size of the image. If not given, will auto size using shortest size constraint. | +| `padding` | EdgeInsets | Padding surrounding the QR code data. | +| `backgroundColor` | Color | The background color (default is none). | +| `eyeStyle` | QrEyeStyle | Configures the QR code eyes' (corners') shape and color. | +| `dataModuleStyle` | QrDataModuleStyle | Configures the shape and the color of the dots. | +| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true). | +| `errorStateBuilder` | QrErrorBuilder | Allows you to show an error state `Widget` in the event there is an error rendering the QR code (e.g.: version is too low, input is too long, etc). | +| `constrainErrorBounds` | bool | If true, the error `Widget` will be constrained to the square that the QR code was going to be drawn in. If false, the error state `Widget` will grow/shrink to whatever size it needs. | +| `embeddedImage` | ImageProvider | An `ImageProvider` that defines an image to be overlaid in the center of the QR code. | +| `embeddedImageStyle` | QrEmbeddedImageStyle | Properties to style the embedded image. | +| `embeddedImageEmitsError` | bool | If true, any failure to load the embedded image will trigger the `errorStateBuilder` or render an empty `Container`. If false, the QR code will be rendered and the embedded image will be ignored. | +| `semanticsLabel` | String | `semanticsLabel` will be used by screen readers to describe the content of the QR code. | # Examples @@ -90,7 +91,7 @@ Also, the following examples give you a quick overview on how to use the library A basic QR code will look something like: ```dart -QrImage( +QrImageView( data: 'This is a simple QR code', version: QrVersions.auto, size: 320, @@ -101,7 +102,7 @@ QrImage( A QR code with an image (from your application's assets) will look like: ```dart -QrImage( +QrImageView( data: 'This QR code has an embedded image as well', version: QrVersions.auto, size: 320, @@ -116,7 +117,7 @@ QrImage( To show an error state in the event that the QR code can't be validated: ```dart -QrImage( +QrImageView( data: 'This QR code will show the error state instead', version: 1, size: 320, @@ -125,7 +126,7 @@ QrImage( return Container( child: Center( child: Text( - "Uh oh! Something went wrong...", + 'Uh oh! Something went wrong...', textAlign: TextAlign.center, ), ), @@ -134,13 +135,6 @@ QrImage( ) ``` - -# FAQ -## Has it been tested in production? Can I use it in production? - -Yep! It's stable and ready to rock. It's currently in use in quite a few production applications including: -- Sixpoint: [Android](https://play.google.com/store/apps/details?id=com.sixpoint.sixpoint&hl=en_US) & [iOS](https://itunes.apple.com/us/app/sixpoint/id663008674?mt=8) - # Outro ## Credits Thanks to Kevin Moore for his awesome [QR - Dart](https://github.com/kevmoo/qr.dart) library. It's the core of this library. diff --git a/analysis_options.yaml b/analysis_options.yaml index 82b26a2..2607df3 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -80,7 +80,6 @@ linter: # libraries # classes - one_member_abstracts # avoid - - avoid_classes_with_only_static_members # avoid - public_member_api_docs # constructors - prefer_constructors_over_static_methods diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 6ca16fd..eb3501b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,16 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + compileSdkVersion flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -39,7 +48,7 @@ android { defaultConfig { applicationId "app.yakka.example" minSdkVersion 16 - targetSdkVersion 33 + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -59,7 +68,7 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index 654eca8..6b107e9 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,7 @@ - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 16aa498..bae8b78 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -8,21 +8,16 @@ FlutterApplication and put your custom class here. --> - - + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index cd32859..c745a5e 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -8,5 +8,4 @@ - diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index 654eca8..6b107e9 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,6 +1,7 @@ - diff --git a/example/android/build.gradle b/example/android/build.gradle index 9816430..29a8f72 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,20 +1,23 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext { + buildGradleVersion = '7.4.2' + kotlinVersion = '1.7.21' + } repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "com.android.tools.build:gradle:$buildGradleVersion" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } } allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 734b469..3c472b9 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Mar 14 10:07:12 PDT 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14f..44e62bc 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..4f8d4d2 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 11.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d96dd44..6802989 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,17 +3,13 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -27,8 +23,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -39,13 +33,11 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -58,8 +50,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -69,9 +59,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -148,7 +136,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -193,6 +181,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -203,10 +192,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -255,7 +245,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -295,7 +284,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -331,7 +320,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -377,7 +365,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -387,7 +375,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -427,7 +414,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index df0c819..f0305a2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,12 +16,14 @@ class ExampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarColor: Colors.white, - statusBarIconBrightness: Brightness.dark, - systemNavigationBarColor: Colors.white, - systemNavigationBarIconBrightness: Brightness.dark, - )); + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + statusBarColor: Colors.white, + statusBarIconBrightness: Brightness.dark, + systemNavigationBarColor: Colors.white, + systemNavigationBarIconBrightness: Brightness.dark, + ), + ); return MaterialApp( title: 'QR.Flutter', theme: ThemeData.light(), diff --git a/example/lib/main_screen.dart b/example/lib/main_screen.dart index 38c67bb..c744be7 100644 --- a/example/lib/main_screen.dart +++ b/example/lib/main_screen.dart @@ -10,13 +10,13 @@ import 'package:qr_flutter/qr_flutter.dart'; /// This is the screen that you'll see when the app starts class MainScreen extends StatefulWidget { @override - _MainScreenState createState() => _MainScreenState(); + State createState() => _MainScreenState(); } class _MainScreenState extends State { @override Widget build(BuildContext context) { - final message = + const String message = // ignore: lines_longer_than_80_chars 'Hey this is a QR code. Change this value in the main_screen.dart file.'; diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9c94669..22cc746 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -2,13 +2,12 @@ name: example description: > The QR.Flutter example app. version: 2.0.0 -author: Yakka, LLC homepage: https://github.com/lukef/qr.flutter publish_to: none environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.7.0" + sdk: '>=2.19.6 <3.0.0' + flutter: ">=3.7.0" dependencies: flutter: diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 2a9f3b2..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:example/main.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Counter increments smoke test', (tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(ExampleApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/lib/src/paint_cache.dart b/lib/src/paint_cache.dart index 6a23e4c..ead3df3 100644 --- a/lib/src/paint_cache.dart +++ b/lib/src/paint_cache.dart @@ -8,19 +8,23 @@ import 'package:flutter/widgets.dart'; import 'types.dart'; -/// +/// Caches painter objects so we do have to recreate them and waste expensive +/// cycles. class PaintCache { final List _pixelPaints = []; final Map _keyedPaints = {}; String _cacheKey(QrCodeElement element, {FinderPatternPosition? position}) { final posKey = position != null ? position.toString() : 'any'; - return '${element.toString()}:$posKey'; + return '$element:$posKey'; } /// Save a [Paint] for the provided element and position into the cache. - void cache(Paint paint, QrCodeElement element, - {FinderPatternPosition? position}) { + void cache( + Paint paint, + QrCodeElement element, { + FinderPatternPosition? position, + }) { if (element == QrCodeElement.codePixel) { _pixelPaints.add(paint); } else { @@ -31,23 +35,21 @@ class PaintCache { /// Retrieve the first [Paint] object from the paint cache for the provided /// element and position. Paint? firstPaint(QrCodeElement element, {FinderPatternPosition? position}) { - if (element == QrCodeElement.codePixel) { - return _pixelPaints.first; - } else { - return _keyedPaints[_cacheKey(element, position: position)]; - } + return element == QrCodeElement.codePixel + ? _pixelPaints.first + : _keyedPaints[_cacheKey(element, position: position)]; } /// Retrieve all [Paint] objects from the paint cache for the provided /// element and position. Note: Finder pattern elements can only have a max /// one [Paint] object per position. As such they will always return a [List] /// with a fixed size of `1`. - List paints(QrCodeElement element, - {FinderPatternPosition? position}) { - if (element == QrCodeElement.codePixel) { - return _pixelPaints; - } else { - return [_keyedPaints[_cacheKey(element, position: position)]]; - } + List paints( + QrCodeElement element, { + FinderPatternPosition? position, + }) { + return element == QrCodeElement.codePixel + ? _pixelPaints + : [_keyedPaints[_cacheKey(element, position: position)]]; } } diff --git a/lib/src/qr_image_view.dart b/lib/src/qr_image_view.dart index 239d552..357d6ca 100644 --- a/lib/src/qr_image_view.dart +++ b/lib/src/qr_image_view.dart @@ -21,10 +21,11 @@ class QrImageView extends StatefulWidget { /// using the default options). QrImageView({ required String data, - Key? key, + super.key, this.size, this.padding = const EdgeInsets.all(10.0), this.backgroundColor = Colors.transparent, + @Deprecated('use colors in eyeStyle and dataModuleStyle instead') this.foregroundColor = Colors.black, this.version = QrVersions.auto, this.errorCorrectionLevel = QrErrorCorrectLevel.L, @@ -42,19 +43,22 @@ class QrImageView extends StatefulWidget { ), this.embeddedImageEmitsError = false, this.gradient, - }) : assert(QrVersions.isSupportedVersion(version)), + }) : assert( + QrVersions.isSupportedVersion(version), + 'QR code version $version is not supported', + ), _data = data, - _qrCode = null, - super(key: key); + _qrCode = null; /// Create a new QR code using the [QrCode] data and the passed options (or /// using the default options). QrImageView.withQr({ required QrCode qr, - Key? key, + super.key, this.size, this.padding = const EdgeInsets.all(10.0), this.backgroundColor = Colors.transparent, + @Deprecated('use colors in eyeStyle and dataModuleStyle instead') this.foregroundColor = Colors.black, this.version = QrVersions.auto, this.errorCorrectionLevel = QrErrorCorrectLevel.L, @@ -74,13 +78,16 @@ class QrImageView extends StatefulWidget { ), this.embeddedImageEmitsError = false, this.gradient, - }) : assert(QrVersions.isSupportedVersion(version)), + }) : assert( + QrVersions.isSupportedVersion(version), + 'QR code version $version is not supported', + ), _data = null, - _qrCode = qr, - super(key: key); + _qrCode = qr; // The data passed to the widget final String? _data; + // The QR code data passed to the widget final QrCode? _qrCode; @@ -149,7 +156,7 @@ class QrImageView extends StatefulWidget { final QrDataModuleStyle dataModuleStyle; @override - _QrImageViewState createState() => _QrImageViewState(); + State createState() => _QrImageViewState(); } class _QrImageViewState extends State { @@ -167,55 +174,53 @@ class _QrImageViewState extends State { version: widget.version, errorCorrectionLevel: widget.errorCorrectionLevel, ); - if (_validationResult.isValid) { - _qr = _validationResult.qrCode; - } else { - _qr = null; - } + _qr = _validationResult.isValid ? _validationResult.qrCode : null; } else if (widget._qrCode != null) { _qr = widget._qrCode; _validationResult = QrValidationResult(status: QrValidationStatus.valid, qrCode: _qr); } - return LayoutBuilder(builder: (context, constraints) { - // validation failed, show an error state widget if builder is present. - if (!_validationResult.isValid) { - return _errorWidget(context, constraints, _validationResult.error); - } - // no error, build the regular widget - final widgetSize = widget.size ?? constraints.biggest.shortestSide; - if (widget.embeddedImage != null) { - // if requesting to embed an image then we need to load via a - // FutureBuilder because the image provider will be async. - return FutureBuilder( - future: _loadQrImage(context, widget.embeddedImageStyle), - builder: (ctx, snapshot) { - if (snapshot.error != null) { - print("snapshot error: ${snapshot.error}"); - if (widget.embeddedImageEmitsError) { - return _errorWidget(context, constraints, snapshot.error); - } else { - return _qrWidget(context, null, widgetSize); + return LayoutBuilder( + builder: (context, constraints) { + // validation failed, show an error state widget if builder is present. + if (!_validationResult.isValid) { + return _errorWidget(context, constraints, _validationResult.error); + } + // no error, build the regular widget + final widgetSize = + widget.size ?? constraints.biggest.shortestSide; + if (widget.embeddedImage != null) { + // if requesting to embed an image then we need to load via a + // FutureBuilder because the image provider will be async. + return FutureBuilder( + future: _loadQrImage(context, widget.embeddedImageStyle), + builder: (ctx, snapshot) { + if (snapshot.error != null) { + debugPrint('snapshot error: ${snapshot.error}'); + return widget.embeddedImageEmitsError + ? _errorWidget(context, constraints, snapshot.error) + : _qrWidget(null, widgetSize); } - } - if (snapshot.hasData) { - print('loaded image'); - final loadedImage = snapshot.data; - return _qrWidget(context, loadedImage, widgetSize); - } else { - return Container(); - } - }, - ); - } else { - return _qrWidget(context, null, widgetSize); - } - }); + if (snapshot.hasData) { + debugPrint('loaded image'); + final loadedImage = snapshot.data; + return _qrWidget(loadedImage, widgetSize); + } else { + return Container(); + } + }, + ); + } else { + return _qrWidget(null, widgetSize); + } + }, + ); } - Widget _qrWidget(BuildContext context, ui.Image? image, double edgeLength) { + Widget _qrWidget(ui.Image? image, double edgeLength) { final painter = QrPainter.withQr( qr: _qr!, + // ignore: deprecated_member_use_from_same_package color: widget.foregroundColor, gapless: widget.gapless, embeddedImageStyle: widget.embeddedImageStyle, @@ -234,40 +239,51 @@ class _QrImageViewState extends State { } Widget _errorWidget( - BuildContext context, BoxConstraints constraints, Object? error) { + BuildContext context, + BoxConstraints constraints, + Object? error, + ) { final errorWidget = widget.errorStateBuilder == null ? Container() : widget.errorStateBuilder!(context, error); - final errorSideLength = (widget.constrainErrorBounds + final errorSideLength = widget.constrainErrorBounds ? widget.size ?? constraints.biggest.shortestSide - : constraints.biggest.longestSide); + : constraints.biggest.longestSide; return _QrContentView( edgeLength: errorSideLength, backgroundColor: widget.backgroundColor, padding: widget.padding, - child: errorWidget, semanticsLabel: widget.semanticsLabel, + child: errorWidget, ); } late ImageStreamListener streamListener; + Future _loadQrImage( - BuildContext buildContext, QrEmbeddedImageStyle? style) async { + BuildContext buildContext, + QrEmbeddedImageStyle? style, + ) { if (style != null) {} final mq = MediaQuery.of(buildContext); final completer = Completer(); - final stream = widget.embeddedImage!.resolve(ImageConfiguration( - devicePixelRatio: mq.devicePixelRatio, - )); + final stream = widget.embeddedImage!.resolve( + ImageConfiguration( + devicePixelRatio: mq.devicePixelRatio, + ), + ); - streamListener = ImageStreamListener((info, err) { - stream.removeListener(streamListener); - completer.complete(info.image); - }, onError: (dynamic err, _) { - stream.removeListener(streamListener); - completer.completeError(err); - }); + streamListener = ImageStreamListener( + (info, err) { + stream.removeListener(streamListener); + completer.complete(info.image); + }, + onError: (err, _) { + stream.removeListener(streamListener); + completer.completeError(err); + }, + ); stream.addListener(streamListener); return completer.future; } @@ -278,7 +294,7 @@ class _QrImageViewState extends State { typedef QrErrorBuilder = Widget Function(BuildContext context, Object? error); class _QrContentView extends StatelessWidget { - _QrContentView({ + const _QrContentView({ required this.edgeLength, required this.child, this.backgroundColor, diff --git a/lib/src/qr_painter.dart b/lib/src/qr_painter.dart index 3168c9a..e773c57 100644 --- a/lib/src/qr_painter.dart +++ b/lib/src/qr_painter.dart @@ -19,7 +19,7 @@ import 'validator.dart'; // ignore_for_file: deprecated_member_use_from_same_package -const _finderPatternLimit = 7; +const int _finderPatternLimit = 7; // default colors for the qr code pixels const Color _qrDefaultColor = Color(0xff000000); @@ -32,7 +32,11 @@ class QrPainter extends CustomPainter { required String data, required this.version, this.errorCorrectionLevel = QrErrorCorrectLevel.L, + @Deprecated('use colors in eyeStyle and dataModuleStyle instead') this.color = _qrDefaultColor, + @Deprecated( + 'You should use the background color value of your container widget', + ) this.emptyColor = _qrDefaultEmptyColor, this.gapless = false, this.embeddedImage, @@ -40,7 +44,10 @@ class QrPainter extends CustomPainter { this.eyeStyle = const QrEyeStyle(), this.dataModuleStyle = const QrDataModuleStyle(), this.gradient, - }) : assert(QrVersions.isSupportedVersion(version)) { + }) : assert( + QrVersions.isSupportedVersion(version), + 'QR code version $version is not supported', + ) { _init(data); } @@ -49,7 +56,11 @@ class QrPainter extends CustomPainter { /// flow or for when you need to pre-validate the QR data. QrPainter.withQr({ required QrCode qr, + @Deprecated('use colors in eyeStyle and dataModuleStyle instead') this.color = _qrDefaultColor, + @Deprecated( + 'You should use the background color value of your container widget', + ) this.emptyColor = _qrDefaultEmptyColor, this.gapless = false, this.embeddedImage, @@ -112,7 +123,7 @@ class QrPainter extends CustomPainter { final double _gapSize = 0.25; /// Cache for all of the [Paint] objects. - final _paintCache = PaintCache(); + final PaintCache _paintCache = PaintCache(); void _init(String data) { if (!QrVersions.isSupportedVersion(version)) { @@ -138,23 +149,33 @@ class QrPainter extends CustomPainter { // Cache the pixel paint object. For now there is only one but we might // expand it to multiple later (e.g.: different colours). _paintCache.cache( - Paint()..style = PaintingStyle.fill, QrCodeElement.codePixel); + Paint()..style = PaintingStyle.fill, + QrCodeElement.codePixel, + ); // Cache the empty pixel paint object. Empty color is deprecated and will go // away. _paintCache.cache( - Paint()..style = PaintingStyle.fill, QrCodeElement.codePixelEmpty); + Paint()..style = PaintingStyle.fill, + QrCodeElement.codePixelEmpty, + ); // Cache the finder pattern painters. We'll keep one for each one in case // we want to provide customization options later. for (final position in FinderPatternPosition.values) { - _paintCache.cache(Paint()..style = PaintingStyle.stroke, - QrCodeElement.finderPatternOuter, - position: position); - _paintCache.cache(Paint()..style = PaintingStyle.stroke, - QrCodeElement.finderPatternInner, - position: position); _paintCache.cache( - Paint()..style = PaintingStyle.fill, QrCodeElement.finderPatternDot, - position: position); + Paint()..style = PaintingStyle.stroke, + QrCodeElement.finderPatternOuter, + position: position, + ); + _paintCache.cache( + Paint()..style = PaintingStyle.stroke, + QrCodeElement.finderPatternInner, + position: position, + ); + _paintCache.cache( + Paint()..style = PaintingStyle.fill, + QrCodeElement.finderPatternDot, + position: position, + ); } } @@ -162,23 +183,34 @@ class QrPainter extends CustomPainter { void paint(Canvas canvas, Size size) { // if the widget has a zero size side then we cannot continue painting. 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"); + debugPrint( + "[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'); return; } final paintMetrics = _PaintMetrics( containerSize: size.shortestSide, moduleCount: _qr!.moduleCount, - gapSize: (gapless ? 0 : _gapSize), + gapSize: gapless ? 0 : _gapSize, ); // draw the finder pattern elements - _drawFinderPatternItem(FinderPatternPosition.topLeft, canvas, paintMetrics); _drawFinderPatternItem( - FinderPatternPosition.bottomLeft, canvas, paintMetrics); + FinderPatternPosition.topLeft, + canvas, + paintMetrics, + ); _drawFinderPatternItem( - FinderPatternPosition.topRight, canvas, paintMetrics); + FinderPatternPosition.bottomLeft, + canvas, + paintMetrics, + ); + _drawFinderPatternItem( + FinderPatternPosition.topRight, + canvas, + paintMetrics, + ); // DEBUG: draw the inner content boundary // final paint = Paint()..style = ui.PaintingStyle.stroke; @@ -271,10 +303,14 @@ class QrPainter extends CustomPainter { for (var x = 0; x < _qr!.moduleCount; x++) { for (var y = 0; y < _qr!.moduleCount; y++) { // draw the finder patterns independently - if (_isFinderPatternPosition(x, y)) continue; + if (_isFinderPatternPosition(x, y)) { + continue; + } final isDark = _qrImage.isDark(y, x); final paint = isDark ? pixelPaint : emptyPixelPaint; - if (!isDark && !isRoundedOutsideCorners) continue; + if (!isDark && !isRoundedOutsideCorners) { + continue; + } // paint a pixel final squareRect = _createDataModuleRect(paintMetrics, x, y, gap); // check safeArea @@ -422,21 +458,25 @@ class QrPainter extends CustomPainter { } bool _hasAdjacentVerticalPixel(int x, int y, int moduleCount) { - if (y + 1 >= moduleCount) return false; + if (y + 1 >= moduleCount) { + return false; + } return _qrImage.isDark(y + 1, x); } bool _hasAdjacentHorizontalPixel(int x, int y, int moduleCount) { - if (x + 1 >= moduleCount) return false; + if (x + 1 >= moduleCount) { + return false; + } return _qrImage.isDark(y, x + 1); } bool _isFinderPatternPosition(int x, int y) { - final isTopLeft = (y < _finderPatternLimit && x < _finderPatternLimit); - final isBottomLeft = (y < _finderPatternLimit && - (x >= _qr!.moduleCount - _finderPatternLimit)); - final isTopRight = (y >= _qr!.moduleCount - _finderPatternLimit && - (x < _finderPatternLimit)); + final isTopLeft = y < _finderPatternLimit && x < _finderPatternLimit; + final isBottomLeft = y < _finderPatternLimit && + (x >= _qr!.moduleCount - _finderPatternLimit); + final isTopRight = y >= _qr!.moduleCount - _finderPatternLimit && + (x < _finderPatternLimit); return isTopLeft || isBottomLeft || isTopRight; } @@ -446,9 +486,10 @@ class QrPainter extends CustomPainter { _PaintMetrics metrics, ) { final totalGap = (_finderPatternLimit - 1) * metrics.gapSize; - final radius = ((_finderPatternLimit * metrics.pixelSize) + totalGap) - - metrics.pixelSize; - final strokeAdjust = (metrics.pixelSize / 2.0); + final radius = + ((_finderPatternLimit * metrics.pixelSize) + totalGap) - + metrics.pixelSize; + final strokeAdjust = metrics.pixelSize / 2.0; final edgePos = (metrics.inset + metrics.innerContentSize) - (radius + strokeAdjust); @@ -463,31 +504,44 @@ class QrPainter extends CustomPainter { } // configure the paints - final outerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternOuter, - position: position)!; + final outerPaint = _paintCache.firstPaint( + QrCodeElement.finderPatternOuter, + position: position, + )!; final color = _priorityColor(eyeStyle.color); outerPaint.strokeWidth = metrics.pixelSize; outerPaint.color = color; - final innerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternInner, - position: position)!; + final innerPaint = _paintCache + .firstPaint(QrCodeElement.finderPatternInner, position: position)!; innerPaint.strokeWidth = metrics.pixelSize; innerPaint.color = emptyColor; - final dotPaint = _paintCache.firstPaint(QrCodeElement.finderPatternDot, - position: position); + final dotPaint = _paintCache.firstPaint( + QrCodeElement.finderPatternDot, + position: position, + ); dotPaint!.color = color; - final outerRect = Rect.fromLTWH(offset.dx, offset.dy, radius, radius); + final outerRect = + Rect.fromLTWH(offset.dx, offset.dy, radius, radius); final innerRadius = radius - (2 * metrics.pixelSize); - final innerRect = Rect.fromLTWH(offset.dx + metrics.pixelSize, - offset.dy + metrics.pixelSize, innerRadius, innerRadius); + final innerRect = Rect.fromLTWH( + offset.dx + metrics.pixelSize, + offset.dy + metrics.pixelSize, + innerRadius, + innerRadius, + ); final gap = metrics.pixelSize * 2; final dotSize = radius - gap - (2 * strokeAdjust); - final dotRect = Rect.fromLTWH(offset.dx + metrics.pixelSize + strokeAdjust, - offset.dy + metrics.pixelSize + strokeAdjust, dotSize, dotSize); + final dotRect = Rect.fromLTWH( + offset.dx + metrics.pixelSize + strokeAdjust, + offset.dy + metrics.pixelSize + strokeAdjust, + dotSize, + dotSize, + ); switch(eyeStyle.eyeShape) { case QrEyeShape.square: @@ -524,7 +578,10 @@ class QrPainter extends CustomPainter { bool _hasOneNonZeroSide(Size size) => size.longestSide > 0; Size _scaledAspectSize( - Size widgetSize, Size originalSize, Size? requestedSize) { + Size widgetSize, + Size originalSize, + Size? requestedSize, + ) { if (requestedSize != null && !requestedSize.isEmpty) { return requestedSize; } else if (requestedSize != null && _hasOneNonZeroSide(requestedSize)) { @@ -539,7 +596,11 @@ class QrPainter extends CustomPainter { } void _drawImageOverlay( - Canvas canvas, Offset position, Size size, QrEmbeddedImageStyle? style) { + Canvas canvas, + Offset position, + Size size, + QrEmbeddedImageStyle? style, + ) { final paint = Paint() ..isAntiAlias = true ..filterQuality = FilterQuality.high; @@ -587,24 +648,26 @@ class QrPainter extends CustomPainter { } /// Returns the raw QR code [ui.Image] object. - Future toImage(double size, - {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { - return await toPicture(size).toImage(size.toInt(), size.toInt()); + Future toImage(double size) { + return toPicture(size).toImage(size.toInt(), size.toInt()); } /// Returns the raw QR code image byte data. - Future toImageData(double size, - {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { - final image = await toImage(size, format: format); + Future toImageData( + double size, { + ui.ImageByteFormat format = ui.ImageByteFormat.png, + }) async { + final image = await toImage(size); return image.toByteData(format: format); } } class _PaintMetrics { - _PaintMetrics( - {required this.containerSize, - required this.gapSize, - required this.moduleCount}) { + _PaintMetrics({ + required this.containerSize, + required this.gapSize, + required this.moduleCount, + }) { _calculateMetrics(); } @@ -623,7 +686,7 @@ class _PaintMetrics { void _calculateMetrics() { final gapTotal = (moduleCount - 1) * gapSize; - var pixelSize = (containerSize - gapTotal) / moduleCount; + final pixelSize = (containerSize - gapTotal) / moduleCount; _pixelSize = (pixelSize * 2).roundToDouble() / 2; _innerContentSize = (_pixelSize * moduleCount) + gapTotal; _inset = (containerSize - _innerContentSize) / 2; diff --git a/lib/src/types.dart b/lib/src/types.dart index 1e7da62..07a85e9 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4,6 +4,7 @@ * See LICENSE for distribution and usage details. */ +import 'package:flutter/widgets.dart'; import 'dart:ui'; @@ -70,6 +71,7 @@ enum EmbeddedImageShape { } /// Styling options for finder pattern eye. +@immutable class QrEyeStyle { /// Create a new set of styling options for QR Eye. const QrEyeStyle({ @@ -100,6 +102,7 @@ class QrEyeStyle { } /// Styling options for data module. +@immutable class QrDataModuleStyle { /// Create a new set of styling options for data modules. const QrDataModuleStyle({ @@ -152,6 +155,7 @@ class QrDataModuleStyle { } /// Styling options for any embedded image overlay +@immutable class QrEmbeddedImageStyle { /// Create a new set of styling options. const QrEmbeddedImageStyle({ diff --git a/lib/src/validator.dart b/lib/src/validator.dart index b40d31c..12a1eb8 100644 --- a/lib/src/validator.dart +++ b/lib/src/validator.dart @@ -30,10 +30,14 @@ class QrValidator { ); } return QrValidationResult( - status: QrValidationStatus.valid, qrCode: qrCode); - } on InputTooLongException catch (itle) { + status: QrValidationStatus.valid, + qrCode: qrCode, + ); + } on InputTooLongException catch (title) { return QrValidationResult( - status: QrValidationStatus.contentTooLong, error: itle); + status: QrValidationStatus.contentTooLong, + error: title, + ); } on Exception catch (ex) { return QrValidationResult(status: QrValidationStatus.error, error: ex); } diff --git a/pubspec.yaml b/pubspec.yaml index 04b46d3..f0e2e74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,22 +2,21 @@ name: qr_flutter description: > QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widget or custom painter. -version: 4.0.1 +version: 4.1.0 homepage: https://github.com/theyakka/qr.flutter environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.16.0" + sdk: '>=2.19.6 <4.0.0' + flutter: ">=3.7.0" dependencies: flutter: sdk: flutter - qr: ^3.0.0 + qr: ^3.0.1 dev_dependencies: flutter_test: sdk: flutter - test: ^1.16.5 flutter: uses-material-design: false diff --git a/test/.golden/qr_image_data_module_styled_golden.png b/test/.golden/qr_image_data_module_styled_golden.png index 47a1641..d50b737 100644 Binary files a/test/.golden/qr_image_data_module_styled_golden.png and b/test/.golden/qr_image_data_module_styled_golden.png differ diff --git a/test/.golden/qr_image_eye_data_module_styled_golden.png b/test/.golden/qr_image_eye_data_module_styled_golden.png index 8f44474..275496d 100644 Binary files a/test/.golden/qr_image_eye_data_module_styled_golden.png and b/test/.golden/qr_image_eye_data_module_styled_golden.png differ diff --git a/test/.golden/qr_image_eye_styled_golden.png b/test/.golden/qr_image_eye_styled_golden.png index fe964e5..867ae26 100644 Binary files a/test/.golden/qr_image_eye_styled_golden.png and b/test/.golden/qr_image_eye_styled_golden.png differ diff --git a/test/.golden/qr_image_foreground_colored_golden.png b/test/.golden/qr_image_foreground_colored_golden.png index affaf04..60de4e7 100644 Binary files a/test/.golden/qr_image_foreground_colored_golden.png and b/test/.golden/qr_image_foreground_colored_golden.png differ diff --git a/test/.golden/qr_image_golden.png b/test/.golden/qr_image_golden.png index 93e2dd8..493c247 100644 Binary files a/test/.golden/qr_image_golden.png and b/test/.golden/qr_image_golden.png differ diff --git a/test/.golden/qr_image_logo_golden.png b/test/.golden/qr_image_logo_golden.png index 0ab1b80..8856a1b 100644 Binary files a/test/.golden/qr_image_logo_golden.png and b/test/.golden/qr_image_logo_golden.png differ diff --git a/test/image_test.dart b/test/image_test.dart index a6bf5be..578c1c8 100644 --- a/test/image_test.dart +++ b/test/image_test.dart @@ -11,7 +11,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:qr_flutter/qr_flutter.dart'; void main() { - testWidgets('QrImageView generates correct image', (tester) async { + testWidgets('QrImageView generates correct image', ( + tester, + ) async { final qrImage = MaterialApp( home: Center( child: RepaintBoundary( @@ -31,143 +33,156 @@ void main() { ); }); - testWidgets('QrImageView generates correct image with eye style', - (tester) async { - final qrImage = MaterialApp( - home: Center( - child: RepaintBoundary( - child: QrImageView( - data: 'This is a test image', - version: QrVersions.auto, - gapless: true, - errorCorrectionLevel: QrErrorCorrectLevel.L, - eyeStyle: const QrEyeStyle( - eyeShape: QrEyeShape.circle, - color: Colors.green, - ), - ), - ), - ), - ); - await tester.pumpWidget(qrImage); - await expectLater( - find.byType(QrImageView), - matchesGoldenFile('./.golden/qr_image_eye_styled_golden.png'), - ); - }); - - testWidgets('QrImageView generates correct image with data module style', - (tester) async { - final qrImage = MaterialApp( - home: Center( - child: RepaintBoundary( - child: QrImageView( - data: 'This is a test image', - version: QrVersions.auto, - gapless: true, - errorCorrectionLevel: QrErrorCorrectLevel.L, - dataModuleStyle: const QrDataModuleStyle( - dataModuleShape: QrDataModuleShape.circle, - color: Colors.blue, - ), - ), - ), - ), - ); - await tester.pumpWidget(qrImage); - await expectLater( - find.byType(QrImageView), - matchesGoldenFile('./.golden/qr_image_data_module_styled_golden.png'), - ); - }); - testWidgets( - 'QrImageView generates correct image with eye and data module sytle', - (tester) async { - final qrImage = MaterialApp( - home: Center( - child: RepaintBoundary( - child: QrImageView( - data: 'This is a test image', - version: QrVersions.auto, - gapless: true, - errorCorrectionLevel: QrErrorCorrectLevel.L, - eyeStyle: const QrEyeStyle( - eyeShape: QrEyeShape.circle, - color: Colors.green, - ), - dataModuleStyle: const QrDataModuleStyle( - dataModuleShape: QrDataModuleShape.circle, - color: Colors.blue, - ), - ), - ), - ), - ); - await tester.pumpWidget(qrImage); - await expectLater( - find.byType(QrImageView), - matchesGoldenFile('./.golden/qr_image_eye_data_module_styled_golden.png'), - ); - }); - - testWidgets( - 'QrImageView does not apply eye and data module color when foreground ' - 'color is also specified', (tester) async { - final qrImage = MaterialApp( - home: Center( - child: RepaintBoundary( - child: QrImageView( - data: 'This is a test image', - version: QrVersions.auto, - gapless: true, - foregroundColor: Colors.red, - errorCorrectionLevel: QrErrorCorrectLevel.L, - eyeStyle: const QrEyeStyle( - eyeShape: QrEyeShape.circle, - color: Colors.green, - ), - dataModuleStyle: const QrDataModuleStyle( - dataModuleShape: QrDataModuleShape.circle, - color: Colors.blue, - ), - ), - ), - ), - ); - await tester.pumpWidget(qrImage); - await expectLater( - find.byType(QrImageView), - matchesGoldenFile('./.golden/qr_image_foreground_colored_golden.png'), - ); - }); - - testWidgets('QrImageView generates correct image with logo', (tester) async { - await pumpWidgetWithImages( - tester, - MaterialApp( + 'QrImageView generates correct image with eye style', + (tester) async { + final qrImage = MaterialApp( home: Center( child: RepaintBoundary( child: QrImageView( - data: 'This is a a qr code with a logo', + data: 'This is a test image', version: QrVersions.auto, gapless: true, errorCorrectionLevel: QrErrorCorrectLevel.L, - embeddedImage: FileImage(File('test/.images/logo_yakka.png')), + eyeStyle: const QrEyeStyle( + eyeShape: QrEyeShape.circle, + color: Colors.green, + ), ), ), ), - ), - ['test/.images/logo_yakka.png'], - ); + ); + await tester.pumpWidget(qrImage); + await expectLater( + find.byType(QrImageView), + matchesGoldenFile('./.golden/qr_image_eye_styled_golden.png'), + ); + }, + ); - await tester.pumpAndSettle(); + testWidgets( + 'QrImageView generates correct image with data module style', + (tester) async { + final qrImage = MaterialApp( + home: Center( + child: RepaintBoundary( + child: QrImageView( + data: 'This is a test image', + version: QrVersions.auto, + gapless: true, + errorCorrectionLevel: QrErrorCorrectLevel.L, + dataModuleStyle: const QrDataModuleStyle( + dataModuleShape: QrDataModuleShape.circle, + color: Colors.blue, + ), + ), + ), + ), + ); + await tester.pumpWidget(qrImage); + await expectLater( + find.byType(QrImageView), + matchesGoldenFile('./.golden/qr_image_data_module_styled_golden.png'), + ); + }, + ); - await expectLater( - find.byType(QrImageView), - matchesGoldenFile('./.golden/qr_image_logo_golden.png'), - ); - }); + testWidgets( + 'QrImageView generates correct image with eye and data module sytle', + (tester) async { + final qrImage = MaterialApp( + home: Center( + child: RepaintBoundary( + child: QrImageView( + data: 'This is a test image', + version: QrVersions.auto, + gapless: true, + errorCorrectionLevel: QrErrorCorrectLevel.L, + eyeStyle: const QrEyeStyle( + eyeShape: QrEyeShape.circle, + color: Colors.green, + ), + dataModuleStyle: const QrDataModuleStyle( + dataModuleShape: QrDataModuleShape.circle, + color: Colors.blue, + ), + ), + ), + ), + ); + await tester.pumpWidget(qrImage); + await expectLater( + find.byType(QrImageView), + matchesGoldenFile( + './.golden/qr_image_eye_data_module_styled_golden.png', + ), + ); + }, + ); + + testWidgets( + 'QrImageView does not apply eye and data module color when foreground ' + 'color is also specified', + (tester) async { + final qrImage = MaterialApp( + home: Center( + child: RepaintBoundary( + child: QrImageView( + data: 'This is a test image', + version: QrVersions.auto, + gapless: true, + // ignore: deprecated_member_use_from_same_package + foregroundColor: Colors.red, + errorCorrectionLevel: QrErrorCorrectLevel.L, + eyeStyle: const QrEyeStyle( + eyeShape: QrEyeShape.circle, + color: Colors.green, + ), + dataModuleStyle: const QrDataModuleStyle( + dataModuleShape: QrDataModuleShape.circle, + color: Colors.blue, + ), + ), + ), + ), + ); + await tester.pumpWidget(qrImage); + await expectLater( + find.byType(QrImageView), + matchesGoldenFile('./.golden/qr_image_foreground_colored_golden.png'), + ); + }, + ); + + testWidgets( + 'QrImageView generates correct image with logo', + (tester) async { + await pumpWidgetWithImages( + tester, + MaterialApp( + home: Center( + child: RepaintBoundary( + child: QrImageView( + data: 'This is a a qr code with a logo', + version: QrVersions.auto, + gapless: true, + errorCorrectionLevel: QrErrorCorrectLevel.L, + embeddedImage: FileImage(File('test/.images/logo_yakka.png')), + ), + ), + ), + ), + ['test/.images/logo_yakka.png'], + ); + + await tester.pumpAndSettle(); + + await expectLater( + find.byType(QrImageView), + matchesGoldenFile('./.golden/qr_image_logo_golden.png'), + ); + }, + ); } /// Pre-cache images to make sure they show up in golden tests. @@ -180,19 +195,24 @@ Future pumpWidgetWithImages( ) async { Future? precacheFuture; await tester.pumpWidget( - Builder(builder: (buildContext) { - precacheFuture = tester.runAsync(() async { - await Future.wait([ - for (final assetName in assetNames) - precacheImage(FileImage(File(assetName)), buildContext), - ]); - }); - return widget; - }), + Builder( + builder: (buildContext) { + precacheFuture = tester.runAsync(() async { + await Future.wait(>[ + for (final String assetName in assetNames) + precacheImage(FileImage(File(assetName)), buildContext), + ]); + }); + return widget; + }, + ), ); await precacheFuture; } Widget buildTestableWidget(Widget widget) { - return MediaQuery(data: MediaQueryData(), child: MaterialApp(home: widget)); + return MediaQuery( + data: const MediaQueryData(), + child: MaterialApp(home: widget), + ); } diff --git a/test/painter_test.dart b/test/painter_test.dart index 31bbba6..3e292c9 100644 --- a/test/painter_test.dart +++ b/test/painter_test.dart @@ -22,9 +22,9 @@ void main() { imageData = await painter.toImageData(600.0); }); final imageBytes = imageData!.buffer.asUint8List(); - final widget = Center( + final Widget widget = Center( child: RepaintBoundary( - child: Container( + child: SizedBox( width: 600, height: 600, child: Image.memory(imageBytes),