Version 3.0 (#55)

Adds auto version selection. Adds error state builder. Adds image overlays. Fixes bugs.
This commit is contained in:
Luke
2019-08-19 15:35:36 -07:00
committed by GitHub
parent 8555e7c087
commit 344b4575c9
86 changed files with 1076 additions and 1899 deletions

2
.gitignore vendored
View File

@ -11,4 +11,4 @@ packages
.flutter-plugins .flutter-plugins
.dart_tool .dart_tool
pubspec.lock pubspec.lock
test_image.png .temp

View File

@ -4,3 +4,4 @@ Roman Rodych: @romkor
Charles Crete: @cretezy Charles Crete: @cretezy
Dmitry: @kelegorm Dmitry: @kelegorm
Pedro Massango: @pedromassango Pedro Massango: @pedromassango
Luke Pighetti: @lukepighetti

View File

@ -1,3 +1,10 @@
# 3.0.0
- Use `QrVersions.auto` and let the library pick the appropriate version for you.
- Add an image that will be overlaid in the centre of the widget. You can specify the image size but not position.
- `QrImage.onError` has been removed. It was horribly broken so we decided to not deprecate it and just remove it totally.
- `QrImage.errorStateBuilder` introduced to allow you to display an in-line `Widget` when an error (such as data too long) occurs. By default the error state `Widget` will be constrained to the `QRImage` bounds (aka a Square) but you can use the `constrainErrorBounds` property to prevent that.
- A bunch of bug fixes you can look at in the source code.
# 2.1.0 # 2.1.0
- The `gapless` option is now `true` by default. - The `gapless` option is now `true` by default.
- Allow assigning `Key` values to `QrImage` widgets. - Allow assigning `Key` values to `QrImage` widgets.

View File

@ -4,26 +4,21 @@ QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widg
# Features # Features
- Built on [QR - Dart](https://github.com/kevmoo/qr.dart) - Built on [QR - Dart](https://github.com/kevmoo/qr.dart)
- Automatic QR code version/type detection or manual entry
- Supports QR code versions 1 - 40 - Supports QR code versions 1 - 40
- Error correction / redundancy - Error correction / redundancy
- Configurable output size, padding, background and foreground colors - Configurable output size, padding, background and foreground colors
- Supports image overlays
- Export to image data to save to file or use in memory - Export to image data to save to file or use in memory
- No internet connection required - No internet connection required
# Installing # Installing
If you're using Flutter 1.2+ or the master/beta channel then you will need to use version `2.0.0` or higher as Flutter 1.2 is not compatible with earlier versions of the Flutter framework. You should add the following to your `pubspec.yaml` file:
```yaml ```yaml
dependencies: dependencies:
qr_flutter: ^2.1.0 qr_flutter: ^3.0.0
```
If you're using an older Flutter version (< 1.2.1), you **must** use version `1.1.6` if you cannot upgrade to the latest version of Flutter:
```yaml
dependencies:
qr_flutter: ^1.1.6
``` ```
**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`:
@ -50,8 +45,9 @@ 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
new QrImage( QrImage(
data: "1234567890", data: "1234567890",
version: QrVersions.auto,
size: 200.0, size: 200.0,
), ),
``` ```
@ -60,16 +56,69 @@ Depending on your data requirements you may want to tweak the QR code output. Th
| Property | Type | Description | | Property | Type | Description |
|----|----|----| |----|----|----|
| `version` | int | A value between 1 and 40. See http://www.qrcode.com/en/about/version.html for 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) | | `foregroundColor` | Color | The foreground color (default is black). |
| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true) | | `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. |
# Example # Example
See the `example` directory for a basic working example.
A basic QR code will look something like:
```dart
QrImage(
data: 'This is a simple QR code',
version: QrVersions.auto,
size: 320,
gapless: false,
)
```
A QR code with an image (from your application's assets) will look like:
```dart
QrImage(
data: 'This QR code has an embedded image as well',
version: QrVersions.auto,
size: 320,
gapless: false,
embeddedImage: AssetImage('assets/images/my_embedded_image.png'),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(80, 80),
),
)
```
To show an error state in the event that the QR code can't be validated:
```dart
QrImage(
data: 'This QR code will show the error state instead',
version: 1,
size: 320,
gapless: false,
errorStateBuilder: (cxt, err) {
return Container(
child: Center(
child: Text(
"Uh oh! Something went wrong...",
textAlign: TextAlign.center,
),
),
);
},
)
```
# FAQ # FAQ
## Has it been tested in production? Can I use it in production? ## Has it been tested in production? Can I use it in production?

View File

@ -15,88 +15,89 @@ analyzer:
- 'build/**' - 'build/**'
- 'ios/**' - 'ios/**'
- 'res/**' - 'res/**'
- 'example/**'
linter: linter:
rules: rules:
- always_declare_return_types # --- STYLE
- always_put_control_body_on_new_line # identifiers
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
- avoid_as
- avoid_empty_else
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_slow_async_io
- avoid_types_as_parameter_names
- avoid_unused_constructor_parameters
- avoid_void_async
- await_only_futures
- camel_case_types - camel_case_types
- cancel_subscriptions
- control_flow_in_finally
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- library_names - library_names
- file_names
- library_prefixes - library_prefixes
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names - non_constant_identifier_names
- overridden_fields - constant_identifier_names # prefer
- package_api_docs # ordering
- package_names - directives_ordering
- package_prefixed_library_names # formatting
- prefer_adjacent_string_concatenation - lines_longer_than_80_chars # avoid
- prefer_asserts_in_initializer_lists - curly_braces_in_flow_control_structures
- prefer_collection_literals # --- DOCUMENTATION
- prefer_conditional_assignment # comments
- prefer_const_constructors # doc comments
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_locals
- prefer_foreach
- prefer_generic_function_type_aliases
- prefer_initializing_formals
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- recursive_getters
- slash_for_doc_comments - slash_for_doc_comments
- sort_constructors_first - package_api_docs # prefer
- sort_unnamed_constructors_first - comment_references
- super_goes_last # markdown
- test_types_in_equals # writing
- throw_in_finally # --- USAGE
- type_init_formals # libraries
- unnecessary_brace_in_string_interps - implementation_imports
- unnecessary_const - avoid_relative_lib_imports
# strings
- prefer_adjacent_string_concatenation
- prefer_interpolation_to_compose_strings # prefer
- unnecessary_brace_in_string_interps # avoid
# collections
- prefer_collection_literals
- avoid_function_literals_in_foreach_calls # avoid
- prefer_iterable_whereType
# functions
- prefer_function_declarations_over_variables
- unnecessary_lambdas
# parameters
- prefer_equal_for_default_values
# variables
- avoid_init_to_null
# members
- unnecessary_getters_setters - unnecessary_getters_setters
- unnecessary_new - prefer_final_fields
- unnecessary_null_aware_assignments #- prefer_expression_function_bodies # consider
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_this - unnecessary_this
- unrelated_type_equality_checks - prefer_typing_uninitialized_variables
# constructors
- prefer_initializing_formals
- type_init_formals
- empty_constructor_bodies
- unnecessary_new
- unnecessary_const
# error handling
- avoid_catches_without_on_clauses # avoid
- use_rethrow_when_possible - use_rethrow_when_possible
- valid_regexps # asynchrony
# --- DESIGN
# names
- use_to_and_as_if_applicable
# libraries
# classes
- one_member_abstracts # avoid
- avoid_classes_with_only_static_members # avoid
- public_member_api_docs
# constructors
- prefer_constructors_over_static_methods
# members
- use_setters_to_change_properties
- avoid_setters_without_getters
- avoid_returning_null # avoid
- avoid_returning_this # avoid
# types
- type_annotate_public_apis # prefer
- omit_local_variable_types # avoid
- avoid_types_on_closure_parameters # avoid
- avoid_return_types_on_setters
- prefer_generic_function_type_aliases
- avoid_private_typedef_functions # prefer
# parameters
- avoid_positional_boolean_parameters # avoid
# equality
- hash_and_equals
- avoid_null_checks_in_equality_operators

13
example/.gitignore vendored
View File

@ -1,13 +0,0 @@
.DS_Store
.atom/
.dart_tool/
.idea
.vscode/
.packages
.pub/
build/
ios/.generated/
packages
.flutter-plugins
.dart_tool
pubspec.lock

View File

@ -1,8 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: aeb2eb6b241b95802ad165897c1b3202ad512fa2
channel: master

View File

@ -1,8 +0,0 @@
# qr
A new Flutter project.
## Getting Started
For help getting started with Flutter, view our online
[documentation](https://flutter.io/).

View File

@ -1,102 +0,0 @@
analyzer:
strong-mode:
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
exclude:
- '.idea/**'
- 'android/**'
- 'assets/**'
- 'build/**'
- 'ios/**'
- 'res/**'
- 'example/**'
linter:
rules:
- always_declare_return_types
- always_put_control_body_on_new_line
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
- avoid_as
- avoid_empty_else
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_slow_async_io
- avoid_types_as_parameter_names
- avoid_unused_constructor_parameters
- avoid_void_async
- await_only_futures
- camel_case_types
- cancel_subscriptions
- control_flow_in_finally
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- library_names
- library_prefixes
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_locals
- prefer_foreach
- prefer_generic_function_type_aliases
- prefer_initializing_formals
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- recursive_getters
- slash_for_doc_comments
- sort_constructors_first
- sort_unnamed_constructors_first
- super_goes_last
- test_types_in_equals
- throw_in_finally
- type_init_formals
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_this
- unrelated_type_equality_checks
- use_rethrow_when_possible
- valid_regexps

View File

@ -1,10 +0,0 @@
*.iml
*.class
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
GeneratedPluginRegistrant.java

View File

@ -1,51 +0,0 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 27
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.qr"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

View File

@ -1,39 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.qr">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="qr"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,14 +0,0 @@
package com.example.qr;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

View File

@ -1,29 +0,0 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -1 +0,0 @@
org.gradle.jvmargs=-Xmx1536M

Binary file not shown.

View File

@ -1,6 +0,0 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

View File

@ -1,160 +0,0 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@ -1,90 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,15 +0,0 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

View File

@ -1,42 +0,0 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/app.flx
/Flutter/app.zip
/Flutter/flutter_assets/
/Flutter/App.framework
/Flutter/Flutter.framework
/Flutter/Generated.xcconfig
/ServiceDefinitions.json
Pods/

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>

View File

@ -1 +0,0 @@
#include "Generated.xcconfig"

View File

@ -1 +0,0 @@
#include "Generated.xcconfig"

View File

@ -1,436 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
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, ); }; };
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 */; };
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
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>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
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>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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>"; };
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; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; 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>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.qr;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.qr;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,6 +0,0 @@
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
@interface AppDelegate : FlutterAppDelegate
@end

