ประยุกต์ระบบ Register Login ด้วย Firebase Auth ใน Flutter

บทความใหม่ เมื่อวานนี้ โดย Ninenik Narkdee
register flutter firebase auth login firebase

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



ต่อเนื่องจากบทความตอนที่แล้ว ที่เราได้เตรียมพร้อมและ
ตั้งค่าส่วนต่างๆ สำหรับใช้งาน Firebase Authentication 
โดยเราจะนำมาปรับใช้งานกับรูปแบบระบบสมาชิกที่เคยแนะนำ
ไปแล้ว ดังนั้นวิธีการทำงานก็แทบจะเหมือนเดิม เปลี่ยนแค่
เพียงตัว provider หรือตัวจัดการ จากเดิมที่เราใช้เป็นการส่ง
ข้อมูลไปยัง server ของเราเอง ก็เปลี่ยนมาใช้เป็นใช้งานระบบ
Firebase ถึงแม้การตั้งค่าในตอนที่แล้วอาจจะยุ่งยากหลายขั้นตอน
แต่วิธีการนำมาใช้ก็ง่ายและสะดวก ทบทวนตอนที่แล้วได้ที่บทความ
    ใช้งาน Firebase Authentication จัดการระบบสมาชิกใน Flutter http://niik.in/1061
 
เนื่องจากระบบ Firebase Authentication เราจะจัดการข้อมูลสมาชิกเบื่องต้น สามารถใช้งาน
ได้ทันที ดังนั้นจึงไม่จำเป็นต้องสร้าง Data User model เอง แต่ตัว Firebase สร้างมาให้แล้ว
แต่เราสามารถนำมาปรับเพิ่มเติมได้ ถ้าต้องการ ในที่นี้จะไม่ใช้ Data model เพิ่มเติม
     ตัวอย่างโครงสร้าง User Data model ของ Firebase
 
User(
	displayName: null, 
	email: example@gmail.com, 
	emailVerified: false, 
	isAnonymous: false, 
	metadata: UserMetadata(
		creationTime: 2021-11-25 10:30:43.396, 
		lastSignInTime: 2021-11-25 10:34:33.124
	), 
	phoneNumber: null, 
	photoURL: null, 
	providerData, [UserInfo(
		displayName: null, 
		email: example@gmail.com, 
		phoneNumber: null, 
		photoURL: null, 
		providerId: password, 
		uid: example@gmail.com)
	], 
	refreshToken: , 
	tenantId: null, 
	uid: 6TWDv0kfdsfdfdfdf4S2
)
 
 
 

เริ่มต้นใช้งาน Firebase Authentication

    ในหน้าหรือไฟล์ที่เราจะใช้งาน Firebase Authentication เราต้องทำการ import class สองค่า
นี้เข้ามาใช้งาน
 
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
 
    และต้องทำการ initialize FlutterFire หรือการกำหนดให้ Flutter กับ Firebase ทำงานร่วมกันได้
เมื่อโหลดหน้านั้นๆ ก่อนเสมอ ด้วยคำสั่ง
 
try {
  await Firebase.initializeApp();
} catch(e) {
  print("Error no initializeApp");
} 
 
 
 

สร้าง Provider สำหรับจัดการข้อมูลกับ Firebase Authentication

    เป็นการรวมคำสั่งการใช้งาน Firebase Authentication ใน app ของเรา ให้เราสร้างไฟล์ ดังนี้
 
    utils > auth_provider.dart
 

    ไฟล์ auth_provider.dart

 
import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';

 
// สร้างข้อมูล app state ชื่อ Counter ใช้งานร่วมกับ ChangeNotifier
class AuthProvider with ChangeNotifier {
  final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
  final FirebaseAuth _auth = FirebaseAuth.instance;

  FirebaseAuth get auth => _auth;

  // ฟังก์ชั่นดึงสถานะการล็อกอิน จากข้อมูล SharedPreferences
  Future<bool> getLoginStatus() async {
    final SharedPreferences prefs = await _prefs;
    return prefs.getBool('loginSuccess') ?? false;
  }     

  // ฟังก์ชั่นกำหนดสถานะการล็อกอิน ไว้ใน SharedPreferences
  Future<bool> setLoginStatus(status) async {
    final SharedPreferences prefs = await _prefs;
    return await prefs.setBool('loginSuccess', status);
  }         

