การใช้งาน Local Auth สำหรับยืนยันตัวตน ใน Flutter

บทความใหม่ ยังไม่ถึงปี โดย Ninenik Narkdee
flutter local auth authentication

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ flutter local_auth authentication

ดูแล้ว 427 ครั้ง


จากเนื้อหาตอนที่แล้ว ที่เราได้ทำความรู้จักกับกับแนวทางการ
ป้องกันหรือการล็อกแอปด้วยรูปแบบ PIN ที่เราสร้างขึ้นมาเอง
เพื่อให้เห็นแนวทางการทำงาน เกี่ยวกับรูปแบบการยืนยันหรือการ
พิสูจน์ยืนยันตัวตนของผู้ใช้งาน เนื้อหาต่อไปนี้ เราจะมาดูกันต่อโดย
จะเป็นการใช้งานปลั้กอินที่ชื่อว่า local auth ที่อาศัยความสามารถหรือ
คุณสมบัติของเครื่องนั้นๆ ถ้ามีมาใช้ในการตรวจสอบยืนยันตัวตน เพื่อเข้า
ใช้งานหรือจัดการในส่วนที่เรากำหนดได้ ซึ่งรองรับทั้ง การตรวจสอบลายนิ้วมือ, 
Touch ID, Face ID, (passcode) รหัสผ่าน, (pin) พิน หรือ (Pattern) รูปแบบ
 
ทบทวนตอนที่แล้วได้ที่บทความ
สรัางระบบล็อก App ด้วย PIN number ใน Flutter อย่างง่าย http://niik.in/1115
 
อย่างไรก็ดี ความสามารถนี้ จะเป็นแค่ตัวเลือกเท่านั้น เพราะเราไม่สามารถใช้งานได้กับทุก
อุปกรณ์ และจะใช้งานได้ผู้ใช้ต้องมีการกำหนดหรือตั้งค่าไว้เท่านั้น หากแอปของเราจำเป็นจะต้องมี
การตรวจสอบยืนยัน ก็ควรจะต้องมีรูปแบบเบื้องต้นที่รองรับทุกๆ อุปกรณ์ก่อน เช่น มีระบบสมาชิก
ต้องล็อคอิน หรือระบบ pin ที่กำหนดเองจากตัวอย่างตอนที่แล้ว แบบนี้เป็นต้น
 

ติดตั้ง local_auth เพื่อใช้งานการพิสูจน์ยืนยันตัวตน 

    ก่อนที่เราจะใช้งาน การพิสูจน์ยืนยันตัวด้วย  local authentication มีสิ่งที่ต้องต้องค่าต่างๆ
ถึงจะใช้งานได้ สามารถดูเพิ่มเติมที่ลิ้งค์ของตัวปลั้กอิน ในที่นี้จะแนะนำสำหรับ android สิ่งแรกเรา
ต้องติดตั้งตัว local_auth ในไฟล์ pubspec.yaml ดังนี้
 
local_auth: ^2.3.0
 
จากนั้นในไฟล์ android > app > src > main > AndroidManifest.xml
 
ให้เพิ่มส่วนนี้  USE_BIOMETRIC เข้าไป
 
1
2
3
4
5
6
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.USE_BIOMETRIC"/>
    <application ....
....
</manifest>
 
จากนั้นในไฟล์ android > app > src > main > kotlin > com > example > 
demo_app > MainActivity.kt  (com.example.demo_app ตรงนี้จะแตกต่างกัน
แล้วของผู้ใช้งาน ให้ดูแค่ในไฟล์ MainActivity.kt )
 
เดิมจะเป็นรูปแบบดังนี้
 
package com.example.demo_app

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity()
 
ให้เราเปลี่ยนเป็น
 
package com.example.demo_app

import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterFragmentActivity() {

}
 
ตอนนี้เราเตรียมพร้อมสำหรับใช้งาน local_auth เรียบร้อยแล้ว
 
 

แนวทางการใช้งาน Local_auth พิสูจน์ยืนยันตัวตน

    เนื้อหานี้เราจะต่อยอดจากบทความตอนที่แล้ว เราจะใช้ local_auth ยืนยันตัวตนแทนระบบ
