diff --git a/android/.classpath b/android/.classpath
index aa556df..3589094 100644
--- a/android/.classpath
+++ b/android/.classpath
@@ -1,6 +1,6 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
index 4a23601..00d9a35 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,6 +1,5 @@
plugins {
id 'com.android.library'
- id 'org.jetbrains.kotlin.android'
}
allprojects {
@@ -17,18 +16,18 @@ android {
namespace 'im.nfc.flutter_nfc_kit'
- compileSdkVersion 33
-
+ compileSdkVersion 36
+
compileOptions {
- sourceCompatibility JavaVersion.VERSION_11
- targetCompatibility JavaVersion.VERSION_11
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
- minSdkVersion 19
+ minSdkVersion 24
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
@@ -36,8 +35,11 @@ android {
}
}
+kotlin {
+ compilerOptions {
+ jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
+ }
+}
+
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KotlinVersion"
- implementation "org.jetbrains.kotlin:kotlin-reflect:$KotlinVersion"
- implementation 'androidx.core:core-ktx:1.10.1'
}
diff --git a/android/gradle.properties b/android/gradle.properties
index 39a8970..bdcc437 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
-AGPVersion=7.0.4
-KotlinVersion=1.9.10
\ No newline at end of file
+AGPVersion=9.2.0
+KotlinVersion=2.3.21
\ No newline at end of file
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..d997cfc
Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 4a16027..c61a118 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Fri Sep 08 22:01:14 CST 2023
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/android/gradlew b/android/gradlew
new file mode 100755
index 0000000..739907d
--- /dev/null
+++ b/android/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+
+
+# 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
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
new file mode 100644
index 0000000..e509b2d
--- /dev/null
+++ b/android/gradlew.bat
@@ -0,0 +1,93 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@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
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@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="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/settings.gradle b/android/settings.gradle
index b30c81d..8813f0c 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -5,8 +5,7 @@ pluginManagement {
mavenCentral()
}
plugins {
- id 'com.android.library' version "${AGPVersion}"
- id 'org.jetbrains.kotlin.android' version "${KotlinVersion}"
+ id 'com.android.library' version "${AGPVersion}" apply false
}
}
diff --git a/android/src/main/kotlin/im/nfc/flutter_nfc_kit/FlutterNfcKitPlugin.kt b/android/src/main/kotlin/im/nfc/flutter_nfc_kit/FlutterNfcKitPlugin.kt
index 6870842..1b29f8b 100644
--- a/android/src/main/kotlin/im/nfc/flutter_nfc_kit/FlutterNfcKitPlugin.kt
+++ b/android/src/main/kotlin/im/nfc/flutter_nfc_kit/FlutterNfcKitPlugin.kt
@@ -43,6 +43,8 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
private var ndefTechnology: Ndef? = null
private var mifareInfo: MifareInfo? = null
+ private var onUserCancelled: (() -> Unit)? = null
+
private fun TagTechnology.transceive(data: ByteArray, timeout: Int?): ByteArray {
if (timeout != null) {
try {
@@ -119,7 +121,10 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
"finish" -> {
- pollingTimeoutTask?.cancel()
+ val isPolling = pollingTimeoutTask?.cancel()
+ if (isPolling == true) {
+ onUserCancelled?.invoke()
+ }
thread {
try {
val tagTech = tagTechnology
@@ -427,7 +432,10 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
override fun onDetachedFromActivity() {
- pollingTimeoutTask?.cancel()
+ val isPolling = pollingTimeoutTask?.cancel()
+ if (isPolling == true) {
+ onUserCancelled?.invoke()
+ }
pollingTimeoutTask = null
tagTechnology = null
ndefTechnology = null
@@ -444,9 +452,18 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
if (activity.get() != null) {
nfcAdapter.disableReaderMode(activity.get())
}
+ onUserCancelled = null
result.error("408", "Polling tag timeout", null)
}
+ onUserCancelled = {
+ if (activity.get() != null) {
+ nfcAdapter.disableReaderMode(activity.get())
+ }
+ onUserCancelled = null
+ result.error("600", "User cancelled", null)
+ }
+
nfcAdapter.enableReaderMode(activity.get(), { tag ->
pollingTimeoutTask?.cancel()
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 0e8a319..d306bd1 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -23,7 +23,7 @@ void main() {
class MyApp extends StatefulWidget {
@override
- _MyAppState createState() => _MyAppState();
+ State createState() => _MyAppState();
}
class _MyAppState extends State with SingleTickerProviderStateMixin {
@@ -43,13 +43,13 @@ class _MyAppState extends State with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
- if (!kIsWeb)
- _platformVersion =
- '${Platform.operatingSystem} ${Platform.operatingSystemVersion}';
- else
+ if (!kIsWeb) {
+ _platformVersion = '${Platform.operatingSystem} ${Platform.operatingSystemVersion}';
+ } else {
_platformVersion = 'Web';
+ }
initPlatformState();
- _tabController = new TabController(length: 2, vsync: this);
+ _tabController = TabController(length: 2, vsync: this);
_records = [];
}
@@ -86,222 +86,201 @@ class _MyAppState extends State with SingleTickerProviderStateMixin {
],
controller: _tabController,
)),
- body: new TabBarView(controller: _tabController, children: [
+ body: TabBarView(controller: _tabController, children: [
Scrollbar(
child: SingleChildScrollView(
child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const SizedBox(height: 20),
- Text('Running on: $_platformVersion\nNFC: $_availability'),
- const SizedBox(height: 10),
- ElevatedButton(
- onPressed: () async {
- try {
- NFCTag tag = await FlutterNfcKit.poll();
- setState(() {
- _tag = tag;
- });
- await FlutterNfcKit.setIosAlertMessage(
- "Working on it...");
- _mifareResult = null;
- if (tag.standard == "ISO 14443-4 (Type B)") {
- String result1 =
- await FlutterNfcKit.transceive("00B0950000");
- String result2 = await FlutterNfcKit.transceive(
- "00A4040009A00000000386980701");
- setState(() {
- _result = '1: $result1\n2: $result2\n';
- });
- } else if (tag.type == NFCTagType.iso18092) {
- String result1 =
- await FlutterNfcKit.transceive("060080080100");
- setState(() {
- _result = '1: $result1\n';
- });
- } else if (tag.ndefAvailable ?? false) {
- var ndefRecords = await FlutterNfcKit.readNDEFRecords();
- var ndefString = '';
- for (int i = 0; i < ndefRecords.length; i++) {
- ndefString += '${i + 1}: ${ndefRecords[i]}\n';
- }
- setState(() {
- _result = ndefString;
- });
- } else if (tag.type == NFCTagType.webusb) {
- var r = await FlutterNfcKit.transceive(
- "00A4040006D27600012401");
- print(r);
- }
- } catch (e) {
- setState(() {
- _result = 'error: $e';
- });
+ child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
+ const SizedBox(height: 20),
+ Text('Running on: $_platformVersion\nNFC: $_availability'),
+ const SizedBox(height: 10),
+ ElevatedButton(
+ onPressed: () async {
+ try {
+ NFCTag tag = await FlutterNfcKit.poll();
+ setState(() {
+ _tag = tag;
+ });
+ await FlutterNfcKit.setIosAlertMessage("Working on it...");
+ _mifareResult = null;
+ if (tag.standard == "ISO 14443-4 (Type B)") {
+ String result1 = await FlutterNfcKit.transceive("00B0950000");
+ String result2 = await FlutterNfcKit.transceive("00A4040009A00000000386980701");
+ setState(() {
+ _result = '1: $result1\n2: $result2\n';
+ });
+ } else if (tag.type == NFCTagType.iso18092) {
+ String result1 = await FlutterNfcKit.transceive("060080080100");
+ setState(() {
+ _result = '1: $result1\n';
+ });
+ } else if (tag.ndefAvailable ?? false) {
+ var ndefRecords = await FlutterNfcKit.readNDEFRecords();
+ var ndefString = '';
+ for (int i = 0; i < ndefRecords.length; i++) {
+ ndefString += '${i + 1}: ${ndefRecords[i]}\n';
}
+ setState(() {
+ _result = ndefString;
+ });
+ } else if (tag.type == NFCTagType.webusb) {
+ var r = await FlutterNfcKit.transceive("00A4040006D27600012401");
+ print(r);
+ }
+ } catch (e) {
+ setState(() {
+ _result = 'error: $e';
+ });
+ }
- // Pretend that we are working
- if (!kIsWeb) sleep(new Duration(seconds: 1));
- await FlutterNfcKit.finish(iosAlertMessage: "Finished!");
- },
- child: Text('Start polling'),
- ),
- const SizedBox(height: 10),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: _tag != null
- ? Text(
- 'ID: ${_tag!.id}\nStandard: ${_tag!.standard}\nType: ${_tag!.type}\nATQA: ${_tag!.atqa}\nSAK: ${_tag!.sak}\nHistorical Bytes: ${_tag!.historicalBytes}\nProtocol Info: ${_tag!.protocolInfo}\nApplication Data: ${_tag!.applicationData}\nHigher Layer Response: ${_tag!.hiLayerResponse}\nManufacturer: ${_tag!.manufacturer}\nSystem Code: ${_tag!.systemCode}\nDSF ID: ${_tag!.dsfId}\nNDEF Available: ${_tag!.ndefAvailable}\nNDEF Type: ${_tag!.ndefType}\nNDEF Writable: ${_tag!.ndefWritable}\nNDEF Can Make Read Only: ${_tag!.ndefCanMakeReadOnly}\nNDEF Capacity: ${_tag!.ndefCapacity}\nMifare Info:${_tag!.mifareInfo} Transceive Result:\n$_result\n\nBlock Message:\n$_mifareResult')
- : const Text('No tag polled yet.')),
- ])))),
+ // Pretend that we are working
+ if (!kIsWeb) sleep(Duration(seconds: 1));
+ await FlutterNfcKit.finish(iosAlertMessage: "Finished!");
+ },
+ child: Text('Start polling'),
+ ),
+ const SizedBox(height: 10),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: _tag != null
+ ? Text(
+ 'ID: ${_tag!.id}\nStandard: ${_tag!.standard}\nType: ${_tag!.type}\nATQA: ${_tag!.atqa}\nSAK: ${_tag!.sak}\nHistorical Bytes: ${_tag!.historicalBytes}\nProtocol Info: ${_tag!.protocolInfo}\nApplication Data: ${_tag!.applicationData}\nHigher Layer Response: ${_tag!.hiLayerResponse}\nManufacturer: ${_tag!.manufacturer}\nSystem Code: ${_tag!.systemCode}\nDSF ID: ${_tag!.dsfId}\nNDEF Available: ${_tag!.ndefAvailable}\nNDEF Type: ${_tag!.ndefType}\nNDEF Writable: ${_tag!.ndefWritable}\nNDEF Can Make Read Only: ${_tag!.ndefCanMakeReadOnly}\nNDEF Capacity: ${_tag!.ndefCapacity}\nMifare Info:${_tag!.mifareInfo} Transceive Result:\n$_result\n\nBlock Message:\n$_mifareResult')
+ : const Text('No tag polled yet.')),
+ ])))),
Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
+ child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [
+ const SizedBox(height: 20),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
- const SizedBox(height: 20),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- ElevatedButton(
- onPressed: () async {
- if (_records!.length != 0) {
- try {
- NFCTag tag = await FlutterNfcKit.poll();
- setState(() {
- _tag = tag;
- });
- if (tag.type == NFCTagType.mifare_ultralight ||
- tag.type == NFCTagType.mifare_classic ||
- tag.type == NFCTagType.iso15693) {
- await FlutterNfcKit.writeNDEFRecords(_records!);
- setState(() {
- _writeResult = 'OK';
- });
- } else {
- setState(() {
- _writeResult =
- 'error: NDEF not supported: ${tag.type}';
- });
- }
- } catch (e, stacktrace) {
- setState(() {
- _writeResult = 'error: $e';
- });
- print(stacktrace);
- } finally {
- await FlutterNfcKit.finish();
- }
+ ElevatedButton(
+ onPressed: () async {
+ if (_records!.isNotEmpty) {
+ try {
+ NFCTag tag = await FlutterNfcKit.poll();
+ setState(() {
+ _tag = tag;
+ });
+ if (tag.type == NFCTagType.mifare_ultralight ||
+ tag.type == NFCTagType.mifare_classic ||
+ tag.type == NFCTagType.iso15693) {
+ await FlutterNfcKit.writeNDEFRecords(_records!);
+ setState(() {
+ _writeResult = 'OK';
+ });
} else {
setState(() {
- _writeResult = 'error: No record';
+ _writeResult = 'error: NDEF not supported: ${tag.type}';
});
}
- },
- child: Text("Start writing"),
- ),
- ElevatedButton(
- onPressed: () {
- showDialog(
- context: context,
- builder: (BuildContext context) {
- return SimpleDialog(
- title: Text("Record Type"),
- children: [
- SimpleDialogOption(
- child: Text("Text Record"),
- onPressed: () async {
- Navigator.pop(context);
- final result = await Navigator.push(
- context, MaterialPageRoute(
- builder: (context) {
- return NDEFTextRecordSetting();
- }));
- if (result != null) {
- if (result is ndef.TextRecord) {
- setState(() {
- _records!.add(result);
- });
- }
- }
- },
- ),
- SimpleDialogOption(
- child: Text("Uri Record"),
- onPressed: () async {
- Navigator.pop(context);
- final result = await Navigator.push(
- context, MaterialPageRoute(
- builder: (context) {
- return NDEFUriRecordSetting();
- }));
- if (result != null) {
- if (result is ndef.UriRecord) {
- setState(() {
- _records!.add(result);
- });
- }
- }
- },
- ),
- SimpleDialogOption(
- child: Text("Raw Record"),
- onPressed: () async {
- Navigator.pop(context);
- final result = await Navigator.push(
- context, MaterialPageRoute(
- builder: (context) {
- return NDEFRecordSetting();
- }));
- if (result != null) {
- if (result is ndef.NDEFRecord) {
- setState(() {
- _records!.add(result);
- });
- }
- }
- },
- ),
- ]);
- });
- },
- child: Text("Add record"),
- )
- ],
+ } catch (e, stacktrace) {
+ setState(() {
+ _writeResult = 'error: $e';
+ });
+ print(stacktrace);
+ } finally {
+ await FlutterNfcKit.finish();
+ }
+ } else {
+ setState(() {
+ _writeResult = 'error: No record';
+ });
+ }
+ },
+ child: Text("Start writing"),
),
- const SizedBox(height: 10),
- Text('Result: $_writeResult'),
- const SizedBox(height: 10),
- Expanded(
- flex: 1,
- child: ListView(
- shrinkWrap: true,
- children: List.generate(
- _records!.length,
- (index) => GestureDetector(
- child: Padding(
- padding: const EdgeInsets.all(10),
- child: Text(
- 'id:${_records![index].idString}\ntnf:${_records![index].tnf}\ntype:${_records![index].type?.toHexString()}\npayload:${_records![index].payload?.toHexString()}\n')),
- onTap: () async {
- final result = await Navigator.push(context,
- MaterialPageRoute(builder: (context) {
- return NDEFRecordSetting(
- record: _records![index]);
- }));
- if (result != null) {
- if (result is ndef.NDEFRecord) {
- setState(() {
- _records![index] = result;
- });
- } else if (result is String &&
- result == "Delete") {
- _records!.removeAt(index);
- }
+ ElevatedButton(
+ onPressed: () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return SimpleDialog(title: Text("Record Type"), children: [
+ SimpleDialogOption(
+ child: Text("Text Record"),
+ onPressed: () async {
+ Navigator.pop(context);
+ final result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
+ return NDEFTextRecordSetting();
+ }));
+ if (result != null) {
+ if (result is ndef.TextRecord) {
+ setState(() {
+ _records!.add(result);
+ });
}
- },
- ))),
- ),
- ]),
+ }
+ },
+ ),
+ SimpleDialogOption(
+ child: Text("Uri Record"),
+ onPressed: () async {
+ Navigator.pop(context);
+ final result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
+ return NDEFUriRecordSetting();
+ }));
+ if (result != null) {
+ if (result is ndef.UriRecord) {
+ setState(() {
+ _records!.add(result);
+ });
+ }
+ }
+ },
+ ),
+ SimpleDialogOption(
+ child: Text("Raw Record"),
+ onPressed: () async {
+ Navigator.pop(context);
+ final result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
+ return NDEFRecordSetting();
+ }));
+ if (result != null) {
+ if (result is ndef.NDEFRecord) {
+ setState(() {
+ _records!.add(result);
+ });
+ }
+ }
+ },
+ ),
+ ]);
+ });
+ },
+ child: Text("Add record"),
+ )
+ ],
+ ),
+ const SizedBox(height: 10),
+ Text('Result: $_writeResult'),
+ const SizedBox(height: 10),
+ Expanded(
+ flex: 1,
+ child: ListView(
+ shrinkWrap: true,
+ children: List.generate(
+ _records!.length,
+ (index) => GestureDetector(
+ child: Padding(
+ padding: const EdgeInsets.all(10),
+ child: Text(
+ 'id:${_records![index].idString}\ntnf:${_records![index].tnf}\ntype:${_records![index].type?.toHexString()}\npayload:${_records![index].payload?.toHexString()}\n')),
+ onTap: () async {
+ final result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
+ return NDEFRecordSetting(record: _records![index]);
+ }));
+ if (result != null) {
+ if (result is ndef.NDEFRecord) {
+ setState(() {
+ _records![index] = result;
+ });
+ } else if (result is String && result == "Delete") {
+ _records!.removeAt(index);
+ }
+ }
+ },
+ ))),
+ ),
+ ]),
)
]),
),
diff --git a/example/lib/ndef_record/raw_record_setting.dart b/example/lib/ndef_record/raw_record_setting.dart
index 5743b4b..b01748f 100644
--- a/example/lib/ndef_record/raw_record_setting.dart
+++ b/example/lib/ndef_record/raw_record_setting.dart
@@ -9,11 +9,11 @@ class NDEFRecordSetting extends StatefulWidget {
: record = record ?? ndef.NDEFRecord(),
super(key: key);
@override
- _NDEFRecordSetting createState() => _NDEFRecordSetting();
+ State createState() => _NDEFRecordSetting();
}
class _NDEFRecordSetting extends State {
- GlobalKey _formKey = new GlobalKey();
+ final GlobalKey _formKey = GlobalKey();
late TextEditingController _identifierController;
late TextEditingController _payloadController;
late TextEditingController _typeController;
@@ -24,27 +24,21 @@ class _NDEFRecordSetting extends State {
super.initState();
if (widget.record.id == null) {
- _identifierController =
- new TextEditingController.fromValue(TextEditingValue(text: ""));
+ _identifierController = TextEditingController.fromValue(TextEditingValue(text: ""));
} else {
- _identifierController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.id!.toHexString()));
+ _identifierController = TextEditingController.fromValue(TextEditingValue(text: widget.record.id!.toHexString()));
}
if (widget.record.payload == null) {
- _payloadController =
- new TextEditingController.fromValue(TextEditingValue(text: ""));
+ _payloadController = TextEditingController.fromValue(TextEditingValue(text: ""));
} else {
- _payloadController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.payload!.toHexString()));
+ _payloadController =
+ TextEditingController.fromValue(TextEditingValue(text: widget.record.payload!.toHexString()));
}
- if (widget.record.encodedType == null &&
- widget.record.decodedType == null) {
+ if (widget.record.encodedType == null && widget.record.decodedType == null) {
// bug in ndef package (fixed in newest version)
- _typeController =
- new TextEditingController.fromValue(TextEditingValue(text: ""));
+ _typeController = TextEditingController.fromValue(TextEditingValue(text: ""));
} else {
- _typeController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.type!.toHexString()));
+ _typeController = TextEditingController.fromValue(TextEditingValue(text: widget.record.type!.toHexString()));
}
_dropButtonValue = ndef.TypeNameFormat.values.indexOf(widget.record.tnf);
}
@@ -84,10 +78,8 @@ class _NDEFRecordSetting extends State {
child: Text('absoluteURI'),
value: 3,
),
- DropdownMenuItem(
- child: Text('nfcExternal'), value: 4),
- DropdownMenuItem(
- child: Text('unchanged'), value: 5),
+ DropdownMenuItem(child: Text('nfcExternal'), value: 4),
+ DropdownMenuItem(child: Text('unchanged'), value: 5),
DropdownMenuItem(
child: Text('unknown'),
value: 6,
@@ -100,57 +92,44 @@ class _NDEFRecordSetting extends State {
},
),
TextFormField(
- decoration:
- InputDecoration(labelText: 'identifier'),
+ decoration: InputDecoration(labelText: 'identifier'),
validator: (v) {
- return v!.trim().length % 2 == 0
- ? null
- : 'length must be even';
+ return v!.trim().length % 2 == 0 ? null : 'length must be even';
},
controller: _identifierController,
),
TextFormField(
decoration: InputDecoration(labelText: 'type'),
validator: (v) {
- return v!.trim().length % 2 == 0
- ? null
- : 'length must be even';
+ return v!.trim().length % 2 == 0 ? null : 'length must be even';
},
controller: _typeController,
),
TextFormField(
decoration: InputDecoration(labelText: 'payload'),
validator: (v) {
- return v!.trim().length % 2 == 0
- ? null
- : 'length must be even';
+ return v!.trim().length % 2 == 0 ? null : 'length must be even';
},
controller: _payloadController,
),
ElevatedButton(
child: Text('OK'),
onPressed: () {
- if ((_formKey.currentState as FormState)
- .validate()) {
+ if ((_formKey.currentState as FormState).validate()) {
Navigator.pop(
context,
ndef.NDEFRecord(
- tnf: ndef.TypeNameFormat
- .values[_dropButtonValue],
- type:
- (_typeController.text).toBytes(),
- id: (_identifierController.text)
- .toBytes(),
- payload: (_payloadController.text)
- .toBytes()));
+ tnf: ndef.TypeNameFormat.values[_dropButtonValue],
+ type: (_typeController.text).toBytes(),
+ id: (_identifierController.text).toBytes(),
+ payload: (_payloadController.text).toBytes()));
}
},
),
ElevatedButton(
child: Text('Delete'),
onPressed: () {
- if ((_formKey.currentState as FormState)
- .validate()) {
+ if ((_formKey.currentState as FormState).validate()) {
Navigator.pop(context, 'Delete');
}
},
diff --git a/example/lib/ndef_record/text_record_setting.dart b/example/lib/ndef_record/text_record_setting.dart
index c893ef8..006e7b6 100644
--- a/example/lib/ndef_record/text_record_setting.dart
+++ b/example/lib/ndef_record/text_record_setting.dart
@@ -8,11 +8,11 @@ class NDEFTextRecordSetting extends StatefulWidget {
: record = record ?? ndef.TextRecord(language: 'en', text: ''),
super(key: key);
@override
- _NDEFTextRecordSetting createState() => _NDEFTextRecordSetting();
+ State createState() => _NDEFTextRecordSetting();
}
class _NDEFTextRecordSetting extends State {
- GlobalKey _formKey = new GlobalKey();
+ final GlobalKey _formKey = GlobalKey();
late TextEditingController _languageController;
late TextEditingController _textController;
late int _dropButtonValue;
@@ -21,10 +21,8 @@ class _NDEFTextRecordSetting extends State {
initState() {
super.initState();
- _languageController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.language!));
- _textController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.text!));
+ _languageController = TextEditingController.fromValue(TextEditingValue(text: widget.record.language!));
+ _textController = TextEditingController.fromValue(TextEditingValue(text: widget.record.text!));
_dropButtonValue = ndef.TextEncoding.values.indexOf(widget.record.encoding);
}
@@ -47,10 +45,8 @@ class _NDEFTextRecordSetting extends State {
DropdownButton(
value: _dropButtonValue,
items: [
- DropdownMenuItem(
- child: Text('UTF-8'), value: 0),
- DropdownMenuItem(
- child: Text('UTF-16'), value: 1),
+ DropdownMenuItem(child: Text('UTF-8'), value: 0),
+ DropdownMenuItem(child: Text('UTF-16'), value: 1),
],
onChanged: (value) {
setState(() {
@@ -59,12 +55,9 @@ class _NDEFTextRecordSetting extends State {
},
),
TextFormField(
- decoration:
- InputDecoration(labelText: 'language'),
+ decoration: InputDecoration(labelText: 'language'),
validator: (v) {
- return v!.trim().length % 2 == 0
- ? null
- : 'length must not be blank';
+ return v!.trim().length % 2 == 0 ? null : 'length must not be blank';
},
controller: _languageController,
),
@@ -75,13 +68,11 @@ class _NDEFTextRecordSetting extends State {
ElevatedButton(
child: Text('OK'),
onPressed: () {
- if ((_formKey.currentState as FormState)
- .validate()) {
+ if ((_formKey.currentState as FormState).validate()) {
Navigator.pop(
context,
ndef.TextRecord(
- encoding: ndef.TextEncoding
- .values[_dropButtonValue],
+ encoding: ndef.TextEncoding.values[_dropButtonValue],
language: (_languageController.text),
text: (_textController.text)));
}
diff --git a/example/lib/ndef_record/uri_record_setting.dart b/example/lib/ndef_record/uri_record_setting.dart
index 037827c..985022e 100644
--- a/example/lib/ndef_record/uri_record_setting.dart
+++ b/example/lib/ndef_record/uri_record_setting.dart
@@ -8,11 +8,11 @@ class NDEFUriRecordSetting extends StatefulWidget {
: record = record ?? ndef.UriRecord(prefix: '', content: ''),
super(key: key);
@override
- _NDEFUriRecordSetting createState() => _NDEFUriRecordSetting();
+ State createState() => _NDEFUriRecordSetting();
}
class _NDEFUriRecordSetting extends State {
- GlobalKey _formKey = new GlobalKey();
+ final GlobalKey _formKey = GlobalKey();
late TextEditingController _contentController;
String? _dropButtonValue;
@@ -20,8 +20,7 @@ class _NDEFUriRecordSetting extends State {
initState() {
super.initState();
- _contentController = new TextEditingController.fromValue(
- TextEditingValue(text: widget.record.content!));
+ _contentController = TextEditingController.fromValue(TextEditingValue(text: widget.record.content!));
_dropButtonValue = widget.record.prefix;
}
@@ -44,8 +43,7 @@ class _NDEFUriRecordSetting extends State {
DropdownButton(
value: _dropButtonValue,
items: ndef.UriRecord.prefixMap.map((value) {
- return DropdownMenuItem(
- child: Text(value), value: value);
+ return DropdownMenuItem(child: Text(value), value: value);
}).toList(),
onChanged: (value) {
setState(() {
@@ -60,8 +58,7 @@ class _NDEFUriRecordSetting extends State {
ElevatedButton(
child: Text('OK'),
onPressed: () {
- if ((_formKey.currentState as FormState)
- .validate()) {
+ if ((_formKey.currentState as FormState).validate()) {
Navigator.pop(
context,
ndef.UriRecord(
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 3ec36c1..b8be7d0 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -21,26 +21,26 @@ packages:
dependency: transitive
description:
name: characters
- sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
- version: "1.3.0"
+ version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
- sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
- sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
+ sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
- version: "1.17.2"
+ version: "1.19.1"
convert:
dependency: transitive
description:
@@ -61,30 +61,30 @@ packages:
dependency: "direct main"
description:
name: cupertino_icons
- sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
+ sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.dev"
source: hosted
- version: "1.0.6"
+ version: "1.0.9"
fake_async:
dependency: transitive
description:
name: fake_async
- sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
- version: "1.3.1"
+ version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_nfc_kit:
- dependency: "direct dev"
+ dependency: "direct main"
description:
path: ".."
relative: true
source: path
- version: "3.4.1"
+ version: "3.6.2"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -95,14 +95,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
- js:
- dependency: transitive
- description:
- name: js
- sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
- url: "https://pub.dev"
- source: hosted
- version: "0.6.7"
json_annotation:
dependency: transitive
description:
@@ -111,59 +103,83 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.8.1"
- logging:
+ leak_tracker:
dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.0.2"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.10"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.2"
+ logging:
+ dependency: "direct main"
description:
name: logging
- sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
- sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
- version: "0.12.16"
+ version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
+ sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
- version: "0.5.0"
+ version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
- sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
- version: "1.9.1"
+ version: "1.18.0"
ndef:
- dependency: transitive
+ dependency: "direct main"
description:
name: ndef
- sha256: "634d2b5c6f2c186e953218bac9905f3f5e1824b15e30bd1ed6e03a91cdbc7293"
+ sha256: "198ba3798e80cea381648569d84059dbba64cd140079fb7b0d9c3f1e0f5973f3"
url: "https://pub.dev"
source: hosted
- version: "0.3.3"
+ version: "0.4.0"
path:
dependency: transitive
description:
name: path
- sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
- version: "1.8.3"
+ version: "1.9.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
- version: "0.0.99"
+ version: "0.0.0"
source_span:
dependency: transitive
description:
@@ -184,18 +200,18 @@ packages:
dependency: transitive
description:
name: stack_trace
- sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
- version: "1.11.0"
+ version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
- sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.4"
string_scanner:
dependency: transitive
description:
@@ -216,10 +232,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
+ sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
- version: "0.6.0"
+ version: "0.7.11"
typed_data:
dependency: transitive
description:
@@ -228,6 +244,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
+ universal_platform:
+ dependency: transitive
+ description:
+ name: universal_platform
+ sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
uuid:
dependency: transitive
description:
@@ -240,18 +264,26 @@ packages:
dependency: transitive
description:
name: vector_math
- sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
- version: "2.1.4"
+ version: "2.2.0"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.2.1"
web:
dependency: transitive
description:
name: web
- sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
- version: "0.1.4-beta"
+ version: "1.1.1"
sdks:
- dart: ">=3.1.0-185.0.dev <4.0.0"
- flutter: ">=2.0.0"
+ dart: ">=3.12.0 <4.0.0"
+ flutter: ">=3.44.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index bf7d3f5..9e6a88e 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -3,61 +3,20 @@ description: Demonstrates how to use the flutter_nfc_kit plugin.
publish_to: 'none'
environment:
- sdk: ">=2.12.0"
+ sdk: ">=3.2.0"
dependencies:
flutter:
sdk: flutter
-
- # The following adds the Cupertino Icons font to your application.
- # Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^1.0.3
+ flutter_nfc_kit:
+ path: ../
+ logging: ^1.3.0
+ ndef: ^0.4.0
+ cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_nfc_kit:
- path: ../
-
-# For information on the generic Dart part of this file, see the
-# following page: https://dart.dev/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.dev/assets-and-images/#resolution-aware.
-
- # For details regarding adding assets from package dependencies, see
- # https://flutter.dev/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.dev/custom-fonts/#from-packages
+ uses-material-design: true
\ No newline at end of file
diff --git a/ios/Classes/SwiftFlutterNfcKitPlugin.swift b/ios/Classes/SwiftFlutterNfcKitPlugin.swift
index 56fe074..f9215bf 100644
--- a/ios/Classes/SwiftFlutterNfcKitPlugin.swift
+++ b/ios/Classes/SwiftFlutterNfcKitPlugin.swift
@@ -35,6 +35,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
var result: FlutterResult?
var tag: NFCTag?
var multipleTagMessage: String?
+ var isActive: Bool = false
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_nfc_kit", binaryMessenger: registrar.messenger())
@@ -359,6 +360,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
} else if call.method == "finish" {
self.result?(FlutterError(code: "406", message: "Session not active", details: nil))
self.result = nil
+ self.isActive = false
if let session = session {
let arguments = call.arguments as! [String: Any?]
@@ -375,7 +377,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
self.session = nil
}
-
+
tag = nil
result(nil)
} else if call.method == "setIosAlertMessage" {
@@ -423,11 +425,13 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
// from NFCTagReaderSessionDelegate
- public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) {}
+ public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) {
+ self.isActive = true
+ }
// from NFCTagReaderSessionDelegate
- public func tagReaderSession(_: NFCTagReaderSession, didInvalidateWithError error: Error) {
- guard result != nil else { return; }
+ public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
+ guard result != nil, self.session == session, self.isActive else { return; }
if let nfcError = error as? NFCReaderError {
NSLog("Got NFCError when reading NFC: %@", nfcError.localizedDescription)
@@ -445,7 +449,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
result = nil
- session = nil
+ self.session = nil
tag = nil
}
diff --git a/lib/flutter_nfc_kit_web.dart b/lib/flutter_nfc_kit_web.dart
deleted file mode 100644
index 170b103..0000000
--- a/lib/flutter_nfc_kit_web.dart
+++ /dev/null
@@ -1,81 +0,0 @@
-import 'dart:async';
-// In order to *not* need this ignore, consider extracting the "web" version
-// of your plugin as a separate package, instead of inlining it in the same
-// package as the core of your plugin.
-// ignore: avoid_web_libraries_in_flutter
-import 'dart:html' as html show window;
-import 'dart:js_util';
-import 'package:convert/convert.dart';
-
-import 'package:flutter/services.dart';
-import 'package:flutter_web_plugins/flutter_web_plugins.dart';
-import 'package:flutter_nfc_kit/webusb_interop.dart';
-
-/// A web implementation of the FlutterNfcKit plugin.
-///
-/// Note: you should **NEVER use this class directly**, but instead use the [FlutterNfcKit] class in your project.
-///
-/// You **need to implement our WebUSB protocol** on the device side for this to work.
-/// See [WebUSB.md](https://github.com/nfcim/flutter_nfc_kit/blob/master/WebUSB.md) for protocol details.
-class FlutterNfcKitWeb {
- static void registerWith(Registrar registrar) {
- final MethodChannel channel = MethodChannel(
- 'flutter_nfc_kit',
- const StandardMethodCodec(),
- registrar,
- );
-
- final pluginInstance = FlutterNfcKitWeb();
- channel.setMethodCallHandler(pluginInstance.handleMethodCall);
- }
-
- /// Handles method calls over the MethodChannel of this plugin.
- /// Note: Check the "federated" architecture for a new way of doing this:
- /// https://flutter.dev/go/federated-plugins
- Future handleMethodCall(MethodCall call) async {
- switch (call.method) {
- case 'getNFCAvailability':
- if (hasProperty(html.window.navigator, 'usb')) {
- return 'available';
- } else {
- return 'not_supported';
- }
-
- case 'poll':
- int timeout = call.arguments["timeout"];
- bool probe = call.arguments["probeWebUSBMagic"];
- return await WebUSB.poll(timeout, probe);
-
- case 'transceive':
- var data = call.arguments["data"];
- if (!(data is Uint8List || data is String)) {
- throw PlatformException(
- code: "400",
- message:
- "Bad argument: data should be String or Uint8List, got $data");
- }
- // always pass hex string to [transceive]
- var encodedData = data;
- if (data is Uint8List) {
- encodedData = hex.encode(data);
- }
- var encodedResp = await WebUSB.transceive(encodedData);
- dynamic resp = encodedResp;
- // return type should be the same as [data]
- if (data is Uint8List) {
- resp = Uint8List.fromList(hex.decode(encodedResp));
- }
- return resp;
-
- case 'finish':
- bool closeWebUSB = call.arguments["closeWebUSB"];
- return await WebUSB.finish(closeWebUSB);
-
- default:
- throw PlatformException(
- code: "501",
- details:
- "flutter_nfc_kit for web does not support \"${call.method}\"");
- }
- }
-}
diff --git a/lib/webusb_interop.dart b/lib/webusb_interop.dart
deleted file mode 100644
index c024654..0000000
--- a/lib/webusb_interop.dart
+++ /dev/null
@@ -1,246 +0,0 @@
-@JS()
-
-/// Library that inter-ops with JavaScript on WebUSB APIs.
-///
-/// Note: you should **NEVER use this library directly**, but instead use the [FlutterNfcKit] class in your project.
-library webusb_interop;
-
-import 'dart:convert';
-import 'dart:js_util';
-import 'dart:async';
-import 'dart:typed_data';
-
-import 'package:convert/convert.dart';
-import 'package:flutter/services.dart';
-import 'package:js/js.dart';
-import 'package:logging/logging.dart';
-
-final log = Logger('FlutterNFCKit:WebUSB');
-
-/// The USB class code used to identify a WebUSB device that supports this protocol.
-const int USB_CLASS_CODE_VENDOR_SPECIFIC = 0xFF;
-
-@JS('navigator.usb')
-class _USB {
- external static dynamic requestDevice(_USBDeviceRequestOptions options);
- // ignore: unused_field
- external static Function ondisconnect;
-}
-
-@JS()
-@anonymous
-class _USBDeviceRequestOptions {
- external factory _USBDeviceRequestOptions({List<_USBDeviceFilter> filters});
-}
-
-@JS()
-@anonymous
-class _USBDeviceFilter {
- external factory _USBDeviceFilter({int classCode});
-}
-
-@JS()
-@anonymous
-class _USBControlTransferParameters {
- external factory _USBControlTransferParameters(
- {String requestType,
- String recipient,
- int request,
- int value,
- int index});
-}
-
-/// Wraps around WebUSB APIs from browsers to provide low-level interfaces such as [poll] for [FlutterNfcKitWeb].
-///
-/// Note: you should **NEVER use this class directly**, but instead use the [FlutterNfcKit] class in your project.
-class WebUSB {
- static dynamic _device;
- static String customProbeData = "";
-
- static bool _deviceAvailable() {
- return _device != null && getProperty(_device, 'opened');
- }
-
- static void _onDisconnect(event) {
- _device = null;
- log.info('device is disconnected from WebUSB API');
- }
-
- static const USB_PROBE_MAGIC = '_NFC_IM_';
-
- /// Try to poll a WebUSB device according to our protocol.
- static Future poll(int timeout, bool probeMagic) async {
- // request WebUSB device with custom classcode
- if (!_deviceAvailable()) {
- var devicePromise = _USB.requestDevice(_USBDeviceRequestOptions(filters: [
- _USBDeviceFilter(classCode: USB_CLASS_CODE_VENDOR_SPECIFIC)
- ]));
- dynamic device = await promiseToFuture(devicePromise);
- try {
- await promiseToFuture(callMethod(device, 'open', List.empty()))
- .then((_) =>
- promiseToFuture(callMethod(device, 'claimInterface', [1])))
- .timeout(Duration(milliseconds: timeout));
- _device = device;
- _USB.ondisconnect = allowInterop(_onDisconnect);
- log.info("WebUSB device opened", _device);
- } on TimeoutException catch (_) {
- log.severe("Polling tag timeout");
- throw PlatformException(code: "408", message: "Polling tag timeout");
- } on Exception catch (e) {
- log.severe("Poll error", e);
- throw PlatformException(
- code: "500", message: "WebUSB API error", details: e);
- }
-
- if (probeMagic) {
- try {
- // PROBE request
- var promise = callMethod(_device, 'controlTransferIn', [
- _USBControlTransferParameters(
- requestType: 'vendor',
- recipient: 'interface',
- request: 0xff,
- value: 0,
- index: 1),
- 1
- ]);
- var resp = await promiseToFuture(promise);
- if (getProperty(resp, 'status') == 'stalled') {
- throw PlatformException(
- code: "500", message: "Device error: transfer stalled");
- }
- var result =
- (getProperty(resp, 'data').buffer as ByteBuffer).asUint8List();
- if (result.length < USB_PROBE_MAGIC.length ||
- result.sublist(0, USB_PROBE_MAGIC.length) !=
- Uint8List.fromList(USB_PROBE_MAGIC.codeUnits)) {
- throw PlatformException(
- code: "500",
- message:
- "Device error: invalid probe response: ${hex.encode(result)}, should begin with $USB_PROBE_MAGIC");
- }
- customProbeData = hex.encode(result.sublist(USB_PROBE_MAGIC.length));
- } on Exception catch (e) {
- log.severe("Probe error", e);
- throw PlatformException(
- code: "500", message: "WebUSB API error", details: e);
- }
- } else {
- customProbeData = "";
- }
- }
- // get VID & PID
- int vendorId = getProperty(_device, 'vendorId');
- int productId = getProperty(_device, 'productId');
- String id =
- '${vendorId.toRadixString(16).padLeft(4, '0')}:${productId.toRadixString(16).padLeft(4, '0')}';
- return json.encode({
- 'type': 'webusb',
- 'id': id,
- 'standard': 'nfc-im-webusb-protocol',
- 'customProbeData': customProbeData
- });
- }
-
- static Future _doTransceive(Uint8List capdu) async {
- // send a command (CMD)
- var promise = callMethod(_device, 'controlTransferOut', [
- _USBControlTransferParameters(
- requestType: 'vendor',
- recipient: 'interface',
- request: 0,
- value: 0,
- index: 1),
- capdu
- ]);
- await promiseToFuture(promise);
- // wait for execution to finish (STAT)
- while (true) {
- promise = callMethod(_device, 'controlTransferIn', [
- _USBControlTransferParameters(
- requestType: 'vendor',
- recipient: 'interface',
- request: 2,
- value: 0,
- index: 1),
- 1
- ]);
- var resp = await promiseToFuture(promise);
- if (getProperty(resp, 'status') == 'stalled') {
- throw PlatformException(
- code: "500", message: "Device error: transfer stalled");
- }
- var code = getProperty(resp, 'data').buffer.asUint8List()[0];
- if (code == 0) {
- break;
- } else if (code == 1) {
- await Future.delayed(const Duration(microseconds: 100));
- } else {
- throw PlatformException(
- code: "500", message: "Device error: unexpected RESP code $code");
- }
- }
- // get the response (RESP)
- promise = callMethod(_device, 'controlTransferIn', [
- _USBControlTransferParameters(
- requestType: 'vendor',
- recipient: 'interface',
- request: 1,
- value: 0,
- index: 1),
- 1500
- ]);
- var resp = await promiseToFuture(promise);
- var deviceStatus = getProperty(resp, 'status');
- if (deviceStatus != 'ok') {
- throw PlatformException(
- code: "500",
- message:
- "Device error: status should be \"ok\", got \"$deviceStatus\"");
- }
- return getProperty(resp, 'data').buffer.asUint8List();
- }
-
- /// Transceive data with polled WebUSB device according to our protocol.
- static Future transceive(String capdu) async {
- log.config('CAPDU: $capdu');
- if (!_deviceAvailable()) {
- throw PlatformException(
- code: "406", message: "No tag polled or device already disconnected");
- }
- try {
- var rawCAPDU = Uint8List.fromList(hex.decode(capdu));
- var rawRAPDU = await _doTransceive(rawCAPDU);
- String rapdu = hex.encode(rawRAPDU);
- log.config('RAPDU: $rapdu');
- return rapdu;
- } on TimeoutException catch (_) {
- log.severe("Transceive timeout");
- throw PlatformException(code: "408", message: "Transceive timeout");
- } on PlatformException catch (e) {
- log.severe("Transceive error", e);
- rethrow;
- } on Exception catch (e) {
- log.severe("Transceive error", e);
- throw PlatformException(
- code: "500", message: "WebUSB API error", details: e);
- }
- }
-
- /// Finish this session, also end WebUSB session if explicitly asked by user.
- static Future finish(bool closeWebUSB) async {
- if (_deviceAvailable()) {
- if (closeWebUSB) {
- try {
- await promiseToFuture(callMethod(_device, "close", List.empty()));
- } on Exception catch (e) {
- log.severe("Finish error: ", e);
- throw PlatformException(
- code: "500", message: "WebUSB API error", details: e);
- }
- _device = null;
- }
- }
- }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index b5feb02..1a55407 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,11 @@
name: flutter_nfc_kit
description: Provide NFC functionality on Android, iOS & Web, including reading metadata, read & write NDEF records, and transceive layer 3 & 4 data with NFC tags / cards
-version: 3.4.1
+version: 3.6.2
homepage: "https://github.com/nfcim/flutter_nfc_kit"
environment:
- sdk: ">=2.12.0 <4.0.0"
- flutter: ">=2.0.0"
+ sdk: ">=3.12.0 <4.0.0"
+ flutter: ">=3.44.0"
dependencies:
flutter:
@@ -13,16 +13,17 @@ dependencies:
flutter_web_plugins:
sdk: flutter
json_annotation: ^4.8.1
- ndef: ^0.3.3
+ ndef: ^0.4.0
convert: ^3.1.1
logging: ^1.2.0
- js: ^0.6.7
+ web: ^1.1.1
+ universal_platform: ^1.1.0
dev_dependencies:
flutter_test:
sdk: flutter
- lints: ^3.0.0
- build_runner: ^2.4.6
+ lints: ^6.0.0
+ build_runner: ^2.4.9
json_serializable: ^6.7.1
flutter:
@@ -33,6 +34,3 @@ flutter:
pluginClass: FlutterNfcKitPlugin
ios:
pluginClass: FlutterNfcKitPlugin
- web:
- pluginClass: FlutterNfcKitWeb
- fileName: flutter_nfc_kit_web.dart