  // ส่วนของการล็อกเอาท์
  Future<bool> logout() async {
    final SharedPreferences prefs = await _prefs;
    await FirebaseAuth.instance.signOut();
    return await prefs.clear();
  }   

  // ส่วนของการล็อกอินผ่าน firebase
  Future<Map<String, dynamic>> authen(String email, String password) async {
    final SharedPreferences prefs = await _prefs; 
    var result;

    try {
      UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password
      );
      await prefs.setBool("loginSuccess", true);
      result = json.decode('{"success": "Login successful"}');
    } on FirebaseAuthException catch (e) {
      String err = '';
      if (e.code == 'user-not-found') {
        err = '{"error": "No user found for that email."}';
        result = json.decode(err);        
      } else if (e.code == 'wrong-password') {
        err = '{"error": "Wrong password provided for that user."}';
        result = json.decode(err);        
      } else {
        err = '{"error": "${e.message}"}';
        result = json.decode(err);            
      }
    } 
    return result;
  }

  // ส่วนของการสมัครสมาชิกผ่าน firebase
  Future<Map<String, dynamic>> create(String email, String password) async {
    var result;
 
    try {
      UserCredential userCredential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
        email: email,
        password: password
      );
      result = json.decode('{"success": "Create new user successful"}');
    } on FirebaseAuthException catch (e) {
      String err = '';
      if (e.code == 'weak-password') {
        err = '{"error": "The password provided is too weak."}';
        result = json.decode(err);
      } else if (e.code == 'email-already-in-use') {
        err = '{"error": "The account already exists for that email."}';
        result = json.decode(err);     
      } else if(e.code == 'unknown'){
        err = '{"error": "Email and Password are required."}';
        result = json.decode(err);     
      } else if(e.code == 'invalid-email'){
        err = '{"error": "The email address is badly formatted."}';
        result = json.decode(err);                
      }else{
/*         print(e.code);
        print(e.message); */
        err = '{"error": "$e"}';
        result = json.decode(err);        
      }
    } catch (e) {
      String err = '{"error": "$e"}';
      result = json.decode(err);
    }    
    return result;
  }    
 
}
 
    เนื่องจากในไฟล์นี้ จะรวมคำสั่งสำหรับระบบสมาชิก ที่ใช้งานร่วมกับ firebase auth ดังนั้นในนี้เราจึง
ไม่มีการกำหนด initialize FlutterFire เพราะยังไม่ใช่งานในนี้ 
     ใน provider เรากำหนดคำสั่งการทำงานเบื้องต้น คือมีสร้างบัญชีใหม่ ล็อกอิน ล็อกเอาท์ และดึงข้อมูล
สถานะการล็อกอินที่เก็บใน SharedPreferences ไปใช้งาน  หากต้องการเพิ่มคำสั่งอื่นๆ ทีหลังก็สามารถ
ปรับเพิ่มได้ตามต้องการ
 
    คำสั่ง createUserWithEmailAndPassword() ของ firebase auth จะทำงานในลักษณะสองขั้นตอนคือ
ทำการสมัครสมาชิก ถ้าสำเร็จก็ล็อกอินอัตโนมัติทันที ล็อกอินในที่นี้คือล็อกอินใน server firebase ดังนั้นสถานะ
ของสมาชิกบน server จึงหมายถึงล็อกอินแล้ว แต่สถานะที่ app ของเรา เรายังไม่กำหนดค่าให้ล็อกอิน 
เมื่อสมัครสมาชิกแล้ว ก็จะแจ้งว่าสมัครสำเร็จ แล้วก็ลิ้งค์มายังหน้าล็อกอินให้ผู้ใช้ทำการล็อกอินอีกที นี่คือที่เราจะทำ
     อย่างไรก็ตามถ้าต้องการประยุกต์ เมื่อล็อกอินแล้วก็ให้ไปยังหน้า profile เลยก็สามารถทำได้ โดยการใช้การ
ตรวจสอบสถานะการล็อกอิน จากนั้นกำหนดการทำงานเข้าไป เช่น ในหน้า login หรือหน้า register แล้วแต่การ
ปรับประยุกต์ใช้งาน
 
