ประยุกต์ระบบ Register Login ผ่าน API บน server ใน Flutter

บทความเมื่อ 2 - 3 วันก่อน โดย Ninenik Narkdee
authen login flutter ระบบสมาชิก register api provider

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



เนื้อหาต่อไปนี้เป็นการประยุกต์ใช้งาน ต่อเนื้อง
จากเนื้อหาตอนที่แล้ว เกี่ยวกับระบบสมาชิก ซึ่งตอน
ที่แล้วเราแค่จำลองข้อมูลที่เครื่องโดยใช้ชนิดข้อมูล
SharedPreferences ทบทวนตอนที่แล้วได้ที่
    ประยุกต์เก็บข้อมูลด้วย shared preferences ใน Flutter http://niik.in/1059
 
เนื้อหาในตอนนี้จะมีการปรับโค้ดเล็กน้อยในไฟล์ login  register  และ profile 
แต่จะมีส่วนต่างๆ ที่เสริมเพิ่มเติมเข้ามาค่อยข้างซับซ้อน เพราะเป็นเนื้อหาจากบทความต่างๆ
ที่เราเคยใช้งานมาแล้ว ไม่ว่าจะเป็นการใช้งาน provider การใช้งาน http เพื่อเชื่อมต่อกับ
server รวมถึงการใช้งาน SharedPreferences ที่ใช้ในตอนที่แล้วด้วย  นอกจากนั้น เรายังมีการ
กำหนดหรือใช้งาน API จากฝั่ง server ซึ่งได้เขียนบทความเป็นแนวทางไว้แล้วตามลิ้งค์ด้านล่าง
    สร้าง REST API ระบบ Login ด้วย Slim framework 4 http://niik.in/1058
 

 

อธิบายสิ่งที่จะทำ

    เราจะมี api สำหรับระบบสมาชิก ที่ทำหน้าที่ สร้างบัญชีผู้ใช้ใหม่ ตรวจสอบการล็อกอินเข้าสู่ระบบ
และดึงข้อมูลสมาชิกจากฐานข้อมูล MySQL บน server มาแสดง ในที่นี้เราจำลองที่เครื่อง local
แนวทาง api ที่เราใช้จะเป็นตามลิ้งค์ดังนี้ http://niik.in/1058 
    ในฝั่ง flutter หรือ app ของเราก็จะทำการใช้งานรูปแบบฟอร์มทั้งหน้าสมัครสมาชิก  และหน้าล็อกอิน
จากตอนที่แล้วมาประยุกต์ โดยส่งข้อมูลจากฟอร์มผ่านการใช้งาน provider ไปทำการบันทึกข้อมูลในฐาน
ข้อมูลบน server และทำการส่งข้อมูลการล็อกอินไปยัง server เพื่อตรวจสอบการล็อกอินเข้าใช้งาน
เมื่อล็อกอินเข้าระบบผ่าน ก็จะทำการดึงข้อมูลผู้ใช้มาแสดง และเก็บข้อมูลเบื้องต้นหรือข้อมูลที่ทั่วไปไว้ใน
SharedPreferences ให้คงไว้ในเครื่อง จนกว่าจะล็อกเอาท์ซึ่งจะทำการลบออกเครืองไป  สำหรับข้อมูล
ผู้ใช้เพิ่มเติม ที่เป็นข้อมูลสำคัญเราก็จะใช้การดึงข้อมูลผ่าน provider มาใช้งาน แนวทางประมาณนี้
 
 

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

 

 

 
 

เนื้อหาที่ควรรู้เพิ่มเติม

    เกี่ยวกับการใช้งาน http http://niik.in/1038 
    เกียวกับการใช้งาน provider http://niik.in/1046 
    เกี่ยวกับการใช้งาน SharedPreferences http://niik.in/1059 
    เกียวกับการใช้งาน form http://niik.in/1048 
 
    เริ่มต้นขั้นตอนการประยุกต์ดังนี้

 
 

สร้าง Data Model ของ user

    เราสร้าง User model ให้สอดคล้องกับฐานข้อมูล จากบทความ REST API ระบบ Login 
ได้ดังนี้ http://niik.in/1058
 
    models >  user_model.dart
 

    ไฟล์ user_model.dart

 
// กำหนดฟิลด์ข้อมูลของตาราง
class UserFields {
  // สร้างเป็นลิสรายการสำหรับคอลัมน์ฟิลด์
  static final List<String> values = [
    id, email, name, token, createdate, lastlogin, active
  ];