View File

@ -1,12 +0,0 @@
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

View File

@ -1,122 +0,0 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

View File

@ -1,5 +0,0 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>qr</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -1,9 +0,0 @@
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@ -1,20 +0,0 @@
import 'package:flutter/material.dart';
import 'screens/main.screen.dart';
void main() => runApp(App());
class App extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QR code demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
debugShowCheckedModeBanner: false,
);
}
}

View File

@ -1,99 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
static const double _topSectionTopPadding = 50.0;
static const double _topSectionBottomPadding = 20.0;
static const double _topSectionHeight = 50.0;
String _dataString = 'Hello from this QR code!';
String _inputErrorText;
final TextEditingController _textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: _contentWidget(),
resizeToAvoidBottomPadding: true,
);
}
@override
void didUpdateWidget(MainScreen oldWidget) {
super.didUpdateWidget(oldWidget);
setState(() {});
}
Widget _contentWidget() {
return Container(
color: const Color(0xFFFFFFFF),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: _topSectionTopPadding,
left: 30.0,
right: 20.0,
bottom: _topSectionBottomPadding,
),
child: Container(
height: _topSectionHeight,
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: TextField(
autofocus: true,
controller: _textController,
decoration: InputDecoration(
hintText: 'Enter a custom message',
errorText: _inputErrorText,
),
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: FlatButton(
child: const Text('SUBMIT'),
onPressed: () {
setState(() {
_dataString = _textController.text;
_inputErrorText = null;
});
},
),
)
],
),
),
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(25.0),
child: QrImage(
data: _dataString,
gapless: false,
foregroundColor: const Color(0xFF111111),
onError: (dynamic ex) {
print('[QR] ERROR - $ex');
setState(() {
_inputErrorText =
'Error! Maybe your input value is too long?';
});
},
),
),
),
),
],
),
);
}
}