auth
  .authStateChanges()
  .listen((User? user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');   
    }
  });    
 
    ในไฟล์ auth_provider.dart รูปแบบคำสั่งก็ไม่มีอะไรซับซ้อน ใช้งานง่าย คำสั่ง create() สำหรับสมัคร
สมาชิกใหม่ คำสั่ง authen() สำหรับล็อกอิน คำสั่ง logout() สำหรับออกจากระบบ จะเห็นว่า เราไม่มีการใช้งาน
User Data model เพราะเราจะใช้งานของ Firebase Auth ที่มีให้มาอยู่แล้ว ซึ่งรองรับข้อมูลที่จำเป็นตามโครง
สร้างข้อมูลด้านบนที่เราแสดงไป ไม่ว่าจะเป็นชื่อที่แสดง วันที่สร้าง วันที่ล็อกอินล่าสุด เบอร์โทร รูปภาพ เป็นต้น
ซึ่งถ้าจะกำหนดใช้งานก็ไปปรับแต่งเพิ่มเติมตามต้องการได้

 
 

กำหนดใช้งาน Provider ใน app

    ในไฟล์ main.dart จะของยกมาบางส่วนของโค้ด เฉพาะส่วนของการใช้งาน provider 
 

    ไฟล์ main.dart บางส่วน

 
......
....
      return MultiProvider(
        providers: [
        ChangeNotifierProvider(create: (context) => AuthProvider()), 
      ], 
        child: MaterialApp(
                  theme: ThemeData(
....
...
 
    ทำการ import AuthProvider เข้าไปใช้งานในไฟล์ main.dart
 
import 'utils/auth_provider.dart';
 

 

การเรียกใช้งาน Provider

    สุดท้ายเป็นการเรียกใช้งาน provider ในไฟล์ต่างๆ ตามลำดับ  คำอธิบายแสดงในโค้ด
 

    ไฟล์ profile.dart

 
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';

import '../utils/auth_provider.dart';

import 'login.dart';
  
class Profile extends StatefulWidget {
    static const routeName = '/profile';
 
    const Profile({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _ProfileState();
    }
}
  
class _ProfileState extends State<Profile> {
    late FirebaseAuth auth; // สำหรับ authen
    late AuthProvider authProvider; // สำหรับ provider
    bool _loginSuccess = false;  // กำหดตัวแปรสถานะการล็อกอิน

    // ส่วนของตัวแปรข้อมูลพื้นฐาน
    String? _id = ''; // ใน firebase ข้อมูลบ uid เป็น string
    String? _email = '';

    @override
    void initState() {
      super.initState();     
      loadSettings(); // เรียกใช้งานตั้งค่าเมื่อเริ่มต้นเป็นฟังก์ชั่น ให้รองรับ async
    }

    // ตั้งค่าเริ่มต้น
    void loadSettings() async {
      try {
        await Firebase.initializeApp(); // เชื่อมต่อ firebase กับ app
        authProvider = context.read<AuthProvider>(); // ใช้งาน provider
        auth = authProvider.auth; // กำหนด authen
        _loginSuccess = await authProvider.getLoginStatus(); // ถึงสถานะการล็อกอิน ถ้ามี
        setState(() {
          _loginSuccess = _loginSuccess;
        });        
      } catch(e) {
        print("Error no initializeApp");
        _loginSuccess = false;
      }      
    }   

    // ฟังก์ชั่นสำหรับดึงข้อมูลผู้ใช้
    Future<bool> fetchUser() async {		
      await Firebase.initializeApp(); // เชื่อมต่อ firebase กับ app	
      // ใช้งาน provider
      authProvider = context.read<AuthProvider>();
      _loginSuccess = await authProvider.getLoginStatus(); // ถึงสถานะการล็อกอิน ถ้ามี 
      var currentUser = auth.currentUser; // อ้างอิงผู้ใช้ปัจจุบัน

      if (currentUser != null) { // ถ้ามีข้อมูล
        _id = currentUser.uid; // เอาค่ามาเก็บในัวแร เพื่อใช้แสดง
        _email = currentUser.email;
        print(currentUser); // แสดงรายละเอียดข้อมูลของผู้ใช้ทั้งหมดว่ามีอะไรบ้าง
      }
      return true;     
    }

    @override
    void dispose() {
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text('Profile'),
            ),
            body: FutureBuilder<bool>( 
              future: fetchUser(), // ข้อมูล Future
              builder: (context, snapshot) { 
                if (snapshot.hasData) { 
                  return Center(
                    child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                            Text('Profile Screen'),
                            Visibility( // ส่วนที่แสดงกรณีล็อกอินแล้ว
                              visible: _loginSuccess, // ใช้สถานะการล็อกอินกำหนดกรแสดง
                              child: Column(
                                children: [
                                  FlutterLogo(size: 100,),
                                  Text('Welcome member'),
                                  Text(_email!), // แสดงอีเมล
                                  ElevatedButton(
                                    onPressed: () async { // เมื่อล็อกเอาท์
                                      // ทำการออกจากระบบ 
                                      await authProvider.logout();
                                      setState(() {
                                        _loginSuccess = false;
                                      });                      
                                    }, 
                                    child: Text('Logout'),
                                  ),        
                                ],
                              ),
                            ),
                            Visibility( // ส่วนที่แสดงกรณียังไม่ได้ล็อกอิน
                              visible: !_loginSuccess, // ใช้สถานะตรงข้ามการล็อกอินกำหนดกรแสดง
                              child: ElevatedButton(
                                onPressed: () async {
                                  // กำหดให้รอค่า หลังจากเปิดไปหน้า lgoin
                                  final result = await Navigator.push(
                                    context, 
                                    MaterialPageRoute(builder: (context) => Login(),
                                      settings: RouteSettings(
                                        arguments: null 
                                      ),
                                    ),
                                  );    
                                  // ถ้ามีการปิดหน้าที่เปิด และส่งค่ากลับมาเป็น true
                                  if (result == true) {
                                    await fetchUser();     
                                    setState(() {
                                      _loginSuccess = true;
                                    });                                                                     
                                  }
                                              
                                }, 
                                child: Text('Go to Login'),
                              ),
                            ),
                        ],
                    )
                );
                } else if (snapshot.hasError) { // ถ้ามี error
                  return Text('${snapshot.error}');
                }
                return const CircularProgressIndicator();
              },
            ),  
        );
    }
}
 
 

    ไฟล์ login.dart

 
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';