PIN เดิมที่เราได้ทำไว้ หรือเป็นอีกตัวเลือกหนึ่ง เพื่อใช้สำหรับทำการล็อกแอปได้ โดยตัว local_auth
จะรองรับรูปแบบข้อมูลชีวภาพ 4 รายการ ดังนี้คือ
 
BiometricType.face:
    หมายถึงการยืนยันตัวตนด้วยการสแกนใบหน้า (Face Recognition)
    ระบบจะใช้การตรวจสอบคุณสมบัติใบหน้าเพื่อล็อกอินหรือยืนยันตัวตน
BiometricType.fingerprint:
    หมายถึงการยืนยันตัวตนด้วยการสแกนลายนิ้วมือ (Fingerprint)
    ระบบจะใช้การสแกนลายนิ้วมือในการยืนยันตัวตน
BiometricType.weak:
    หมายถึงการยืนยันตัวตนด้วยวิธีที่มีระดับความปลอดภัยต่ำ เช่น Face Unlock ที่ใช้แค่การจับ
    คู่รูปร่างใบหน้าพื้นฐาน
    ระบบนี้อาจจะสามารถหลอกได้ง่าย เช่น การใช้รูปถ่ายแทน
BiometricType.strong:
    หมายถึงการยืนยันตัวตนด้วยวิธีที่มีระดับความปลอดภัยสูง เช่น Face ID หรือระบบสแกนลาย
    นิ้วมือที่แม่นยำและยากที่จะถูกหลอก
    ระบบนี้มีการป้องกันที่แข็งแกร่งกว่าเพื่อป้องกันการปลอมแปลง
 
โดยที่ BiometricType.face และ BiometricType.fingerprint เป็นการระบุประเภทที่ชัดเจน
ของไบโอเมตริกซ์ ส่วน BiometricType.weak และ BiometricType.strong จะบ่งบอกถึงระดับ
ความปลอดภัยของการยืนยันตัวตนทางไบโอเมตริกซ์แต่ละประเภท
 
ดังนั้นเมื่อเรามีการใช้งาน หากขึ้นรองรับอย่างใดอย่างหนึ่ง ก็จะสามารถใช้งานปลั้กอินตัวนี้ได้ ในตัวอย่าง
ที่ผู้เขียนบทความนำมาใช้ทดสอบแสดงรายการที่รองรับ จะมี  
 
[BiometricType.weak, BiometricType.strong]
 
ทั้งนี้เครื่องทดสอบ รองรับ Face Unlock สแกนลายนิ้วมือ และการใช้ pattern
ดูโค้ดตัวอย่างในหน้าตั้งค่า คำอธิบายแสดงในโค้ด
 

ไฟล์ settings.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
import 'dart:async';
 
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:local_auth/local_auth.dart';
import 'package:flutter/services.dart';
 
import 'pincode.dart';
 
enum _SupportState {
  unknown,
  supported,
  unsupported,
}
 
class Settings extends StatefulWidget {
  static const routeName = '/settings';
 
  const Settings({Key? key}) : super(key: key);
 
  @override
  State<StatefulWidget> createState() {
    return _SettingsState();
  }
}
 
class _SettingsState extends State<Settings> {
  late final SharedPreferences prefs;
  bool _biometricstatus = false; // สถานะเปิดใช้ biometric
 
  // ส่วนจัดการ local auth
  final LocalAuthentication auth = LocalAuthentication(); // เรียกใช้งาน
  _SupportState _supportState = _SupportState.unknown; // สถานะการรองรับ
  bool? _canCheckBiometrics; // ตรวจสอบรองรับยืนยันด้วยข้อมูลชีวภาพได้ไหม
  List<BiometricType>? _availableBiometrics; // รายการยืนยันตัวตนที่รองรับ
  bool _isAuthenticating = false; // เก็บสถานะกำลังยืนยันตัวตน
 
  bool _pincodestatus = false; // สถานะเปิดใช้ pin
  bool _authorized = false; // สถานะเข้าใช้งาน
  String _pincodevalue = ''; // ค่า pin ในที่นี้ใช้ 4 ตัวเลข
  bool _isLoadingPrefs = true; // สถานะการโหลดค่าจาก SharedPreferences
 