  // กำหนดแต่ละฟิลด์ของตาราง ต้องเป็น String ทั้งหมด
  static final String id = 'user_id'; 
  static final String email = 'user_email';
  static final String name = 'user_name';
  static final String token = 'user_token';
  static final String createdate = 'user_createdate';
  static final String lastlogin = 'user_lastlogin';
  static final String active = 'user_active';
}


// ส่วนของ Data Model 
class User {
  final int id; 
  final String email;
  final String? name;
  final String? token;
  final DateTime? createdate;
  final DateTime? lastlogin;
  final bool? active;

  // constructor
  const User({
    required this.id,
    required this.email,
    this.name,
    this.token,
    this.createdate,
    this.lastlogin,
    this.active,
  });

   // สำหรับแปลงข้อมูลจาก Json เป็น  object
  static User fromJson(Map<String, Object?> json) =>  
    User(
      id: json[UserFields.id] as int,
      email: json[UserFields.email] as String,
      name: json[UserFields.name] as String,
      token: json[UserFields.token] as String,
      createdate: DateTime.parse(json[UserFields.createdate] as String),
      lastlogin: DateTime.parse(json[UserFields.lastlogin] as String),
      active: json[UserFields.active] == 1,
    );

  // สำหรับแปลง  object เป็น Json 
  Map<String, Object?> toJson() => {
    UserFields.id: id,
    UserFields.email: email,    
    UserFields.name: name,
    UserFields.token: token,
    UserFields.createdate: createdate?.toIso8601String() ?? "",
    UserFields.lastlogin: lastlogin?.toIso8601String() ?? "",    
    UserFields.active: active!=null ? 1 : 0,
  }; 

}
 
    ข้างต้นเรากำหนดให้แค่ id กับ email เป็นข้อมูลที่จำเป็นในการสร้าง User object ที่ต้องมี
ส่วนค่าอื่นๆ จะมีหรือไม่ก็ได้ไม่บังคับ 
 
 

กำหนด API path ไฟล์

    ให้เราสร้างไฟล์ สำหรับเก็บค่าคงที่ของ url ที่จะใช้งาน api ในที่นี้จะใช้เก็บไว้ใน
 
    constants > api_parth.dart
 

    ไฟล์ api_parth.dart

 
class ApiUrl {
  // ค่าตัวแปรสำหรับใช้งานจริง
  static const String liveBaseURL = "https://localhost/demo/api";
  // กรณีทดสอบที่ localhost android ใช้ค่าตามนี้ได้เลย เปลี่ยน path และ port เท่านั้น
  static const String localBaseURL = "https://10.0.2.2:443/demo/api";

  static const String baseURL = localBaseURL; // ทดสอบที่เครื่องใช้ค่านี้
  static const String login = baseURL + "/user/authen";
  static const String register = baseURL + "/user/create";
}
 
 

กำหนดส่วนของ Provider

    ต่อไปเป็นส่วนของการจัดการทั้งหมดของระบบสมาชิก เรารวมไว้ใน UserProvider class ในไฟล์
 
    utils > user_provider.dart
 

    ไฟล์ user_provider.dart

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

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../constants/api_path.dart';
import '../models/user_model.dart';
 
class UserProvider with ChangeNotifier {
  // ใช้งานข้อมูล SharedPreferences
  final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

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

  // ฟังก์ชั่นดึงข้อมูลผู้ใช้ทั่วไป จากข้อมูล SharedPreferences
  Future<User> getUser() async {
    final SharedPreferences prefs = await _prefs; 
    return User(
        id: prefs.getInt('user_id')!, 
        email: prefs.getString('user_email')!, 
        token: prefs.getString('user_token')!, 
      );
  }

  // ฟังก์ชั่นดึงข้อมูล token จากข้อมูล SharedPreferences
  Future<String> getToken() async {
    final SharedPreferences prefs = await _prefs;
    String token = prefs.getString("user_token")!;
    return token;
  }  

  //  ฟังก์ชั่นล็อกเอาท์ออกจากระบบ ล้างค่าข้อมูล SharedPreferences
  Future<bool> logout() async {
    final SharedPreferences prefs = await _prefs;
    return await prefs.clear();
  }    

  // ฟังก์ชั่นดึงข้อมูลจาก server โดยใช้ token
  Future<Map<String, dynamic>> get(String token,int id) async {
    final SharedPreferences prefs = await _prefs; 
    var result;

    // ทำการดึงข้อมูลจาก server ตาม url ที่กำหนด
    final response = await http.get(
      Uri.parse(ApiUrl.baseURL+'/user/'+id.toString()),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer '+token
      },
    );
  