import '../utils/auth_provider.dart';
import 'register.dart';
  
class Login extends StatefulWidget {
    static const routeName = '/login';
 
    const Login({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _LoginState();
    }
}
  
class _LoginState extends State<Login> {
    late FirebaseAuth auth;
    late AuthProvider authProvider;
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    // กำหนดตัวแปรรับค่า
    final _email = TextEditingController();
    final _password = TextEditingController();

    // กำหนดสถานะการแสดงแบบรหัสผ่าน
    bool _isHidden = true;    
    bool _authenticatingStatus = false;        
 
    @override
    void initState() {
      super.initState();
      loadSettings(); 
    }

    // ตั้งค่าเริ่มต้น
    void loadSettings() async {
      try {
        await Firebase.initializeApp();
        authProvider = context.read<AuthProvider>();
        auth = authProvider.auth;
        auth
          .authStateChanges()
          .listen((User? user) {
            if (user == null) {
              print('User is currently signed out!');
            } else {
              print('User is signed in!');   
              authProvider.setLoginStatus(true);
              Navigator.pop(context, true);   // ปิดหน้านี้พร้อมคืนค่า true
            }
          });       
      } catch(e) {
        print("Error no initializeApp");
      }             
    }   

    @override
    void dispose() {
      _email.dispose(); // ยกเลิกการใช้งานที่เกี่ยวข้องทั้งหมดถ้ามี
      _password.dispose(); 
      super.dispose();
    }    