  @override
  void initState() {
    super.initState();
    // โหลดค่าจาก SharedPreferences
    _loadValueFromSharedPreferences();
    auth.isDeviceSupported().then((bool isSupported) {
      setState(() {
        if (isSupported) {
          _supportState = _SupportState.supported;
        } else {
          _supportState = _SupportState.unsupported;
        }
        // ตรวจสอบรองรับ biometric หรือไม่
        _checkBiometrics();
        print("debug: ${_supportState}");
      });
    });
  }
 
  // โหลดข้อมูลจาก SharedPreferences
  Future<void> _loadValueFromSharedPreferences() async {
    prefs = await SharedPreferences.getInstance();
 
    setState(() {
      _biometricstatus = prefs.getBool('biometricstatus') ?? _biometricstatus;
      _pincodestatus = prefs.getBool('pincodestatus') ?? _pincodestatus;
      _authorized = prefs.getBool('authorized') ?? _authorized;
      _pincodevalue = prefs.getString('pincodevalue') ?? _pincodevalue;
      _isLoadingPrefs = false; //  โหลดค่าเรียบร้อยแล้ว
    });
  }
 
  // ตรวจสอบการรองรับการใช้ข้อมูลชีวภาพยืนยันตัวตนหรือไม่
  // เช่น การสแกนลาวนิ้วมือที่มีความแม่นยำสูง การใช้ face ID
  Future<void> _checkBiometrics() async {
    late bool canCheckBiometrics;
    try {
      canCheckBiometrics = await auth.canCheckBiometrics;
    } on PlatformException catch (e) {
      canCheckBiometrics = false;
      print(e);
    }
    if (!mounted) {
      return;
    }
 
    setState(() {
      print("debug: _canCheckBiometrics ${canCheckBiometrics}");
      _canCheckBiometrics = canCheckBiometrics;
    });
  
 
  // ฟังก์ชั่นแสดงรายการตรวจสอบที่รองรับ
  Future<void> _getAvailableBiometrics() async {
    late List<BiometricType> availableBiometrics;
    try {
      availableBiometrics = await auth.getAvailableBiometrics();
    } on PlatformException catch (e) {
      availableBiometrics = <BiometricType>[];
      print(e);
    }
    if (!mounted) {
      return;
    }
 
    setState(() {
      // สำหรับทดสอบแสดงรายการที่รองรับ
      print("debug: ${availableBiometrics}");
      _availableBiometrics = availableBiometrics;
    });
  }
 
  // ฟังก์ชั่นสำหรับพิสูจน์ยืนยันโดยให้ระบบเลือกวิธีการให้
  Future<void> _authenticate() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
     //   _authorized = false;
      });
      authenticated = await auth.authenticate(
        localizedReason: 'Let OS determine authentication method',
        options: const AuthenticationOptions(
          stickyAuth: true,
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
      //  _authorized = false;
      });
      return;
    }
    if (!mounted) {
      return;
    }
    setState(() {
      // สำหรับยกเลิกการใช้งาน
      if(_biometricstatus && authenticated){
        print("debug: cancel using authen");
        _authorized = false;
        _biometricstatus =  false;
        prefs.setBool("authorized", _authorized);
        prefs.setBool("biometricstatus", _biometricstatus);
      }else{
        // สำหรับกำหนดใช้งาน
        print("debug: using authen");       
        if(authenticated){
          _authorized = authenticated ? true : false;
          _biometricstatus =  authenticated ? true : false;
          prefs.setBool("authorized", _authorized);
          prefs.setBool("biometricstatus", _biometricstatus);
        }
      }
    });
  
 
  // ฟังก์ชั่นสำหรับพิสูจน์ยืนยันโดยเลือกเป็นข้อมูลชีวภาพ
  Future<void> _authenticateWithBiometrics() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
      });
      authenticated = await auth.authenticate(
        localizedReason:
            'Scan your fingerprint (or face or whatever) to authenticate',
        options: const AuthenticationOptions(
          stickyAuth: true,
          biometricOnly: true, // ใช้กรณีรองรับ
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
      });
      return;
    }
    if (!mounted) {
      return;
    }
 
    setState(() {
      // สำหรับยกเลิกการใช้งาน
      if(_biometricstatus && authenticated){
        print("debug: cancel using authen");
        _authorized = false;
        _biometricstatus =  false;
        prefs.setBool("authorized", _authorized);
        prefs.setBool("biometricstatus", _biometricstatus);
      }else{
        // สำหรับกำหนดใช้งาน
        print("debug: using authen");       
        if(authenticated){
          _authorized = authenticated ? true : false;
          _biometricstatus =  authenticated ? true : false;
          prefs.setBool("authorized", _authorized);
          prefs.setBool("biometricstatus", _biometricstatus);
        }
      }
    });
  
 
  // ฟังก์ชั่นสำหรับยกเลิกการตรวจสอบ เช่น ยังไม่สแกนนิ้ว ขอยกเลิกก่อน
  Future<void> _cancelAuthentication() async {
    await auth.stopAuthentication();
    setState(() => _isAuthenticating = false);
  }
 
  @override
  Widget build(BuildContext context) {
    if (_isLoadingPrefs) {
      // คืนค่ากรณี กำลังโหลดค่า SharedPreferences ยังไม่เสร็จ
      return const Center(child: SizedBox.shrink());
    }
    print("debug: _biometricstatus ${_biometricstatus}");
    print("debug: _pincodestatus ${_pincodestatus}");
    print("debug: _authorized ${_authorized}");
    print("debug: _pincodevalue ${_pincodevalue}");
    return Scaffold(
      appBar: AppBar(
        title: Text('Settings'),
      ),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          // Text('Settings Screen'),
          ListTile(
            title: Text('Lock app with PIN number'),
            trailing: Checkbox(
              value: _pincodestatus,
              onChanged: (bool? value) async {
                // เมื่อมีการเปลี่ยนแปลงการตั้งค่า
                // เปิดหน้ากำหนด pin และรอดำเนินการจากหน้านั้น
                final result = await Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => Pincode()),
                );
                // จัดการเงื่อนไขรับค่าที่ส่งกลับมา
                print("debug: ${result}");
                if (result == 'cancel') {
                  // ถ้าเป็นการยกเลิก
                  setState(() {
                    _authorized = false;
                    _pincodestatus = false;
                    _pincodevalue = '';
                  });
                } else {
                  // ถ้ามีการตั้งค่า result ที่ส่งกลับมาเป็นค่า code
                  if (result != null) {
                    setState(() {
                      _authorized = true;
                      _pincodestatus = true;
                      _pincodevalue = result;
                    });
                  }
                }
              },
            ),
          ),
          if (_supportState == _SupportState.supported)
            ListTile(
              title: Text('Lock app with Biometric'),
              trailing: Checkbox(
                value: _biometricstatus,
                onChanged: (bool? value) async {
                  // ทดสอบเรียกฟังก์ชั่นแสดงรายการที่รองรับ
                  //  _getAvailableBiometrics();
 
                  // ทดสอบเรียกใช้ฟังก์ชั่นพิสูจน์ข้อมูลชีวภาพ
                  //  _checkBiometrics();
 
                  // ใช้การตรวจสอบตามระบบเลือกให้
                  //  _authenticate();
                    // สำหรับใช้แบบ biometrics
                  //  _authenticateWithBiometrics();     
 
                  if(_canCheckBiometrics==true){
                    _authenticateWithBiometrics();   
                  }else{
                    _authenticate();
                  }            
 
                },
              ),
            ),
        ],
      )),
    );
  }
}
 