View File

@ -1,59 +0,0 @@
name: flutter_qr_demo
description: Demo project for the Flutter QR library.
dependencies:
flutter:
sdk: flutter
qr_flutter:
path: ..
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.0
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/android/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/android/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/android/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/android/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/android/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/android/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/android/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/android/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>

View File

@ -1,9 +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.
void main() {
// TODO - no tests yet
}

View File

@ -6,5 +6,9 @@
export 'package:qr/qr.dart'; export 'package:qr/qr.dart';
export 'src/errors.dart';
export 'src/qr_image.dart'; export 'src/qr_image.dart';
export 'src/qr_painter.dart'; export 'src/qr_painter.dart';
export 'src/qr_versions.dart';
export 'src/types.dart';
export 'src/validator.dart';

48
lib/src/errors.dart Normal file
View File

@ -0,0 +1,48 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'package:flutter/widgets.dart';
import 'qr_versions.dart';
/// An exception that is thrown when an invalid QR code version / type is
/// requested.
class QrUnsupportedVersionException implements Exception {
/// Create a new QrUnsupportedVersionException.
factory QrUnsupportedVersionException(int providedVersion) {
final message =
'Invalid version. $providedVersion is not >= ${QrVersions.min} '
'and <= ${QrVersions.max}';
return QrUnsupportedVersionException._internal(providedVersion, message);
}
QrUnsupportedVersionException._internal(this.providedVersion, this.message);
/// The version you passed to the QR code operation.
final int providedVersion;
/// A message describing the exception state.
final String message;
@override
String toString() => 'QrUnsupportedVersionException: $message';
}
/// An exception that is thrown when something goes wrong with the
/// [ImageProvider] for the embedded image of a QrImage or QrPainter.
class QrEmbeddedImageException implements Exception {
/// Create a new QrEmbeddedImageException.
factory QrEmbeddedImageException(String message) {
return QrEmbeddedImageException._internal(message);
}
QrEmbeddedImageException._internal(this.message);
/// A message describing the exception state.
final String message;
@override
String toString() => 'QrEmbeddedImageException: $message';
}

57
lib/src/paint_cache.dart Normal file
View File

@ -0,0 +1,57 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'package:flutter/widgets.dart';
import 'types.dart';
///
class PaintCache {
final List<Paint> _pixelPaints = <Paint>[];
final Map<String, Paint> _finderPaints = <String, Paint>{};
String _cacheKey(QrCodeElement element, {FinderPatternPosition position}) {
final posKey = position != null ? position.toString() : 'any';
return '${element.toString()}:$posKey';
}
/// Save a [Paint] for the provided element and position into the cache.
void cache(Paint paint, QrCodeElement element,
{FinderPatternPosition position}) {
if (element == QrCodeElement.codePixel) {
_pixelPaints.add(paint);
} else if (element == QrCodeElement.finderPatternOuter ||
element == QrCodeElement.finderPatternInner) {
_finderPaints[_cacheKey(element, position: position)] = paint;
}
}
/// 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 if (element == QrCodeElement.finderPatternOuter ||
element == QrCodeElement.finderPatternInner) {
return _finderPaints[_cacheKey(element, position: position)];
}
return null;
}
/// 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<Paint> paints(QrCodeElement element, {FinderPatternPosition position}) {
if (element == QrCodeElement.codePixel) {
return _pixelPaints;
} else if (element == QrCodeElement.finderPatternOuter ||
element == QrCodeElement.finderPatternInner) {
return <Paint>[_finderPaints[_cacheKey(element, position: position)]];
}
return null;
}
}

View File