    @override
    Widget build(BuildContext context) { 
        return Scaffold(
            appBar: AppBar(
                title: Text('Login'),
            ),
            body: SingleChildScrollView(
              child: Form(
                key: _formKey, // กำหนด key
                child: Padding(
                  padding: const EdgeInsets.all(15.0),
                  child: Center(
                      child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                              SizedBox(height: 20.0,), 
                              FlutterLogo(
                                size: 100,
                              ),
                              Text('Login Screen'),
                              TextFormField(
                                decoration: InputDecoration(
                                  hintText: 'Email',
                                  icon: Icon(Icons.email_outlined),
                                ),
                                controller: _email, // ผูกกับ TextFormField ที่จะใช้
                              ), 
                              SizedBox(height: 5.0,),                                
                              TextFormField(
                                decoration: InputDecoration(
                                  hintText: 'Password',
                                  icon: Icon(Icons.vpn_key),
                                  suffixIcon: IconButton(
                                    onPressed: (){
                                      setState(() {
                                        _isHidden = !_isHidden; // เมื่อกดก็เปลี่ยนค่าตรงกันข้าม
                                      });
                                    }, 
                                    icon: Icon(
                                      _isHidden // เงื่อนไขการสลับ icon
                                      ? Icons.visibility_off 
                                      : Icons.visibility
                                    ),
                                  ),
                                ),
                                controller: _password, // ผูกกับ TextFormField ที่จะใช้                        
                                obscureText: _isHidden, // ก่อนซ่อนหรือแสดงข้อความในรูปแบบรหัสผ่าน
                              ), 
                              SizedBox(height: 10.0,),                                  
                              Visibility(
                                visible: !_authenticatingStatus,
                                child: ElevatedButton(
                                onPressed: () async {
                                  // เปลี่ยนสถานะเป็นกำลังล็อกอิน
                                  setState(() {
                                    _authenticatingStatus = !_authenticatingStatus;
                                  });

                                  // อ้างอิงฟอร์มที่กำลังใช้งาน ตรวจสอบความถูกต้องข้อมูลในฟอร์ม
                                  if (_formKey.currentState!.validate()) { //หากผ่าน 
                                    FocusScope.of(context).unfocus(); // ยกเลิดโฟกัส ให้แป้นพิมพ์ซ่อนไป

                                    String email = _email.text;
                                    String password = _password.text;

                                    // ทำการล็อกอิน firebase โดยใช้งาน provider
                                    var result = await authProvider.authen(email, password);
                                
                                    // จำลองเปรียบเทียบค่า เพื่อทำการล็อกอิน  
                                    if(result['success']!=null){ // ล็อกอินผ่าน
                                      ScaffoldMessenger.of(context).showSnackBar(
                                        const SnackBar(content: Text('Login Successful')),
                                      );
                                      Navigator.pop(context, true);   // ปิดหน้านี้พร้อมคืนค่า true 
                                    }else{
                                      if(result['error']!=null){ // ล็อกอินไม่ผ่านมี error
                                        String error = result['error'];
                                        ScaffoldMessenger.of(context).showSnackBar(
                                           SnackBar(content: Text('${error}..  try agin!')),
                                        );
                                        setState(() {
                                          _authenticatingStatus = !_authenticatingStatus;
                                        });
                                      }else{ // ล็อกอินไม่ผ่าน อื่นๆ
                                        ScaffoldMessenger.of(context).showSnackBar(
                                           SnackBar(content: Text('Error..  try agin!')),
                                        );
                                        setState(() {
                                          _authenticatingStatus = !_authenticatingStatus;
                                        });
                                      }
                                    }                                       
                                  }                                
                                },
                                  child: Container( 
                                    alignment: Alignment.center, 
                                    width: double.infinity, 
                                    child: const Text('Login'),  
                                  ),
                                ),
                              ),
                              Visibility(
                                visible: _authenticatingStatus,
                                child: Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: <Widget>[
                                    CircularProgressIndicator(),
                                    SizedBox(width: 10.0,), 
                                    Text(" Authenticating ... Please wait")
                                  ],
                                ),
                              ),
                              SizedBox(height: 30.0,),   
                              Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Text('Or '),
                                  InkWell(
                                    child: Text('Register', 
                                      style: TextStyle(
                                        decoration: TextDecoration.underline, 
                                        color: Colors.blue
                                      )), 
                                    onTap: () async {
                                      // เปิดหน้า สมัครสมาชิก โดย แทนที่ route ล็อกอินเดิม
                                      Navigator.pushReplacement(
                                        context, 
                                        MaterialPageRoute(builder: (context) => Register(),
                                          settings: RouteSettings(
                                            arguments: null 
                                          ),
                                        ),
                                      );    
                                    },
                                  )
                                ],
                              )
                          ],
                      )
                  ),
                ),
              ),
            ),
        );
    }
}
 

    ไฟล์ register.dart

 
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';

import '../utils/auth_provider.dart';

import 'login.dart';
  
class Register extends StatefulWidget {
    static const routeName = '/register';
 