ผลลัพธ์ที่ได้



 
 
 
เมื่อกดเปิดการใช้งาน จะขึ้นการตรวจสอบพิสูจน์ยืนยันตัวตน ในที่นี้ขึ้นเป็นแบบสแกนลายนิ้วมือ หาก
เราทำการสแกนผ่านเรียบร้อย สถานะเปิดใช้งานก็จะถูกเลือก และกรณีเราต้องการปิด ก็แค่เพียงกดอีก
ครั้งจะขึ้นให้สแกนยืนยัน หากสแกนเรียบร้อยแล้วก็จะเป็นการปิดใช้งาน จะเห็นว่าวิธีที่สะดวกรวดเร็วกว่า
วิธีแรกที่เราทำโดยการใช้งาน PIN
 

การนำ Local_auth ไปใช้งานสำหรับล็อกแอป

    จริงๆ แล้วเราสามารถประยุกต์การใช้งาน local_auth ได้ในหลายรูปแบบไม่ใช่แค่เพียงนำมาล็อก
แอป เรายังสามารถใช้สำหรับดึงข้อมูลสมาชิกได้ ทั้งนี้ก็ขึ้นกับการนำไปประยุกต์ใช้งานที่ซับซ้อน ในที่
นี่จะใช้รูปแบบง่ายคือใช้สำหรับล็อกแอปแทนรูปแบบในตอนที่แล้ว เราจะทำที่ไฟล์ launcher.dart ดังนี้
 