@ -3,57 +3,264 @@
* Copyright (c) 2019 the QR.Flutter authors. * Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details. * See LICENSE for distribution and usage details.
*/ */
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:qr/qr.dart'; import 'package:qr/qr.dart';
import 'qr_painter.dart'; import 'qr_painter.dart';
import 'qr_versions.dart';
import 'types.dart';
import 'validator.dart';
class QrImage extends StatelessWidget { /// A widget that shows a QR code.
class QrImage extends StatefulWidget {
/// Create a new QR code using the [String] data and the passed options (or
/// using the default options).
QrImage({ QrImage({
@required String data, @required String data,
Key key, Key key,
this.size, this.size,
this.padding = const EdgeInsets.all(10.0), this.padding = const EdgeInsets.all(10.0),
this.backgroundColor, this.backgroundColor = const Color(0x00FFFFFF),
Color foregroundColor = const Color(0xFF000000), this.foregroundColor = const Color(0xFF000000),
int version = 4, this.version = QrVersions.auto,
int errorCorrectionLevel = QrErrorCorrectLevel.L, this.errorCorrectionLevel = QrErrorCorrectLevel.L,
this.onError, this.errorStateBuilder,
this.constrainErrorBounds = true,
this.gapless = true, this.gapless = true,
}) : _painter = QrPainter( this.embeddedImage,
data: data, this.embeddedImageStyle,
color: foregroundColor, this.embeddedImageEmitsError = false,
version: version, }) : assert(QrVersions.isSupportedVersion(version)),
errorCorrectionLevel: errorCorrectionLevel, _data = data,
gapless: gapless, _qrCode = null,
onError: onError),
super(key: key); super(key: key);
final QrPainter _painter; /// Create a new QR code using the [QrCode] data and the passed options (or
/// using the default options).
QrImage.withQr({
@required QrCode qr,
Key key,
this.size,
this.padding = const EdgeInsets.all(10.0),
this.backgroundColor = const Color(0x00FFFFFF),
this.foregroundColor = const Color(0xFF000000),
this.version = QrVersions.auto,
this.errorCorrectionLevel = QrErrorCorrectLevel.L,
this.errorStateBuilder,
this.constrainErrorBounds = true,
this.gapless = true,
this.embeddedImage,
this.embeddedImageStyle,
this.embeddedImageEmitsError = false,
}) : assert(QrVersions.isSupportedVersion(version)),
_data = null,
_qrCode = qr,
super(key: key);
// The data passed to the widget
final String _data;
// The QR code data passed to the widget
final QrCode _qrCode;
/// The background color of the final QR code widget.
final Color backgroundColor; final Color backgroundColor;
/// The foreground color of the final QR code widget.
final Color foregroundColor;
/// The QR code version to use.
final int version;
/// The QR code error correction level to use.
final int errorCorrectionLevel;
/// The external padding between the edge of the widget and the content.
final EdgeInsets padding; final EdgeInsets padding;
/// The intended size of the widget.
final double size; final double size;
final QrError onError;
/// The callback that is executed in the event of an error so that you can
/// interrogate the exception and construct an alternative view to present
/// to your user.
final QrErrorBuilder errorStateBuilder;
/// If `true` then the error widget will be constrained to the boundary of the
/// QR widget if it had been valid. If `false` the error widget will grow to
/// the size it needs. If the error widget is allowed to grow, your layout may
/// jump around (depending on specifics).
///
/// NOTE: Setting a [size] value will override this setting and both the
/// content widget and error widget will adhere to the size value.
final bool constrainErrorBounds;
/// If set to false, each of the squares in the QR code will have a small
/// gap. Default is true.
final bool gapless; final bool gapless;
/// The image data to embed (as an overlay) in the QR code. The image will
/// be added to the center of the QR code.
final ImageProvider embeddedImage;
/// Styling options for the image overlay.
final QrEmbeddedImageStyle embeddedImageStyle;
/// If set to true and there is an error loading the embedded image, the
/// [errorStateBuilder] callback will be called (if it is defined). If false,
/// the widget will ignore the embedded image and just display the QR code.
/// The default is false.
final bool embeddedImageEmitsError;
@override
_QrImageState createState() => _QrImageState();
}
class _QrImageState extends State<QrImage> {
/// The QR code string data.
QrCode _qr;
/// The current validation status.
QrValidationResult _validationResult;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder( if (widget._data != null) {
builder: (BuildContext context, BoxConstraints constraints) { _validationResult = QrValidator.validate(
final double widgetSize = size ?? constraints.biggest.shortestSide; data: widget._data,
return Container( version: widget.version,
width: widgetSize, errorCorrectionLevel: widget.errorCorrectionLevel,
height: widgetSize, );
color: backgroundColor, if (_validationResult.isValid) {
child: Padding( _qr = _validationResult.qrCode;
padding: padding, } else {
child: CustomPaint( _qr = null;
painter: _painter, }
), } 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) {
if (widget.embeddedImageEmitsError) {
return _errorWidget(context, constraints, snapshot.error);
} else {
return _qrWidget(context, null, widgetSize);
}
}
if (snapshot.hasData) {
final ui.Image loadedImage = snapshot.data;
return _qrWidget(context, loadedImage, widgetSize);
} else {
return Container();
}
},
); );
}, } else {
return _qrWidget(context, null, widgetSize);
}
});
}
Widget _qrWidget(BuildContext context, ui.Image image, double edgeLength) {
final painter = QrPainter.withQr(
qr: _qr,
color: widget.foregroundColor,
gapless: widget.gapless,
embeddedImageStyle: widget.embeddedImageStyle,
embeddedImage: image,
);
return _QrContentView(
edgeLength: edgeLength,
backgroundColor: widget.backgroundColor,
padding: widget.padding,
child: CustomPaint(painter: painter),
);
}
Widget _errorWidget(
BuildContext context, BoxConstraints constraints, Object error) {
final errorWidget = widget.errorStateBuilder == null
? Container()
: widget.errorStateBuilder(context, error) ?? Container();
final errorSideLength = (widget.constrainErrorBounds
? widget.size ?? constraints.biggest.shortestSide
: constraints.biggest.longestSide);
return _QrContentView(
edgeLength: errorSideLength,
backgroundColor: widget.backgroundColor,
padding: widget.padding,
child: errorWidget,
);
}
Future<ui.Image> _loadQrImage(
BuildContext buildContext, QrEmbeddedImageStyle style) async {
if (style != null) {}
final mq = MediaQuery.of(buildContext);
final completer = Completer<ui.Image>();
final stream = widget.embeddedImage.resolve(ImageConfiguration(
devicePixelRatio: mq.devicePixelRatio,
));
stream.addListener(ImageStreamListener((info, err) {
completer.complete(info.image);
}, onError: (dynamic err, _) {
completer.completeError(err);
}));
return completer.future;
}
}
typedef QrErrorBuilder = Widget Function(BuildContext context, Object error);
class _QrContentView extends StatelessWidget {
_QrContentView({
@required this.edgeLength,
@required this.child,
this.backgroundColor,
this.padding,
});
/// The length of both edges (because it has to be a square).
final double edgeLength;
/// The background color of the containing widget.
final Color backgroundColor;
/// The padding that surrounds the child widget.
final EdgeInsets padding;
/// The child widget.
final Widget child;
@override
Widget build(BuildContext context) {
return Container(
width: edgeLength,
height: edgeLength,
color: backgroundColor,
child: Padding(
padding: padding,
child: child,
),
); );
} }
} }