      // เมื่อมีข้อมูลกลับมา
    if (response.statusCode == 200) {
      var body = response.body;
      result = await json.decode(body);  
      result = result[0]; // เนื่องจากข้อมูลจาก api เป็น array เลยใช้เฉพาะข้อมูล key = 0
      notifyListeners();
    } else { // กรณี error
      throw Exception('Failed to load data');
    }
    return result;
  }  

  // ฟังก์ชั่นสำหรับทำการล็อกอิน โดยส่งค่าไปยัง server 
  Future<Map<String, dynamic>> authen(String email, String password) async {
    final SharedPreferences prefs = await _prefs; 
    var result;

    final Map<String, dynamic> loginData = {
        'email': email,
        'password': password
      };

    // ทำการดึงข้อมูลจาก server ตาม url ที่กำหนด
    final response = await http.post(
      Uri.parse(ApiUrl.login),
      body: json.encode(loginData),
      headers: {'Content-Type': 'application/json'},
    );
  
      // เมื่อมีข้อมูลกลับมา
    if (response.statusCode == 200) {
      var body = response.body;
      result = await json.decode(body);  
      if(result['success']!=null){ // กรณีมีข้อมูลกลับบมา และล็อกอินผ่าน
        // บันทึกข้อมูลเบื้องต้นลงใน SharedPreferences
        await prefs.setInt("user_id", int.parse(result['id']));
        await prefs.setString("user_email", result['email']);
        await prefs.setString("user_token", result['jwt']);
        await prefs.setInt("user_token_expired", result['expireAt']);
        await prefs.setBool("loginSuccess", true);
        notifyListeners();
      }
    } else { // กรณี error
      throw Exception('Failed to load data');
    }
    return result;
  }

  // ฟังก์ชั่นสำหรับสร้างบัญชีใหม่ โดยส่งค่าไปยัง server แล้วบันทึกลงฐานข้อมูล
  Future<Map<String, dynamic>> create(String email, String password) async {
    var result;

    // ข้อมูลผู้ใช้ที่ต้องส่งไป
    final Map<String, dynamic> userData = {
        'email': email,
        'password': password
      };

    // ทำการดึงข้อมูลจาก server ตาม url ที่กำหนด
    final response = await http.post(
      Uri.parse(ApiUrl.register), // ใช้ url จากค่าที่กำหนด
      body: json.encode(userData),
      headers: {'Content-Type': 'application/json'},
    );
  
      // เมื่อมีข้อมูลกลับมา
    if (response.statusCode == 200) {
      var body = response.body;
      result = await json.decode(body);  
    } else { // กรณี error
      throw Exception('Failed to load data');
    }
    return result;
  }  
 
}
 
    คำอธิบายเขียนไว้ในโค้ด
 
 

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

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

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

 
......
....
      return MultiProvider(
        providers: [
        ChangeNotifierProvider(create: (context) => UserProvider()), 
      ], 
        child: MaterialApp(
                  theme: ThemeData(
....
...
 
 

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

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

    ไฟล์ profile.dart

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

import '../utils/user_provider.dart';
import '../models/user_model.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 bool _loginSuccess;  // กำหดตัวแปรสถานะการล็อกอิน
    // ส่วนของตัวแปรข้อมูลพื้นฐาน
    User? _user;
    int _id = 0;
    String _email = '';
    String _token = '';
    // ส่วนของตัวแปรสำหรับข้อมูลที่ดึงเพิ่มเติม
    String _createdate = '';


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

    // ตั้งค่าเริ่มต้น
    void loadSettings() async {
      // ใช้งาน provider
      UserProvider userProvider = context.read<UserProvider>();
      _loginSuccess = await userProvider.getLoginStatus(); // ถึงสถานะการล็อกอิน ถ้ามี
      if(_loginSuccess){ // ถ้าล็อกอินอยู่
        fetchUser(); // ดึงข้อมูลของผู้ใช้ ถ้าล็อกอินอยู่
      }
    }   

    // ฟังก์ชั่นสำหรับดึงข้อมูลผู้ใช้
    void fetchUser() async {
      // ใช้งาน provider
      UserProvider userProvider = context.read<UserProvider>();
      setState(() {
        _loginSuccess = true;
      });           
      // ดึงข้อมูลทั่วไปของผู้ใช้ 
      _user = await userProvider.getUser();
      _email = _user!.email;
      _id = _user!.id;
      _token = _user!.token ?? '';
      // ดึงข้อมูลเพิ่มเติมในฐานข้อมูลบน server
      Map<String, dynamic> _userExt = await userProvider.get(_token,_id);
      _createdate = _userExt['user_cratedate'];
    }

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

    @override
    Widget build(BuildContext context) {
      // ใช้งาน provider
        UserProvider userProvider = context.read<UserProvider>();
        return Scaffold(
            appBar: AppBar(
                title: Text('Profile'),
            ),
            body: FutureBuilder<bool>( 
              future: userProvider.getLoginStatus(), // ข้อมูล 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 userProvider.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) {
                                    // ทำคำสั่งดึงข้อมูลผู้ใช้ เมื่อล็อกอินผ่าน
                                    fetchUser();   
                                  }
                                              
                                }, 
                                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 '../utils/user_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> {
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    // กำหนดตัวแปรรับค่า
    final _email = TextEditingController();
    final _password = TextEditingController();
 
    @override
    void initState() {
      super.initState();
    }

    // กำหนดสถานะการแสดงแบบรหัสผ่าน
    bool _isHidden = true;    
    bool _authenticatingStatus = false;    

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

    @override
    Widget build(BuildContext context) {
        // ใช้งาน provider
        UserProvider userProvider = context.read<UserProvider>();
        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;

                                    // ใช้ provider ส่ง request ล็อกอินไปยัง server
                                    var result = await userProvider.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 '../utils/user_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> {
   
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

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

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

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

    @override
    Widget build(BuildContext context) {
      // ใช้งาน provider
      UserProvider userProvider = context.read<UserProvider>();
        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;

                                    // ใช้ provider ส่ง request สร้างบัญชีสมาชิกใหม่
                                    var result = await userProvider.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 
                                            ),
                                          ),
                                        );    
                                    },
                                  )
                                ],
                              )
                          ],
                      )
                  ),
                ),
              ),
            ),
        );
    }
}
 
    เนื้อหาบทความนี้จะเน้นที่ตัวอย่าง และคำอธิบายในโค้ดเท่านั้น เป็นแนวทางทำความเข้าใจ หรือ