    const Register({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _RegisterState();
    }
}
  
class _RegisterState extends State<Register> {
    late FirebaseAuth auth;
    late AuthProvider authProvider;
   
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    // กำหนดตัวแปรรับค่า
    final _email = TextEditingController();
    final _password = TextEditingController();

    // กำหนดสถานะการแสดงแบบรหัสผ่าน
    bool _isHidden = true;    
    bool _registeringStatus = false;    
 
    @override
    void initState() {
      super.initState();        
      loadSettings(); // เรียกใช้งานตั้งค่าเมื่อเริ่มต้นเป็นฟังก์ชั่น ให้รองรับ async    
    }

    // ตั้งค่าเริ่มต้น
    void loadSettings() async {
      try {
        await Firebase.initializeApp();
        authProvider = context.read<AuthProvider>();
        auth = authProvider.auth;     
      } catch(e) {
        print("Error no initializeApp");
      }    
    }       

    

    @override
    void dispose() {
      _email.dispose(); // ยกเลิกการใช้งานที่เกี่ยวข้องทั้งหมดถ้ามี
      _password.dispose(); 
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text('Register'),
            ),
            body: SingleChildScrollView(
              child: Form(
                key: _formKey, // กำหนด key
                child: Padding(
                  padding: const EdgeInsets.all(15.0),
                  child: Center(
                      child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                              SizedBox(height: 20.0,), 
                              FlutterLogo(
                                size: 100,
                              ),
                              Text('Register Screen'),
                              TextFormField(
                                decoration: InputDecoration(
                                  hintText: 'Email',
                                  icon: Icon(Icons.email_outlined),
                                ),
                                controller: _email, // ผูกกับ TextFormField ที่จะใช้
                              ), 
                              SizedBox(height: 5.0,),                                
                              TextFormField(
                                decoration: InputDecoration(
                                  hintText: 'Password',
                                  icon: Icon(Icons.vpn_key),
                                  suffixIcon: IconButton(
                                    onPressed: (){
                                      setState(() {
                                        _isHidden = !_isHidden; // เมื่อกดก็เปลี่ยนค่าตรงกันข้าม
                                      });
                                    }, 
                                    icon: Icon(
                                      _isHidden // เงื่อนไขการสลับ icon
                                      ? Icons.visibility_off 
                                      : Icons.visibility
                                    ),
                                  ),
                                ),    
                                controller: _password, // ผูกกับ TextFormField ที่จะใช้                        
                                obscureText: _isHidden, // ก่อนซ่อนหรือแสดงข้อความในรูปแบบรหัสผ่าน
                              ), 
                              SizedBox(height: 10.0,),                                  
                              Visibility(
                                visible: !_registeringStatus,
                                child: ElevatedButton(
                                onPressed: () async {
                                  // เปลี่ยนสถานะกำลังสมัครสมาชิก
                                  setState(() {
                                    _registeringStatus = !_registeringStatus;
                                  });
 
                                  if (_formKey.currentState!.validate()) { //หากผ่าน 
                                    FocusScope.of(context).unfocus(); // ยกเลิดโฟกัส ให้แป้นพิมพ์ซ่อนไป

                                    String email = _email.text;
                                    String password = _password.text;

                                    // เรียกใช้งานการสมัครสมาชิกใหม่ด้วย firebase ผ่าน provider
                                    var result = await authProvider.create(email, password);

                                    if(result['success']!=null){ // สร้างบัญชีสำเร็จ
                                      ScaffoldMessenger.of(context).showSnackBar(
                                          const SnackBar(content: Text('Create new user Successful')),
                                      );    
                                      Navigator.pushReplacement( // ไปหน้าล็อกอิน
                                          context, 
                                          MaterialPageRoute(builder: (context) => Login(),
                                            settings: RouteSettings(
                                              arguments: null 
                                            ),
                                          ),
                                      );       
                                    }else{
                                      if(result['error']!=null){ // สร้างบัญชีไม่ผ่าน
                                        String error = result['error'];
                                        ScaffoldMessenger.of(context).showSnackBar(
                                           SnackBar(content: Text('${error}..  try agin!')),
                                        );
                                        setState(() {
                                          _registeringStatus = !_registeringStatus;
                                        });                                           
                                      }else{ // สร้างบัญชีไม่ผ่าน อื่นๆ 
                                        ScaffoldMessenger.of(context).showSnackBar(
                                           SnackBar(content: Text('Error..  try agin!')),
                                        );
                                        setState(() {
                                          _registeringStatus = !_registeringStatus;
                                        });                                                                                
                                      }
                                    } 
                     
                                  }                                                           
                                },
                                  child: Container( 
                                    alignment: Alignment.center, 
                                    width: double.infinity, 
                                    child: const Text('Register'),  
                                  ),
                                ),
                              ),
                              Visibility(
                                visible: _registeringStatus,
                                child: Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: <Widget>[
                                    CircularProgressIndicator(),
                                    SizedBox(width: 10.0,), 
                                    Text(" Registering ... Please wait")
                                  ],
                                ),
                              ),                              
                              SizedBox(height: 30.0,),   
                              Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Text('Already member? '),
                                  InkWell(
                                    child: Text('Login', 
                                      style: TextStyle(
                                        decoration: TextDecoration.underline, 
                                        color: Colors.blue
                                      )), 
                                    onTap: (){
                                      Navigator.pushReplacement(
                                          context, 
                                          MaterialPageRoute(builder: (context) => Login(),
                                            settings: RouteSettings(
                                              arguments: null 
                                            ),
                                          ),
                                        );    
                                    },
                                  )
                                ],
                              )
                          ],
                      )
                  ),
                ),
              ),
            ),
        );
    }
}
 
 
 