ไฟล์ launcher.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import 'dart:async';
 
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:local_auth/local_auth.dart';
import 'package:flutter/services.dart';
import 'home.dart';
import 'contact.dart';
import 'profile.dart';
import 'about.dart';
import 'settings.dart';
import 'pincode.dart';
 
import '../components/sidemenu.dart';
 
enum _SupportState {
  unknown,
  supported,
  unsupported,
}
 
class Launcher extends StatefulWidget {
  static const routeName = '/';
 
  const Launcher({Key? key}) : super(key: key);
 
  @override
  State<StatefulWidget> createState() {
    return _LauncherState();
  }
}
 
class _LauncherState extends State<Launcher> with WidgetsBindingObserver {
  late final SharedPreferences prefs;
  bool _biometricstatus = false; // สถานะเปิดใช้ biometric
 
  // ส่วนจัดการ local auth
  final LocalAuthentication auth = LocalAuthentication(); // เรียกใช้งาน
  _SupportState _supportState = _SupportState.unknown; // สถานะการรองรับ
  bool? _canCheckBiometrics; // ตรวจสอบรองรับยืนยันด้วยข้อมูลชีวภาพได้ไหม
  List<BiometricType>? _availableBiometrics; // รายการยืนยันตัวตนที่รองรับ
  bool _isAuthenticating = false; // เก็บสถานะกำลังยืนยันตัวตน
 
  bool _pincodestatus = false; // สถานะเปิดใช้ pin
  bool _authorized = false; // สถานะเข้าใช้งาน
  String _pincodevalue = ''; // ค่า pin ในที่นี้ใช้ 4 ตัวเลข
  bool _isLoadingPrefs = true; // สถานะการโหลดค่าจาก SharedPreferences
 
  int _selectedIndex = 0;
  final List<Widget> _pageWidget = <Widget>[
    const Home(),
    const About(),
    const Profile(),
    const Contact(),
    const Settings(),
  ];
  final List<BottomNavigationBarItem> _menuBar = <BottomNavigationBarItem>[
    const BottomNavigationBarItem(
      icon: Icon(FontAwesomeIcons.house),
      label: 'Home',
    ),
    const BottomNavigationBarItem(
      icon: Icon(FontAwesomeIcons.circleInfo),
      label: 'About',
    ),
    const BottomNavigationBarItem(
      icon: Icon(FontAwesomeIcons.userLarge),
      label: 'Profile',
    ),
    const BottomNavigationBarItem(
      icon: Icon(FontAwesomeIcons.addressCard),
      label: 'Contact',
    ),
    const BottomNavigationBarItem(
      icon: Icon(FontAwesomeIcons.gear),
      label: 'Settings',
    ),
  ];
 
  @override
  void initState() {
    super.initState();
    // โหลดค่าจาก SharedPreferences
    _loadValueFromSharedPreferences();
    // กำหนดการตรวจจับ สถานะ state ของ app
    WidgetsBinding.instance.addObserver(this);
    auth.isDeviceSupported().then((bool isSupported) {
      setState(() {
        if (isSupported) {
          _supportState = _SupportState.supported;
        } else {
          _supportState = _SupportState.unsupported;
        }
        // ตรวจสอบรองรับ biometric หรือไม่
        _checkBiometrics();
        print("debug: ${_supportState}");
      });
    });
     
  }
 