นำไปปรับประยุกต์ตามต้องการ หลักการทำงานก็คล้ายๆจากบทความตอนที่แล้ว เพียงแต่เนื้อหานี้
เราประยุกต์ใช้งานจริง
 
 

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

    เมื่อเรามายังหน้าสมัครสมาชิก ลองกรอกข้อมูลเฉพาะอีเมล แล้วทดสอบสมัครสมาชิก
ก็จะไม่ผ่าน และขึ้นข้อความแจ้งว่าต้องกรอกทั้งอีเมลและรหัสผ่าน ตามรูป
 
 


 
 
    ต่อไปเราลองกรอกข้อมูลตามตัวอย่าง โดยให้แสดงรหัสผ่านให้เห็น จะได้ขั้นตอนขณะ
กำลังสมัคร ดังนี้
 
 


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


 
 
    ในหน้าล็อกอินเราจะลองทดสอบ error ต่างๆ เช่น ไม่กรอกข้อมูลใดๆ กรอกเฉพาะอีเมล กรอก
รูปแบบอีเมลไม่ถูกต้อง ก็จะขึ้น error ต่างๆ ตามที่กำหนดใน API
 
 


 
 
    ต่อไปกรอกข้อมูลสมาชิกที่เราเพิ่งสมัครไป ให้ถูกต้อง ก็จะล็อกอินผ่าน และแสดงข้อมูล
สมาชิกดังตัวอย่าง
 
 


 
 
 
    เกี่ยวกับการจัดการข้อมูลสมาชิกบน server ของเราเองก็มีเนื้อหาประมาณนี้ หวังว่าจะเป็นแนวทาง
ปรับใช้งานต่อไป เนื้อหาตอนหน้าจะเป็นอะไร รอติดตาม


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


แก้ปัญหากรณีทดสอบ server ที่เครื่อง local

 
ให้เพิ่มส่วนของการกำหนดค่าดังนี้เข้าไป ในไฟล์ main.dart แล้วรีสตาร์ท app ใหม่อีกครั้ง
 
void main() { 
  HttpOverrides.global = MyHttpOverrides(); // เพิ่มส่วนนี้ให้รองรับกรณีทดสอบ
  runApp(const MyApp());
}


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



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









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









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











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