From 3ff093697584ffa3a116972cfc09330dd2492c37 Mon Sep 17 00:00:00 2001 From: David Djordjevic Date: Wed, 25 Sep 2024 14:33:55 +0200 Subject: [PATCH 1/5] -Fix edge case with button animation - Handle not supported case during biometrics authentication --- .../widget_toolkit_pin/example/lib/main.dart | 2 +- .../widget_toolkit_pin/example/pubspec.lock | 8 ++-- .../blocs/pin_code_bloc.dart | 5 ++ .../pin_biometrics_repository.dart | 4 +- .../ui_components/pin_code_component.dart | 4 +- .../ui_components/pin_code_key.dart | 48 +++++++++++-------- 6 files changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/widget_toolkit_pin/example/lib/main.dart b/packages/widget_toolkit_pin/example/lib/main.dart index abcb1e8f..19205ac7 100644 --- a/packages/widget_toolkit_pin/example/lib/main.dart +++ b/packages/widget_toolkit_pin/example/lib/main.dart @@ -137,7 +137,7 @@ class MyHomePage extends StatelessWidget { return 'To use biometrics, you need to turn it on in your device settings!'; case BiometricsMessage.notSupported: - return 'You don\'t have biometric feature on your device!'; + return 'Biometric features aren’t supported on this device!'; case BiometricsMessage.enabled: return 'Your biometrics are enabled!'; diff --git a/packages/widget_toolkit_pin/example/pubspec.lock b/packages/widget_toolkit_pin/example/pubspec.lock index 01179841..3c4cf862 100644 --- a/packages/widget_toolkit_pin/example/pubspec.lock +++ b/packages/widget_toolkit_pin/example/pubspec.lock @@ -883,10 +883,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" watcher: dependency: transitive description: @@ -932,14 +932,14 @@ packages: path: "../../widget_toolkit_biometrics" relative: true source: path - version: "0.0.3" + version: "0.1.0" widget_toolkit_pin: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.1.1" + version: "0.2.0" xdg_directories: dependency: transitive description: diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart index f0d1ff11..2f80704a 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart @@ -2,7 +2,9 @@ import 'package:rx_bloc/rx_bloc.dart'; import 'package:rxdart/rxdart.dart'; import 'package:widget_toolkit/extensions.dart'; import 'package:widget_toolkit/models.dart'; +import 'package:widget_toolkit_biometrics/widget_toolkit_biometrics.dart'; +import '../models/error_enable_biometrics.dart'; import '../services/pin_biometrics_service.dart'; import '../services/pin_code_service.dart'; @@ -162,6 +164,9 @@ class PinCodeBloc extends $PinCodeBloc { /// Authenticates the user with biometrics after which the pin code is /// retrieved from the device and checked. Future _authenticateWithBiometrics() async { + if (!await biometricAuthenticationService.isDeviceSupported) { + throw ErrorEnableBiometrics(BiometricsMessage.notSupported); + } if (await biometricAuthenticationService.authenticate(localizedReason)) { final pinCode = await pinCodeService.getPinCode(); if (pinCode != null) { diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/repositories/pin_biometrics_repository.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/repositories/pin_biometrics_repository.dart index dc57e349..ec605259 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/repositories/pin_biometrics_repository.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/repositories/pin_biometrics_repository.dart @@ -5,7 +5,9 @@ import '../models/biometrics_authentication_type.dart'; class PinBiometricsRepository extends BiometricsRepository { PinBiometricsRepository( - this.biometricAuthenticationDataSource, this.localDataSource); + this.biometricAuthenticationDataSource, + this.localDataSource, + ); @override final PinBiometricsAuthDataSource biometricAuthenticationDataSource; diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart index 17bcfb46..ab2c5873 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart @@ -391,9 +391,7 @@ class _PinCodeComponentState extends State ), ), _buildPinCodeKey(context, 0, 0), - _buildBiometricsButton(context, pinLength - // pin - ), + _buildBiometricsButton(context, pinLength), ], ), ], diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_key.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_key.dart index 9fa4dcc7..69b517ef 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_key.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_key.dart @@ -11,18 +11,31 @@ class PinCodeKey extends StatefulWidget { this.isFaceScan = false, this.isLoading = false, this.showDefaultIcon = false, + this.tintDuration = const Duration(milliseconds: 300), super.key, }); + /// The number to be displayed on the key final int? number; + + /// Shows the finger scan icon if it is set to true final bool isFingerScan; + + /// Shows the face scan icon if it is set to true final bool isFaceScan; + + /// Shows the loading state of the key final bool isLoading; /// Shows the face scan icon if it is set to true final bool showDefaultIcon; + + /// Callback executed once the key is pressed final void Function(int?) onPressed; + /// The duration of the tint animation once a button is pressed/released + final Duration tintDuration; + @override State createState() => _PinCodeKeyState(); } @@ -39,28 +52,14 @@ class _PinCodeKeyState extends State { isPressed = true; }); if (widget.isFingerScan || widget.isFaceScan) { - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(widget.tintDuration); } widget.onPressed(widget.number); }, - onTapUp: widget.isLoading - ? null - : (_) async { - await Future.delayed(const Duration(milliseconds: 300)); - setState(() { - isPressed = false; - }); - }, - onHorizontalDragEnd: (_) { - setState(() { - isPressed = false; - }); - }, - onVerticalDragEnd: (_) { - setState(() { - isPressed = false; - }); - }, + onTapUp: widget.isLoading ? null : (_) => _delayedButtonRelease(), + onTapCancel: widget.isLoading ? null : _delayedButtonRelease, + onHorizontalDragEnd: (_) => _cancelPress(), + onVerticalDragEnd: (_) => _cancelPress(), child: widget.isFingerScan ? AnimatedSwitcher( duration: const Duration(milliseconds: 300), @@ -108,4 +107,15 @@ class _PinCodeKeyState extends State { double _calculateSize(BuildContext context) => calculateKeyboardButtonSize(context); + + void _cancelPress() { + setState(() { + isPressed = false; + }); + } + + void _delayedButtonRelease() async { + await Future.delayed(widget.tintDuration); + _cancelPress(); + } } From 3e2046b862396a999a9a6cfe64b449d48f104a4b Mon Sep 17 00:00:00 2001 From: David Djordjevic Date: Wed, 2 Oct 2024 10:03:36 +0200 Subject: [PATCH 2/5] Update changelog --- packages/widget_toolkit_pin/CHANGELOG.md | 4 ++++ .../ui_components/pin_code_component.dart | 15 +++++++++++---- packages/widget_toolkit_pin/pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/widget_toolkit_pin/CHANGELOG.md b/packages/widget_toolkit_pin/CHANGELOG.md index ebd2881f..3b63d471 100644 --- a/packages/widget_toolkit_pin/CHANGELOG.md +++ b/packages/widget_toolkit_pin/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.2.1] +* Fixed a visual bug where held buttons would remain stuck in the pressed state +* Throw an error if biometrics aren't supported during biometrics authentication + ## [0.2.0] - Updated dependencies: * `rxdart` to `0.28.0` diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart index ab2c5873..025ec7fb 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart @@ -57,6 +57,10 @@ class PinCodeComponent extends StatefulWidget { /// to make it clickable. final PinCodeCustomKey? bottomRightKeyboardButton; + /// The duration of the input label animation presenting the error or input dots + final Duration inputLabelAnimationDuration = + const Duration(milliseconds: 300); + @override State createState() => _PinCodeComponentState(); } @@ -240,7 +244,7 @@ class _PinCodeComponentState extends State child: child, ), child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), + duration: widget.inputLabelAnimationDuration, switchInCurve: Curves.easeOut, switchOutCurve: Curves.easeOut, transitionBuilder: (child, animation) { @@ -255,10 +259,13 @@ class _PinCodeComponentState extends State child: child, ), ); - } else { - return AnimatedSwitcher.defaultTransitionBuilder - .call(child, animation); } + if (animation.value <= 0) { + print("Error: $hasErrorText Anim: ${animation.value}"); + } + + return AnimatedSwitcher.defaultTransitionBuilder + .call(child, animation); }, child: hasErrorText && widget.error != null ? _buildErrorText(context, widget.error!) diff --git a/packages/widget_toolkit_pin/pubspec.yaml b/packages/widget_toolkit_pin/pubspec.yaml index 51cccacd..c6dd83fc 100644 --- a/packages/widget_toolkit_pin/pubspec.yaml +++ b/packages/widget_toolkit_pin/pubspec.yaml @@ -1,7 +1,7 @@ name: widget_toolkit_pin description: This package provide out of the box entering PIN code functionality, which can be used with biometric authentication. -version: 0.2.0 +version: 0.2.1 homepage: https://primeholding.com/ environment: From 815c6664239912d3c2aade4a616feb261a2697f2 Mon Sep 17 00:00:00 2001 From: David Djordjevic Date: Wed, 2 Oct 2024 10:28:09 +0200 Subject: [PATCH 3/5] Remove unused code and fix warning --- .../ui_components/pin_code_component.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart index 025ec7fb..1a16bd9b 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart @@ -260,9 +260,6 @@ class _PinCodeComponentState extends State ), ); } - if (animation.value <= 0) { - print("Error: $hasErrorText Anim: ${animation.value}"); - } return AnimatedSwitcher.defaultTransitionBuilder .call(child, animation); From 7d4609eba0c2522300c40bb0645b6f49a38f27ae Mon Sep 17 00:00:00 2001 From: David Djordjevic Date: Wed, 2 Oct 2024 15:29:55 +0200 Subject: [PATCH 4/5] Fix pin not shown automatically after error --- packages/widget_toolkit_pin/CHANGELOG.md | 1 + .../widget_toolkit_pin/example/pubspec.lock | 2 +- .../blocs/pin_code_bloc.dart | 4 ++++ .../ui_components/pin_code_component.dart | 21 +++---------------- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/widget_toolkit_pin/CHANGELOG.md b/packages/widget_toolkit_pin/CHANGELOG.md index 3b63d471..2b37be2f 100644 --- a/packages/widget_toolkit_pin/CHANGELOG.md +++ b/packages/widget_toolkit_pin/CHANGELOG.md @@ -1,5 +1,6 @@ ## [0.2.1] * Fixed a visual bug where held buttons would remain stuck in the pressed state +* Fixed a visual bug where pin indicators wouldn't be displayed after an error is presented * Throw an error if biometrics aren't supported during biometrics authentication ## [0.2.0] diff --git a/packages/widget_toolkit_pin/example/pubspec.lock b/packages/widget_toolkit_pin/example/pubspec.lock index 3c4cf862..b634de33 100644 --- a/packages/widget_toolkit_pin/example/pubspec.lock +++ b/packages/widget_toolkit_pin/example/pubspec.lock @@ -939,7 +939,7 @@ packages: path: ".." relative: true source: path - version: "0.2.0" + version: "0.2.1" xdg_directories: dependency: transitive description: diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart index 2f80704a..4252f7b1 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart @@ -77,6 +77,10 @@ class PinCodeBloc extends $PinCodeBloc { return Stream.value(_pinCode.value.length); }, ).asResultStream(), + errorState + .delay(const Duration(milliseconds: 300)) + .mapTo(0) + .asResultStream(), ]).whereSuccess().startWith(0).share(); @override diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart index 1a16bd9b..8035e658 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart @@ -57,10 +57,6 @@ class PinCodeComponent extends StatefulWidget { /// to make it clickable. final PinCodeCustomKey? bottomRightKeyboardButton; - /// The duration of the input label animation presenting the error or input dots - final Duration inputLabelAnimationDuration = - const Duration(milliseconds: 300); - @override State createState() => _PinCodeComponentState(); } @@ -197,20 +193,13 @@ class _PinCodeComponentState extends State child: RxBlocBuilder( state: (bloc) => bloc.states.digitsCount, builder: (context, pinLength, bloc) => _buildPageContent( - pinLength: _pinLengthData(pinLength), + pinLength: pinLength.data ?? 0, context: context, ), ), ), ); - int _pinLengthData(AsyncSnapshot pinLength) { - if (pinLength.hasData && pinLength.data != null) { - return pinLength.data!; - } - return 0; - } - Widget _buildPageContent({ required int pinLength, required BuildContext context, @@ -244,7 +233,7 @@ class _PinCodeComponentState extends State child: child, ), child: AnimatedSwitcher( - duration: widget.inputLabelAnimationDuration, + duration: const Duration(milliseconds: 300), switchInCurve: Curves.easeOut, switchOutCurve: Curves.easeOut, transitionBuilder: (child, animation) { @@ -266,11 +255,7 @@ class _PinCodeComponentState extends State }, child: hasErrorText && widget.error != null ? _buildErrorText(context, widget.error!) - : RxBlocBuilder( - state: (bloc) => bloc.states.digitsCount, - builder: (context, pinLength, bloc) => - _buildMaskedKeysRow(context, pinLength.data ?? 0), - ), + : _buildMaskedKeysRow(context, pinLength), ), ); From 955ba6e91e1c83f44df05fecd75546582227bd72 Mon Sep 17 00:00:00 2001 From: David Djordjevic Date: Thu, 3 Oct 2024 13:54:15 +0200 Subject: [PATCH 5/5] Update delay after which the pin code is displayed after error --- .../blocs/pin_code_bloc.dart | 2 +- .../ui_components/pin_code_component.dart | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart index 4252f7b1..94363891 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/blocs/pin_code_bloc.dart @@ -78,7 +78,7 @@ class PinCodeBloc extends $PinCodeBloc { }, ).asResultStream(), errorState - .delay(const Duration(milliseconds: 300)) + .delay(const Duration(milliseconds: 1000)) .mapTo(0) .asResultStream(), ]).whereSuccess().startWith(0).share(); diff --git a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart index 8035e658..0e15791f 100644 --- a/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart +++ b/packages/widget_toolkit_pin/lib/src/lib_pin_code_with_biometrics/ui_components/pin_code_component.dart @@ -436,16 +436,16 @@ class _PinCodeComponentState extends State context, showButton, ); - } else { - return Opacity( - opacity: isLoading ? 0.5 : 1, - child: _buildIconContent( - context, - showButton, - pinLength, - ), - ); } + + return Opacity( + opacity: isLoading ? 0.5 : 1, + child: _buildIconContent( + context, + showButton, + pinLength, + ), + ); } Widget _buildIconContent(