システム開発部のTです。
普段はAndroid、iOSなどのネイティブアプリ開発やフロントエンドアプリ開発に携わっています。Flutterで久々に面白いプラグインが出てきたので、試しに使ってみようと思いました。今回はデバイス認証です。
local_authプラグイン
今回はlocal_authというFlutter公式で出しているプラグインです。
デバイスの認証をアプリで取り扱うことが可能になるプラグインになります。
普段、スマホのホーム画面を表示するときに、画面ロックを設定しているとパスワード認証、PIN認証、生体認証・・・などなどご自身のデバイスを守っていると思われますが、それらの認証処理をアプリ側で利用できるようにしたのが、本プラグインになります。
この記事を書いている時点では最新バージョン2.1.0となっております。
対応プラットフォームはAndroid、iOS、Windowsです。
詳細は、以下のページを見てください。
https://pub.dev/packages/local_auth
早速、準備、実装に進みます。
なお、開発環境はAndroidStudioの最新版(v2021.2.1)、Flutter3(v3.0.3)にて開発していきます。
準備
プロジェクト生成
このへんは特に気にするところはなく、普通に作っていきます。
最後に「Finish」ボタンを押下してください。

とりあえずプロジェクト生成後の画面。

local_authプラグインのインストール
プラグインのページまで遷移してください。
https://pub.dev/packages/local_auth
赤丸のところにカーソルを合わせると、クリップボードにプラグイン内容がコピーされます。

pubspec.yamlのdependenciesに上記でコピーした内容をペーストしてください。
必須ではありませんが、認証時にToastで表示したいので、fluttertoastも追加しています。
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
local_auth: ^2.1.0 # ←ここにペースト
fluttertoast: ^8.0.9 # ←認証時にToast表示したいので追加、必須ではありません。
dev_dependencies:
「Pub get」を押下することで、プラグインが反映されます。

ここから、更にAndroid側、iOS側の設定が必要になります。
Android側の設定
プロジェクトフォルダ「android」にカーソルを合わせてメニューを表示します。
「Flutter→Open Android module in Android Studio」の順で画面を起動すると、Androidのプロジェクトを開くことができます。

以下のようにAndroid側のプロジェクトが起動します。

これ以降の設定は、このAndroid側のプロジェクトから行います。
MainActivityの編集
MainActivityを開いてください。

コードを以下のように変更します。
package com.example.sample_local_auth
//import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity
//class MainActivity: FlutterActivity() {
class MainActivity: FlutterFragmentActivity() { // <- FlutterFragmentActivityに置き換え
}
AndroidManifest.xmlの編集
AndroidManifest.xmlを開いてください。

「android.permission.USE_FINGERPRINT」を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sample_local_auth">
<uses-permission android:name="android.permission.USE_FINGERPRINT"/> # ←これを追加
<application
android:label="sample_local_auth"
android:name="${applicationName}"
注)「android.permission.USE_FINGERPRINT」についてはAPIレベル28でDeprecatedとなっております。本件では一旦公式のとおりに設定することをご了承ください。
Android側の設定は以上になります。
つづけてiOS側の設定を行います。
iOS側の設定
Flutterのプロジェクトに戻り、プロジェクトフォルダ「iOS」にカーソルを合わせてメニューを表示。
「Flutter→Open iOS module in Xcode」の順で画面を起動すると、iOSのプロジェクトを開くことができます。

以下のようにXcodeが起動します。

Info.plistを開きます。

