diff --git a/.gitignore b/.gitignore index dd3d0b6..a9f7d92 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ packages .flutter-plugins .dart_tool pubspec.lock -test_image.png \ No newline at end of file +.temp \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index 0f5da95..9fc4dd1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,3 +4,4 @@ Roman Rodych: @romkor Charles Crete: @cretezy Dmitry: @kelegorm Pedro Massango: @pedromassango +Luke Pighetti: @lukepighetti \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e29d49d..79a756d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 - The `gapless` option is now `true` by default. - Allow assigning `Key` values to `QrImage` widgets. diff --git a/README.md b/README.md index cab1fb3..ff8e990 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,21 @@ QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widg # Features - 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 - Error correction / redundancy - Configurable output size, padding, background and foreground colors +- Supports image overlays - Export to image data to save to file or use in memory - No internet connection required # 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 dependencies: - qr_flutter: ^2.1.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 + qr_flutter: ^3.0.0 ``` **Note**: If you're using the Flutter `master` channel, if you encounter build issues, or want to try the latest and greatest then you should use the `master` branch and not a specific release version. To do so, use the following configuration in your `pubspec.yaml`: @@ -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): ```dart -new QrImage( +QrImage( data: "1234567890", + version: QrVersions.auto, 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 | |----|----|----| -| `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`. | | `size` | double | The (square) size of the image. If not given, will auto size using shortest size constraint. | -| `padding` | EdgeInsets | Padding surrounding the QR code data | -| `backgroundColor` | Color | The background color (default is none) | -| `foregroundColor` | Color | The foreground color (default is black) | -| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true) | +| `padding` | EdgeInsets | Padding surrounding the QR code data. | +| `backgroundColor` | Color | The background color (default is none). | +| `foregroundColor` | Color | The foreground color (default is black). | +| `gapless` | bool | Adds an extra pixel in size to prevent gaps (default is true). | +| `errorStateBuilder` | QrErrorBuilder | Allows you to show an error state `Widget` in the event there is an error rendering the QR code (e.g.: version is too low, input is too long, etc). | +| `constrainErrorBounds` | bool | If true, the error `Widget` will be constrained to the square that the QR code was going to be drawn in. If false, the error state `Widget` will grow/shrink to whatever size it needs. | +| `embeddedImage` | ImageProvider | An `ImageProvider` that defines an image to be overlaid in the center of the QR code. | +| `embeddedImageStyle` | QrEmbeddedImageStyle | Properties to style the embedded image. | +| `embeddedImageEmitsError` | bool | If true, any failure to load the embedded image will trigger the `errorStateBuilder` or render an empty `Container`. If false, the QR code will be rendered and the embedded image will be ignored. | # 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 ## Has it been tested in production? Can I use it in production? diff --git a/analysis_options.yaml b/analysis_options.yaml index c1bf808..82b26a2 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -15,88 +15,89 @@ analyzer: - '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 + # --- STYLE + # identifiers - 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 + - file_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 + - constant_identifier_names # prefer + # ordering + - directives_ordering + # formatting + - lines_longer_than_80_chars # avoid + - curly_braces_in_flow_control_structures + # --- DOCUMENTATION + # comments + # doc comments - 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 + - package_api_docs # prefer + - comment_references + # markdown + # writing + # --- USAGE + # libraries + - implementation_imports + - 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_new - - unnecessary_null_aware_assignments - - unnecessary_null_in_if_null_operators - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements + - prefer_final_fields + #- prefer_expression_function_bodies # consider - 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 - - valid_regexps \ No newline at end of file + # 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 \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 8c96d43..0000000 --- a/example/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -.atom/ -.dart_tool/ -.idea -.vscode/ -.packages -.pub/ -build/ -ios/.generated/ -packages -.flutter-plugins -.dart_tool -pubspec.lock \ No newline at end of file diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index 644fdda..0000000 --- a/example/.metadata +++ /dev/null @@ -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 diff --git a/example/README.md b/example/README.md deleted file mode 100644 index d13b1d1..0000000 --- a/example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# qr - -A new Flutter project. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.io/). diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index a337fba..0000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/example/android/.gitignore b/example/android/.gitignore deleted file mode 100644 index 65b7315..0000000 --- a/example/android/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -*.iml -*.class -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -GeneratedPluginRegistrant.java diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index d0a3f84..0000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -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' -} diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index df8bc71..0000000 --- a/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/example/android/app/src/main/java/com/example/qr/MainActivity.java b/example/android/app/src/main/java/com/example/qr/MainActivity.java deleted file mode 100644 index 08f1ce6..0000000 --- a/example/android/app/src/main/java/com/example/qr/MainActivity.java +++ /dev/null @@ -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); - } -} diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f..0000000 --- a/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4..0000000 Binary files a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b7..0000000 Binary files a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391..0000000 Binary files a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d..0000000 Binary files a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372e..0000000 Binary files a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 00fa441..0000000 --- a/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index 4476887..0000000 --- a/example/android/build.gradle +++ /dev/null @@ -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 -} diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index 8bd86f6..0000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 13372ae..0000000 Binary files a/example/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index aa901e1..0000000 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -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 diff --git a/example/android/gradlew b/example/android/gradlew deleted file mode 100755 index 9d82f78..0000000 --- a/example/android/gradlew +++ /dev/null @@ -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 "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat deleted file mode 100644 index 8a0b282..0000000 --- a/example/android/gradlew.bat +++ /dev/null @@ -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 diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 5a2f14f..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -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 -} diff --git a/example/ios/.gitignore b/example/ios/.gitignore deleted file mode 100644 index 1e1aafd..0000000 --- a/example/ios/.gitignore +++ /dev/null @@ -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/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6c2de80..0000000 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - UIRequiredDeviceCapabilities - - arm64 - - MinimumOSVersion - 8.0 - - diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index ae0eabb..0000000 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -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 = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 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 = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* 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 = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 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 = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* 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 = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* 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 */; -} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 1263ac8..0000000 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h deleted file mode 100644 index cf210d2..0000000 --- a/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 112becd..0000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -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 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fa..0000000 --- a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -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" - } -} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index 3d43d11..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde121..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc230..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b86..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b86..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d16..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58..0000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2..0000000 --- a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -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" - } -} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725..0000000 --- a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c..0000000 --- a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist deleted file mode 100644 index 117b8b7..0000000 --- a/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - qr - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index 0ccc450..0000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index 09d0e6c..0000000 --- a/example/lib/main.dart +++ /dev/null @@ -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, - ); - } -} diff --git a/example/lib/screens/main.screen.dart b/example/lib/screens/main.screen.dart deleted file mode 100644 index a95f51c..0000000 --- a/example/lib/screens/main.screen.dart +++ /dev/null @@ -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 { - 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: [ - 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: [ - 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?'; - }); - }, - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/example/pubspec.yaml b/example/pubspec.yaml deleted file mode 100644 index 22a7c32..0000000 --- a/example/pubspec.yaml +++ /dev/null @@ -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 diff --git a/example/qr.iml b/example/qr.iml deleted file mode 100644 index 485a35d..0000000 --- a/example/qr.iml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/example/qr_android.iml b/example/qr_android.iml deleted file mode 100644 index 0ca70ed..0000000 --- a/example/qr_android.iml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index d9d789a..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -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 -} diff --git a/lib/qr_flutter.dart b/lib/qr_flutter.dart index 38e605a..1a51ffa 100644 --- a/lib/qr_flutter.dart +++ b/lib/qr_flutter.dart @@ -6,5 +6,9 @@ export 'package:qr/qr.dart'; +export 'src/errors.dart'; export 'src/qr_image.dart'; export 'src/qr_painter.dart'; +export 'src/qr_versions.dart'; +export 'src/types.dart'; +export 'src/validator.dart'; diff --git a/lib/src/errors.dart b/lib/src/errors.dart new file mode 100644 index 0000000..fd1500b --- /dev/null +++ b/lib/src/errors.dart @@ -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'; +} diff --git a/lib/src/paint_cache.dart b/lib/src/paint_cache.dart new file mode 100644 index 0000000..ff8b20c --- /dev/null +++ b/lib/src/paint_cache.dart @@ -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 _pixelPaints = []; + final Map _finderPaints = {}; + + 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 paints(QrCodeElement element, {FinderPatternPosition position}) { + if (element == QrCodeElement.codePixel) { + return _pixelPaints; + } else if (element == QrCodeElement.finderPatternOuter || + element == QrCodeElement.finderPatternInner) { + return [_finderPaints[_cacheKey(element, position: position)]]; + } + return null; + } +} diff --git a/lib/src/qr_image.dart b/lib/src/qr_image.dart index 23f45b2..629a976 100644 --- a/lib/src/qr_image.dart +++ b/lib/src/qr_image.dart @@ -3,57 +3,264 @@ * Copyright (c) 2019 the QR.Flutter authors. * See LICENSE for distribution and usage details. */ + +import 'dart:async'; +import 'dart:ui' as ui; + import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:qr/qr.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({ @required String data, Key key, this.size, this.padding = const EdgeInsets.all(10.0), - this.backgroundColor, - Color foregroundColor = const Color(0xFF000000), - int version = 4, - int errorCorrectionLevel = QrErrorCorrectLevel.L, - this.onError, + 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, - }) : _painter = QrPainter( - data: data, - color: foregroundColor, - version: version, - errorCorrectionLevel: errorCorrectionLevel, - gapless: gapless, - onError: onError), + this.embeddedImage, + this.embeddedImageStyle, + this.embeddedImageEmitsError = false, + }) : assert(QrVersions.isSupportedVersion(version)), + _data = data, + _qrCode = null, 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; + + /// 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; + + /// The intended size of the widget. 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; + /// 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 { + /// The QR code string data. + QrCode _qr; + + /// The current validation status. + QrValidationResult _validationResult; + @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - final double widgetSize = size ?? constraints.biggest.shortestSide; - return Container( - width: widgetSize, - height: widgetSize, - color: backgroundColor, - child: Padding( - padding: padding, - child: CustomPaint( - painter: _painter, - ), - ), + if (widget._data != null) { + _validationResult = QrValidator.validate( + data: widget._data, + version: widget.version, + errorCorrectionLevel: widget.errorCorrectionLevel, + ); + if (_validationResult.isValid) { + _qr = _validationResult.qrCode; + } else { + _qr = null; + } + } else if (widget._qrCode != null) { + _qr = widget._qrCode; + _validationResult = + QrValidationResult(status: QrValidationStatus.valid, qrCode: _qr); + } + return LayoutBuilder(builder: (context, constraints) { + // validation failed, show an error state widget if builder is present. + if (!_validationResult.isValid) { + return _errorWidget(context, constraints, _validationResult.error); + } + // no error, build the regular widget + final widgetSize = widget.size ?? constraints.biggest.shortestSide; + if (widget.embeddedImage != null) { + // if requesting to embed an image then we need to load via a + // FutureBuilder because the image provider will be async. + return FutureBuilder( + future: _loadQrImage(context, widget.embeddedImageStyle), + builder: (ctx, snapshot) { + if (snapshot.error != null) { + 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 _loadQrImage( + BuildContext buildContext, QrEmbeddedImageStyle style) async { + if (style != null) {} + + final mq = MediaQuery.of(buildContext); + final completer = Completer(); + 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, + ), ); } } diff --git a/lib/src/qr_painter.dart b/lib/src/qr_painter.dart index 24a0716..cb6659d 100644 --- a/lib/src/qr_painter.dart +++ b/lib/src/qr_painter.dart @@ -3,102 +3,375 @@ * Copyright (c) 2019 the QR.Flutter authors. * See LICENSE for distribution and usage details. */ + import 'dart:async'; +import 'dart:typed_data'; import 'dart:ui' as ui; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.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 { + /// Create a new QRPainter with passed options (or defaults). QrPainter({ @required String data, @required this.version, this.errorCorrectionLevel = QrErrorCorrectLevel.L, - this.color = const Color(0xff000000), + this.color = _qrDefaultColor, this.emptyColor, - this.onError, this.gapless = false, - }) : _qr = QrCode(version, errorCorrectionLevel) { + this.embeddedImage, + this.embeddedImageStyle, + }) : assert(QrVersions.isSupportedVersion(version)) { _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 + + /// The error correction level of the QR code. final int errorCorrectionLevel; // the qr code error correction level + + /// The color of the 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 QrError onError; + /// If set to false, the painter will leave a 1px gap between each of the + /// squares. final bool gapless; - final QrCode _qr; // our qr code data - final Paint _paint = Paint()..style = PaintingStyle.fill; - bool _hasError = false; + /// 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 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) { - _paint.color = color; + if (!QrVersions.isSupportedVersion(version)) { + throw QrUnsupportedVersionException(version); + } // configure and make the QR code data - try { - _qr.addData(data); - _qr.make(); - } catch (ex) { - if (onError != null) { - _hasError = true; - this.onError(ex); - } + final validationResult = QrValidator.validate( + data: data, + version: version, + errorCorrectionLevel: errorCorrectionLevel, + ); + if (!validationResult.isValid) { + 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 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; } - 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"); - } - + // DEPRECATED: the `emptyColor` property will be removed soon if (emptyColor != null) { canvas.drawColor(emptyColor, BlendMode.color); } - final double squareSize = size.shortestSide / _qr.moduleCount.toDouble(); - final int pxAdjustValue = gapless ? 1 : 0; - for (int x = 0; x < _qr.moduleCount; x++) { - for (int y = 0; y < _qr.moduleCount; y++) { + final paintMetrics = _PaintMetrics( + containerSize: size.shortestSide, + moduleCount: _qr.moduleCount, + 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)) { - final Rect squareRect = Rect.fromLTWH(x * squareSize, y * squareSize, - squareSize + pxAdjustValue, squareSize + pxAdjustValue); - canvas.drawRect(squareRect, _paint); + left = paintMetrics.inset + (x * (paintMetrics.pixelSize + gap)); + top = paintMetrics.inset + (y * (paintMetrics.pixelSize + gap)); + 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 - bool shouldRepaint(CustomPainter oldDelegate) { - if (oldDelegate is QrPainter) { - return color != oldDelegate.color || - errorCorrectionLevel != oldDelegate.errorCorrectionLevel || - version != oldDelegate.version || - _qr != oldDelegate._qr; + bool shouldRepaint(CustomPainter oldPainter) { + if (oldPainter is QrPainter) { + return color != oldPainter.color || + errorCorrectionLevel != oldPainter.errorCorrectionLevel || + _calcVersion != oldPainter._calcVersion || + _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) { - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final Canvas canvas = Canvas(recorder); + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); paint(canvas, Size(size, size)); return recorder.endRecording(); } + /// Returns the raw QR code [ui.Image] object. + Future toImage(double size, + {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { + return await toPicture(size).toImage(size.toInt(), size.toInt()); + } + + /// Returns the raw QR code image byte data. Future toImageData(double size, {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async { - final ui.Image uiImage = - await toPicture(size).toImage(size.toInt(), size.toInt()); - return await uiImage.toByteData(format: format); + final image = await toImage(size, format: format); + return image.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; } } diff --git a/lib/src/qr_versions.dart b/lib/src/qr_versions.dart new file mode 100644 index 0000000..0d4713f --- /dev/null +++ b/lib/src/qr_versions.dart @@ -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); +} diff --git a/lib/src/types.dart b/lib/src/types.dart new file mode 100644 index 0000000..b49369c --- /dev/null +++ b/lib/src/types.dart @@ -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; + } +} diff --git a/lib/src/validator.dart b/lib/src/validator.dart new file mode 100644 index 0000000..f837003 --- /dev/null +++ b/lib/src/validator.dart @@ -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, +} diff --git a/pubspec.yaml b/pubspec.yaml index 2ca6263..5b051e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,23 +2,23 @@ name: qr_flutter description: > QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widget or custom painter. -version: 2.1.0+55 -author: Luke Freeman +version: 3.0.0 +author: Yakka, LLC homepage: https://github.com/lukef/qr.flutter environment: sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.2.0" + flutter: ">=1.5.0" dependencies: flutter: sdk: flutter - qr: ^1.1.0 + qr: 1.2.0 dev_dependencies: flutter_test: sdk: flutter - test: ^1.3.4 + test: ^1.6.0 flutter: uses-material-design: false diff --git a/qr.flutter.code-workspace b/qr.flutter.code-workspace deleted file mode 100644 index f6ffe6a..0000000 --- a/qr.flutter.code-workspace +++ /dev/null @@ -1,12 +0,0 @@ -{ - "folders": [ - { - "path": "." - }, - { - "path": "example", - "name": "example" - } - ], - "settings": {} -} \ No newline at end of file diff --git a/test/.golden/qr_image_golden.png b/test/.golden/qr_image_golden.png new file mode 100644 index 0000000..93e2dd8 Binary files /dev/null and b/test/.golden/qr_image_golden.png differ diff --git a/test/.golden/qr_image_logo_golden.png b/test/.golden/qr_image_logo_golden.png new file mode 100644 index 0000000..6a97f2a Binary files /dev/null and b/test/.golden/qr_image_logo_golden.png differ diff --git a/test/.golden/qr_painter_golden.png b/test/.golden/qr_painter_golden.png new file mode 100644 index 0000000..8b1ab8c Binary files /dev/null and b/test/.golden/qr_painter_golden.png differ diff --git a/test/.images/logo_yakka.png b/test/.images/logo_yakka.png new file mode 100644 index 0000000..dabd102 Binary files /dev/null and b/test/.images/logo_yakka.png differ diff --git a/test/all_test.dart b/test/all_test.dart new file mode 100644 index 0000000..58683ca --- /dev/null +++ b/test/all_test.dart @@ -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); +} diff --git a/test/image_test.dart b/test/image_test.dart new file mode 100644 index 0000000..cc26898 --- /dev/null +++ b/test/image_test.dart @@ -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'), + ); + }); +} diff --git a/test/painter_test.dart b/test/painter_test.dart index b65403b..7293453 100644 --- a/test/painter_test.dart +++ b/test/painter_test.dart @@ -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:qr/qr.dart'; -import 'package:qr_flutter/src/qr_painter.dart'; +import 'package:qr_flutter/qr_flutter.dart'; 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 { - final QrPainter painter = QrPainter( - data: 'This is a test image', - color: const Color(0xff222222), - emptyColor: const Color(0xffffffff), - version: 4, - gapless: true, - errorCorrectionLevel: QrErrorCorrectLevel.L, - ); - final 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)); + imageData = await painter.toImageData(600.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'), + ); }); } diff --git a/tool/updategoldens.sh b/tool/updategoldens.sh new file mode 100755 index 0000000..32df67d --- /dev/null +++ b/tool/updategoldens.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +flutter test --update-goldens test/image_test.dart +flutter test --update-goldens test/painter_test.dart