Merge branch 'master' of https://github.com/theyakka/qr.flutter
Conflicts: example/android/app/build.gradle example/android/build.gradle example/lib/main_screen.dart lib/src/qr_image_view.dart lib/src/qr_painter.dart lib/src/types.dart
3
AUTHORS
@ -5,4 +5,5 @@ Charles Crete: @cretezy
|
|||||||
Dmitry: @kelegorm
|
Dmitry: @kelegorm
|
||||||
Pedro Massango: @pedromassango
|
Pedro Massango: @pedromassango
|
||||||
Luke Pighetti: @lukepighetti
|
Luke Pighetti: @lukepighetti
|
||||||
Jonas Zebari: @jonas-zebari
|
Jonas Zebari: @jonas-zebari
|
||||||
|
Ivan Semkin: @vanyasem
|
@ -1,3 +1,6 @@
|
|||||||
|
# 4.1.0
|
||||||
|
- Bump `qr` dependency (from `^3.0.0` to `^3.0.1`).
|
||||||
|
|
||||||
# 4.0.1
|
# 4.0.1
|
||||||
- Bump `qr` dependency (from `^2.0.0` to `^3.0.0`).
|
- Bump `qr` dependency (from `^2.0.0` to `^3.0.0`).
|
||||||
- **BREAKING**: Rename `QrImage` to `QrImageView`
|
- **BREAKING**: Rename `QrImage` to `QrImageView`
|
||||||
|
54
README.md
@ -28,7 +28,7 @@ You should add the following to your `pubspec.yaml` file:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
dependencies:
|
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`:
|
**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:
|
dependencies:
|
||||||
qr_flutter:
|
qr_flutter:
|
||||||
git:
|
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.
|
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):
|
Next, to render a basic QR code you can use the following code (or something like it):
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
QrImage(
|
QrImageView(
|
||||||
data: "1234567890",
|
data: '1234567890',
|
||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
size: 200.0,
|
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:
|
Depending on your data requirements you may want to tweak the QR code output. The following options are available:
|
||||||
|
|
||||||
| Property | Type | Description |
|
| 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. |
|
| `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`. |
|
| `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. |
|
| `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. |
|
| `padding` | EdgeInsets | Padding surrounding the QR code data. |
|
||||||
| `backgroundColor` | Color | The background color (default is none). |
|
| `backgroundColor` | Color | The background color (default is none). |
|
||||||
| `foregroundColor` | Color | The foreground color (default is black). |
|
| `eyeStyle` | QrEyeStyle | Configures the QR code eyes' (corners') shape and color. |
|
||||||
| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true). |
|
| `dataModuleStyle` | QrDataModuleStyle | Configures the shape and the color of the dots. |
|
||||||
| `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). |
|
| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true). |
|
||||||
| `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. |
|
| `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). |
|
||||||
| `embeddedImage` | ImageProvider | An `ImageProvider` that defines an image to be overlaid in the center of the QR code. |
|
| `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. |
|
||||||
| `embeddedImageStyle` | QrEmbeddedImageStyle | Properties to style the embedded image. |
|
| `embeddedImage` | ImageProvider | An `ImageProvider` that defines an image to be overlaid in the center of the QR code. |
|
||||||
| `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. |
|
| `embeddedImageStyle` | QrEmbeddedImageStyle | Properties to style the embedded image. |
|
||||||
|`semanticsLabel`|String|`semanticsLabel` will be used by screen readers to describe the content of the QR code.|
|
| `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
|
# 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:
|
A basic QR code will look something like:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
QrImage(
|
QrImageView(
|
||||||
data: 'This is a simple QR code',
|
data: 'This is a simple QR code',
|
||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
size: 320,
|
size: 320,
|
||||||
@ -101,7 +102,7 @@ QrImage(
|
|||||||
A QR code with an image (from your application's assets) will look like:
|
A QR code with an image (from your application's assets) will look like:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
QrImage(
|
QrImageView(
|
||||||
data: 'This QR code has an embedded image as well',
|
data: 'This QR code has an embedded image as well',
|
||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
size: 320,
|
size: 320,
|
||||||
@ -116,7 +117,7 @@ QrImage(
|
|||||||
To show an error state in the event that the QR code can't be validated:
|
To show an error state in the event that the QR code can't be validated:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
QrImage(
|
QrImageView(
|
||||||
data: 'This QR code will show the error state instead',
|
data: 'This QR code will show the error state instead',
|
||||||
version: 1,
|
version: 1,
|
||||||
size: 320,
|
size: 320,
|
||||||
@ -125,7 +126,7 @@ QrImage(
|
|||||||
return Container(
|
return Container(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
"Uh oh! Something went wrong...",
|
'Uh oh! Something went wrong...',
|
||||||
textAlign: TextAlign.center,
|
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
|
# Outro
|
||||||
## Credits
|
## Credits
|
||||||
Thanks to Kevin Moore for his awesome [QR - Dart](https://github.com/kevmoo/qr.dart) library. It's the core of this library.
|
Thanks to Kevin Moore for his awesome [QR - Dart](https://github.com/kevmoo/qr.dart) library. It's the core of this library.
|
||||||
|
@ -80,7 +80,6 @@ linter:
|
|||||||
# libraries
|
# libraries
|
||||||
# classes
|
# classes
|
||||||
- one_member_abstracts # avoid
|
- one_member_abstracts # avoid
|
||||||
- avoid_classes_with_only_static_members # avoid
|
|
||||||
- public_member_api_docs
|
- public_member_api_docs
|
||||||
# constructors
|
# constructors
|
||||||
- prefer_constructors_over_static_methods
|
- prefer_constructors_over_static_methods
|
||||||
|
@ -26,7 +26,16 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
@ -39,7 +48,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "app.yakka.example"
|
applicationId "app.yakka.example"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 33
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@ -59,7 +68,7 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="app.yakka.example">
|
package="app.yakka.example">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
@ -8,21 +8,16 @@
|
|||||||
FlutterApplication and put your custom class here. -->
|
FlutterApplication and put your custom class here. -->
|
||||||
<application
|
<application
|
||||||
android:label="example"
|
android:label="example"
|
||||||
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<meta-data
|
|
||||||
android:name="flutterEmbedding"
|
|
||||||
android:value="2" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data
|
|
||||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
|
||||||
android:resource="@drawable/launch_background" />
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme"
|
||||||
@ -32,5 +27,10 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -8,5 +8,4 @@
|
|||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="app.yakka.example">
|
package="app.yakka.example">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.21'
|
ext {
|
||||||
|
buildGradleVersion = '7.4.2'
|
||||||
|
kotlinVersion = '1.7.21'
|
||||||
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath "com.android.tools.build:gradle:$buildGradleVersion"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#Sun Mar 14 10:07:12 PDT 2021
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
include ':app'
|
include ':app'
|
||||||
|
|
||||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
def plugins = new Properties()
|
assert localPropertiesFile.exists()
|
||||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
if (pluginsFile.exists()) {
|
|
||||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins.each { name, path ->
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
include ":$name"
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||||
project(":$name").projectDir = pluginDirectory
|
|
||||||
}
|
|
||||||
|
@ -21,6 +21,6 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>8.0</string>
|
<string>11.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -3,17 +3,13 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 46;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
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 */; };
|
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 */; };
|
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
@ -27,8 +23,6 @@
|
|||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
|
|
||||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
|
|
||||||
);
|
);
|
||||||
name = "Embed Frameworks";
|
name = "Embed Frameworks";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -39,13 +33,11 @@
|
|||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
|
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
|
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -58,8 +50,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
|
|
||||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -69,9 +59,7 @@
|
|||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3B80C3931E831B6300D905FE /* App.framework */,
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
9740EEBA1CF902C7004384FC /* Flutter.framework */,
|
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
@ -148,7 +136,7 @@
|
|||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1020;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "The Chromium Authors";
|
ORGANIZATIONNAME = "The Chromium Authors";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
@ -193,6 +181,7 @@
|
|||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
@ -203,10 +192,11 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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 */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
@ -255,7 +245,6 @@
|
|||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -295,7 +284,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@ -331,7 +320,6 @@
|
|||||||
};
|
};
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -377,7 +365,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -387,7 +375,6 @@
|
|||||||
};
|
};
|
||||||
97C147041CF9000F007C117D /* Release */ = {
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -427,7 +414,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1020"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -41,5 +41,9 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -16,12 +16,14 @@ class ExampleApp extends StatelessWidget {
|
|||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
statusBarColor: Colors.white,
|
const SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.dark,
|
statusBarColor: Colors.white,
|
||||||
systemNavigationBarColor: Colors.white,
|
statusBarIconBrightness: Brightness.dark,
|
||||||
systemNavigationBarIconBrightness: Brightness.dark,
|
systemNavigationBarColor: Colors.white,
|
||||||
));
|
systemNavigationBarIconBrightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
);
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'QR.Flutter',
|
title: 'QR.Flutter',
|
||||||
theme: ThemeData.light(),
|
theme: ThemeData.light(),
|
||||||
|
@ -10,13 +10,13 @@ import 'package:qr_flutter/qr_flutter.dart';
|
|||||||
/// This is the screen that you'll see when the app starts
|
/// This is the screen that you'll see when the app starts
|
||||||
class MainScreen extends StatefulWidget {
|
class MainScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_MainScreenState createState() => _MainScreenState();
|
State<MainScreen> createState() => _MainScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MainScreenState extends State<MainScreen> {
|
class _MainScreenState extends State<MainScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final message =
|
const String message =
|
||||||
// ignore: lines_longer_than_80_chars
|
// ignore: lines_longer_than_80_chars
|
||||||
'Hey this is a QR code. Change this value in the main_screen.dart file.';
|
'Hey this is a QR code. Change this value in the main_screen.dart file.';
|
||||||
|
|
||||||
|
@ -2,13 +2,12 @@ name: example
|
|||||||
description: >
|
description: >
|
||||||
The QR.Flutter example app.
|
The QR.Flutter example app.
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
author: Yakka, LLC <hello@yakka.agency>
|
|
||||||
homepage: https://github.com/lukef/qr.flutter
|
homepage: https://github.com/lukef/qr.flutter
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: '>=2.19.6 <3.0.0'
|
||||||
flutter: ">=1.7.0"
|
flutter: ">=3.7.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
@ -8,19 +8,23 @@ import 'package:flutter/widgets.dart';
|
|||||||
|
|
||||||
import 'types.dart';
|
import 'types.dart';
|
||||||
|
|
||||||
///
|
/// Caches painter objects so we do have to recreate them and waste expensive
|
||||||
|
/// cycles.
|
||||||
class PaintCache {
|
class PaintCache {
|
||||||
final List<Paint> _pixelPaints = <Paint>[];
|
final List<Paint> _pixelPaints = <Paint>[];
|
||||||
final Map<String, Paint> _keyedPaints = <String, Paint>{};
|
final Map<String, Paint> _keyedPaints = <String, Paint>{};
|
||||||
|
|
||||||
String _cacheKey(QrCodeElement element, {FinderPatternPosition? position}) {
|
String _cacheKey(QrCodeElement element, {FinderPatternPosition? position}) {
|
||||||
final posKey = position != null ? position.toString() : 'any';
|
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.
|
/// Save a [Paint] for the provided element and position into the cache.
|
||||||
void cache(Paint paint, QrCodeElement element,
|
void cache(
|
||||||
{FinderPatternPosition? position}) {
|
Paint paint,
|
||||||
|
QrCodeElement element, {
|
||||||
|
FinderPatternPosition? position,
|
||||||
|
}) {
|
||||||
if (element == QrCodeElement.codePixel) {
|
if (element == QrCodeElement.codePixel) {
|
||||||
_pixelPaints.add(paint);
|
_pixelPaints.add(paint);
|
||||||
} else {
|
} else {
|
||||||
@ -31,23 +35,21 @@ class PaintCache {
|
|||||||
/// Retrieve the first [Paint] object from the paint cache for the provided
|
/// Retrieve the first [Paint] object from the paint cache for the provided
|
||||||
/// element and position.
|
/// element and position.
|
||||||
Paint? firstPaint(QrCodeElement element, {FinderPatternPosition? position}) {
|
Paint? firstPaint(QrCodeElement element, {FinderPatternPosition? position}) {
|
||||||
if (element == QrCodeElement.codePixel) {
|
return element == QrCodeElement.codePixel
|
||||||
return _pixelPaints.first;
|
? _pixelPaints.first
|
||||||
} else {
|
: _keyedPaints[_cacheKey(element, position: position)];
|
||||||
return _keyedPaints[_cacheKey(element, position: position)];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve all [Paint] objects from the paint cache for the provided
|
/// Retrieve all [Paint] objects from the paint cache for the provided
|
||||||
/// element and position. Note: Finder pattern elements can only have a max
|
/// 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]
|
/// one [Paint] object per position. As such they will always return a [List]
|
||||||
/// with a fixed size of `1`.
|
/// with a fixed size of `1`.
|
||||||
List<Paint?> paints(QrCodeElement element,
|
List<Paint?> paints(
|
||||||
{FinderPatternPosition? position}) {
|
QrCodeElement element, {
|
||||||
if (element == QrCodeElement.codePixel) {
|
FinderPatternPosition? position,
|
||||||
return _pixelPaints;
|
}) {
|
||||||
} else {
|
return element == QrCodeElement.codePixel
|
||||||
return <Paint?>[_keyedPaints[_cacheKey(element, position: position)]];
|
? _pixelPaints
|
||||||
}
|
: <Paint?>[_keyedPaints[_cacheKey(element, position: position)]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,11 @@ class QrImageView extends StatefulWidget {
|
|||||||
/// using the default options).
|
/// using the default options).
|
||||||
QrImageView({
|
QrImageView({
|
||||||
required String data,
|
required String data,
|
||||||
Key? key,
|
super.key,
|
||||||
this.size,
|
this.size,
|
||||||
this.padding = const EdgeInsets.all(10.0),
|
this.padding = const EdgeInsets.all(10.0),
|
||||||
this.backgroundColor = Colors.transparent,
|
this.backgroundColor = Colors.transparent,
|
||||||
|
@Deprecated('use colors in eyeStyle and dataModuleStyle instead')
|
||||||
this.foregroundColor = Colors.black,
|
this.foregroundColor = Colors.black,
|
||||||
this.version = QrVersions.auto,
|
this.version = QrVersions.auto,
|
||||||
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
||||||
@ -42,19 +43,22 @@ class QrImageView extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
this.embeddedImageEmitsError = false,
|
this.embeddedImageEmitsError = false,
|
||||||
this.gradient,
|
this.gradient,
|
||||||
}) : assert(QrVersions.isSupportedVersion(version)),
|
}) : assert(
|
||||||
|
QrVersions.isSupportedVersion(version),
|
||||||
|
'QR code version $version is not supported',
|
||||||
|
),
|
||||||
_data = data,
|
_data = data,
|
||||||
_qrCode = null,
|
_qrCode = null;
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
/// Create a new QR code using the [QrCode] data and the passed options (or
|
/// Create a new QR code using the [QrCode] data and the passed options (or
|
||||||
/// using the default options).
|
/// using the default options).
|
||||||
QrImageView.withQr({
|
QrImageView.withQr({
|
||||||
required QrCode qr,
|
required QrCode qr,
|
||||||
Key? key,
|
super.key,
|
||||||
this.size,
|
this.size,
|
||||||
this.padding = const EdgeInsets.all(10.0),
|
this.padding = const EdgeInsets.all(10.0),
|
||||||
this.backgroundColor = Colors.transparent,
|
this.backgroundColor = Colors.transparent,
|
||||||
|
@Deprecated('use colors in eyeStyle and dataModuleStyle instead')
|
||||||
this.foregroundColor = Colors.black,
|
this.foregroundColor = Colors.black,
|
||||||
this.version = QrVersions.auto,
|
this.version = QrVersions.auto,
|
||||||
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
||||||
@ -74,13 +78,16 @@ class QrImageView extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
this.embeddedImageEmitsError = false,
|
this.embeddedImageEmitsError = false,
|
||||||
this.gradient,
|
this.gradient,
|
||||||
}) : assert(QrVersions.isSupportedVersion(version)),
|
}) : assert(
|
||||||
|
QrVersions.isSupportedVersion(version),
|
||||||
|
'QR code version $version is not supported',
|
||||||
|
),
|
||||||
_data = null,
|
_data = null,
|
||||||
_qrCode = qr,
|
_qrCode = qr;
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
// The data passed to the widget
|
// The data passed to the widget
|
||||||
final String? _data;
|
final String? _data;
|
||||||
|
|
||||||
// The QR code data passed to the widget
|
// The QR code data passed to the widget
|
||||||
final QrCode? _qrCode;
|
final QrCode? _qrCode;
|
||||||
|
|
||||||
@ -149,7 +156,7 @@ class QrImageView extends StatefulWidget {
|
|||||||
final QrDataModuleStyle dataModuleStyle;
|
final QrDataModuleStyle dataModuleStyle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_QrImageViewState createState() => _QrImageViewState();
|
State<QrImageView> createState() => _QrImageViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _QrImageViewState extends State<QrImageView> {
|
class _QrImageViewState extends State<QrImageView> {
|
||||||
@ -167,55 +174,53 @@ class _QrImageViewState extends State<QrImageView> {
|
|||||||
version: widget.version,
|
version: widget.version,
|
||||||
errorCorrectionLevel: widget.errorCorrectionLevel,
|
errorCorrectionLevel: widget.errorCorrectionLevel,
|
||||||
);
|
);
|
||||||
if (_validationResult.isValid) {
|
_qr = _validationResult.isValid ? _validationResult.qrCode : null;
|
||||||
_qr = _validationResult.qrCode;
|
|
||||||
} else {
|
|
||||||
_qr = null;
|
|
||||||
}
|
|
||||||
} else if (widget._qrCode != null) {
|
} else if (widget._qrCode != null) {
|
||||||
_qr = widget._qrCode;
|
_qr = widget._qrCode;
|
||||||
_validationResult =
|
_validationResult =
|
||||||
QrValidationResult(status: QrValidationStatus.valid, qrCode: _qr);
|
QrValidationResult(status: QrValidationStatus.valid, qrCode: _qr);
|
||||||
}
|
}
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(
|
||||||
// validation failed, show an error state widget if builder is present.
|
builder: (context, constraints) {
|
||||||
if (!_validationResult.isValid) {
|
// validation failed, show an error state widget if builder is present.
|
||||||
return _errorWidget(context, constraints, _validationResult.error);
|
if (!_validationResult.isValid) {
|
||||||
}
|
return _errorWidget(context, constraints, _validationResult.error);
|
||||||
// no error, build the regular widget
|
}
|
||||||
final widgetSize = widget.size ?? constraints.biggest.shortestSide;
|
// no error, build the regular widget
|
||||||
if (widget.embeddedImage != null) {
|
final widgetSize =
|
||||||
// if requesting to embed an image then we need to load via a
|
widget.size ?? constraints.biggest.shortestSide;
|
||||||
// FutureBuilder because the image provider will be async.
|
if (widget.embeddedImage != null) {
|
||||||
return FutureBuilder<ui.Image>(
|
// if requesting to embed an image then we need to load via a
|
||||||
future: _loadQrImage(context, widget.embeddedImageStyle),
|
// FutureBuilder because the image provider will be async.
|
||||||
builder: (ctx, snapshot) {
|
return FutureBuilder<ui.Image>(
|
||||||
if (snapshot.error != null) {
|
future: _loadQrImage(context, widget.embeddedImageStyle),
|
||||||
print("snapshot error: ${snapshot.error}");
|
builder: (ctx, snapshot) {
|
||||||
if (widget.embeddedImageEmitsError) {
|
if (snapshot.error != null) {
|
||||||
return _errorWidget(context, constraints, snapshot.error);
|
debugPrint('snapshot error: ${snapshot.error}');
|
||||||
} else {
|
return widget.embeddedImageEmitsError
|
||||||
return _qrWidget(context, null, widgetSize);
|
? _errorWidget(context, constraints, snapshot.error)
|
||||||
|
: _qrWidget(null, widgetSize);
|
||||||
}
|
}
|
||||||
}
|
if (snapshot.hasData) {
|
||||||
if (snapshot.hasData) {
|
debugPrint('loaded image');
|
||||||
print('loaded image');
|
final loadedImage = snapshot.data;
|
||||||
final loadedImage = snapshot.data;
|
return _qrWidget(loadedImage, widgetSize);
|
||||||
return _qrWidget(context, loadedImage, widgetSize);
|
} else {
|
||||||
} else {
|
return Container();
|
||||||
return Container();
|
}
|
||||||
}
|
},
|
||||||
},
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
return _qrWidget(null, widgetSize);
|
||||||
return _qrWidget(context, null, widgetSize);
|
}
|
||||||
}
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _qrWidget(BuildContext context, ui.Image? image, double edgeLength) {
|
Widget _qrWidget(ui.Image? image, double edgeLength) {
|
||||||
final painter = QrPainter.withQr(
|
final painter = QrPainter.withQr(
|
||||||
qr: _qr!,
|
qr: _qr!,
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
color: widget.foregroundColor,
|
color: widget.foregroundColor,
|
||||||
gapless: widget.gapless,
|
gapless: widget.gapless,
|
||||||
embeddedImageStyle: widget.embeddedImageStyle,
|
embeddedImageStyle: widget.embeddedImageStyle,
|
||||||
@ -234,40 +239,51 @@ class _QrImageViewState extends State<QrImageView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _errorWidget(
|
Widget _errorWidget(
|
||||||
BuildContext context, BoxConstraints constraints, Object? error) {
|
BuildContext context,
|
||||||
|
BoxConstraints constraints,
|
||||||
|
Object? error,
|
||||||
|
) {
|
||||||
final errorWidget = widget.errorStateBuilder == null
|
final errorWidget = widget.errorStateBuilder == null
|
||||||
? Container()
|
? Container()
|
||||||
: widget.errorStateBuilder!(context, error);
|
: widget.errorStateBuilder!(context, error);
|
||||||
final errorSideLength = (widget.constrainErrorBounds
|
final errorSideLength = widget.constrainErrorBounds
|
||||||
? widget.size ?? constraints.biggest.shortestSide
|
? widget.size ?? constraints.biggest.shortestSide
|
||||||
: constraints.biggest.longestSide);
|
: constraints.biggest.longestSide;
|
||||||
return _QrContentView(
|
return _QrContentView(
|
||||||
edgeLength: errorSideLength,
|
edgeLength: errorSideLength,
|
||||||
backgroundColor: widget.backgroundColor,
|
backgroundColor: widget.backgroundColor,
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
child: errorWidget,
|
|
||||||
semanticsLabel: widget.semanticsLabel,
|
semanticsLabel: widget.semanticsLabel,
|
||||||
|
child: errorWidget,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late ImageStreamListener streamListener;
|
late ImageStreamListener streamListener;
|
||||||
|
|
||||||
Future<ui.Image> _loadQrImage(
|
Future<ui.Image> _loadQrImage(
|
||||||
BuildContext buildContext, QrEmbeddedImageStyle? style) async {
|
BuildContext buildContext,
|
||||||
|
QrEmbeddedImageStyle? style,
|
||||||
|
) {
|
||||||
if (style != null) {}
|
if (style != null) {}
|
||||||
|
|
||||||
final mq = MediaQuery.of(buildContext);
|
final mq = MediaQuery.of(buildContext);
|
||||||
final completer = Completer<ui.Image>();
|
final completer = Completer<ui.Image>();
|
||||||
final stream = widget.embeddedImage!.resolve(ImageConfiguration(
|
final stream = widget.embeddedImage!.resolve(
|
||||||
devicePixelRatio: mq.devicePixelRatio,
|
ImageConfiguration(
|
||||||
));
|
devicePixelRatio: mq.devicePixelRatio,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
streamListener = ImageStreamListener((info, err) {
|
streamListener = ImageStreamListener(
|
||||||
stream.removeListener(streamListener);
|
(info, err) {
|
||||||
completer.complete(info.image);
|
stream.removeListener(streamListener);
|
||||||
}, onError: (dynamic err, _) {
|
completer.complete(info.image);
|
||||||
stream.removeListener(streamListener);
|
},
|
||||||
completer.completeError(err);
|
onError: (err, _) {
|
||||||
});
|
stream.removeListener(streamListener);
|
||||||
|
completer.completeError(err);
|
||||||
|
},
|
||||||
|
);
|
||||||
stream.addListener(streamListener);
|
stream.addListener(streamListener);
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
@ -278,7 +294,7 @@ class _QrImageViewState extends State<QrImageView> {
|
|||||||
typedef QrErrorBuilder = Widget Function(BuildContext context, Object? error);
|
typedef QrErrorBuilder = Widget Function(BuildContext context, Object? error);
|
||||||
|
|
||||||
class _QrContentView extends StatelessWidget {
|
class _QrContentView extends StatelessWidget {
|
||||||
_QrContentView({
|
const _QrContentView({
|
||||||
required this.edgeLength,
|
required this.edgeLength,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
@ -19,7 +19,7 @@ import 'validator.dart';
|
|||||||
|
|
||||||
// ignore_for_file: deprecated_member_use_from_same_package
|
// ignore_for_file: deprecated_member_use_from_same_package
|
||||||
|
|
||||||
const _finderPatternLimit = 7;
|
const int _finderPatternLimit = 7;
|
||||||
|
|
||||||
// default colors for the qr code pixels
|
// default colors for the qr code pixels
|
||||||
const Color _qrDefaultColor = Color(0xff000000);
|
const Color _qrDefaultColor = Color(0xff000000);
|
||||||
@ -32,7 +32,11 @@ class QrPainter extends CustomPainter {
|
|||||||
required String data,
|
required String data,
|
||||||
required this.version,
|
required this.version,
|
||||||
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
|
||||||
|
@Deprecated('use colors in eyeStyle and dataModuleStyle instead')
|
||||||
this.color = _qrDefaultColor,
|
this.color = _qrDefaultColor,
|
||||||
|
@Deprecated(
|
||||||
|
'You should use the background color value of your container widget',
|
||||||
|
)
|
||||||
this.emptyColor = _qrDefaultEmptyColor,
|
this.emptyColor = _qrDefaultEmptyColor,
|
||||||
this.gapless = false,
|
this.gapless = false,
|
||||||
this.embeddedImage,
|
this.embeddedImage,
|
||||||
@ -40,7 +44,10 @@ class QrPainter extends CustomPainter {
|
|||||||
this.eyeStyle = const QrEyeStyle(),
|
this.eyeStyle = const QrEyeStyle(),
|
||||||
this.dataModuleStyle = const QrDataModuleStyle(),
|
this.dataModuleStyle = const QrDataModuleStyle(),
|
||||||
this.gradient,
|
this.gradient,
|
||||||
}) : assert(QrVersions.isSupportedVersion(version)) {
|
}) : assert(
|
||||||
|
QrVersions.isSupportedVersion(version),
|
||||||
|
'QR code version $version is not supported',
|
||||||
|
) {
|
||||||
_init(data);
|
_init(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +56,11 @@ class QrPainter extends CustomPainter {
|
|||||||
/// flow or for when you need to pre-validate the QR data.
|
/// flow or for when you need to pre-validate the QR data.
|
||||||
QrPainter.withQr({
|
QrPainter.withQr({
|
||||||
required QrCode qr,
|
required QrCode qr,
|
||||||
|
@Deprecated('use colors in eyeStyle and dataModuleStyle instead')
|
||||||
this.color = _qrDefaultColor,
|
this.color = _qrDefaultColor,
|
||||||
|
@Deprecated(
|
||||||
|
'You should use the background color value of your container widget',
|
||||||
|
)
|
||||||
this.emptyColor = _qrDefaultEmptyColor,
|
this.emptyColor = _qrDefaultEmptyColor,
|
||||||
this.gapless = false,
|
this.gapless = false,
|
||||||
this.embeddedImage,
|
this.embeddedImage,
|
||||||
@ -112,7 +123,7 @@ class QrPainter extends CustomPainter {
|
|||||||
final double _gapSize = 0.25;
|
final double _gapSize = 0.25;
|
||||||
|
|
||||||
/// Cache for all of the [Paint] objects.
|
/// Cache for all of the [Paint] objects.
|
||||||
final _paintCache = PaintCache();
|
final PaintCache _paintCache = PaintCache();
|
||||||
|
|
||||||
void _init(String data) {
|
void _init(String data) {
|
||||||
if (!QrVersions.isSupportedVersion(version)) {
|
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
|
// Cache the pixel paint object. For now there is only one but we might
|
||||||
// expand it to multiple later (e.g.: different colours).
|
// expand it to multiple later (e.g.: different colours).
|
||||||
_paintCache.cache(
|
_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
|
// Cache the empty pixel paint object. Empty color is deprecated and will go
|
||||||
// away.
|
// away.
|
||||||
_paintCache.cache(
|
_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
|
// Cache the finder pattern painters. We'll keep one for each one in case
|
||||||
// we want to provide customization options later.
|
// we want to provide customization options later.
|
||||||
for (final position in FinderPatternPosition.values) {
|
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(
|
_paintCache.cache(
|
||||||
Paint()..style = PaintingStyle.fill, QrCodeElement.finderPatternDot,
|
Paint()..style = PaintingStyle.stroke,
|
||||||
position: position);
|
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) {
|
void paint(Canvas canvas, Size size) {
|
||||||
// if the widget has a zero size side then we cannot continue painting.
|
// if the widget has a zero size side then we cannot continue painting.
|
||||||
if (size.shortestSide == 0) {
|
if (size.shortestSide == 0) {
|
||||||
print("[QR] WARN: width or height is zero. You should set a 'size' value "
|
debugPrint(
|
||||||
"or nest this painter in a Widget that defines a non-zero size");
|
"[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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final paintMetrics = _PaintMetrics(
|
final paintMetrics = _PaintMetrics(
|
||||||
containerSize: size.shortestSide,
|
containerSize: size.shortestSide,
|
||||||
moduleCount: _qr!.moduleCount,
|
moduleCount: _qr!.moduleCount,
|
||||||
gapSize: (gapless ? 0 : _gapSize),
|
gapSize: gapless ? 0 : _gapSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
// draw the finder pattern elements
|
// draw the finder pattern elements
|
||||||
_drawFinderPatternItem(FinderPatternPosition.topLeft, canvas, paintMetrics);
|
|
||||||
_drawFinderPatternItem(
|
_drawFinderPatternItem(
|
||||||
FinderPatternPosition.bottomLeft, canvas, paintMetrics);
|
FinderPatternPosition.topLeft,
|
||||||
|
canvas,
|
||||||
|
paintMetrics,
|
||||||
|
);
|
||||||
_drawFinderPatternItem(
|
_drawFinderPatternItem(
|
||||||
FinderPatternPosition.topRight, canvas, paintMetrics);
|
FinderPatternPosition.bottomLeft,
|
||||||
|
canvas,
|
||||||
|
paintMetrics,
|
||||||
|
);
|
||||||
|
_drawFinderPatternItem(
|
||||||
|
FinderPatternPosition.topRight,
|
||||||
|
canvas,
|
||||||
|
paintMetrics,
|
||||||
|
);
|
||||||
|
|
||||||
// DEBUG: draw the inner content boundary
|
// DEBUG: draw the inner content boundary
|
||||||
// final paint = Paint()..style = ui.PaintingStyle.stroke;
|
// 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 x = 0; x < _qr!.moduleCount; x++) {
|
||||||
for (var y = 0; y < _qr!.moduleCount; y++) {
|
for (var y = 0; y < _qr!.moduleCount; y++) {
|
||||||
// draw the finder patterns independently
|
// draw the finder patterns independently
|
||||||
if (_isFinderPatternPosition(x, y)) continue;
|
if (_isFinderPatternPosition(x, y)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final isDark = _qrImage.isDark(y, x);
|
final isDark = _qrImage.isDark(y, x);
|
||||||
final paint = isDark ? pixelPaint : emptyPixelPaint;
|
final paint = isDark ? pixelPaint : emptyPixelPaint;
|
||||||
if (!isDark && !isRoundedOutsideCorners) continue;
|
if (!isDark && !isRoundedOutsideCorners) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// paint a pixel
|
// paint a pixel
|
||||||
final squareRect = _createDataModuleRect(paintMetrics, x, y, gap);
|
final squareRect = _createDataModuleRect(paintMetrics, x, y, gap);
|
||||||
// check safeArea
|
// check safeArea
|
||||||
@ -422,21 +458,25 @@ class QrPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _hasAdjacentVerticalPixel(int x, int y, int moduleCount) {
|
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);
|
return _qrImage.isDark(y + 1, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _hasAdjacentHorizontalPixel(int x, int y, int moduleCount) {
|
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);
|
return _qrImage.isDark(y, x + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isFinderPatternPosition(int x, int y) {
|
bool _isFinderPatternPosition(int x, int y) {
|
||||||
final isTopLeft = (y < _finderPatternLimit && x < _finderPatternLimit);
|
final isTopLeft = y < _finderPatternLimit && x < _finderPatternLimit;
|
||||||
final isBottomLeft = (y < _finderPatternLimit &&
|
final isBottomLeft = y < _finderPatternLimit &&
|
||||||
(x >= _qr!.moduleCount - _finderPatternLimit));
|
(x >= _qr!.moduleCount - _finderPatternLimit);
|
||||||
final isTopRight = (y >= _qr!.moduleCount - _finderPatternLimit &&
|
final isTopRight = y >= _qr!.moduleCount - _finderPatternLimit &&
|
||||||
(x < _finderPatternLimit));
|
(x < _finderPatternLimit);
|
||||||
return isTopLeft || isBottomLeft || isTopRight;
|
return isTopLeft || isBottomLeft || isTopRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,9 +486,10 @@ class QrPainter extends CustomPainter {
|
|||||||
_PaintMetrics metrics,
|
_PaintMetrics metrics,
|
||||||
) {
|
) {
|
||||||
final totalGap = (_finderPatternLimit - 1) * metrics.gapSize;
|
final totalGap = (_finderPatternLimit - 1) * metrics.gapSize;
|
||||||
final radius = ((_finderPatternLimit * metrics.pixelSize) + totalGap) -
|
final radius =
|
||||||
metrics.pixelSize;
|
((_finderPatternLimit * metrics.pixelSize) + totalGap) -
|
||||||
final strokeAdjust = (metrics.pixelSize / 2.0);
|
metrics.pixelSize;
|
||||||
|
final strokeAdjust = metrics.pixelSize / 2.0;
|
||||||
final edgePos =
|
final edgePos =
|
||||||
(metrics.inset + metrics.innerContentSize) - (radius + strokeAdjust);
|
(metrics.inset + metrics.innerContentSize) - (radius + strokeAdjust);
|
||||||
|
|
||||||
@ -463,31 +504,44 @@ class QrPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configure the paints
|
// configure the paints
|
||||||
final outerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternOuter,
|
final outerPaint = _paintCache.firstPaint(
|
||||||
position: position)!;
|
QrCodeElement.finderPatternOuter,
|
||||||
|
position: position,
|
||||||
|
)!;
|
||||||
final color = _priorityColor(eyeStyle.color);
|
final color = _priorityColor(eyeStyle.color);
|
||||||
outerPaint.strokeWidth = metrics.pixelSize;
|
outerPaint.strokeWidth = metrics.pixelSize;
|
||||||
outerPaint.color = color;
|
outerPaint.color = color;
|
||||||
|
|
||||||
final innerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternInner,
|
final innerPaint = _paintCache
|
||||||
position: position)!;
|
.firstPaint(QrCodeElement.finderPatternInner, position: position)!;
|
||||||
innerPaint.strokeWidth = metrics.pixelSize;
|
innerPaint.strokeWidth = metrics.pixelSize;
|
||||||
innerPaint.color = emptyColor;
|
innerPaint.color = emptyColor;
|
||||||
|
|
||||||
final dotPaint = _paintCache.firstPaint(QrCodeElement.finderPatternDot,
|
final dotPaint = _paintCache.firstPaint(
|
||||||
position: position);
|
QrCodeElement.finderPatternDot,
|
||||||
|
position: position,
|
||||||
|
);
|
||||||
dotPaint!.color = color;
|
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 innerRadius = radius - (2 * metrics.pixelSize);
|
||||||
final innerRect = Rect.fromLTWH(offset.dx + metrics.pixelSize,
|
final innerRect = Rect.fromLTWH(
|
||||||
offset.dy + metrics.pixelSize, innerRadius, innerRadius);
|
offset.dx + metrics.pixelSize,
|
||||||
|
offset.dy + metrics.pixelSize,
|
||||||
|
innerRadius,
|
||||||
|
innerRadius,
|
||||||
|
);
|
||||||
|
|
||||||
final gap = metrics.pixelSize * 2;
|
final gap = metrics.pixelSize * 2;
|
||||||
final dotSize = radius - gap - (2 * strokeAdjust);
|
final dotSize = radius - gap - (2 * strokeAdjust);
|
||||||
final dotRect = Rect.fromLTWH(offset.dx + metrics.pixelSize + strokeAdjust,
|
final dotRect = Rect.fromLTWH(
|
||||||
offset.dy + metrics.pixelSize + strokeAdjust, dotSize, dotSize);
|
offset.dx + metrics.pixelSize + strokeAdjust,
|
||||||
|
offset.dy + metrics.pixelSize + strokeAdjust,
|
||||||
|
dotSize,
|
||||||
|
dotSize,
|
||||||
|
);
|
||||||
|
|
||||||
switch(eyeStyle.eyeShape) {
|
switch(eyeStyle.eyeShape) {
|
||||||
case QrEyeShape.square:
|
case QrEyeShape.square:
|
||||||
@ -524,7 +578,10 @@ class QrPainter extends CustomPainter {
|
|||||||
bool _hasOneNonZeroSide(Size size) => size.longestSide > 0;
|
bool _hasOneNonZeroSide(Size size) => size.longestSide > 0;
|
||||||
|
|
||||||
Size _scaledAspectSize(
|
Size _scaledAspectSize(
|
||||||
Size widgetSize, Size originalSize, Size? requestedSize) {
|
Size widgetSize,
|
||||||
|
Size originalSize,
|
||||||
|
Size? requestedSize,
|
||||||
|
) {
|
||||||
if (requestedSize != null && !requestedSize.isEmpty) {
|
if (requestedSize != null && !requestedSize.isEmpty) {
|
||||||
return requestedSize;
|
return requestedSize;
|
||||||
} else if (requestedSize != null && _hasOneNonZeroSide(requestedSize)) {
|
} else if (requestedSize != null && _hasOneNonZeroSide(requestedSize)) {
|
||||||
@ -539,7 +596,11 @@ class QrPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _drawImageOverlay(
|
void _drawImageOverlay(
|
||||||
Canvas canvas, Offset position, Size size, QrEmbeddedImageStyle? style) {
|
Canvas canvas,
|
||||||
|
Offset position,
|
||||||
|
Size size,
|
||||||
|
QrEmbeddedImageStyle? style,
|
||||||
|
) {
|
||||||
final paint = Paint()
|
final paint = Paint()
|
||||||
..isAntiAlias = true
|
..isAntiAlias = true
|
||||||
..filterQuality = FilterQuality.high;
|
..filterQuality = FilterQuality.high;
|
||||||
@ -587,24 +648,26 @@ class QrPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw QR code [ui.Image] object.
|
/// Returns the raw QR code [ui.Image] object.
|
||||||
Future<ui.Image> toImage(double size,
|
Future<ui.Image> toImage(double size) {
|
||||||
{ui.ImageByteFormat format = ui.ImageByteFormat.png}) async {
|
return toPicture(size).toImage(size.toInt(), size.toInt());
|
||||||
return await toPicture(size).toImage(size.toInt(), size.toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw QR code image byte data.
|
/// Returns the raw QR code image byte data.
|
||||||
Future<ByteData?> toImageData(double size,
|
Future<ByteData?> toImageData(
|
||||||
{ui.ImageByteFormat format = ui.ImageByteFormat.png}) async {
|
double size, {
|
||||||
final image = await toImage(size, format: format);
|
ui.ImageByteFormat format = ui.ImageByteFormat.png,
|
||||||
|
}) async {
|
||||||
|
final image = await toImage(size);
|
||||||
return image.toByteData(format: format);
|
return image.toByteData(format: format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PaintMetrics {
|
class _PaintMetrics {
|
||||||
_PaintMetrics(
|
_PaintMetrics({
|
||||||
{required this.containerSize,
|
required this.containerSize,
|
||||||
required this.gapSize,
|
required this.gapSize,
|
||||||
required this.moduleCount}) {
|
required this.moduleCount,
|
||||||
|
}) {
|
||||||
_calculateMetrics();
|
_calculateMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,7 +686,7 @@ class _PaintMetrics {
|
|||||||
|
|
||||||
void _calculateMetrics() {
|
void _calculateMetrics() {
|
||||||
final gapTotal = (moduleCount - 1) * gapSize;
|
final gapTotal = (moduleCount - 1) * gapSize;
|
||||||
var pixelSize = (containerSize - gapTotal) / moduleCount;
|
final pixelSize = (containerSize - gapTotal) / moduleCount;
|
||||||
_pixelSize = (pixelSize * 2).roundToDouble() / 2;
|
_pixelSize = (pixelSize * 2).roundToDouble() / 2;
|
||||||
_innerContentSize = (_pixelSize * moduleCount) + gapTotal;
|
_innerContentSize = (_pixelSize * moduleCount) + gapTotal;
|
||||||
_inset = (containerSize - _innerContentSize) / 2;
|
_inset = (containerSize - _innerContentSize) / 2;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* See LICENSE for distribution and usage details.
|
* See LICENSE for distribution and usage details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ enum EmbeddedImageShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Styling options for finder pattern eye.
|
/// Styling options for finder pattern eye.
|
||||||
|
@immutable
|
||||||
class QrEyeStyle {
|
class QrEyeStyle {
|
||||||
/// Create a new set of styling options for QR Eye.
|
/// Create a new set of styling options for QR Eye.
|
||||||
const QrEyeStyle({
|
const QrEyeStyle({
|
||||||
@ -100,6 +102,7 @@ class QrEyeStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Styling options for data module.
|
/// Styling options for data module.
|
||||||
|
@immutable
|
||||||
class QrDataModuleStyle {
|
class QrDataModuleStyle {
|
||||||
/// Create a new set of styling options for data modules.
|
/// Create a new set of styling options for data modules.
|
||||||
const QrDataModuleStyle({
|
const QrDataModuleStyle({
|
||||||
@ -152,6 +155,7 @@ class QrDataModuleStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Styling options for any embedded image overlay
|
/// Styling options for any embedded image overlay
|
||||||
|
@immutable
|
||||||
class QrEmbeddedImageStyle {
|
class QrEmbeddedImageStyle {
|
||||||
/// Create a new set of styling options.
|
/// Create a new set of styling options.
|
||||||
const QrEmbeddedImageStyle({
|
const QrEmbeddedImageStyle({
|
||||||
|
@ -30,10 +30,14 @@ class QrValidator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return QrValidationResult(
|
return QrValidationResult(
|
||||||
status: QrValidationStatus.valid, qrCode: qrCode);
|
status: QrValidationStatus.valid,
|
||||||
} on InputTooLongException catch (itle) {
|
qrCode: qrCode,
|
||||||
|
);
|
||||||
|
} on InputTooLongException catch (title) {
|
||||||
return QrValidationResult(
|
return QrValidationResult(
|
||||||
status: QrValidationStatus.contentTooLong, error: itle);
|
status: QrValidationStatus.contentTooLong,
|
||||||
|
error: title,
|
||||||
|
);
|
||||||
} on Exception catch (ex) {
|
} on Exception catch (ex) {
|
||||||
return QrValidationResult(status: QrValidationStatus.error, error: ex);
|
return QrValidationResult(status: QrValidationStatus.error, error: ex);
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,21 @@ name: qr_flutter
|
|||||||
description: >
|
description: >
|
||||||
QR.Flutter is a Flutter library for simple and fast QR code rendering via a
|
QR.Flutter is a Flutter library for simple and fast QR code rendering via a
|
||||||
Widget or custom painter.
|
Widget or custom painter.
|
||||||
version: 4.0.1
|
version: 4.1.0
|
||||||
homepage: https://github.com/theyakka/qr.flutter
|
homepage: https://github.com/theyakka/qr.flutter
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: '>=2.19.6 <4.0.0'
|
||||||
flutter: ">=1.16.0"
|
flutter: ">=3.7.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
qr: ^3.0.0
|
qr: ^3.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
test: ^1.16.5
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: false
|
uses-material-design: false
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
@ -11,7 +11,9 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('QrImageView generates correct image', (tester) async {
|
testWidgets('QrImageView generates correct image', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
final qrImage = MaterialApp(
|
final qrImage = MaterialApp(
|
||||||
home: Center(
|
home: Center(
|
||||||
child: RepaintBoundary(
|
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(
|
testWidgets(
|
||||||
'QrImageView generates correct image with eye and data module sytle',
|
'QrImageView generates correct image with eye style',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
final qrImage = MaterialApp(
|
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(
|
|
||||||
home: Center(
|
home: Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
child: QrImageView(
|
child: QrImageView(
|
||||||
data: 'This is a a qr code with a logo',
|
data: 'This is a test image',
|
||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
gapless: true,
|
gapless: true,
|
||||||
errorCorrectionLevel: QrErrorCorrectLevel.L,
|
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(
|
testWidgets(
|
||||||
find.byType(QrImageView),
|
'QrImageView generates correct image with eye and data module sytle',
|
||||||
matchesGoldenFile('./.golden/qr_image_logo_golden.png'),
|
(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')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
<String>['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.
|
/// Pre-cache images to make sure they show up in golden tests.
|
||||||
@ -180,19 +195,24 @@ Future<void> pumpWidgetWithImages(
|
|||||||
) async {
|
) async {
|
||||||
Future<void>? precacheFuture;
|
Future<void>? precacheFuture;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Builder(builder: (buildContext) {
|
Builder(
|
||||||
precacheFuture = tester.runAsync(() async {
|
builder: (buildContext) {
|
||||||
await Future.wait([
|
precacheFuture = tester.runAsync(() async {
|
||||||
for (final assetName in assetNames)
|
await Future.wait(<Future<void>>[
|
||||||
precacheImage(FileImage(File(assetName)), buildContext),
|
for (final String assetName in assetNames)
|
||||||
]);
|
precacheImage(FileImage(File(assetName)), buildContext),
|
||||||
});
|
]);
|
||||||
return widget;
|
});
|
||||||
}),
|
return widget;
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await precacheFuture;
|
await precacheFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTestableWidget(Widget widget) {
|
Widget buildTestableWidget(Widget widget) {
|
||||||
return MediaQuery(data: MediaQueryData(), child: MaterialApp(home: widget));
|
return MediaQuery(
|
||||||
|
data: const MediaQueryData(),
|
||||||
|
child: MaterialApp(home: widget),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,9 @@ void main() {
|
|||||||
imageData = await painter.toImageData(600.0);
|
imageData = await painter.toImageData(600.0);
|
||||||
});
|
});
|
||||||
final imageBytes = imageData!.buffer.asUint8List();
|
final imageBytes = imageData!.buffer.asUint8List();
|
||||||
final widget = Center(
|
final Widget widget = Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
child: Container(
|
child: SizedBox(
|
||||||
width: 600,
|
width: 600,
|
||||||
height: 600,
|
height: 600,
|
||||||
child: Image.memory(imageBytes),
|
child: Image.memory(imageBytes),
|
||||||
|