  // ตรวจสอบการรองรับการใช้ข้อมูลชีวภาพยืนยันตัวตนหรือไม่
  // เช่น การสแกนลาวนิ้วมือที่มีความแม่นยำสูง การใช้ face ID
  Future<void> _checkBiometrics() async {
    late bool canCheckBiometrics;
    try {
      canCheckBiometrics = await auth.canCheckBiometrics;
    } on PlatformException catch (e) {
      canCheckBiometrics = false;
      print(e);
    }
    if (!mounted) {
      return;
    }
 
    setState(() {
      print("debug: _canCheckBiometrics ${canCheckBiometrics}");
      _canCheckBiometrics = canCheckBiometrics;
    });
  
 
  @override
  void dispose() {
    // ล้างค่าตรวจจับ สถานะ state ของ app
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
 
  // ส่วนของการทำงานเมื่อ state ของ app มีการเปลี่ยนแปลง
  // บาง ค่าของ LifecycleState อาจจะไม่ทำงานได้
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    print("debug: AppLifecycleState: $state");
    if (state == AppLifecycleState.paused) {
      // ล้างค่าการปลดล็อก ถ้ามีการหยุดชั่วคราวของแอป
      _authorized = false;
      await prefs.setBool('authorized', false);
      print("debug: App is in background.");
    } else if (state == AppLifecycleState.resumed) {
      print("debug: App is in foreground.");
    } else if (state == AppLifecycleState.detached) {
      // ล้างค่าการปลดล็อก ถ้ามีการปิดแอป ส่วนนี้อาจจะไทม่ทำงานได้
      // และบางครั้ง เวลาเริ่มแอปใหม่ อาจจะไม่มีการใส่ pin ได้
      _authorized = false;
      await prefs.setBool('authorized', false);     
      print("debug: App is about to be terminated.");
    }
  
 
  // โหลดข้อมูลจาก SharedPreferences
  Future<void> _loadValueFromSharedPreferences() async {
    prefs = await SharedPreferences.getInstance();
 
    setState(() {
      _biometricstatus = prefs.getBool('biometricstatus') ?? _biometricstatus;
      _pincodestatus = prefs.getBool('pincodestatus') ?? _pincodestatus;
      _authorized = prefs.getBool('authorized') ?? _authorized;
      _pincodevalue = prefs.getString('pincodevalue') ?? _pincodevalue;
      _isLoadingPrefs = false//  โหลดค่าเรียบร้อยแล้ว
      // เงื่่อนไขถ้ามีการเปิดใช้งานการล็อกแอป และยังไม่ปลดล็อก
/*       if (_pincodestatus && !_authorized) {
        _authen(); // เรียกใช้งานการตรวจสอบ pin
      }    */
 
      // เงื่่อนไขถ้ามีการเปิดใช้งานการล็อกแอป และยังไม่ปลดล็อกด้วย biometric
      if(_biometricstatus && !_authorized){
        if(_canCheckBiometrics==true){
          _authenticateWithBiometrics();   
        }else{
          _authenticate();
        }          
      }
    });
  }
 
  // ฟังก์ชั่นสำหรับพิสูจน์ยืนยันโดยให้ระบบเลือกวิธีการให้
  Future<void> _authenticate() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
     //   _authorized = false;
      });
      authenticated = await auth.authenticate(
        localizedReason: 'Let OS determine authentication method',
        options: const AuthenticationOptions(
          stickyAuth: true,
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
      //  _authorized = false;
      });
      return;
    }
    if (!mounted) {
      return;
    }
    setState(() {
        // สำหรับกำหนดใช้งาน
        print("debug: using authen");       
        if(authenticated){
          _authorized = authenticated ? true : false;
          _biometricstatus =  authenticated ? true : false;
          prefs.setBool("authorized", _authorized);
          prefs.setBool("biometricstatus", _biometricstatus);
        }else{
          // บังคับต้องยืนยันตัวตนถึงจะใช้งานได้         
          if(_canCheckBiometrics==true){
            _authenticateWithBiometrics();   
          }else{
            _authenticate();
          }            
        }
 
    });
  
 
  // ฟังก์ชั่นสำหรับพิสูจน์ยืนยันโดยเลือกเป็นข้อมูลชีวภาพ
  Future<void> _authenticateWithBiometrics() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
      });
      authenticated = await auth.authenticate(
        localizedReason:
            'Scan your fingerprint (or face or whatever) to authenticate',
        options: const AuthenticationOptions(
          stickyAuth: true,
          biometricOnly: true, // ใช้กรณีรองรับ
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
      });
      return;
    }
    if (!mounted) {
      return;
    }
 
    setState(() {
        // สำหรับกำหนดใช้งาน
        print("debug: using authen");       
        if(authenticated){
          _authorized = authenticated ? true : false;
          _biometricstatus =  authenticated ? true : false;
          prefs.setBool("authorized", _authorized);
          prefs.setBool("biometricstatus", _biometricstatus);
        }else{
          // บังคับต้องยืนยันตัวตนถึงจะใช้งานได้
          if(_canCheckBiometrics==true){
            _authenticateWithBiometrics();   
          }else{
            _authenticate();
          }            
        }
 
    });
  
 
  // ฟังก์ชั่นสำหรับยกเลิกการตรวจสอบ เช่น ยังไม่สแกนนิ้ว ขอยกเลิกก่อน
  Future<void> _cancelAuthentication() async {
    await auth.stopAuthentication();
    setState(() => _isAuthenticating = false);
  }
 
  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }
 
  // การตรวจสอบ pin โดยแทนที่หน้าหลักด้วยหน้า pincode
  void _authen() async {
      String? args = 'pin'; // ส่งค่านี้ไป เพื่อใช้แยกว่าเป็นการ ตรวจสอบเข้าใช้งาน
      await Navigator.pushReplacement(
        context,
        MaterialPageRoute(
          builder: (context) => Pincode(),
            settings: RouteSettings(arguments: args // ส่งค่าไปใน  arguments
          ),
        ),
      );
  }
 
  @override
  Widget build(BuildContext context) {
    if (_isLoadingPrefs) {
      // คืนค่ากรณี กำลังโหลดค่า SharedPreferences ยังไม่เสร็จ     
      return const Center(child: SizedBox.shrink());
    }
    print("debug: _pincodestatus ${_pincodestatus}");
    print("debug: _authorized ${_authorized}");
    print("debug: _pincodevalue ${_pincodevalue}");
    return Scaffold(
      body: _pageWidget.elementAt(_selectedIndex),
      bottomNavigationBar: BottomNavigationBar(
        items: _menuBar,
        currentIndex: _selectedIndex,
        selectedItemColor: Theme.of(context).primaryColor,
        unselectedItemColor: Colors.grey,
        onTap: _onItemTapped,
      ),
      drawer: SideMenu(),
    );
  }
}
 

