Flutter in-app lock screens
Flutter is getting more popular by the day, especially with the recent 1.12 announcement at Flutter Interact in Brooklyn, New York.
We’ve learnt that Flutter has grown to the #2 fastest growing open source project and is being used in loads of different sectors. Some of these sectors have sensitive information at the core of an app, and user’s data needs protecting at an app level - not just at a remote service level.
A lot of fin-tech apps such as Monzo and ClearScore using in-app lock screens for protecting the app from unauthorized users. These work by requiring the user to enter a password, passcode or use biometrics before being allowed on to the app from a cold launch, as well as when going away from the app and coming back.
In this post, we’ll look at showing lock screens when a user goes away from the app and comes back.
Flutter provides us with the lifecycle events we need to implement this without the need to manually hook in to lifecycle events using method channels. On a given State
, simply use the WidgetsBindingObserver
as a mixin
.
class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver { @override
Widget build(BuildContext context) {
...
}
}
Once you’ve added WidgetsBindingObserver
as a mixin
, you can now add this class as an observer on the WidgetsBinding
instance which provides the app lifecycle events. You do this in initState
and remove this class as an observer in the dispose
method:
@override
void initState() {
WidgetsBinding.instance.addObserver(this); super.initState();
}@override
void dispose() {
WidgetsBinding.instance.removeObserver(this); super.initState();
}
Once you’ve done this, you can now listen to app lifecycle change events using the didChangeAppLifecycleState
method the WidgetsBindingObserver
gives you.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
}
AppLifecycleState
is an enum and can be either resumed
, inactive
, paused
or detached
. The main one we need to worry about though is paused
. We need to keep track of whether the app has been paused or not so make a bool
property on your State
to keep track of this, otherwise multiple lock screens could be shown.
bool _isPaused = false;@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused && !_isPaused) {
_isPaused = true;
_showLockScreen(); // this should use the `Navigator` to push a new route
} super.didChangeAppLifecycleState(state);
}
The _showLockScreen()
method I mentioned in the code snippet above should use the Navigator
to push a route fir for your own implementation of a lock screen, which is a Widget
wrapped in a WillPopScope
widget to prevent the user from dismissing the lock screen using the Android back button or the iOS swipe gesture. Below is a simple example using a MaterialPageRoute
:
void _showLockScreen() async {
await Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
WillPopScope(
child: ...,
onWillPop: () => Future.value(false), // prevents the system from dismissing this route
)));
_isPaused = false;
}
The widget for the lock screen can still dismiss itself using Navigator.of(context).pop();
once a successful login has occurred.
And that’s it! This should be enough to help you show a lock screen when the app is no longer visible to the user.
“But Tom, didn’t you mentioned showing an in-app lock screen on launch?”
There are many different ways of deciding whether or not to show the lock screen on app launch which is why I didn’t choose to discuss them here, but I’ve written a Flutter package which provides a short and neat way of doing this, as well as handling subsequent app pauses!
Check it out here: https://pub.dev/packages/flutter_app_lock
Thanks for reading!
https://www.linkedin.com/in/tom-a/
https://github.com/tomalabaster