ตัวอย่างลำดับการทำงาน

    เราลองสมัครสมาชิกโดยกรอกข้อมูลที่ไม่ถูกต้องดูการทำงาน จะได้ดังรูป
 
 


 
 
    ต่อไปลองกรอกข้อมูลให้ผ่านเงื่อนไข และถูกต้อง ก็จะได้เป็นดังนี้
 
 


 
 
    เมื่อกรอกข้อมูลถูกต้อง และทำการสมัครสมาชิกเรียบร้อย ก็กลับมายังหน้าล็อกอิน
ให้เราทำการล็อกอินเข้าใช้งานใน app อีกที
 
    ตอนนี้บนเว็บไซต์ในหน้า project Firebase ของเราก็จะมี user เพิ่มเข้ามาดังรูป
 
 




 
    ต่อไปลองทำการล็อกอินเข้าใช้งาน จะได้เป็นดังนี้
 
 


 
 
    หากยังไม่ได้ล็อกเอาท์ เมื่อปิด app แล้วเปิดขึ้นมาใหม่ ระบบก็จะยังจำสถานะการล็อกอินไว้
เราสามารถดึงข้อมูลต่างๆ มาแสดงในหน้า profile ได้ หากไปประยุกต๋เพิ่มเติม เช่น แสดงชื่อ เบอร์
โทร หรือวันล่าเข้าใช้งานล่าสุด หรือรูปภาพโพรไฟล์ เป็นต้น
 
    ถึงแม้ว่า Firebase Auth จะสะดวก เราก็ควรจะใช้เฉพาะที่จำเป็น หรือให้เหมาะกับ
รูปแบบงานที่จะใช้ เพราะเป็นการใช้ส่วนเสริมจากภายนอก หากมีปัญหาอาจจะกระทบการใช้งานรวม
โดยที่เราอาจจะไม่สามารถควบคุมได้ 
 
    เนื้อหาเกี่ยวกับ Firebase Auth ก็เป็นแนวทางหนึ่งสำหรับการปรับประยุกต์ใช้งานให็หลากหลาย สามารถ
นำไปต่อยอดเพิ่มเติมได้ หรืออาจจะต้องปรับเปลี่ยนการใช้งานให้เข้ากับ logic การทำงานของระบบของเราด้วย
เพราะการใช้งานในอีกรูปแบบ ก็อาจจะต้องใช้คำสั่งและวิธีที่แตกต่างกันไป 


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 25-11-2021


กรณีต้องการใช้งาน การล็อกอินแบบไม่ระบุตัวตน Anonymous sign-in

    สมมติว่าเราต้องการวิเคราะห์ หรือเก็บสถิติ หรือใช้งานร่วมกับ google analytic หรืออื่นๆ 