以下に設定を追加します。
今回はFaceIDをサポートする必要があるため、以下を追記しております。
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>NSFaceIDUsageDescription</key> <- これを追加
<string>Why is my app authenticating using face id?</string> <- これを追加
</dict>
</plist>
iOS側の設定は以上です。
Xcodeを閉じて、Flutterのプロジェクトに戻ってください。
Flutter側に認証処理の実装
この記事を見ている方は早く実装して機能を確認されたいかと思いますので、
ここでは最速で実装していきます。
認証メソッドを実装
main.dartの_MyHomePageStateに以下のメソッドを追加してください。
認証成功時、Toastで「認証成功!!!」、失敗時「認証失敗・・・」を表示、例外発生時は例外メッセージをToastで表示するようにします。
Future<bool> normalAuthenticate() async {
final LocalAuthentication auth = LocalAuthentication();
try {
final bool didAuthenticate = await auth.authenticate(localizedReason: 'Please authenticate to show account balance');
if (didAuthenticate) {
Fluttertoast.showToast(msg: '認証成功!!!');
} else {
Fluttertoast.showToast(msg: '認証失敗・・・');
}
return didAuthenticate;
} on PlatformException catch (e) {
Fluttertoast.showToast(msg: 'code: ${e.code} message: ${(e.message ?? e.details)}');
}
return false;
}
行っていることは簡単で、LocalAuthentication
のインスタンスを取得し、認証処理であるauthenticate
メソッドを実行しているだけです。返却値はbool
で認証成功時はtrue
が返却され、認証失敗時はfalse
が返却されます。例外はPlatformException
のみcatchするようにし、例外内容をToastで表示するようにしています。
認証メソッドのコール
上記で実装した認証メソッドは、インクリメントボタン押下時に実行するようにし、認証成功時は値をプラスさせるように実装します。
floatingActionButton: FloatingActionButton(
onPressed: () async {
var isSuccess = await normalAuthenticate();
if (isSuccess) {
_incrementCounter();
}
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
以上で実装完了です。
それでは実行してみましょう。
アプリを実行する
ここまで出来たら、アプリを実行してみます。
まずはiPhone(本件ではエミュレーター)で実行します。

「+」を押下

パスコードを入力
(エミュレータの場合、MACのパスワードを入力)

認証が通り、値がインクリメントされる
つづけてAndroidでの実行です。

「+」を押下

パターン認証を入力
(画面ロックをパターン認証設定時)

認証が通り、値がインクリメントされる
いかがでしょうか。
このように、短いコードでデバイスロックを簡単に利用することができることが、ご理解いただけたかと思います。
画面ロック未設定時の挙動について
ここまで読んでいただけた方は、もしかしたらもうお気づきかと思いますが、このlocal_authはOSの画面ロックの設定に依存しております。OSの画面ロックの設定がパターン設定であれば、local_authで呼び出す認証はパターン設定になります。今流行りの生体認証(指紋認証、顔認証)が設定されていれば、呼び出される認証は生体認証になります。OSの画面ロックで設定された以外の認証をアプリ側で呼び出すことはできないようです。また、local_authにもそういった設定は今のところなさそうです。local_authに生体認証のみの認証にするような設定はありますが、それは別の機会に記事にしようと思っております。
では、上記を踏まえて認証自体を設定しなかった場合の挙動です。
生体認証はおろか、パスワード認証もかけていなかった場合、本プラグインではどういった動きをするのか?ちょっとやってみましょう。

画面ロックを「None」
または「Swipe」に設定

「+」を押下

NotAvailableエラーを返却
上記の通り、PlatformException例外が発生し、必要なセキュリティが設定されていないというエラーが返却されました。本件では、画面ロック未設定時の挙動は考慮していなかったので、未設定時は認証未済扱いとして動作したのでした。
このへんの挙動を考慮するための実装もlocal_authでは可能ですが、別の記事で紹介します。
ソースコード
以下、サンプルコード一式になります。
pubspec.yaml
name: sample_local_auth
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.17.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
local_auth: ^2.1.0
fluttertoast: ^8.0.9
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';
import 'package:fluttertoast/fluttertoast.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Future<bool> normalAuthenticate() async {
final LocalAuthentication auth = LocalAuthentication();
try {
final bool didAuthenticate = await auth.authenticate(localizedReason: 'Please authenticate to show account balance');
if (didAuthenticate) {
Fluttertoast.showToast(msg: '認証成功!!!');
} else {
Fluttertoast.showToast(msg: '認証失敗・・・');
}
return didAuthenticate;
} on PlatformException catch (e) {
Fluttertoast.showToast(msg: 'code: ${e.code} message: ${(e.message ?? e.details)}');
}
return false;
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
var isSuccess = await normalAuthenticate();
if (isSuccess) {
_incrementCounter();
}
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
最後に
local_authというライブラリの名前の通り、デバイスの認証設定を呼び出して利用できるライブラリであることがご理解できたかと思います。気軽に利用できるのと、いろいろな場面で活用可能なので、ぜひ皆さんも試してみてはいかがでしょうか。