Reputation: 1500
When I start the app, it should check if it is possible to use biometric authentication (fingerprint/face id). I have a class that checks this and the login page need the result of it. I have the following code:
class LocalAuthenticationUtil with ChangeNotifier {
static LocalAuthentication _auth = LocalAuthentication();
static List<BiometricType> biometricTypes;
static bool haveBiometrics = true;
bool _biometricAuthenticated = true;
static LocalAuthenticationUtil _instance;
static LocalAuthenticationUtil getInstance() {
if (_instance == null) {
_instance = LocalAuthenticationUtil();
print("GetInstance CanCheckBiometrics before");
_instance._canCheckBiometrics();
print("GetInstance CanCheckBiometrics after");
if (haveBiometrics) {
_instance.addListener(() {
_instance.authenticate();
});
_instance.authenticate();
}
}
return _instance;
}
Future<void> _canCheckBiometrics() async {
print("CanCheckBiometrics before");
haveBiometrics = await _auth.canCheckBiometrics;
print("CanCheckBiometrics after");
if (haveBiometrics) {
biometricTypes = await _auth.getAvailableBiometrics();
}
}
set biometricAuthenticated(bool value) {
if (_biometricAuthenticated != value) {
_biometricAuthenticated = value;
notifyListeners();
}
}
When the code runs this is the result:
I/flutter (23495): GetInstance CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics before
I/flutter (23495): GetInstance CanCheckBiometrics after
I/flutter (23495): CanCheckBiometrics after
While the order that I want to happen is:
I/flutter (23495): GetInstance CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics after
I/flutter (23495): GetInstance CanCheckBiometrics after
Upvotes: 1
Views: 268
Reputation: 2193
cameron1024 is right. What you need to do is to create a StatefulWidget which will redirect the user once the check is completed.
class AuthWidget extends StatefulWidget {
AuthWidget({Key key}) : super(key: key);
@override
_AuthWidgetState createState() => _AuthWidgetState();
}
class _AuthWidgetState extends State<AuthWidget> {
@override
void initState() {
super.initState();
checkBiometrics(); // perform the check asynchronously and then use Navigator.of(context).push/replace
}
@override
Widget build(BuildContext context) {
// Display a loader while checking
return CircularProgressIndicator();
}
}
Upvotes: 0
Reputation: 10136
You're not await
ing _instance._canCheckBiometrics();
Dart executes synchonously until it hits an await
, at which point the function immediately returns, but "remembers where it was", so it can continue where it left off when the await
ed Future
completes:
Here, when you call _instance._canCheckBiometrics()
, it immediately runs the first print
statement, then hits the await _auth.canCheckBiometrics
and immediately returns a Future
representing the result of _instance._canCheckBiometrics()
.
Simply replace _instance._canCheckBiometrics()
with await _instance._canCheckBiometrics()
and it should work.
BTW, you can create an analysis_options.yaml
file to customise your linter warnings for your project. One in particular, called unawaited_futures
warns you when you have a Future
-returning function in an async context that doesn't have an await
. This is usually an error, but you can suppress it manually if you're certain. This rule often helps catch bugs like this.
To use the linter, check out: https://dart.dev/guides/language/analysis-options#enabling-linter-rules
Upvotes: 1