และไม่ต้องการให้ข้อมูลของสมาชิกในส่วนนี้ ต้องการแค่ประสบการณ์การใช้งาน เราสามารถกำหนด
ให้เรียกใช้รูปแบบการล็อกอินแบบไม่ระบุตัวต้น ซึ่งใน firebase authen ก็มีให้เราเปิดใช้งาน
โดยเข้าไปที่ firebase project console ทำการเพิ่ม provider ใหม่ แล้วเลือก Anonymous แล้ว
เปิดใช้งาน
 


 
 



 
 
     Anonymous จะมี User Data model เหมือนกันผู้ใช้ในระบบ Email,Password เพียงแต่ข้อมูลที่
ต้องระบุตัวตนต่างๆ จะเป็นค่า null และ มี isAnonymous เป็น true  สำหรับ Anonymous user ก็จะมี
uid ที่ไม่ซ้ำด้วย ดังนั้นถ้าเราใช้งานร่วมกันกับผู้ใช้ที่สมัครสมาชิกปกติ และเวลาเรียกใช้ข้อมูล เราอาจจะ
ต้องกำหนดค่าข้อมูล เป็นว่าง ในลักษณะนี้แทน
 
if (currentUser != null) { // ถ้ามีข้อมูล
  _id = currentUser.uid; // เอาค่ามาเก็บในัวแร เพื่อใช้แสดง
  _email = currentUser.email ?? '';
  print(currentUser); // แสดงรายละเอียดข้อมูลของผู้ใช้ทั้งหมดว่ามีอะไรบ้าง
}
 
    จะเห็นส่วนของอีเมล ถ้าเป็น Anonymous user จะมีค่าเป็น null ดังนั้น ถ้าเราไม่กำหนดเงื่อนไขว่า ถ้าเป็น
null ให้เป็น String ค่าว่าง เวลาตัวแปร _email ถูกเรียกใช้ จะเกิด error เป็นค่า null ได้  หรือวิธีแก้ปัญหาอีก
วิธี เราสามารถจำแนกประเภทของผู้ใช้โดยใช้ค่า isAnonymous คือ ถ้าเป็นสมาชิกปกติ ค่านี้จะเป็น false แต่
ถ้าเป็น Anonymous user ค่านี้จะเป็น true 
    จำไว้ว่า Anonymous user ค่า user ไม่ใช่ค่า null เพียงแต่มีข้อมูลด้านบนบางค่าเป็น null  เมื่อจะสร้างส่วน
ของการล็อกอินสำหรับกรณีนี้ สมมติเราใช้เป็น Guest  ก็มีเพียงแค่ปุ่มเดียว เพื่อทำคำสั่งก็ได้แล้ว  โดยเรียกใช้งาน
คำสั่งดังนี้
 
UserCredential userCredential = await FirebaseAuth.instance.signInAnonymously();
 
    กรณีใช้งาน AuthProvider ในไฟล์ auth_provider.dart สามารถเพิ่มคำสั่งนี้เข้าไปได้
 
Future<Map<String, dynamic>> guest() async {
  final SharedPreferences prefs = await _prefs; 
  var result;

  try{
    UserCredential userCredential = await FirebaseAuth.instance.signInAnonymously();
    await prefs.setBool("loginSuccess", true);
    result = json.decode('{"success": "Login successful"}');      
  }catch(e){
    String err = '{"error": "$e"}';
    result = json.decode(err);
  }
  return result;
} 
 
    เวลาเรียกใช้งาน เช่นในหน้า profile ก็อาจจะเพิ่มปุ่ม สำหรับ Guest แล้วไปในลักษณะดังนี้ได้
 
ElevatedButton(
  onPressed: () async {
    var result = await authProvider.guest();
    setState(() {
      _loginSuccess = true;
    });         
  }, 
  child: Text('Guest'),
),
 
    กรณีที่เป็น Anonymous user ถ้าออกจากระบบและเข้าใหม่ ระบบจะเพิ่มข้อมูลเข้าไปใน Firebase 
ใหม่ทุกครั้ง โดยมีค่า uid ใหม่  
 
 
 



กรณีเราไม่ต้องการให้มีจำนวนของ user มากเกินไป อาจจะปิดส่วนของ
การล็อกเอาท์ของ user นี้ออกไปก็ได้ อย่างไรก็ตามขึ้นกับการปรับประยุกต์ใช้งานและความเหมาะสม


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







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









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











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