View File

@ -3,102 +3,375 @@
* Copyright (c) 2019 the QR.Flutter authors. * Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details. * See LICENSE for distribution and usage details.
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:qr/qr.dart'; import 'package:qr/qr.dart';
typedef QrError = void Function(dynamic error); import 'errors.dart';
import 'paint_cache.dart';
import 'qr_versions.dart';
import 'types.dart';
import 'validator.dart';
// ignore_for_file: deprecated_member_use_from_same_package
const _finderPatternLimit = 7;
// default color for the qr code pixels
const _qrDefaultColor = Color(0xff111111);
/// A [CustomPainter] object that you can use to paint a QR code.
class QrPainter extends CustomPainter { class QrPainter extends CustomPainter {
/// Create a new QRPainter with passed options (or defaults).
QrPainter({ QrPainter({
@required String data, @required String data,
@required this.version, @required this.version,
this.errorCorrectionLevel = QrErrorCorrectLevel.L, this.errorCorrectionLevel = QrErrorCorrectLevel.L,
this.color = const Color(0xff000000), this.color = _qrDefaultColor,
this.emptyColor, this.emptyColor,
this.onError,
this.gapless = false, this.gapless = false,
}) : _qr = QrCode(version, errorCorrectionLevel) { this.embeddedImage,
this.embeddedImageStyle,
}) : assert(QrVersions.isSupportedVersion(version)) {
_init(data); _init(data);
} }
/// Create a new QrPainter with a pre-validated/created [QrCode] object. This
/// constructor is useful when you have a custom validation / error handling
/// flow or for when you need to pre-validate the QR data.
QrPainter.withQr({
QrCode qr,
this.color = _qrDefaultColor,
this.emptyColor,
this.gapless = false,
this.embeddedImage,
this.embeddedImageStyle,
}) : _qr = qr,
version = qr.typeNumber,
errorCorrectionLevel = qr.errorCorrectLevel {
_calcVersion = version;
_initPaints();
}
/// The QR code version.
final int version; // the qr code version final int version; // the qr code version
/// The error correction level of the QR code.
final int errorCorrectionLevel; // the qr code error correction level final int errorCorrectionLevel; // the qr code error correction level
/// The color of the squares.
final Color color; // the color of the dark squares final Color color; // the color of the dark squares
/// The color of the non-squares (background).
@Deprecated(
'You should us the background color value of your container widget')
final Color emptyColor; // the other color final Color emptyColor; // the other color
final QrError onError; /// If set to false, the painter will leave a 1px gap between each of the
/// squares.
final bool gapless; final bool gapless;
final QrCode _qr; // our qr code data /// The image data to embed (as an overlay) in the QR code. The image will
final Paint _paint = Paint()..style = PaintingStyle.fill; /// be added to the center of the QR code.
bool _hasError = false; final ui.Image embeddedImage;
/// Styling options for the image overlay.
final QrEmbeddedImageStyle embeddedImageStyle;
/// The base QR code data
QrCode _qr;
/// This is the version (after calculating) that we will use if the user has
/// requested the 'auto' version.
int _calcVersion;
/// The size of the 'gap' between the pixels
final double _gapSize = 0.25;
/// Cache for all of the [Paint] objects.
final _paintCache = PaintCache();
void _init(String data) { void _init(String data) {
_paint.color = color; if (!QrVersions.isSupportedVersion(version)) {
throw QrUnsupportedVersionException(version);
}
// configure and make the QR code data // configure and make the QR code data
try { final validationResult = QrValidator.validate(
_qr.addData(data); data: data,
_qr.make(); version: version,
} catch (ex) { errorCorrectionLevel: errorCorrectionLevel,
if (onError != null) { );
_hasError = true; if (!validationResult.isValid) {
this.onError(ex); throw validationResult.error;
} }
_qr = validationResult.qrCode;
_calcVersion = _qr.typeNumber;
_initPaints();
}
void _initPaints() {
// 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);
// 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.fill, QrCodeElement.finderPatternInner,
position: position);
} }
} }
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
if (_hasError) { // 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");
return; return;
} }
if (size.shortestSide == 0) { // DEPRECATED: the `emptyColor` property will be removed soon
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");
}
if (emptyColor != null) { if (emptyColor != null) {
canvas.drawColor(emptyColor, BlendMode.color); canvas.drawColor(emptyColor, BlendMode.color);
} }
final double squareSize = size.shortestSide / _qr.moduleCount.toDouble(); final paintMetrics = _PaintMetrics(
final int pxAdjustValue = gapless ? 1 : 0; containerSize: size.shortestSide,
for (int x = 0; x < _qr.moduleCount; x++) { moduleCount: _qr.moduleCount,
for (int y = 0; y < _qr.moduleCount; y++) { gapSize: (gapless ? 0 : _gapSize),
);
// draw the finder pattern elements
_drawFinderPatternItem(FinderPatternPosition.topLeft, canvas, paintMetrics);
_drawFinderPatternItem(
FinderPatternPosition.bottomLeft, canvas, paintMetrics);
_drawFinderPatternItem(
FinderPatternPosition.topRight, canvas, paintMetrics);
// DEBUG: draw the inner content boundary
// final paint = Paint()..style = ui.PaintingStyle.stroke;
// paint.strokeWidth = 1;
// paint.color = const Color(0x55222222);
// canvas.drawRect(
// Rect.fromLTWH(paintMetrics.inset, paintMetrics.inset,
// paintMetrics.innerContentSize, paintMetrics.innerContentSize),
// paint);
double left;
double top;
final gap = !gapless ? _gapSize : 0;
final pixelPaint = _paintCache.firstPaint(QrCodeElement.codePixel);
pixelPaint.color = color;
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;
// paint an 'on' pixel
if (_qr.isDark(y, x)) { if (_qr.isDark(y, x)) {
final Rect squareRect = Rect.fromLTWH(x * squareSize, y * squareSize, left = paintMetrics.inset + (x * (paintMetrics.pixelSize + gap));
squareSize + pxAdjustValue, squareSize + pxAdjustValue); top = paintMetrics.inset + (y * (paintMetrics.pixelSize + gap));
canvas.drawRect(squareRect, _paint); var pixelHTweak = 0.0;
var pixelVTweak = 0.0;
if (gapless && _hasAdjacentHorizontalPixel(x, y, _qr.moduleCount)) {
pixelHTweak = 0.5;
}
if (gapless && _hasAdjacentVerticalPixel(x, y, _qr.moduleCount)) {
pixelVTweak = 0.5;
}
final squareRect = Rect.fromLTWH(
left,
top,
paintMetrics.pixelSize + pixelHTweak,
paintMetrics.pixelSize + pixelVTweak,
);
canvas.drawRect(squareRect, pixelPaint);
} }
} }
} }
if (embeddedImage != null) {
final originalSize = Size(
embeddedImage.width.toDouble(),
embeddedImage.height.toDouble(),
);
final requestedSize =
embeddedImageStyle != null ? embeddedImageStyle.size : null;
final imageSize = _scaledAspectSize(size, originalSize, requestedSize);
final position = Offset(
(size.width - imageSize.width) / 2.0,
(size.height - imageSize.height) / 2.0,
);
// draw the image overlay.
_drawImageOverlay(canvas, position, imageSize, embeddedImageStyle);
}
}
bool _hasAdjacentVerticalPixel(int x, int y, int moduleCount) {
if (y + 1 >= moduleCount) return false;
return _qr.isDark(y + 1, x);
}
bool _hasAdjacentHorizontalPixel(int x, int y, int moduleCount) {
if (x + 1 >= moduleCount) return false;
return _qr.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));
return isTopLeft || isBottomLeft || isTopRight;
}
void _drawFinderPatternItem(
FinderPatternPosition position,
Canvas canvas,
_PaintMetrics metrics,
) {
final totalGap = (_finderPatternLimit - 1) * metrics.gapSize;
final radius = ((_finderPatternLimit * metrics.pixelSize) + totalGap) -
metrics.pixelSize;
final strokeAdjust = (metrics.pixelSize / 2.0);
final edgePos =
(metrics.inset + metrics.innerContentSize) - (radius + strokeAdjust);
Offset offset;
if (position == FinderPatternPosition.topLeft) {
offset =
Offset(metrics.inset + strokeAdjust, metrics.inset + strokeAdjust);
} else if (position == FinderPatternPosition.bottomLeft) {
offset = Offset(metrics.inset + strokeAdjust, edgePos);
} else {
offset = Offset(edgePos, metrics.inset + strokeAdjust);
}
// configure the paints
final outerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternOuter,
position: position);
outerPaint.strokeWidth = metrics.pixelSize;
outerPaint.color = color;
final innerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternInner,
position: position);
innerPaint.color = color;
final strokeRect = Rect.fromLTWH(offset.dx, offset.dy, radius, radius);
canvas.drawRect(strokeRect, outerPaint);
final gapHalf = metrics.pixelSize;
final gap = gapHalf * 2;
final innerSize = radius - gap - (2 * strokeAdjust);
final innerRect = Rect.fromLTWH(offset.dx + gapHalf + strokeAdjust,
offset.dy + gapHalf + strokeAdjust, innerSize, innerSize);
canvas.drawRect(innerRect, innerPaint);
}
bool _hasOneNonZeroSide(Size size) => size.longestSide > 0;
Size _scaledAspectSize(
Size widgetSize, Size originalSize, Size requestedSize) {
if (requestedSize != null && !requestedSize.isEmpty) {
return requestedSize;
} else if (requestedSize != null && _hasOneNonZeroSide(requestedSize)) {
final maxSide = requestedSize.longestSide;
final ratio = maxSide / originalSize.longestSide;
return Size(ratio * originalSize.width, ratio * originalSize.height);
} else {
final maxSide = 0.25 * widgetSize.shortestSide;
final ratio = maxSide / originalSize.longestSide;
return Size(ratio * originalSize.width, ratio * originalSize.height);
}
}
void _drawImageOverlay(
Canvas canvas, Offset position, Size size, QrEmbeddedImageStyle style) {
final paint = Paint()
..isAntiAlias = true
..filterQuality = FilterQuality.high;
if (style != null) {
if (style.color != null) {
paint.colorFilter = ColorFilter.mode(style.color, BlendMode.srcATop);
}
}
final srcSize =
Size(embeddedImage.width.toDouble(), embeddedImage.height.toDouble());
final src = Alignment.center.inscribe(srcSize, Offset.zero & srcSize);
final dst = Alignment.center.inscribe(size, position & size);
canvas.drawImageRect(embeddedImage, src, dst, paint);
} }
@override @override
bool shouldRepaint(CustomPainter oldDelegate) { bool shouldRepaint(CustomPainter oldPainter) {
if (oldDelegate is QrPainter) { if (oldPainter is QrPainter) {
return color != oldDelegate.color || return color != oldPainter.color ||
errorCorrectionLevel != oldDelegate.errorCorrectionLevel || errorCorrectionLevel != oldPainter.errorCorrectionLevel ||
version != oldDelegate.version || _calcVersion != oldPainter._calcVersion ||
_qr != oldDelegate._qr; _qr != oldPainter._qr ||
gapless != oldPainter.gapless ||
embeddedImage != oldPainter.embeddedImage ||
embeddedImageStyle != oldPainter.embeddedImageStyle;
} }
return false; return true;
} }
/// Returns a [ui.Picture] object containing the QR code data.
ui.Picture toPicture(double size) { ui.Picture toPicture(double size) {
final ui.PictureRecorder recorder = ui.PictureRecorder(); final recorder = ui.PictureRecorder();
final Canvas canvas = Canvas(recorder); final canvas = Canvas(recorder);
paint(canvas, Size(size, size)); paint(canvas, Size(size, size));
return recorder.endRecording(); return recorder.endRecording();
} }
/// Returns the raw QR code [ui.Image] object.
Future<ui.Image> toImage(double size,
{ui.ImageByteFormat format = ui.ImageByteFormat.png}) async {
return await toPicture(size).toImage(size.toInt(), size.toInt());
}
/// Returns the raw QR code image byte data.
Future<ByteData> toImageData(double size, Future<ByteData> toImageData(double size,
{ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async {
final ui.Image uiImage = final image = await toImage(size, format: format);
await toPicture(size).toImage(size.toInt(), size.toInt()); return image.toByteData(format: format);
return await uiImage.toByteData(format: format); }
}
class _PaintMetrics {
_PaintMetrics(
{@required this.containerSize,
@required this.gapSize,
@required this.moduleCount}) {
_calculateMetrics();
}
final int moduleCount;
final double containerSize;
final double gapSize;
double _pixelSize;
double get pixelSize => _pixelSize;
double _innerContentSize;
double get innerContentSize => _innerContentSize;
double _inset;
double get inset => _inset;
void _calculateMetrics() {
final gapTotal = (moduleCount - 1) * gapSize;
var pixelSize = (containerSize - gapTotal) / moduleCount;
_pixelSize = (pixelSize * 2).roundToDouble() / 2;
_innerContentSize = (_pixelSize * moduleCount) + gapTotal;
_inset = (containerSize - _innerContentSize) / 2;
} }
} }