ผลลัพธ์ที่ได้


 
 
เมื่อมีการเปิดการใช้งานการล็อกแอปด้วยข้อมูลชีวภาพ ไม่ว่าจะเป็นการสแกนลายนิ้วมือ หรือการใช้งาน
face ID, Touch ID หรือ Pattern หลังจากเราปิดแอป และกลับมาเปิดใช้งานทุกครั้ง จะขึ้นให้เรา
ยืนยันตัวตนหรือสแกนเพื่อใช้งานก่อนเสมอ ไม่เช่นนั้นก็จะค้างหน้าสแกนจนกว่าจะยืนยันตัวตนผ่าน 
และ สมมติเราไม่ต้องการใช้งานแล้ว ก็สามารถเข้าไปใช้งาน และไปปิดการใช้งานเมื่อต้องการได้
 
เราสามารถนำไปประยุกต์กับระบบสมาชิกได้ เช่น กรณีเป็นระบบสมาชิกที่ต้องทำการล็อคอินเข้าใช้งาน
เราสามารถเก็บข้อมูล key และ api ที่ใช้สำหรับดึงข้อมูลไว้ จะเปิดใช้งานเมื่อยืนยันตัวตนผ่าน ก็สามารถ
นำไปประยุกต์ใช้งานได้
 
หวังว่าเนื้อหาเกี่ยวกับ Local authentication หรือการตรวจพิสูจน์ยืนยันตัวตนผ่านปลั้กอินที่ชื่อว่า
local_auth จะมีประโยชน์นำไปปรับประยุกต์ใช้งานต่อไปได้ ไม่มากก็น้อย เนื้อหาตอนหน้าจะเป็น
อะไรรอติดตาม


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 22-09-2024


ดาวน์โหลดโค้ดตัวอย่าง สามารถนำไปประยุกต์ หรือ run ทดสอบได้

http://niik.in/download/flutter/demo_045_22092024_source.rar


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง






เนื้อหาพิเศษ เฉพาะสำหรับสมาชิก

กรุณาล็อกอิน เพื่ออ่านเนื้อหาบทความ

ยังไม่เป็นสมาชิก

สมาชิกล็อกอิน



( หรือ เข้าใช้งานผ่าน Social Login )




URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