diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67824d3..f9abac8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## [8.0.0] - 2022-10-27
+
+- Change all callback names from `didSomething` to `onSomething`.
+- Change `screenLock` with `confirm: true` to `screenLockCreate`.
+- Change `ScreenLock` with `confirm: true` to `ScreenLock.create`.
+- Replace `StyledInputConfig` with `KeyPadButtonConfig`.
+- Replace `spacingRatio` with fixed value `spacing` in `Secrets`.
+
## [7.0.3] - 2022-07-21
- Added option for onValidate callback.
@@ -80,6 +88,7 @@
## [5.0.6+1] - 2022-03-01
- I have formatted the code properly.
+
## [5.0.6] - 2022-02-21
- Clear input chars on long pressed (#42).
diff --git a/README.md b/README.md
index cf6ef15..f98bc20 100644
--- a/README.md
+++ b/README.md
@@ -10,29 +10,6 @@ You can also use biometric authentication as an option.
-## 6.x to 7 migration
-
-Change delayChilde to delayBuilder.
-It used to push another screen, but has been changed to display a message in TextWidget.
-
-We would like to thank [clragon](https://github.com/clragon) for their significant contribution to these changes.
-
-## 5.x to 6 migration
-
-The major change is that Navigator.pop will be controlled by the developer.
-This is because it is undesirable to pop inside the package in various situations.
-However, we continue to pop in the initial value of the callback as before.
-
-We would like to thank [clragon](https://github.com/clragon) for their significant contribution to these changes.
-
-## 4.x to 5 migration
-
-Change to the next import only.
-
-```dart
-import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-```
-
## Features
- By the length of the character count
@@ -63,9 +40,9 @@ screenLock(
);
```
-### Change digits
+### Block user
-Provides a screen lock that cannot be canceled.
+Provides a screen lock that cannot be cancelled.
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
@@ -77,46 +54,38 @@ screenLock(
);
```
-### Confirmation screen
+### Passcode creation
-You can display the confirmation screen and get the first input with didConfirmed if the first and second inputs match.
+You can have users create a new passcode with confirmation
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-screenLock(
+screenLockCreate(
context: context,
- correctString: '',
- confirmation: true,
- didConfirmed: (matchedText) {
- print(matchedText);
- },
+ onConfirmed: (value) => print(value), // store new passcode somewhere here
);
```
-### Control the confirmation state
+### Control the creation state
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
final inputController = InputController();
-screenLock(
+screenLockCreate(
context: context,
- correctString: '',
- confirmation: true,
inputController: inputController,
);
-// Release the confirmation state at any event.
-inputController.unsetConfirmed();
+// Somewhere else...
+inputController.unsetConfirmed(); // reset first and confirm input
```
### Use local_auth
-Add the local_auth package to pubspec.yml.
-
-https://pub.dev/packages/local_auth
+Add the [local_auth](https://pub.dev/packages/local_auth) package to pubspec.yml.
It includes an example that calls biometrics as soon as screenLock is displayed in `didOpened`.
@@ -125,7 +94,6 @@ import 'package:flutter_screen_lock/flutter_screen_lock.dart';
import 'package:local_auth/local_auth.dart';
import 'package:flutter/material.dart';
-/// Method extraction to call by initial display and custom buttons.
Future localAuth(BuildContext context) async {
final localAuth = LocalAuthentication();
final didAuthenticate = await localAuth.authenticateWithBiometrics(
@@ -138,31 +106,27 @@ Future localAuth(BuildContext context) async {
screenLock(
context: context,
correctString: '1234',
- customizedButtonChild: Icon(
- Icons.fingerprint,
- ),
- customizedButtonTap: () async {
- await localAuth(context);
- },
- didOpened: () async {
- await localAuth(context);
- },
+ customizedButtonChild: Icon(Icons.fingerprint),
+ customizedButtonTap: () async => await localAuth(context),
+ didOpened: () async => await localAuth(context),
);
```
-### Full customize
+### Fully customize
+
+You can customize every aspect of the screenlock.
+Here is an example:
```dart
import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-screenLock(
+screenLockCreate(
context: context,
- title: Text('change title'),
- confirmTitle: Text('change confirm title'),
- correctString: '1234',
- confirmation: true,
- screenLockConfig: ScreenLockConfig(
+ title: const Text('change title'),
+ confirmTitle: const Text('change confirm title'),
+ onConfirmed: (value) => Navigator.of(context).pop(),
+ screenLockConfig: const ScreenLockConfig(
backgroundColor: Colors.deepOrange,
),
secretsConfig: SecretsConfig(
@@ -175,39 +139,50 @@ screenLock(
enabledColor: Colors.amber,
height: 15,
width: 15,
- build: (context, {config, enabled}) {
- return SizedBox(
- child: Container(
- decoration: BoxDecoration(
- shape: BoxShape.rectangle,
- color: enabled
- ? config.enabledColor
- : config.disabledColor,
- border: Border.all(
- width: config.borderSize,
- color: config.borderColor,
- ),
- ),
- padding: EdgeInsets.all(10),
- width: config.width,
- height: config.height,
+ build: (context,
+ {required config, required enabled}) =>
+ Container(
+ decoration: BoxDecoration(
+ shape: BoxShape.rectangle,
+ color: enabled
+ ? config.enabledColor
+ : config.disabledColor,
+ border: Border.all(
+ width: config.borderSize,
+ color: config.borderColor,
),
- width: config.width,
- height: config.height,
- );
- },
+ ),
+ padding: const EdgeInsets.all(10),
+ width: config.width,
+ height: config.height,
+ ),
),
),
- inputButtonConfig: InputButtonConfig(
- textStyle:
- InputButtonConfig.getDefaultTextStyle(context).copyWith(
- color: Colors.orange,
- fontWeight: FontWeight.bold,
- ),
- buttonStyle: OutlinedButton.styleFrom(
- shape: RoundedRectangleBorder(),
- backgroundColor: Colors.deepOrange,
+ keyPadConfig: KeyPadConfig(
+ buttonConfig: StyledInputConfig(
+ textStyle:
+ StyledInputConfig.getDefaultTextStyle(context)
+ .copyWith(
+ color: Colors.orange,
+ fontWeight: FontWeight.bold,
+ ),
+ buttonStyle: OutlinedButton.styleFrom(
+ shape: const RoundedRectangleBorder(),
+ backgroundColor: Colors.deepOrange,
+ ),
),
+ displayStrings: [
+ '零',
+ '壱',
+ '弐',
+ '参',
+ '肆',
+ '伍',
+ '陸',
+ '質',
+ '捌',
+ '玖'
+ ],
),
cancelButton: const Icon(Icons.close),
deleteButton: const Icon(Icons.delete),
@@ -216,14 +191,52 @@ screenLock(
-## Apps I use
+## Version migration
+
+### 7.x to 8 migration
+
+- Change all callback names from `didSomething` to `onSomething`
+- Change `screenLock` with `confirm: true` to `screenLockCreate`
+- Change `ScreenLock` with `confirm: true` to `ScreenLock.create`
+- Replace `StyledInputConfig` with `KeyPadButtonConfig`
+- Replace `spacingRatio` with fixed value `spacing` in `Secrets`
+
+### 6.x to 7 migration
+
+- Requires dart >= 2.17 and Flutter 3.0
+- Replace `InputButtonConfig` with `KeyPadConfig`.
+- Change `delayChild` to `delayBuilder`.
+ `delayBuilder` is no longer displayed in a new screen. Instead, it is now located above the `Secrets`.
+- Accept `BuildContext` in `secretsBuilder`.
+
+### 5.x to 6 migration
+
+- `ScreenLock` does not use `Navigator.pop` internally anymore.
+ The developer should now pop by themselves when desired.
+ `screenLock` call will pop automatically if `onUnlocked` is `null`.
+
+### 4.x to 5 migration
+
+Import name has changed from:
+
+```dart
+import 'package:flutter_screen_lock/functions.dart';
+```
+
+to
+
+```dart
+import 'package:flutter_screen_lock/flutter_screen_lock.dart';
+```
+
+## Apps that use this library
-TimeKey
+### TimeKey
-[iOS](https://apps.apple.com/us/app/timekey-authenticator/id1506129753)
+- [iOS](https://apps.apple.com/us/app/timekey-authenticator/id1506129753)
-[Android](https://play.google.com/store/apps/details?id=net.incrementleaf.TimeKey)
+- [Android](https://play.google.com/store/apps/details?id=net.incrementleaf.TimeKey)
-## Back me up!
+## Support me!
diff --git a/example/.metadata b/example/.metadata
index 0c50e4f..514cc27 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -1,10 +1,27 @@
# 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.
+# This file should be version controlled.
version:
- revision: e6b34c2b5c96bb95325269a29a84e83ed8909b5f
+ revision: eb6d86ee27deecba4a83536aa20f366a6044895c
channel: stable
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
+ base_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/example/README.md b/example/README.md
index 7719aa8..5ff7e18 100644
--- a/example/README.md
+++ b/example/README.md
@@ -4,17 +4,19 @@ This Flutter plugin provides an feature for screen lock.
Enter your passcode to unlock the screen.
You can also use biometric authentication as an option.
-
+
## Features
-- Any number of digits can be specified
-- You can change `Cancel` and `Delete` text
-- The UI expands and contracts according to the size of the device
+- By the length of the character count
+- You can change `Cancel` and `Delete` widget
+- Optimizes the UI for device size and orientation
- You can disable cancellation
-- You can use biometrics
+- You can use biometrics (local_auth plugin)
- Biometrics can be displayed on first launch
- Unlocked callback
+- You can specify a mismatch event.
+- Limit the maximum number of retries
## Usage
@@ -23,7 +25,7 @@ To unlock, enter correctString.
### Simple
-If the passcode you entered matches, you can callback onUnlocked.
+If the passcode the user entered matches, `onUnlocked` is called.
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
@@ -37,85 +39,65 @@ showLockScreen(
### Change digits
-Default 4 digits can be changed. Change the correctString accordingly.
+Digits will be adjusted to the length of `correctString`.
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-showLockScreen(
+lockScreen(
context: context,
- digits: 6,
correctString: '123456',
);
```
-### Use local_auth
-
-Specify `canBiometric` and `biometricAuthenticate`.
-
-Add local_auth processing to `biometricAuthenticate`. See the following page for details.
-
-https://pub.dev/packages/local_auth
+When creating a PIN, you can specify the amount:
```dart
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-showLockScreen(
+lockScreenCreate(
context: context,
- correctString: '1234',
- canBiometric: true,
- // biometricButton is default Icon(Icons.fingerprint)
- // When you want to change the icon with `BiometricType.face`, etc.
- biometricButton: Icon(Icons.face),
- biometricAuthenticate: (context) async {
- final localAuth = LocalAuthentication();
- final didAuthenticate =
- await localAuth.authenticateWithBiometrics(
- localizedReason: 'Please authenticate');
-
- if (didAuthenticate) {
- return true;
- }
-
- return false;
- },
+ digits: 6,
+ onConfirmed: (value) => print(value),
);
```
-### Open biometric first & onUnlocked callback
-add option showBiometricFirst.
+### Use local_auth
+
+Add the [local_auth](https://pub.dev/packages/local_auth) package to pubspec.yml.
+
+It includes an example that calls biometrics as soon as screenLock is displayed in `didOpened`.
```dart
-showLockScreen(
+import 'package:flutter_screen_lock/flutter_screen_lock.dart';
+import 'package:local_auth/local_auth.dart';
+import 'package:flutter/material.dart';
+
+Future localAuth(BuildContext context) async {
+ final localAuth = LocalAuthentication();
+ final didAuthenticate = await localAuth.authenticateWithBiometrics(
+ localizedReason: 'Please authenticate');
+ if (didAuthenticate) {
+ Navigator.pop(context);
+ }
+}
+
+screenLock(
context: context,
correctString: '1234',
- canBiometric: true,
- showBiometricFirst: true,
- biometricAuthenticate: (context) async {
- final localAuth = LocalAuthentication();
- final didAuthenticate =
- await localAuth.authenticateWithBiometrics(
- localizedReason: 'Please authenticate');
-
- if (didAuthenticate) {
- return true;
- }
-
- return false;
- },
- onUnlocked: () {
- print('Unlocked.');
- },
+ customizedButtonChild: Icon(Icons.fingerprint),
+ customizedButtonTap: () async => await localAuth(context),
+ didOpened: () async => await localAuth(context),
);
```
-### Can't cancel
+### Block user
This is the case where you want to force authentication when the app is first launched.
```dart
-showLockScreen(
+lockScreen(
context: context,
correctString: '1234',
canCancel: false,
@@ -125,67 +107,32 @@ showLockScreen(
### Customize text
You can change `Cancel` and `Delete` text.
-We recommend no more than 6 characters at this time.
```dart
showLockScreen(
context: context,
correctString: '1234',
- cancelText: 'Close',
- deleteText: 'Remove',
+ cancelButton: Text('Close'),
+ deleteButton: Text('Remove'),
);
```
-### Verifycation passcode (v1.1.1)
+### User creating new passcode
-use `showConfirmPasscode` function.
+Will let user enter a new passcode and confirm it.
-
+You have to store the passcode somewhere manually.
```dart
-showConfirmPasscode(
- context: context,
- confirmTitle: 'This is the second input.',
- onCompleted: (context, verifyCode) {
- // verifyCode is verified passcode
- print(verifyCode);
- // Please close yourself
- Navigator.of(context).maybePop();
- },
-)
-```
-
-### Customize your style (v1.1.2)
-
-use `circleInputButtonConfig` option.
-
-
+import 'package:flutter_screen_lock/flutter_screen_lock.dart';
-```dart
-showLockScreen(
+screenLockCreate(
context: context,
- correctString: '1234',
- backgroundColor: Colors.grey.shade50,
- backgroundColorOpacity: 1,
- circleInputButtonConfig: CircleInputButtonConfig(
- textStyle: TextStyle(
- fontSize: MediaQuery.of(context).size.width * 0.1,
- color: Colors.white,
- ),
- backgroundColor: Colors.blue,
- backgroundOpacity: 0.5,
- shape: RoundedRectangleBorder(
- side: BorderSide(
- width: 1,
- color: Colors.blue,
- style: BorderStyle.solid,
- ),
- ),
- ),
-)
+ onConfirmed: (value) => print(value), // store new passcode somewhere here
+);
```
-## Help
+## FAQ
### How to prevent the background from being transparent
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index f2872cf..4f8d4d2 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 9.0
+ 11.0
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 1e8c3c9..88359b2 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '9.0'
+# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 41b70b0..49cd1fc 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -14,9 +14,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/local_auth/ios"
SPEC CHECKSUMS:
- Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd
-PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.3
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 3dc1b48..48338df 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -347,7 +347,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -369,7 +369,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -433,7 +433,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -482,7 +482,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -506,7 +506,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -537,7 +537,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/example/lib/main.dart b/example/lib/main.dart
index f9f5fa9..e1acd7f 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,16 +1,12 @@
-import 'dart:io';
-
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
import 'package:local_auth/local_auth.dart';
-void main() {
- runApp(const MyApp());
-}
+void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
- const MyApp({Key? key}) : super(key: key);
+ const MyApp({super.key});
@override
Widget build(BuildContext context) {
@@ -26,7 +22,7 @@ class MyApp extends StatelessWidget {
}
class MyHomePage extends StatefulWidget {
- const MyHomePage({Key? key}) : super(key: key);
+ const MyHomePage({super.key});
@override
_MyHomePageState createState() => _MyHomePageState();
@@ -47,277 +43,268 @@ class _MyHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(
- title: const Text('Next Screen Lock'),
- ),
- body: SizedBox(
- width: double.infinity,
- child: Wrap(
- spacing: 10,
- alignment: WrapAlignment.center,
- children: [
- ElevatedButton(
- onPressed: () => showDialog(
- context: context,
- builder: (context) {
- return ScreenLock(
- correctString: '1234',
- didCancelled: Navigator.of(context).pop,
- didUnlocked: Navigator.of(context).pop,
- );
- },
- ),
- child: const Text('Manualy open'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- canCancel: false,
+ appBar: AppBar(
+ title: const Text('Next Screen Lock'),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Center(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 700,
),
- child: const Text('Not cancelable'),
- ),
- ElevatedButton(
- onPressed: () {
- // Define it to control the confirmation state with its own events.
- final inputController = InputController();
- screenLock(
- context: context,
- correctString: '',
- confirmation: true,
- inputController: inputController,
- didConfirmed: (matchedText) {
- // ignore: avoid_print
- print(matchedText);
- },
- footer: TextButton(
+ child: Wrap(
+ spacing: 16,
+ runSpacing: 16,
+ alignment: WrapAlignment.start,
+ children: [
+ ElevatedButton(
+ onPressed: () => showDialog(
+ context: context,
+ builder: (context) {
+ return ScreenLock(
+ correctString: '1234',
+ onCancelled: Navigator.of(context).pop,
+ onUnlocked: Navigator.of(context).pop,
+ );
+ },
+ ),
+ child: const Text('Manually open'),
+ ),
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ canCancel: false,
+ ),
+ child: const Text('Not cancelable'),
+ ),
+ ElevatedButton(
onPressed: () {
- // Release the confirmation state and return to the initial input state.
- inputController.unsetConfirmed();
+ // Define it to control the confirmation state with its own events.
+ final controller = InputController();
+ screenLockCreate(
+ context: context,
+ inputController: controller,
+ onConfirmed: (matchedText) =>
+ Navigator.of(context).pop(),
+ footer: TextButton(
+ onPressed: () {
+ // Release the confirmation state and return to the initial input state.
+ controller.unsetConfirmed();
+ },
+ child: const Text('Reset input'),
+ ),
+ );
},
- child: const Text('Return enter mode.'),
+ child: const Text('Confirm mode'),
),
- );
- },
- child: const Text('Confirm mode'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- customizedButtonChild: const Icon(
- Icons.fingerprint,
- ),
- customizedButtonTap: () async {
- await localAuth(context);
- },
- didOpened: () async {
- await localAuth(context);
- },
- ),
- child: const Text(
- 'use local_auth \n(Show local_auth when opened)',
- textAlign: TextAlign.center,
- ),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '123456',
- digits: '123456'.length,
- canCancel: false,
- footer: Container(
- padding: const EdgeInsets.only(top: 10),
- child: OutlinedButton(
- child: const Text('Cancel'),
- onPressed: () => Navigator.pop(context),
- style: OutlinedButton.styleFrom(
- backgroundColor: Colors.transparent,
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ customizedButtonChild: const Icon(
+ Icons.fingerprint,
+ ),
+ customizedButtonTap: () async => await localAuth(context),
+ onOpened: () async => await localAuth(context),
+ ),
+ child: const Text(
+ 'use local_auth \n(Show local_auth when opened)',
+ textAlign: TextAlign.center,
),
),
- ),
- ),
- child: const Text('Using footer'),
- ),
- ElevatedButton(
- onPressed: () {
- screenLock(
- context: context,
- title: const Text('change title'),
- confirmTitle: const Text('change confirm title'),
- correctString: '',
- confirmation: true,
- screenLockConfig: const ScreenLockConfig(
- backgroundColor: Colors.deepOrange,
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '123456',
+ canCancel: false,
+ footer: Container(
+ width: 68,
+ height: 68,
+ padding: const EdgeInsets.only(top: 10),
+ child: OutlinedButton(
+ child: const Padding(
+ padding: EdgeInsets.all(10),
+ child: Text('Cancel'),
+ ),
+ onPressed: () => Navigator.pop(context),
+ style: OutlinedButton.styleFrom(
+ backgroundColor: Colors.transparent,
+ ),
+ ),
+ ),
+ ),
+ child: const Text('Using footer'),
),
- secretsConfig: SecretsConfig(
- spacing: 15, // or spacingRatio
- padding: const EdgeInsets.all(40),
- secretConfig: SecretConfig(
- borderColor: Colors.amber,
- borderSize: 2.0,
- disabledColor: Colors.black,
- enabledColor: Colors.amber,
- height: 15,
- width: 15,
- build: (context, {required config, required enabled}) {
- return SizedBox(
- child: Container(
- decoration: BoxDecoration(
- shape: BoxShape.rectangle,
- color: enabled
- ? config.enabledColor
- : config.disabledColor,
- border: Border.all(
- width: config.borderSize,
- color: config.borderColor,
+ ElevatedButton(
+ onPressed: () {
+ screenLockCreate(
+ context: context,
+ title: const Text('change title'),
+ confirmTitle: const Text('change confirm title'),
+ onConfirmed: (value) => Navigator.of(context).pop(),
+ screenLockConfig: const ScreenLockConfig(
+ backgroundColor: Colors.deepOrange,
+ titleTextStyle: TextStyle(fontSize: 24),
+ ),
+ secretsConfig: SecretsConfig(
+ spacing: 15, // or spacingRatio
+ padding: const EdgeInsets.all(40),
+ secretConfig: SecretConfig(
+ borderColor: Colors.amber,
+ borderSize: 2.0,
+ disabledColor: Colors.black,
+ enabledColor: Colors.amber,
+ size: 15,
+ builder: (context, config, enabled) => Container(
+ decoration: BoxDecoration(
+ shape: BoxShape.rectangle,
+ color: enabled
+ ? config.enabledColor
+ : config.disabledColor,
+ border: Border.all(
+ width: config.borderSize,
+ color: config.borderColor,
+ ),
),
+ padding: const EdgeInsets.all(10),
+ width: config.size,
+ height: config.size,
),
- padding: const EdgeInsets.all(10),
- width: config.width,
- height: config.height,
),
- width: config.width,
- height: config.height,
- );
+ ),
+ keyPadConfig: KeyPadConfig(
+ buttonConfig: KeyPadButtonConfig(
+ buttonStyle: OutlinedButton.styleFrom(
+ textStyle: const TextStyle(
+ color: Colors.orange,
+ fontWeight: FontWeight.bold,
+ ),
+ shape: const RoundedRectangleBorder(),
+ backgroundColor: Colors.deepOrange,
+ ),
+ ),
+ displayStrings: [
+ '零',
+ '壱',
+ '弐',
+ '参',
+ '肆',
+ '伍',
+ '陸',
+ '質',
+ '捌',
+ '玖',
+ ],
+ ),
+ cancelButton: const Icon(Icons.close),
+ deleteButton: const Icon(Icons.delete),
+ );
+ },
+ child: const Text('Customize styles'),
+ ),
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ onUnlocked: () {
+ Navigator.pop(context);
+ NextPage.show(context);
},
),
+ child: const Text('Next page with unlock'),
),
- keyPadConfig: KeyPadConfig(
- buttonConfig: StyledInputConfig(
- textStyle: StyledInputConfig.getDefaultTextStyle(context)
- .copyWith(
- color: Colors.orange,
- fontWeight: FontWeight.bold,
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ useBlur: false,
+ screenLockConfig: const ScreenLockConfig(
+ /// If you don't want it to be transparent, override the config
+ backgroundColor: Colors.black,
+ titleTextStyle: TextStyle(fontSize: 24),
),
- buttonStyle: OutlinedButton.styleFrom(
- shape: const RoundedRectangleBorder(),
- backgroundColor: Colors.deepOrange,
+ ),
+ child: const Text('Not blur'),
+ ),
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ maxRetries: 2,
+ retryDelay: const Duration(seconds: 3),
+ delayBuilder: (context, delay) => Text(
+ 'Cannot be entered for ${(delay.inMilliseconds / 1000).ceil()} seconds.',
),
),
- displayStrings: [
- '零',
- '壱',
- '弐',
- '参',
- '肆',
- '伍',
- '陸',
- '質',
- '捌',
- '玖'
- ],
+ child: const Text('Delay next retry'),
),
- cancelButton: const Icon(Icons.close),
- deleteButton: const Icon(Icons.delete),
- );
- },
- child: const Text('Customize styles'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- didUnlocked: () {
- Navigator.pop(context);
- NextPage.show(context);
- },
- ),
- child: const Text('Next page with unlock'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- withBlur: false,
- screenLockConfig: const ScreenLockConfig(
- /// If you don't want it to be transparent, override the config
- backgroundColor: Colors.black,
- ),
- ),
- child: const Text('Not blur'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- maxRetries: 2,
- retryDelay: const Duration(seconds: 3),
- delayBuilder: (context, delay) => Text(
- 'Cannot be entered for ${(delay.inMilliseconds / 1000).ceil()} seconds.',
- ),
- ),
- child: const Text('Delay next retry'),
- ),
- ElevatedButton(
- onPressed: () => showDialog(
- context: context,
- builder: (context) {
- return ScreenLock(
- correctString: '1234',
- keyPadConfig: const KeyPadConfig(
- clearOnLongPressed: true,
+ ElevatedButton(
+ onPressed: () => showDialog(
+ context: context,
+ builder: (context) => ScreenLock(
+ correctString: '1234',
+ keyPadConfig: const KeyPadConfig(
+ clearOnLongPressed: true,
+ ),
+ onUnlocked: Navigator.of(context).pop,
+ ),
),
- didUnlocked: Navigator.of(context).pop,
- );
- },
- ),
- child: const Text('Delete long pressed to clear input'),
- ),
- ElevatedButton(
- onPressed: () => showDialog(
- context: context,
- builder: (context) {
- return ScreenLock(
- correctString: '1234',
- secretsBuilder: (
- context,
- config,
- length,
- input,
- verifyStream,
- ) =>
- SecretsWithCustomAnimation(
- verifyStream: verifyStream,
- config: config,
- input: input,
- length: length,
+ child: const Text('Delete long pressed to clear input'),
+ ),
+ ElevatedButton(
+ onPressed: () => showDialog(
+ context: context,
+ builder: (context) => ScreenLock(
+ correctString: '1234',
+ secretsBuilder: (
+ context,
+ config,
+ length,
+ input,
+ verifyStream,
+ ) =>
+ SecretsWithCustomAnimation(
+ verifyStream: verifyStream,
+ config: config,
+ input: input,
+ length: length,
+ ),
+ onUnlocked: Navigator.of(context).pop,
+ ),
),
- didUnlocked: Navigator.of(context).pop,
- );
- },
- ),
- child: const Text('Secrets custom animation widget'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- correctString: '1234',
- useLandscape: false,
- ),
- child: const Text('Disable landscape mode'),
- ),
- ElevatedButton(
- onPressed: () => screenLock(
- context: context,
- digits: 4,
- correctString: '',
- onValidate: (x) async {
- sleep(Duration(milliseconds: 500));
- return '1234' == x;
- },
+ child: const Text('Secrets custom animation widget'),
+ ),
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: '1234',
+ useLandscape: false,
+ ),
+ child: const Text('Disable landscape mode'),
+ ),
+ ElevatedButton(
+ onPressed: () => screenLock(
+ context: context,
+ correctString: 'x' * 4,
+ onValidate: (value) async => await Future.delayed(
+ const Duration(milliseconds: 500),
+ () => value == '1234',
+ ),
+ ),
+ child: const Text('Callback validation'),
+ ),
+ ],
),
- child: const Text('Callback validation'),
),
- ],
- ),
- ),
- );
+ ),
+ ));
}
}
class NextPage extends StatelessWidget {
- const NextPage({Key? key}) : super(key: key);
+ const NextPage({super.key});
static show(BuildContext context) {
Navigator.of(context)
@@ -336,12 +323,13 @@ class NextPage extends StatelessWidget {
class SecretsWithCustomAnimation extends StatefulWidget {
const SecretsWithCustomAnimation({
- Key? key,
+ super.key,
required this.config,
required this.length,
required this.input,
required this.verifyStream,
- }) : super(key: key);
+ });
+
final SecretsConfig config;
final int length;
final ValueListenable input;
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 79da53c..cc95f45 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -7,7 +7,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.2.1"
clock:
dependency: transitive
description:
@@ -47,7 +47,7 @@ packages:
path: ".."
relative: true
source: path
- version: "7.0.4"
+ version: "8.0.0"
intl:
dependency: transitive
description:
@@ -75,14 +75,14 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
- version: "0.1.4"
+ version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.7.0"
+ version: "1.8.0"
path:
dependency: transitive
description:
@@ -110,5 +110,5 @@ packages:
source: hosted
version: "2.1.2"
sdks:
- dart: ">=2.17.0-0 <3.0.0"
+ dart: ">=2.17.0 <3.0.0"
flutter: ">=1.20.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index bed536b..7bce65e 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -5,7 +5,7 @@ publish_to: "none"
version: 4.0.0
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
dependencies:
flutter:
diff --git a/lib/flutter_screen_lock.dart b/lib/flutter_screen_lock.dart
index bae7202..4c03a71 100644
--- a/lib/flutter_screen_lock.dart
+++ b/lib/flutter_screen_lock.dart
@@ -1,6 +1,6 @@
library flutter_screen_lock;
-export 'src/configurations/styled_input_button_config.dart';
+export 'src/configurations/key_pad_button_config.dart';
export 'src/configurations/key_pad_config.dart';
export 'src/configurations/screen_lock_config.dart';
export 'src/configurations/secret_config.dart';
diff --git a/lib/src/configurations/key_pad_button_config.dart b/lib/src/configurations/key_pad_button_config.dart
new file mode 100644
index 0000000..a0304c3
--- /dev/null
+++ b/lib/src/configurations/key_pad_button_config.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/material.dart';
+
+class KeyPadButtonConfig {
+ const KeyPadButtonConfig({
+ double? size,
+ double? fontSize,
+ this.foregroundColor,
+ this.backgroundColor,
+ this.buttonStyle,
+ }) : size = size ?? 68,
+ fontSize = fontSize ?? 36;
+
+ /// Button width and height.
+ final double size;
+
+ /// Button font size.
+ final double fontSize;
+
+ /// Button foreground (text) color.
+ final Color? foregroundColor;
+
+ /// Button background color.
+ final Color? backgroundColor;
+
+ /// Base [ButtonStyle] that is overriden by other specified values.
+ final ButtonStyle? buttonStyle;
+
+ /// Returns this config as a [ButtonStyle].
+ ButtonStyle toButtonStyle() {
+ ButtonStyle composed = OutlinedButton.styleFrom(
+ textStyle: TextStyle(fontSize: fontSize),
+ foregroundColor: foregroundColor,
+ backgroundColor: backgroundColor,
+ );
+ if (buttonStyle != null) {
+ return buttonStyle!.copyWith(
+ textStyle: composed.textStyle,
+ foregroundColor: composed.foregroundColor,
+ backgroundColor: composed.backgroundColor,
+ );
+ } else {
+ return composed;
+ }
+ }
+
+ /// Copies a [KeyPadButtonConfig] with new values.
+ KeyPadButtonConfig copyWith({
+ double? size,
+ double? fontSize,
+ Color? foregroundColor,
+ Color? backgroundColor,
+ ButtonStyle? buttonStyle,
+ }) {
+ return KeyPadButtonConfig(
+ size: size ?? this.size,
+ fontSize: fontSize ?? this.fontSize,
+ foregroundColor: foregroundColor ?? this.foregroundColor,
+ backgroundColor: backgroundColor ?? this.backgroundColor,
+ buttonStyle: buttonStyle ?? this.buttonStyle,
+ );
+ }
+}
diff --git a/lib/src/configurations/key_pad_config.dart b/lib/src/configurations/key_pad_config.dart
index 363d4f0..75a9d0c 100644
--- a/lib/src/configurations/key_pad_config.dart
+++ b/lib/src/configurations/key_pad_config.dart
@@ -1,6 +1,27 @@
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
class KeyPadConfig {
+ const KeyPadConfig({
+ this.buttonConfig,
+ this.inputStrings = _numbers,
+ List? displayStrings,
+ this.clearOnLongPressed = false,
+ }) : displayStrings = displayStrings ?? inputStrings;
+
+ /// Config for all [KeyPadButton] children.
+ final KeyPadButtonConfig? buttonConfig;
+
+ /// The strings the user can input.
+ final List inputStrings;
+
+ /// The strings that are displayed to the user.
+ /// Mapped 1:1 to [inputStrings].
+ /// Defaults to [inputStrings].
+ final List displayStrings;
+
+ /// Whether to clear the input when long pressing the clear key.
+ final bool clearOnLongPressed;
+
static const List _numbers = [
'0',
'1',
@@ -14,15 +35,18 @@ class KeyPadConfig {
'9',
];
- final StyledInputConfig? buttonConfig;
- final List inputStrings;
- final List displayStrings;
- final bool clearOnLongPressed;
-
- const KeyPadConfig({
- this.buttonConfig,
- this.inputStrings = _numbers,
+ /// Copies a [KeyPadConfig] with new values.
+ KeyPadConfig copyWith({
+ KeyPadButtonConfig? buttonConfig,
+ List? inputStrings,
List? displayStrings,
- this.clearOnLongPressed = false,
- }) : displayStrings = displayStrings ?? inputStrings;
+ bool? clearOnLongPressed,
+ }) {
+ return KeyPadConfig(
+ buttonConfig: buttonConfig ?? this.buttonConfig,
+ inputStrings: inputStrings ?? this.inputStrings,
+ displayStrings: displayStrings ?? this.displayStrings,
+ clearOnLongPressed: clearOnLongPressed ?? this.clearOnLongPressed,
+ );
+ }
}
diff --git a/lib/src/configurations/screen_lock_config.dart b/lib/src/configurations/screen_lock_config.dart
index fa21198..bcd3f12 100644
--- a/lib/src/configurations/screen_lock_config.dart
+++ b/lib/src/configurations/screen_lock_config.dart
@@ -3,39 +3,73 @@ import 'package:flutter/material.dart';
class ScreenLockConfig {
const ScreenLockConfig({
this.backgroundColor,
+ this.titleTextStyle,
+ this.textStyle,
+ this.buttonStyle,
this.themeData,
});
- /// ScreenLock default theme data.
- ///
- /// - Heading title is textTheme.heading1
- /// - Bottom both side text color is outlinedButtonTheme > style > primary
- /// - Number text button is outlinedButtonTheme
- static final ThemeData defaultThemeData = ThemeData(
- scaffoldBackgroundColor: const Color(0x88545454),
- outlinedButtonTheme: OutlinedButtonThemeData(
- style: OutlinedButton.styleFrom(
- primary: const Color(0xFFFFFFFF),
- backgroundColor: const Color(0xFF808080),
- shape: const CircleBorder(),
- padding: const EdgeInsets.all(0),
- side: BorderSide.none,
+ /// Background color of the ScreenLock.
+ final Color? backgroundColor;
+
+ /// Text style for title Texts.
+ final TextStyle? titleTextStyle;
+
+ /// Text style for other Texts.
+ final TextStyle? textStyle;
+
+ /// Button style for keypad buttons.
+ final ButtonStyle? buttonStyle;
+
+ /// Base [ThemeData] that is overriden by other specified values.
+ final ThemeData? themeData;
+
+ /// Returns this config as a [ThemeData].
+ ThemeData toThemeData() {
+ return (themeData ?? ThemeData()).copyWith(
+ scaffoldBackgroundColor: backgroundColor,
+ outlinedButtonTheme: OutlinedButtonThemeData(style: buttonStyle),
+ textTheme: TextTheme(
+ headline1: titleTextStyle,
+ bodyText2: textStyle,
),
+ );
+ }
+
+ /// Copies a [ScreenLockConfig] with new values.
+ ScreenLockConfig copyWith({
+ Color? backgroundColor,
+ TextStyle? titleTextStyle,
+ TextStyle? textStyle,
+ ButtonStyle? buttonStyle,
+ ThemeData? themeData,
+ }) {
+ return ScreenLockConfig(
+ backgroundColor: backgroundColor ?? this.backgroundColor,
+ titleTextStyle: titleTextStyle ?? this.titleTextStyle,
+ textStyle: textStyle ?? this.textStyle,
+ buttonStyle: buttonStyle ?? this.buttonStyle,
+ themeData: themeData ?? this.themeData,
+ );
+ }
+
+ /// Default [ScreenLockConfig].
+ static final ScreenLockConfig defaultConfig = ScreenLockConfig(
+ backgroundColor: const Color(0x88545454),
+ buttonStyle: OutlinedButton.styleFrom(
+ foregroundColor: const Color(0xFFFFFFFF),
+ backgroundColor: const Color(0xFF808080),
+ shape: const CircleBorder(),
+ padding: const EdgeInsets.all(0),
+ side: BorderSide.none,
),
- textTheme: const TextTheme(
- headline1: TextStyle(
- color: Colors.white,
- fontSize: 20,
- ),
- bodyText2: TextStyle(
- color: Colors.white,
- fontSize: 18,
- ),
+ titleTextStyle: const TextStyle(
+ color: Colors.white,
+ fontSize: 20,
+ ),
+ textStyle: const TextStyle(
+ color: Colors.white,
+ fontSize: 18,
),
);
-
- /// background color of ScreenLock.
- final Color? backgroundColor;
-
- final ThemeData? themeData;
}
diff --git a/lib/src/configurations/secret_config.dart b/lib/src/configurations/secret_config.dart
index 639b99b..4d42534 100644
--- a/lib/src/configurations/secret_config.dart
+++ b/lib/src/configurations/secret_config.dart
@@ -1,42 +1,46 @@
import 'package:flutter/material.dart';
-/// Configuration of [Secret]
+/// Configuration of a [Secret] widget.
class SecretConfig {
const SecretConfig({
- this.width = 16,
- this.height = 16,
- this.borderSize = 1.0,
+ this.size = 16,
+ this.borderSize = 1,
this.borderColor = Colors.white,
this.enabledColor = Colors.white,
this.disabledColor = Colors.transparent,
- this.build,
+ this.builder,
});
- final double width;
- final double height;
+ /// Size (width and height) the secret.
+ final double size;
+
+ /// Border size for the secret.
final double borderSize;
+
+ /// Border color for the secret.
final Color borderColor;
+
+ /// Color for the enabled secret.
final Color enabledColor;
+
+ /// Color for the disabled secret.
final Color disabledColor;
- /// `build` override function
final Widget Function(
- BuildContext context, {
- required bool enabled,
- required SecretConfig config,
- })? build;
+ BuildContext context,
+ SecretConfig config,
+ bool enabled,
+ )? builder;
SecretConfig copyWith({
- double? width,
- double? height,
+ double? size,
double? borderSize,
Color? borderColor,
Color? enabledColor,
Color? disabledColor,
}) {
return SecretConfig(
- width: width ?? this.width,
- height: height ?? this.height,
+ size: size ?? this.size,
borderSize: borderSize ?? this.borderSize,
borderColor: borderColor ?? this.borderColor,
enabledColor: enabledColor ?? this.enabledColor,
diff --git a/lib/src/configurations/secrets_config.dart b/lib/src/configurations/secrets_config.dart
index abe6cd7..2e6ac67 100644
--- a/lib/src/configurations/secrets_config.dart
+++ b/lib/src/configurations/secrets_config.dart
@@ -3,25 +3,30 @@ import 'package:flutter_screen_lock/src/configurations/secret_config.dart';
class SecretsConfig {
const SecretsConfig({
- this.spacing,
- this.spacingRatio = 0.05,
+ double? spacing,
this.padding = const EdgeInsets.only(top: 20, bottom: 50),
this.secretConfig = const SecretConfig(),
- });
+ }) : spacing = 12;
- /// Absolute space between secret widgets.
- /// If specified together with spacingRatio, this will take precedence.
- final double? spacing;
+ /// Space between secret widgets.
+ final double spacing;
- /// Space ratio between secret widgets.
- ///
- /// Default `0.05`
- final double spacingRatio;
-
- /// padding of Secrets Widget.
- ///
- /// Default [EdgeInsets.only(top: 20, bottom: 50)]
+ /// Padding of secrets widget.
final EdgeInsetsGeometry padding;
+ /// Config for secrets.
final SecretConfig secretConfig;
+
+ /// Copies a [SecretsConfig] with new values.
+ SecretsConfig copyWith({
+ double? spacing,
+ EdgeInsetsGeometry? padding,
+ SecretConfig? secretConfig,
+ }) {
+ return SecretsConfig(
+ spacing: spacing ?? this.spacing,
+ padding: padding ?? this.padding,
+ secretConfig: secretConfig ?? this.secretConfig,
+ );
+ }
}
diff --git a/lib/src/configurations/styled_input_button_config.dart b/lib/src/configurations/styled_input_button_config.dart
deleted file mode 100644
index baec6de..0000000
--- a/lib/src/configurations/styled_input_button_config.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-import 'package:flutter/material.dart';
-
-class StyledInputConfig {
- const StyledInputConfig({
- this.height,
- this.width,
- this.autoSize = true,
- this.textStyle,
- this.buttonStyle,
- });
-
- // TextStyle for this button
- final TextStyle? textStyle;
-
- /// Button height
- final double? height;
-
- /// Button width
- final double? width;
-
- /// Automatically adjust the size of the square to fit the orientation of the device.
- ///
- /// Default: `true`
- final bool autoSize;
-
- /// It is recommended that you use [OutlinedButton.styleFrom()] to change it.
- final ButtonStyle? buttonStyle;
-
- /// Returns the default text style for buttons.
- static TextStyle getDefaultTextStyle(BuildContext context) {
- if (MediaQuery.of(context).orientation == Orientation.landscape) {
- return TextStyle(
- fontSize: MediaQuery.of(context).size.height * 0.07,
- );
- }
-
- return TextStyle(
- fontSize: MediaQuery.of(context).size.height * 0.045,
- );
- }
-}
diff --git a/lib/src/functions.dart b/lib/src/functions.dart
index 53bf7fb..f0e2c90 100644
--- a/lib/src/functions.dart
+++ b/lib/src/functions.dart
@@ -1,67 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
+import 'package:flutter_screen_lock/src/layout/key_pad.dart';
/// Animated ScreenLock
///
/// - `correctString`: Input correct string (Required).
-/// If [confirmation] is `true`, it will be ignored, so set it to any string or empty.
-/// - `screenLockConfig`: Configurations of [ScreenLock]
-/// - `secretsConfig`: Configurations of [Secrets]
-/// - `inputButtonConfig`: Configurations of [InputButton]
-/// - `canCancel`: `true` is show cancel button
-/// - `confirmation`: Make sure the first and second inputs are the same.
-/// - `digits`: Set the maximum number of characters to enter when [confirmation] is `true`.
-/// - `maxRetries`: `0` is unlimited. For example, if it is set to 1, didMaxRetries will be called on the first failure. Default `0`
-/// - `retryDelay`: Delay until we can retry. Duration.zero is no delay.
-/// - `delayChild`: Specify the widget during input invalidation by retry delay.
-/// - `didUnlocked`: Called if the value matches the correctString.
-/// - `didError`: Called if the value does not match the correctString.
-/// - `didMaxRetries`: Events that have reached the maximum number of attempts
-/// - `didOpened`: For example, when you want to perform biometric authentication
-/// - `didConfirmed`: Called when the first and second inputs match during confirmation
-/// - `didCancelled`: Called when the user cancels the screen
-/// - `customizedButtonTap`: Tapped for left side lower button
-/// - `customizedButtonChild`: Child for bottom left side button
-/// - `footer`: Add a Widget to the footer
-/// - `cancelButton`: Change the child widget for the delete button
-/// - `deleteButton`: Change the child widget for the delete button
-/// - `title`: Change the title widget
-/// - `confirmTitle`: Change the confirm title widget
-/// - `inputController`: Control inputs externally
-/// - `withBlur`: Blur the background
-/// - `secretsBuilder`: Custom secrets animation widget builder
-/// - `useLandscape`: Use a landscape orientation. Default `true`
+/// - `onUnlocked`: Called if the value matches the correctString.
+/// - `onOpened`: For example, when you want to perform biometric authentication.
/// - `onValidate`: Callback to validate input values filled in [digits].
+/// - `onCancelled`: Called when the user cancels the screen.
+/// - `onError`: Called if the value does not match the correctString.
+/// - `onMaxRetries`: Events that have reached the maximum number of attempts.
+/// - `maxRetries`: `0` is unlimited. For example, if it is set to 1, didMaxRetries will be called on the first failure. Default `0`.
+/// - `retryDelay`: Delay until we can retry. Duration.zero is no delay.
+/// - `title`: Change the title widget.
+/// - `screenLockConfig`: Configurations of [ScreenLock].
+/// - `secretsConfig`: Configurations of [Secrets].
+/// - `keyPadConfig`: Configuration of [KeyPad].
+/// - `delayBuilder`: Specify the widget during input invalidation by retry delay.
+/// - `customizedButtonChild`: Child for bottom left side button.
+/// - `customizedButtonTap`: Tapped for left side lower button.
+/// - `footer`: Add a Widget to the footer.
+/// - `cancelButton`: Change the child widget for the delete button.
+/// - `deleteButton`: Change the child widget for the delete button.
+/// - `inputController`: Control inputs externally.
+/// - `secretsBuilder`: Custom secrets animation widget builder.
+/// - `useBlur`: Blur the background.
+/// - `useLandscape`: Use a landscape orientation. Default `true`.
+/// - `canCancel`: `true` is show cancel button.
Future screenLock({
required BuildContext context,
required String correctString,
- VoidCallback? didUnlocked,
- VoidCallback? didOpened,
- VoidCallback? didCancelled,
- void Function(String matchedText)? didConfirmed,
- void Function(int retries)? didError,
- void Function(int retries)? didMaxRetries,
- VoidCallback? customizedButtonTap,
- bool confirmation = false,
- bool canCancel = true,
- int digits = 4,
+ VoidCallback? onUnlocked,
+ VoidCallback? onOpened,
+ ValidationCallback? onValidate,
+ VoidCallback? onCancelled,
+ ValueChanged? onError,
+ ValueChanged? onMaxRetries,
int maxRetries = 0,
Duration retryDelay = Duration.zero,
Widget? title,
- Widget? confirmTitle,
ScreenLockConfig? screenLockConfig,
SecretsConfig? secretsConfig,
KeyPadConfig? keyPadConfig,
DelayBuilderCallback? delayBuilder,
Widget? customizedButtonChild,
+ VoidCallback? customizedButtonTap,
Widget? footer,
Widget? cancelButton,
Widget? deleteButton,
InputController? inputController,
- bool withBlur = true,
SecretsBuilderCallback? secretsBuilder,
+ bool useBlur = true,
bool useLandscape = true,
- ValidationCallback? onValidate,
+ bool canCancel = true,
}) async {
return Navigator.push(
context,
@@ -69,36 +61,140 @@ Future screenLock({
opaque: false,
barrierColor: Colors.black.withOpacity(0.8),
pageBuilder: (context, animation, secondaryAnimation) => WillPopScope(
- onWillPop: () async => canCancel && didCancelled == null,
+ onWillPop: () async => canCancel && onCancelled == null,
child: ScreenLock(
correctString: correctString,
+ onUnlocked: onUnlocked ?? Navigator.of(context).pop,
+ onOpened: onOpened,
+ onValidate: onValidate,
+ onCancelled:
+ canCancel ? onCancelled ?? Navigator.of(context).pop : null,
+ onError: onError,
+ onMaxRetries: onMaxRetries,
+ maxRetries: maxRetries,
+ retryDelay: retryDelay,
+ title: title,
screenLockConfig: screenLockConfig,
secretsConfig: secretsConfig,
keyPadConfig: keyPadConfig,
- didCancelled:
- canCancel ? didCancelled ?? Navigator.of(context).pop : null,
- confirmation: confirmation,
- digits: digits,
- maxRetries: maxRetries,
- retryDelay: retryDelay,
delayBuilder: delayBuilder,
- didUnlocked: didUnlocked ?? Navigator.of(context).pop,
- didError: didError,
- didMaxRetries: didMaxRetries,
- didConfirmed: didConfirmed,
- didOpened: didOpened,
- customizedButtonTap: customizedButtonTap,
customizedButtonChild: customizedButtonChild,
+ customizedButtonTap: customizedButtonTap,
footer: footer,
- deleteButton: deleteButton,
cancelButton: cancelButton,
+ deleteButton: deleteButton,
+ inputController: inputController,
+ secretsBuilder: secretsBuilder,
+ useBlur: useBlur,
+ useLandscape: useLandscape,
+ ),
+ ),
+ transitionsBuilder: (context, animation, secondaryAnimation, child) =>
+ SlideTransition(
+ position: Tween(
+ begin: const Offset(0.0, 2.4),
+ end: Offset.zero,
+ ).animate(animation),
+ child: SlideTransition(
+ position: Tween(
+ begin: Offset.zero,
+ end: const Offset(0.0, 2.4),
+ ).animate(secondaryAnimation),
+ child: child,
+ ),
+ ),
+ ),
+ );
+}
+
+/// Animated ScreenLock
+///
+/// - `onConfirmed`: Called when the first and second inputs match during confirmation.
+/// - `onOpened`: For example, when you want to perform biometric authentication.
+/// - `onValidate`: Callback to validate input values filled in [digits].
+/// - `onCancelled`: Called when the user cancels the screen.
+/// - `onError`: Called if the value does not match the correctString.
+/// - `onMaxRetries`: Events that have reached the maximum number of attempts.
+/// - `maxRetries`: `0` is unlimited. For example, if it is set to 1, didMaxRetries will be called on the first failure. Default `0`.
+/// - `digits`: Set the maximum number of characters to enter.
+/// - `retryDelay`: Delay until we can retry. Duration.zero is no delay.
+/// - `title`: Change the title widget.
+/// - `confirmTitle`: Change the confirm title widget.
+/// - `screenLockConfig`: Configurations of [ScreenLock].
+/// - `secretsConfig`: Configurations of [Secrets].
+/// - `keyPadConfig`: Configuration of [KeyPad].
+/// - `delayBuilder`: Specify the widget during input invalidation by retry delay.
+/// - `customizedButtonChild`: Child for bottom left side button.
+/// - `customizedButtonTap`: Tapped for left side lower button.
+/// - `footer`: Add a Widget to the footer.
+/// - `cancelButton`: Change the child widget for the delete button.
+/// - `deleteButton`: Change the child widget for the delete button.
+/// - `inputController`: Control inputs externally.
+/// - `secretsBuilder`: Custom secrets animation widget builder.
+/// - `useBlur`: Blur the background.
+/// - `useLandscape`: Use a landscape orientation. Default `true`.
+/// - `canCancel`: `true` is show cancel button.
+Future screenLockCreate({
+ required BuildContext context,
+ required ValueChanged onConfirmed,
+ VoidCallback? onOpened,
+ ValidationCallback? onValidate,
+ VoidCallback? onCancelled,
+ ValueChanged? onError,
+ ValueChanged? onMaxRetries,
+ int maxRetries = 0,
+ int digits = 4,
+ Duration retryDelay = Duration.zero,
+ Widget? title,
+ Widget? confirmTitle,
+ ScreenLockConfig? screenLockConfig,
+ SecretsConfig? secretsConfig,
+ KeyPadConfig? keyPadConfig,
+ DelayBuilderCallback? delayBuilder,
+ Widget? customizedButtonChild,
+ VoidCallback? customizedButtonTap,
+ Widget? footer,
+ Widget? cancelButton,
+ Widget? deleteButton,
+ InputController? inputController,
+ SecretsBuilderCallback? secretsBuilder,
+ bool useBlur = true,
+ bool useLandscape = true,
+ bool canCancel = true,
+}) async {
+ return Navigator.push(
+ context,
+ PageRouteBuilder(
+ opaque: false,
+ barrierColor: Colors.black.withOpacity(0.8),
+ pageBuilder: (context, animation, secondaryAnimation) => WillPopScope(
+ onWillPop: () async => canCancel && onCancelled == null,
+ child: ScreenLock.create(
+ onConfirmed: onConfirmed,
+ onOpened: onOpened,
+ onValidate: onValidate,
+ onCancelled:
+ canCancel ? onCancelled ?? Navigator.of(context).pop : null,
+ onError: onError,
+ onMaxRetries: onMaxRetries,
+ maxRetries: maxRetries,
+ digits: digits,
+ retryDelay: retryDelay,
title: title,
confirmTitle: confirmTitle,
+ screenLockConfig: screenLockConfig,
+ secretsConfig: secretsConfig,
+ keyPadConfig: keyPadConfig,
+ delayBuilder: delayBuilder,
+ customizedButtonChild: customizedButtonChild,
+ customizedButtonTap: customizedButtonTap,
+ footer: footer,
+ cancelButton: cancelButton,
+ deleteButton: deleteButton,
inputController: inputController,
- withBlur: withBlur,
secretsBuilder: secretsBuilder,
+ useBlur: useBlur,
useLandscape: useLandscape,
- onValidate: onValidate,
),
),
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
diff --git a/lib/src/input_controller.dart b/lib/src/input_controller.dart
index c4383f4..d8aa287 100644
--- a/lib/src/input_controller.dart
+++ b/lib/src/input_controller.dart
@@ -7,8 +7,7 @@ class InputController {
InputController();
late int _digits;
- late String _correctString;
- late bool _isConfirmed;
+ late String? _correctString;
late ValidationCallback? _validationCallback;
final List _currentInputs = [];
@@ -43,7 +42,7 @@ class InputController {
return;
}
- if (_isConfirmed && _firstInput.isEmpty) {
+ if (_correctString == null && _firstInput.isEmpty) {
setConfirmed();
clear();
} else {
@@ -87,10 +86,10 @@ class InputController {
final inputText = _currentInputs.join();
late String correctString;
- if (_isConfirmed) {
- correctString = _firstInput;
+ if (_correctString != null) {
+ correctString = _correctString!;
} else {
- correctString = _correctString;
+ correctString = _firstInput;
}
if (_validationCallback == null) {
@@ -110,18 +109,17 @@ class InputController {
}
/// Create each stream.
- void initialize(
- {required int digits,
- required String correctString,
- bool isConfirmed = false,
- ValidationCallback? onValidate}) {
+ void initialize({
+ required int digits,
+ required String? correctString,
+ ValidationCallback? onValidate,
+ }) {
_inputValueNotifier = ValueNotifier('');
_verifyController = StreamController.broadcast();
_confirmedController = StreamController.broadcast();
_digits = digits;
_correctString = correctString;
- _isConfirmed = isConfirmed;
_validationCallback = onValidate;
}
diff --git a/lib/src/layout/key_pad.dart b/lib/src/layout/key_pad.dart
index bff04df..e9b6122 100644
--- a/lib/src/layout/key_pad.dart
+++ b/lib/src/layout/key_pad.dart
@@ -1,15 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/src/configurations/key_pad_config.dart';
-import 'package:flutter_screen_lock/src/layout/styled_input_button.dart';
+import 'package:flutter_screen_lock/src/layout/key_pad_button.dart';
import 'package:flutter_screen_lock/src/input_controller.dart';
-/// In order to arrange the buttons neatly by their size,
-/// I dared to adjust them without using GridView or Wrap.
-/// If you use GridView, you have to specify the overall width to adjust the size of the button,
-/// which makes it difficult to specify the size intuitively.
+/// [GridView] or [Wrap] make it difficult to specify the item size intuitively.
+/// We therefore arrange them manually with [Column]s and [Row]s
class KeyPad extends StatelessWidget {
const KeyPad({
- Key? key,
+ super.key,
required this.inputState,
required this.didCancelled,
this.enabled = true,
@@ -18,8 +16,7 @@ class KeyPad extends StatelessWidget {
this.customizedButtonTap,
this.deleteButton,
this.cancelButton,
- }) : keyPadConfig = keyPadConfig ?? const KeyPadConfig(),
- super(key: key);
+ }) : keyPadConfig = keyPadConfig ?? const KeyPadConfig();
final InputController inputState;
final VoidCallback? didCancelled;
@@ -31,7 +28,7 @@ class KeyPad extends StatelessWidget {
final Widget? cancelButton;
Widget _buildDeleteButton() {
- return StyledInputButton.transparent(
+ return KeyPadButton.transparent(
onPressed: () => inputState.removeCharacter(),
onLongPress:
keyPadConfig.clearOnLongPressed ? () => inputState.clear() : null,
@@ -45,24 +42,22 @@ class KeyPad extends StatelessWidget {
return _buildHiddenButton();
}
- return StyledInputButton.transparent(
+ return KeyPadButton.transparent(
onPressed: didCancelled,
config: keyPadConfig.buttonConfig,
child: cancelButton ??
- const FittedBox(
- child: Text(
- 'Cancel',
- style: TextStyle(
- fontSize: 16,
- ),
- softWrap: false,
+ const Text(
+ 'Cancel',
+ style: TextStyle(
+ fontSize: 16,
),
+ softWrap: false,
),
);
}
Widget _buildHiddenButton() {
- return StyledInputButton.transparent(
+ return KeyPadButton.transparent(
onPressed: () {},
config: keyPadConfig.buttonConfig,
);
@@ -86,7 +81,7 @@ class KeyPad extends StatelessWidget {
return _buildHiddenButton();
}
- return StyledInputButton.transparent(
+ return KeyPadButton.transparent(
onPressed: customizedButtonTap!,
config: keyPadConfig.buttonConfig,
child: customizedButtonChild!,
@@ -101,7 +96,7 @@ class KeyPad extends StatelessWidget {
final input = keyPadConfig.inputStrings[number];
final display = keyPadConfig.displayStrings[number];
- return StyledInputButton(
+ return KeyPadButton(
config: keyPadConfig.buttonConfig,
onPressed: enabled ? () => inputState.addCharacter(input) : null,
child: Text(display),
@@ -119,7 +114,7 @@ class KeyPad extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildLeftSideButton(),
- StyledInputButton(
+ KeyPadButton(
config: keyPadConfig.buttonConfig,
onPressed: enabled ? () => inputState.addCharacter(input) : null,
child: Text(display),
diff --git a/lib/src/layout/key_pad_button.dart b/lib/src/layout/key_pad_button.dart
new file mode 100644
index 0000000..e4fb9c6
--- /dev/null
+++ b/lib/src/layout/key_pad_button.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screen_lock/src/configurations/key_pad_button_config.dart';
+
+/// Button in a [KeyPad].
+class KeyPadButton extends StatelessWidget {
+ const KeyPadButton({
+ super.key,
+ this.child,
+ required this.onPressed,
+ this.onLongPress,
+ KeyPadButtonConfig? config,
+ }) : config = config ?? const KeyPadButtonConfig();
+
+ factory KeyPadButton.transparent({
+ Key? key,
+ Widget? child,
+ required VoidCallback? onPressed,
+ VoidCallback? onLongPress,
+ KeyPadButtonConfig? config,
+ }) =>
+ KeyPadButton(
+ key: key,
+ onPressed: onPressed,
+ onLongPress: onLongPress,
+ config: KeyPadButtonConfig(
+ size: config?.size,
+ fontSize: config?.fontSize,
+ foregroundColor: config?.foregroundColor,
+ backgroundColor: Colors.transparent,
+ buttonStyle: config?.buttonStyle?.copyWith(
+ backgroundColor:
+ MaterialStateProperty.all(Colors.transparent),
+ ),
+ ),
+ child: child,
+ );
+
+ final Widget? child;
+ final VoidCallback? onPressed;
+ final VoidCallback? onLongPress;
+ final KeyPadButtonConfig config;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: config.size,
+ width: config.size,
+ margin: const EdgeInsets.all(10),
+ child: OutlinedButton(
+ onPressed: onPressed,
+ onLongPress: onLongPress,
+ style: config.toButtonStyle(),
+ child: child ?? const SizedBox.shrink(),
+ ),
+ );
+ }
+}
diff --git a/lib/src/layout/secrets.dart b/lib/src/layout/secrets.dart
index 3810dd2..30658a6 100644
--- a/lib/src/layout/secrets.dart
+++ b/lib/src/layout/secrets.dart
@@ -7,12 +7,13 @@ import 'package:flutter_screen_lock/src/configurations/secrets_config.dart';
class SecretsWithShakingAnimation extends StatefulWidget {
const SecretsWithShakingAnimation({
- Key? key,
+ super.key,
required this.config,
required this.length,
required this.input,
required this.verifyStream,
- }) : super(key: key);
+ });
+
final SecretsConfig config;
final int length;
final ValueListenable input;
@@ -81,11 +82,11 @@ class _SecretsWithShakingAnimationState
class Secrets extends StatefulWidget {
const Secrets({
- Key? key,
- this.config = const SecretsConfig(),
+ super.key,
+ SecretsConfig? config,
required this.input,
required this.length,
- }) : super(key: key);
+ }) : config = config ?? const SecretsConfig();
final SecretsConfig config;
final ValueListenable input;
@@ -96,66 +97,54 @@ class Secrets extends StatefulWidget {
}
class _SecretsState extends State with SingleTickerProviderStateMixin {
- double _computeSpacing(BuildContext context) {
- if (widget.config.spacing != null) {
- return widget.config.spacing!;
- }
-
- return MediaQuery.of(context).size.width * widget.config.spacingRatio;
- }
-
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: widget.input,
- builder: (context, value, child) {
- return Container(
- padding: widget.config.padding,
- child: Wrap(
- spacing: _computeSpacing(context),
- children: List.generate(
- widget.length,
- (index) {
- if (value.isEmpty) {
- return Secret(
- config: widget.config.secretConfig,
- enabled: false,
- );
- }
-
+ builder: (context, value, child) => Padding(
+ padding: widget.config.padding,
+ child: Wrap(
+ spacing: widget.config.spacing,
+ children: List.generate(
+ widget.length,
+ (index) {
+ if (value.isEmpty) {
return Secret(
config: widget.config.secretConfig,
- enabled: index < value.length,
+ enabled: false,
);
- },
- growable: false,
- ),
+ }
+
+ return Secret(
+ config: widget.config.secretConfig,
+ enabled: index < value.length,
+ );
+ },
+ growable: false,
),
- );
- },
+ ),
+ ),
);
}
}
class Secret extends StatelessWidget {
const Secret({
- Key? key,
+ super.key,
+ SecretConfig? config,
this.enabled = false,
- this.config = const SecretConfig(),
- }) : super(key: key);
-
- final bool enabled;
+ }) : config = config ?? const SecretConfig();
final SecretConfig config;
+ final bool enabled;
@override
Widget build(BuildContext context) {
- if (config.build != null) {
- // Custom build.
- return config.build!(
+ if (config.builder != null) {
+ return config.builder!(
context,
- config: config,
- enabled: enabled,
+ config,
+ enabled,
);
}
@@ -168,8 +157,8 @@ class Secret extends StatelessWidget {
color: config.borderColor,
),
),
- width: config.width,
- height: config.height,
+ width: config.size,
+ height: config.size,
);
}
}
diff --git a/lib/src/layout/styled_input_button.dart b/lib/src/layout/styled_input_button.dart
deleted file mode 100644
index 9f77198..0000000
--- a/lib/src/layout/styled_input_button.dart
+++ /dev/null
@@ -1,105 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_screen_lock/src/configurations/styled_input_button_config.dart';
-
-/// [OutlinedButton] based button.
-class StyledInputButton extends StatelessWidget {
- const StyledInputButton({
- Key? key,
- this.child,
- required this.onPressed,
- this.onLongPress,
- StyledInputConfig? config,
- }) : config = config ?? const StyledInputConfig(),
- super(key: key);
-
- factory StyledInputButton.transparent({
- Key? key,
- Widget? child,
- required VoidCallback? onPressed,
- VoidCallback? onLongPress,
- StyledInputConfig? config,
- }) =>
- StyledInputButton(
- key: key,
- onPressed: onPressed,
- onLongPress: onLongPress,
- config: StyledInputConfig(
- height: config?.height,
- width: config?.width,
- autoSize: config?.autoSize ?? true,
- buttonStyle:
- (config?.buttonStyle ?? OutlinedButton.styleFrom()).copyWith(
- backgroundColor:
- MaterialStateProperty.all(Colors.transparent),
- ),
- ),
- child: child,
- );
-
- final Widget? child;
- final VoidCallback? onPressed;
- final VoidCallback? onLongPress;
- final StyledInputConfig config;
-
- double computeHeight(Size boxSize) {
- if (config.autoSize) {
- return _computeAutoSize(boxSize);
- }
-
- return boxSize.height;
- }
-
- double computeWidth(Size boxSize) {
- if (config.autoSize) {
- return _computeAutoSize(boxSize);
- }
-
- return boxSize.width;
- }
-
- Size defaultSize(BuildContext context) {
- if (MediaQuery.of(context).orientation == Orientation.landscape) {
- return Size(
- config.height ?? MediaQuery.of(context).size.height * 0.125,
-
- /// Subtract padding(horizontal: 50) from screen_lock.dart to calculate
- config.width ?? (MediaQuery.of(context).size.width - 100) * 0.14,
- );
- }
-
- return Size(
- config.height ?? MediaQuery.of(context).size.height * 0.6 * 0.16,
-
- /// Subtract padding(horizontal: 50) from screen_lock.dart to calculate
- config.width ?? (MediaQuery.of(context).size.width - 100) * 0.22,
- );
- }
-
- double _computeAutoSize(Size size) {
- return size.width < size.height ? size.width : size.height;
- }
-
- ButtonStyle _makeButtonStyle(BuildContext context) {
- return (config.buttonStyle ?? OutlinedButton.styleFrom()).copyWith(
- textStyle: MaterialStateProperty.all(
- config.textStyle ?? StyledInputConfig.getDefaultTextStyle(context),
- ),
- );
- }
-
- @override
- Widget build(BuildContext context) {
- final boxSize = defaultSize(context);
- return Container(
- height: computeHeight(boxSize),
- width: computeWidth(boxSize),
- margin: const EdgeInsets.all(10),
- child: OutlinedButton(
- onPressed: onPressed,
- onLongPress: onLongPress,
- style: _makeButtonStyle(context),
- child: child ?? const Text(''),
- ),
- );
- }
-}
diff --git a/lib/src/screen_lock.dart b/lib/src/screen_lock.dart
index aa7c40a..172aad1 100644
--- a/lib/src/screen_lock.dart
+++ b/lib/src/screen_lock.dart
@@ -6,97 +6,116 @@ import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
import 'package:flutter_screen_lock/src/layout/key_pad.dart';
-typedef DelayBuilderCallback = Widget Function(
- BuildContext context, Duration delay);
-
-typedef SecretsBuilderCallback = Widget Function(
- BuildContext context,
- SecretsConfig config,
- int length,
- ValueListenable input,
- Stream verifyStream,
-);
-
-typedef ValidationCallback = Future Function(
- String input,
-);
-
class ScreenLock extends StatefulWidget {
+ /// Animated ScreenLock
const ScreenLock({
- Key? key,
- required this.correctString,
- required this.didUnlocked,
- this.didOpened,
- this.didCancelled,
- this.didConfirmed,
- this.didError,
- this.didMaxRetries,
+ super.key,
+ required String this.correctString,
+ required VoidCallback this.onUnlocked,
+ this.onOpened,
+ this.onValidate,
+ this.onCancelled,
+ this.onError,
+ this.onMaxRetries,
+ this.maxRetries = 0,
+ this.retryDelay = Duration.zero,
+ Widget? title,
+ this.screenLockConfig,
+ SecretsConfig? secretsConfig,
+ this.keyPadConfig,
+ this.delayBuilder,
+ this.customizedButtonChild,
this.customizedButtonTap,
- this.confirmation = false,
- this.digits = 4,
+ this.footer,
+ this.cancelButton,
+ this.deleteButton,
+ this.inputController,
+ this.secretsBuilder,
+ this.useBlur = true,
+ this.useLandscape = true,
+ }) : title = title ?? const Text('Please enter passcode.'),
+ confirmTitle = null,
+ digits = correctString.length,
+ onConfirmed = null,
+ secretsConfig = secretsConfig ?? const SecretsConfig(),
+ assert(maxRetries > -1, 'max retries cannot be less than 0'),
+ assert(correctString.length > 0, 'correct string cannot be empty');
+
+ /// Animated ScreenLock
+ const ScreenLock.create({
+ super.key,
+ required ValueChanged this.onConfirmed,
+ this.onOpened,
+ this.onValidate,
+ this.onCancelled,
+ this.onError,
+ this.onMaxRetries,
this.maxRetries = 0,
+ this.digits = 4,
this.retryDelay = Duration.zero,
Widget? title,
Widget? confirmTitle,
- ScreenLockConfig? screenLockConfig,
+ this.screenLockConfig,
SecretsConfig? secretsConfig,
this.keyPadConfig,
this.delayBuilder,
this.customizedButtonChild,
+ this.customizedButtonTap,
this.footer,
this.cancelButton,
this.deleteButton,
this.inputController,
- this.withBlur = true,
this.secretsBuilder,
+ this.useBlur = true,
this.useLandscape = true,
- this.onValidate,
- }) : title = title ?? const Text('Please enter passcode.'),
+ }) : correctString = null,
+ title = title ?? const Text('Please enter new passcode.'),
confirmTitle =
- confirmTitle ?? const Text('Please enter confirm passcode.'),
- screenLockConfig = screenLockConfig ?? const ScreenLockConfig(),
+ confirmTitle ?? const Text('Please confirm new passcode.'),
+ onUnlocked = null,
secretsConfig = secretsConfig ?? const SecretsConfig(),
- assert(maxRetries > -1),
- super(key: key);
+ assert(maxRetries > -1);
/// Input correct string.
- final String correctString;
+ final String? correctString;
/// Called if the value matches the correctString.
- final VoidCallback didUnlocked;
+ final VoidCallback? onUnlocked;
+
+ /// Callback to validate input values filled in [digits].
+ ///
+ /// If `true` is returned, the lock is unlocked.
+ final ValidationCallback? onValidate;
/// Called when the screen is shown the first time.
///
/// Useful if you want to show biometric authentication.
- final VoidCallback? didOpened;
+ final VoidCallback? onOpened;
/// Called when the user cancels.
///
/// If null, the user cannot cancel.
- final VoidCallback? didCancelled;
+ final VoidCallback? onCancelled;
/// Called when the first and second inputs match during confirmation.
- final void Function(String matchedText)? didConfirmed;
+ final ValueChanged? onConfirmed;
/// Called if the value does not match the correctString.
- final void Function(int retries)? didError;
+ final ValueChanged? onError;
/// Events that have reached the maximum number of attempts.
- final void Function(int retries)? didMaxRetries;
+ final ValueChanged? onMaxRetries;
/// Tapped for left side lower button.
final VoidCallback? customizedButtonTap;
- /// Make sure the first and second inputs are the same.
- final bool confirmation;
-
- /// Set the maximum number of characters to enter when confirmation is true.
- final int digits;
-
/// `0` is unlimited.
/// For example, if it is set to 1, didMaxRetries will be called on the first failure.
final int maxRetries;
+ /// Set the maximum number of characters to enter when confirmation is true.
+ final int digits;
+
/// Delay until we can retry.
///
/// Duration.zero is no delay.
@@ -106,10 +125,10 @@ class ScreenLock extends StatefulWidget {
final Widget title;
/// Heading confirm title for ScreenLock.
- final Widget confirmTitle;
+ final Widget? confirmTitle;
/// Configurations of [ScreenLock].
- final ScreenLockConfig screenLockConfig;
+ final ScreenLockConfig? screenLockConfig;
/// Configurations of [Secrets].
final SecretsConfig secretsConfig;
@@ -135,40 +154,76 @@ class ScreenLock extends StatefulWidget {
/// Control inputs externally.
final InputController? inputController;
- /// Blur the background.
- final bool withBlur;
-
/// Custom secrets animation widget builder.
final SecretsBuilderCallback? secretsBuilder;
- /// Use a landscape orientation.
- final bool useLandscape;
+ /// Blur the background.
+ final bool useBlur;
- /// Callback to validate input values filled in [digits].
- ///
- /// If `true` is returned, the lock is unlocked.
- final ValidationCallback? onValidate;
+ /// Use a landscape orientation when sufficient width is available.
+ final bool useLandscape;
@override
State createState() => _ScreenLockState();
}
class _ScreenLockState extends State {
- late InputController inputController;
+ late InputController inputController =
+ widget.inputController ?? InputController();
/// Logging retries.
int retries = 1;
- /// First input completed.
- bool firstInputCompleted = false;
-
- String firstInput = '';
-
final StreamController inputDelayController =
StreamController.broadcast();
bool inputDelayed = false;
+ @override
+ void initState() {
+ super.initState();
+ inputController.initialize(
+ correctString: widget.correctString,
+ digits: widget.digits,
+ onValidate: widget.onValidate,
+ );
+
+ inputController.verifyInput.listen((success) {
+ // Wait for the animation on failure.
+ Future.delayed(const Duration(milliseconds: 300), () {
+ inputController.clear();
+ });
+
+ if (success) {
+ if (widget.correctString != null) {
+ widget.onUnlocked!();
+ } else {
+ widget.onConfirmed!(inputController.confirmedInput);
+ }
+ } else {
+ error();
+ }
+ });
+
+ WidgetsBinding.instance
+ .addPostFrameCallback((_) => widget.onOpened?.call());
+ }
+
+ @override
+ void didUpdateWidget(covariant ScreenLock oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (oldWidget.inputController != widget.inputController) {
+ inputController.dispose();
+ inputController = widget.inputController ?? InputController();
+ }
+ }
+
+ @override
+ void dispose() {
+ inputController.dispose();
+ super.dispose();
+ }
+
void inputDelay() {
if (widget.retryDelay == (Duration.zero)) {
return;
@@ -201,10 +256,10 @@ class _ScreenLockState extends State {
}
void error() {
- widget.didError?.call(retries);
+ widget.onError?.call(retries);
if (widget.maxRetries >= 1 && widget.maxRetries <= retries) {
- widget.didMaxRetries?.call(retries);
+ widget.onMaxRetries?.call(retries);
// reset retries
retries = 0;
@@ -215,7 +270,7 @@ class _ScreenLockState extends State {
retries++;
}
- Widget makeDelayBuilder(Duration duration) {
+ Widget buildDelayChild(Duration duration) {
if (widget.delayBuilder != null) {
return widget.delayBuilder!(context, duration);
} else {
@@ -227,15 +282,11 @@ class _ScreenLockState extends State {
Widget buildHeadingText() {
Widget buildConfirmed(Widget child) {
- if (widget.confirmation) {
+ if (widget.correctString == null) {
return StreamBuilder(
stream: inputController.confirmed,
- builder: (context, snapshot) {
- if (snapshot.hasData && snapshot.data!) {
- return widget.confirmTitle;
- }
- return child;
- },
+ builder: (context, snapshot) =>
+ snapshot.data == true ? widget.confirmTitle! : child,
);
}
return child;
@@ -247,7 +298,7 @@ class _ScreenLockState extends State {
stream: inputDelayController.stream,
builder: (context, snapshot) {
if (inputDelayed && snapshot.hasData) {
- return makeDelayBuilder(snapshot.data!);
+ return buildDelayChild(snapshot.data!);
}
return child;
},
@@ -269,86 +320,31 @@ class _ScreenLockState extends State {
);
}
- ThemeData makeThemeData() {
- return widget.screenLockConfig.themeData ??
- ScreenLockConfig.defaultThemeData;
- }
-
- @override
- void initState() {
- super.initState();
- inputController = widget.inputController ?? InputController();
- inputController.initialize(
- correctString: widget.correctString,
- digits: widget.digits,
- isConfirmed: widget.confirmation,
- onValidate: widget.onValidate,
- );
-
- inputController.verifyInput.listen((success) {
- // Wait for the animation on failure.
- Future.delayed(const Duration(milliseconds: 300), () {
- inputController.clear();
- });
-
- if (success) {
- if (widget.confirmation) {
- widget.didConfirmed?.call(inputController.confirmedInput);
- } else {
- widget.didUnlocked();
- }
- } else {
- error();
- }
- });
-
- WidgetsBinding.instance
- .addPostFrameCallback((_) => widget.didOpened?.call());
- }
-
- @override
- void dispose() {
- inputController.dispose();
- super.dispose();
- }
-
@override
Widget build(BuildContext context) {
- late final int secretLength;
-
- if (widget.confirmation) {
- secretLength = widget.digits;
- } else {
- secretLength = widget.correctString.isNotEmpty
- ? widget.correctString.length
- : widget.digits;
- }
-
final orientations = {
Orientation.portrait: Axis.vertical,
+ Orientation.landscape:
+ widget.useLandscape ? Axis.horizontal : Axis.vertical,
};
- if (widget.useLandscape) {
- orientations[Orientation.landscape] = Axis.horizontal;
- } else {
- orientations[Orientation.landscape] = Axis.vertical;
- }
-
Widget buildSecrets() {
- return widget.secretsBuilder == null
- ? SecretsWithShakingAnimation(
- config: widget.secretsConfig,
- length: secretLength,
- input: inputController.currentInput,
- verifyStream: inputController.verifyInput,
- )
- : widget.secretsBuilder!(
- context,
- widget.secretsConfig,
- secretLength,
- inputController.currentInput,
- inputController.verifyInput,
- );
+ if (widget.secretsBuilder != null) {
+ return widget.secretsBuilder!(
+ context,
+ widget.secretsConfig,
+ widget.digits,
+ inputController.currentInput,
+ inputController.verifyInput,
+ );
+ } else {
+ return SecretsWithShakingAnimation(
+ config: widget.secretsConfig,
+ length: widget.digits,
+ input: inputController.currentInput,
+ verifyStream: inputController.verifyInput,
+ );
+ }
}
Widget buildKeyPad() {
@@ -357,7 +353,7 @@ class _ScreenLockState extends State {
enabled: !inputDelayed,
keyPadConfig: widget.keyPadConfig,
inputState: inputController,
- didCancelled: widget.didCancelled,
+ didCancelled: widget.onCancelled,
customizedButtonTap: widget.customizedButtonTap,
customizedButtonChild: widget.customizedButtonChild,
deleteButton: widget.deleteButton,
@@ -391,21 +387,40 @@ class _ScreenLockState extends State {
);
}
- Widget buildContentWithBlur() {
- return BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 3.5, sigmaY: 3.5),
- child: buildContent(),
- );
+ Widget buildContentWithBlur({required bool useBlur}) {
+ Widget child = buildContent();
+ if (useBlur) {
+ return BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 3.5, sigmaY: 3.5),
+ child: child,
+ );
+ }
+ return child;
}
return Theme(
- data: makeThemeData(),
+ data: (widget.screenLockConfig ?? ScreenLockConfig.defaultConfig)
+ .toThemeData(),
child: Scaffold(
- backgroundColor: widget.screenLockConfig.backgroundColor,
body: SafeArea(
- child: widget.withBlur ? buildContentWithBlur() : buildContent(),
+ child: buildContentWithBlur(useBlur: widget.useBlur),
),
),
);
}
}
+
+typedef DelayBuilderCallback = Widget Function(
+ BuildContext context, Duration delay);
+
+typedef SecretsBuilderCallback = Widget Function(
+ BuildContext context,
+ SecretsConfig config,
+ int length,
+ ValueListenable input,
+ Stream verifyStream,
+);
+
+typedef ValidationCallback = Future Function(
+ String input,
+);
diff --git a/pubspec.lock b/pubspec.lock
index 0916f91..aaff18a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -21,7 +21,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
- version: "2.8.2"
+ version: "2.9.0"
boolean_selector:
dependency: transitive
description:
@@ -35,21 +35,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
- charcode:
- dependency: transitive
- description:
- name: charcode
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.3.1"
+ version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0"
+ version: "1.1.1"
collection:
dependency: transitive
description:
@@ -77,7 +70,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
- version: "1.3.0"
+ version: "1.3.1"
file:
dependency: transitive
description:
@@ -122,21 +115,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.11"
+ version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
- version: "0.1.4"
+ version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.7.0"
+ version: "1.8.0"
package_config:
dependency: transitive
description:
@@ -150,7 +143,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.1"
+ version: "1.8.2"
pub_semver:
dependency: transitive
description:
@@ -169,7 +162,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.2"
+ version: "1.9.0"
stack_trace:
dependency: transitive
description:
@@ -190,21 +183,21 @@ packages:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0"
+ version: "1.1.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.4.9"
+ version: "0.4.12"
typed_data:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index f742192..6db4a6c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: flutter_screen_lock
description: Provides the ability to lock the screen on ios and android. Biometric authentication can be used in addition to passcode.
homepage: https://github.com/naoki0719/flutter_screen_lock
-version: 7.0.4
+version: 8.0.0
environment:
sdk: ">=2.17.0-0 <3.0.0"
diff --git a/test/input_state_test.dart b/test/input_state_test.dart
index 38003ad..fd5e192 100644
--- a/test/input_state_test.dart
+++ b/test/input_state_test.dart
@@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
test('input stream test', () {
final state = InputController();
- state.initialize(digits: 4, correctString: '1234', isConfirmed: false);
+ state.initialize(digits: 4, correctString: '1234');
expectLater(
state.currentInput,
@@ -33,7 +33,7 @@ void main() {
test('input verify', () {
final state = InputController();
- state.initialize(digits: 4, correctString: '1234', isConfirmed: false);
+ state.initialize(digits: 4, correctString: '1234');
expectLater(state.verifyInput, emitsInOrder([true]));
@@ -45,7 +45,7 @@ void main() {
test('input verify as failed', () {
final state = InputController();
- state.initialize(digits: 4, correctString: '1234', isConfirmed: false);
+ state.initialize(digits: 4, correctString: '1234');
expectLater(state.verifyInput, emitsInOrder([false]));