23
lib/src/qr_versions.dart Normal file
View File

@ -0,0 +1,23 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
/// This class only contains special version codes. QR codes support version
/// numbers from 1-40 and you should just use the numeric version directly.
class QrVersions {
/// Automatically determine the QR code version based on input and an
/// error correction level.
static const int auto = -1;
/// The minimum supported version code.
static const int min = 1;
/// The maximum supported version code.
static const int max = 40;
/// Checks to see if the supplied version is a valid QR code version
static bool isSupportedVersion(int version) =>
version == auto || (version >= min && version <= max);
}

66
lib/src/types.dart Normal file
View File

@ -0,0 +1,66 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'dart:ui';
import 'package:flutter/widgets.dart';
/// Represents a specific element / part of a QR code. This is used to isolate
/// the different parts so that we can style and modify specific parts
/// independently.
enum QrCodeElement {
/// The 'stroke' / outer square of the QR code finder pattern element.
finderPatternOuter,
/// The inner square of the QR code finder pattern element.
finderPatternInner,
/// The individual pixels of the QR code
codePixel,
}
/// Enumeration representing the three finder pattern (square 'eye') locations.
enum FinderPatternPosition {
/// The top left position.
topLeft,
/// The top right position.
topRight,
/// The bottom left position.
bottomLeft,
}
/// Styling options for any embedded image overlay
class QrEmbeddedImageStyle {
/// Create a new set of styling options.
QrEmbeddedImageStyle({
this.size,
this.color,
});
/// The size of the image. If one dimension is zero then the other dimension
/// will be used to scale the zero dimension based on the original image
/// size.
Size size;
/// Color to tint the image.
Color color;
/// Check to see if the style object has a non-null, non-zero size.
bool get hasDefinedSize => size != null && size.longestSide > 0;
@override
int get hashCode => size.hashCode ^ color.hashCode;
@override
bool operator ==(Object other) {
if (other is QrEmbeddedImageStyle) {
return size == other.size && color == other.color;
}
return false;
}
}

78
lib/src/validator.dart Normal file
View File

@ -0,0 +1,78 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'package:flutter/foundation.dart';
import 'package:qr/qr.dart';
import 'qr_versions.dart';
/// A utility class for validating and pre-rendering QR code data.
class QrValidator {
/// Attempt to parse / generate the QR code data and check for any errors. The
/// resulting [QrValidationResult] object will hold the status of the QR code
/// as well as the generated QR code data.
static QrValidationResult validate({
@required String data,
int version = QrVersions.auto,
int errorCorrectionLevel = QrErrorCorrectLevel.L,
}) {
QrCode qrCode;
try {
if (version != QrVersions.auto) {
qrCode = QrCode(version, errorCorrectionLevel);
qrCode.addData(data);
} else {
qrCode = QrCode.fromData(
data: data,
errorCorrectLevel: errorCorrectionLevel,
);
}
qrCode.make();
return QrValidationResult(
status: QrValidationStatus.valid, qrCode: qrCode);
} on InputTooLongException catch (itle) {
return QrValidationResult(
status: QrValidationStatus.contentTooLong, error: itle);
} on Exception catch (ex) {
return QrValidationResult(status: QrValidationStatus.error, error: ex);
}
}
}
/// Captures the status or a QR code validation operations, as well as the
/// rendered and validated data / object so that it can be used in any
/// secondary operations (to avoid re-rendering). It also keeps any exception
/// that was thrown.
class QrValidationResult {
/// Create a new validation result instance.
QrValidationResult({@required this.status, this.qrCode, this.error});
/// The status of the validation operation.
QrValidationStatus status;
/// The rendered QR code data / object.
QrCode qrCode;
/// The exception that was thrown in the event of a non-valid result (if any).
Exception error;
/// The validation result returned a status of valid;
bool get isValid => status == QrValidationStatus.valid;
}
/// The status of the QR code data you requested to be validated.
enum QrValidationStatus {
/// The QR code data is valid for the provided parameters.
valid,
/// The QR code data is too long for the provided version + error check
/// configuration or too long to be contained in a QR code.
contentTooLong,
/// An unknown / unexpected error occurred when we tried to validate the QR
/// code data.
error,
}

View File

@ -2,23 +2,23 @@ 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: 2.1.0+55 version: 3.0.0
author: Luke Freeman <freeman.luke@gmail.com> author: Yakka, LLC <hello@yakka.agency>
homepage: https://github.com/lukef/qr.flutter homepage: https://github.com/lukef/qr.flutter
environment: environment:
sdk: ">=2.1.0 <3.0.0" sdk: ">=2.1.0 <3.0.0"
flutter: ">=1.2.0" flutter: ">=1.5.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
qr: ^1.1.0 qr: 1.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
test: ^1.3.4 test: ^1.6.0
flutter: flutter:
uses-material-design: false uses-material-design: false

View File

@ -1,12 +0,0 @@
{
"folders": [
{
"path": "."
},
{
"path": "example",
"name": "example"
}
],
"settings": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
test/.images/logo_yakka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

14
test/all_test.dart Normal file
View File

@ -0,0 +1,14 @@
/*
* QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'package:flutter_test/flutter_test.dart';
import 'image_test.dart' as image;
import 'painter_test.dart' as painter;
void main() {
group('image:', image.main);
group('painter:', painter.main);
}

43
test/image_test.dart Normal file
View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:qr/qr.dart';
import 'package:qr_flutter/qr_flutter.dart';
void main() {
testWidgets('QrImage generates correct image', (tester) async {
final qrImage = Center(
child: RepaintBoundary(
child: QrImage(
data: 'This is a test image',
version: QrVersions.auto,
gapless: true,
errorCorrectionLevel: QrErrorCorrectLevel.L,
),
),
);
await tester.pumpWidget(qrImage);
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('./.golden/qr_image_golden.png'),
);
});
testWidgets('QrImage generates correct image with logo', (tester) async {
final qrImage = Center(
child: RepaintBoundary(
child: QrImage(
data: 'This is a test image',
version: QrVersions.auto,
gapless: true,
errorCorrectionLevel: QrErrorCorrectLevel.L,
embeddedImage: AssetImage('assets/images/logo_yakka.png'),
),
),
);
await tester.pumpWidget(qrImage);
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('./.golden/qr_image_logo_golden.png'),
);
});
}

View File

@ -1,27 +1,44 @@
import 'dart:io'; /*
import 'dart:ui'; * QR.Flutter
* Copyright (c) 2019 the QR.Flutter authors.
* See LICENSE for distribution and usage details.
*/
import 'dart:typed_data';
import 'package:flutter/services.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:qr/qr.dart'; import 'package:qr/qr.dart';
import 'package:qr_flutter/src/qr_painter.dart'; import 'package:qr_flutter/qr_flutter.dart';
void main() { void main() {
testWidgets('Painter generates an image', (WidgetTester tester) async { testWidgets('QrPainter generates correct image', (tester) async {
final painter = QrPainter(
data: 'The painter is this thing',
version: QrVersions.auto,
gapless: true,
errorCorrectionLevel: QrErrorCorrectLevel.L,
color: const Color(0xFF000000),
);
ByteData imageData;
await tester.runAsync(() async { await tester.runAsync(() async {
final QrPainter painter = QrPainter( imageData = await painter.toImageData(600.0);
data: 'This is a test image',
color: const Color(0xff222222),
emptyColor: const Color(0xffffffff),
version: 4,
gapless: true,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
final ByteData imageData = await painter.toImageData(300.0);
File file = File('./test_image.png');
file = await file.writeAsBytes(imageData.buffer.asUint8List());
final int len = await file.length();
expect(len, greaterThan(0));
}); });
final imageBytes = imageData.buffer.asUint8List();
final widget = Center(
child: RepaintBoundary(
child: Container(
width: 600,
height: 600,
child: Image.memory(imageBytes),
),
),
);
await tester.pumpWidget(widget);
await tester.pumpAndSettle();
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('./.golden/qr_painter_golden.png'),
);
}); });
} }

3
tool/updategoldens.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
flutter test --update-goldens test/image_test.dart
flutter test --update-goldens test/painter_test.dart