การสร้าง Login Page & Authentication ผ่าน json API โดย Flutter และ PHP

1

การสร้างระบบ Login Page แล้วให้มีการส่งค่าไปยัง Server ที่ให้บริการตรวจสอบการ Sign in (Login) ใน Flutter สามารถทำได้ โดยผ่าน Firebase แต่ถ้าระบบ Authentication เราใช้ร่วมกับ Web Application อื่นๆที่เรามีใช้อยู่ก่อนแล้วนั้น เราจะต้องใช้วิธีการส่งผ่านค่าไปยัง Service ด้วย http โดยสามารถ ส่งมนรูปแบบ json เพื่อรับค่ากลับมาใช้งานจาก Web Service ได้ ในบันทึกนี้ได้จัดทำตัวอย่างไฟล์สำหรับเรียกใช้งาน โดยมีไฟล์ที่เป็น Fultter Project และไฟล์ .php

1.ไฟล์ API สำหรับตรวจสอบ password myapi.php

Code

<?php
header('Content-Type: application/json');
if(json_last_error()=== JSON_ERROR_NONE){
$result = json_decode(file_get_contents('php://input'),true);
}
$u = $result['user'];
$p = $result['password'];
$foud_user = true;
$fail_user = false;
function get_content($URL){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $URL);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}
$URL ="https://www.ubu.ac.th/ubuservice/ldap/ldapauthen.php?login_user=
$u&login_password=$p&sys_user=EngUser&sys_pass=xxxxxxxxxxxxx"; // เปลี่ยน xxx เป็น Key API ที่หน่วยงานกำหนดให้
// echo $URL."
";
$AuthenResult = get_content($URL);
if ($AuthenResult == "true") {
$ret =array("status" => $foud_user,"message" =>"Authen success user is $u and $p");
}else{
$ret =array("status" => $fail_user,"message" =>"Authen fail for user $u : $p : ");
}
echo json_encode($ret);
?>

โปรแกรม myapi.php จะรับค่าจาก Mobile App เพื่อมาทำการตรวจสอบ การ Authentication โดย รับค่าเข้าตัวแปร $result เป็นอาเรย์ และส่ง เข้าตัวแปร $p,$u เพื่อนำไปใช้งานต่อไป

ตัวแปร $URL จะระบุ url ที่จะทำการรับการตรวจสอบ โดยตัวอย่างนี้จะทำการส่งค่าไปยัง Server UBU เพื่อทำการขอตรวจสอบ โดยจะรับค่าที่ตรวจสอบส่งกลับมายังตัวแปร $AuthenResult เป็น Boolean True , False

เมื่อตรวจสอบเงื่อนไขแล้ว myapi.php จะทำการส่งค่ากลับไปยัง App ในรูปแบบ json_encode เพื่อให้ App ได้นำไปใช้งานต่อไป

หากท่านไม่มีระบบ Authentication ท่านสามารถสร้าง function php ตรวจสอบกับ Database MySQL ที่ท่านเก็บ User ไว้ได้ โดยประบเปลี่ยน code เพียงเล็กน้อยก็จะสามารถใช้งานได้

2.ส่วนของ Flutter Project

2.1 ไฟล์ pubspec.yaml เพิ่ม package

dependencies:

  •  http: ^0.13.5
  •   shared_preferences: ^2.0.15

2.3 สร้างไฟล์ main.dart

import 'package:flutter/material.dart';
import 'package:loginsystem/screen/login.dart';


void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: LoginScreen());
  }
}

2.4 ไฟล์ login.dart

import 'package:flutter/material.dart';
import 'package:form_validator/form_validator.dart';
import 'dart:convert';
import "dart:io";
import 'package:http/http.dart' as http;
import 'package:loginsystem/screen/roomlist.dart';
import 'package:shared_preferences/shared_preferences.dart';

class LoginScreen extends StatefulWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final formkey = GlobalKey<FormState>();
  var username = TextEditingController();
  var password = TextEditingController();
  @override
  void initState() {
    super.initState();
    checkLogin();
  }

  void checkLogin() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var status = prefs.getBool('isLoggedIn') ?? false;
    print(status);
    if (status == true) {
      Navigator.push(context, MaterialPageRoute(builder: (context) {
        return roomList();
      }));
    }

    // runApp(MaterialApp(home: status == true ? LoginScreen() : roomList()));
  }

  Future<void> login() async {
    var url = Uri.parse('http://it.eng.ubu.ac.th/online/api/myapi.php');
    var myReq = {};
    myReq['user'] = username.text;
    myReq['password'] = password.text;
    String jsonReq = jsonEncode(myReq);
    var response = await http.post(url,
        body: jsonReq,
        headers: {HttpHeaders.contentTypeHeader: 'application/json'});
    var res = jsonDecode(response.body);
    if (response.statusCode == 200) {
      print(res);
      if (res['status'] == true) {
        // print("Login Success");
        SharedPreferences prefs = await SharedPreferences.getInstance();
        prefs.setBool("isLoggedIn", true);
        Navigator.pushReplacement(context,
            MaterialPageRoute(builder: (context) {
          return roomList();
        }));
      } else {
        showDialog(
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text("Login Failed"),
                content: Text("โปรดตรวจสอบ User Password"),
                actions: <Widget>[
                  ElevatedButton(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      child: Text("Ok"))
                  // SizedBox(child: ,)
                ],
              );
            });
      }
    } else {
      print("Error ok");
    }
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("เข้าสู่ระบบ"),
      ),
      body: Padding(
        padding: const EdgeInsets.fromLTRB(15, 20, 15, 0),
        child: Container(
          child: Form(
              key: formkey,
              child: SingleChildScrollView(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SizedBox(
                      height: 10,
                    ),
                    Image.asset("assets/images/logo.png"),
                    SizedBox(
                      height: 10,
                    ),
                    Text(
                      "UBU ID:",
                      style: TextStyle(fontSize: 20),
                    ),
                    TextFormField(
                      controller: username,
                      validator: ValidationBuilder().maxLength(50).build(),
                      keyboardType: TextInputType.emailAddress,
                    ),
                    SizedBox(
                      height: 15,
                    ),
                    Text(
                      "PASSWORD:",
                      style: TextStyle(fontSize: 20),
                    ),
                    TextFormField(
                      controller: password,
                      validator: (value) {
                        if (value!.isEmpty) {
                          return "กรุณาป้อน Password";
                        }
                      },
                      obscureText: true,
                    ),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        child: Text(
                          "Login",
                          style: TextStyle(fontSize: 20),
                        ),
                        onPressed: () {
                          if (formkey.currentState!.validate()) {
                            login();
                          }
                          print("user = ${username} password =${password}");
                        },
                      ),
                    ),
                  ],
                ),
              )),
        ),
      ),
    );
  }
}

จาก code ด้านบน มีจุดสำคัญของการทำงานอยู่ สองส่วน คือ

  1. การส่งค่า จาก App ไปยัง Web Service ด้วย http POST ซึ่งเป็นการทำงานแบบ asynchronized โดย มีการใช้ Function แบบ Future เพื่อรรอรับข้อมูล จาก Web Service API ใน Function Login
  2. การใช้ SharePreference ในการเก็บสถานะการ Login ไว้ในการเปิดใช้ App กรณีที่ เปิดค้างไว้ Login สำเร็จแล้วกลับมาเปิดใหม่ สามารถตรวจสอบสถานะการ Login ระบบ หากการ Login ยังไม่มีการ Logout ออกจากระบบ ก็จะข้ามหน้าการ Login ไปยังการทำงานส่วนอื่นต่อไป ใน Code ที่แสดง จะมีส่วนประกอบ Function Login ,Check login , Logout เป็นประเด็นสำคัญ

2.5 ไฟล์ room.dart <json format>

class Room {
  Room(
      {required this.roomName,
      required this.locations,
      required this.picture,
      required this.capacity,
      required this.projector,
      required this.tv,
      required this.wifi,
      required this.mic,
      required this.online});
  late final String roomName;
  late final String locations;
  late final String picture;
  late final String capacity;
  late final String projector;
  late final String tv;
  late final String wifi;
  late final String mic;
  late final String online;

  Room.fromJson(Map<String, dynamic> json) {
    roomName = json['room_name'];
    locations = json['locations'];
    picture = json['picture'];
    capacity = json['capacity'];
    projector = json['projector'];
    tv = json['tv'];
    wifi = json['wifi'];
    mic = json['mic'];
    online = json['online'];
  }

  Map<String, dynamic> toJson() {
    final _data = <String, dynamic>{};
    _data['room_name'] = roomName;
    _data['locations'] = locations;
    _data['picture'] = picture;
    _data['capacity'] = capacity;
    _data['projector'] = projector;
    _data['tv'] = tv;
    _data['wifi'] = wifi;
    _data['mic'] = mic;
    _data['online'] = online;

    return _data;
  }
}

จาก Code ด้านบนเป็นการกำหนดรูปแบบของ List ที่จะทำการรับค่าจาก json ที่ได้รับจาก Web Service API จะถูกนำไปใช้ใน Widget roomList ในไฟล์ข้อ 2.6

2.6 ไฟล์ roomlist.dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:loginsystem/main.dart';
import 'dart:convert';
import 'package:loginsystem/model/room.dart';
import 'package:loginsystem/screen/showroom.dart';
import 'package:shared_preferences/shared_preferences.dart';

class roomList extends StatefulWidget {
  const roomList({Key? key}) : super(key: key);

  @override
  State<roomList> createState() => _roomListState();
}

class _roomListState extends State<roomList> {
  List<Room> ListRoom = [];
  //var url_pic = "http://app.eng.ubu.ac.th/online/booking/assets/upload/";

  @override
  void initState() {
    // TODO: implement initState
    // super.initState();
    print("refresh data");
    ListRoom.clear;
    fetchData();
  }

  Future fetchData() async {
    var urldat =
        Uri.parse("http://it.eng.ubu.ac.th/online/api/room3_api.php");
    final response = await http.get(urldat);

    if (response.statusCode == 200) {
      final jsonStr = jsonDecode(response.body);
      // print(jsonStr);
      setState(() {
        jsonStr.forEach((data) {
          final tmpDat = Room.fromJson(data);
          ListRoom.add(tmpDat);
        });
      });
    } else if (response.statusCode == 400) {
      print("Fail Get data");
    } else {
      return const CircularProgressIndicator();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        // appBar: AppBar(
        //   title: Text("Room"),
        //   centerTitle: true,
        //   actions: [Icon(Icons.refresh)],
        // ),
        // drawer: Drawer(),
        backgroundColor: Colors.pink[400],
        body: ListView(children: [
          SizedBox(height: 50.0),
          Padding(
            padding: EdgeInsets.only(left: 40),
            child: Row(
              children: [
                Text(
                  "Room",
                  style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 25.0,
                      color: Colors.white),
                ),
                SizedBox(
                  width: 5.0,
                ),
                Text(
                  "Booking",
                  style: TextStyle(fontSize: 25.0, color: Colors.white60),
                ),
                SizedBox(
                  width: 5,
                ),
                SizedBox(
                  child: ElevatedButton.icon(
                    onPressed: () {
                      logout();
                    },
                    icon: Icon(Icons.logout),
                    label: Text("Logout"),
                    style: ElevatedButton.styleFrom(primary: Colors.pink),
                  ),
                )
              ],
            ),
          ),
          SizedBox(
            height: 50.0,
          ),
          Container(
            height: MediaQuery.of(context).size.height - 250.0,
            decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(65.0),
                    bottomRight: Radius.circular(65.0))),
            child: ListView(
              padding: EdgeInsets.only(left: 25, right: 20),
              children: [
                Container(
                    height: MediaQuery.of(context).size.height - 300,
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: ListView.builder(
                        itemCount: ListRoom.length,
                        itemBuilder: (context, index) => Card(
                          child: ListTile(
                            leading: CircleAvatar(
                              backgroundImage: NetworkImage(
                                  'http://app.eng.ubu.ac.th/online/booking/assets/upload/' +
                                      ListRoom[index].picture),
                              backgroundColor: Colors.pink,
                              radius: 30.0,
                            ),
                            title: Text(ListRoom[index].roomName),
                            subtitle: Text(ListRoom[index].locations),
                            trailing: const Icon(
                              Icons.arrow_forward,
                            ),
                            onTap: () {
                              Navigator.of(context).push(MaterialPageRoute(
                                  builder: (context) =>
                                      ShowRoom(showroom: ListRoom[index])));
                            },
                          ),
                          // itemBuilder: (BuildContext context, int index) {
                          //   return BuildRoomItem(ListRoom[index].roomName,
                          //       ListRoom[index].locations,        ListRoom[index].picture);
                          // },
                        ),
                      ),
                    )),
              ],
            ),
          ),
          SizedBox(
            height: 10,
          ),
        ]),
        // floatingActionButton: FloatingActionButton(
        //   onPressed: () {
        //     initState();
        //   },
        //   tooltip: 'Refresh',
        //   child: const Icon(Icons.refresh),
        //   backgroundColor: Colors.pink,
        // ),
      ),
    );
  }
  void logout() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setBool("isLoggedIn", false);
    Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) {
      return MyApp();
    }));
  }
  // End Widget
}

จาก Code ด้านบนจะเป็นการสร้างรายการ List room ที่ดึงค่ามาจาก API Web Service โดยใช้ http.get แล้วนำมา สร้างรายการด้วย ListView.builder และ itemBuilder และ card ในการสร้างรายการ

2.7 ไฟล์ showroom.dart

import 'package:flutter/material.dart';
import 'package:loginsystem/model/room.dart';

class ShowRoom extends StatelessWidget {
  final Room showroom;

  const ShowRoom({Key? key, required this.showroom}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text(showroom.roomName),
        ),
        body: Container(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: SingleChildScrollView(
              child: Center(
                  child: Container(
                decoration: BoxDecoration(
                  color: Colors.blue,
                  borderRadius: BorderRadius.circular(20),
                ),
                child: Column(
                  children: <Widget>[
                    SizedBox(
                      height: 10,
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Image.network(
                        'http://app.eng.ubu.ac.th/online/booking/assets/upload/' +
                            showroom.picture,
                        height: 350,
                        width: double.infinity,
                        fit: BoxFit.cover,
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    Text(
                      showroom.roomName,
                      style:
                          TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    Text(
                      "ที่ตั้ง ${showroom.locations}",
                      style: TextStyle(fontSize: 16),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Container(
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(20),
                        ),
                        // borderRadius: BorderRadius.only(
                        //     topLeft: Radius.circular(15.0),
                        //     bottomRight: Radius.circular(15.0))),
                        child: Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Column(
                            children: [
                              Text(
                                "ข้อมูลห้องประชุม",
                                style: TextStyle(fontSize: 20),
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "จำนวนที่นั่ง :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  Text(
                                    "${showroom.capacity} ที่นั่ง",
                                    style: TextStyle(fontSize: 16),
                                  ),
                                ],
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "Projector :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  SizedBox(
                                    child: GetDevice(showroom.projector),
                                  ),
                                ],
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "โทรทัศน์ :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  SizedBox(
                                    child: GetDevice(showroom.tv),
                                  ),
                                ],
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "รองรับ WiFi :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  SizedBox(
                                    child: GetDevice(showroom.wifi),
                                  ),
                                ],
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "เครื่องเสียง+ไมค์ :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  SizedBox(
                                    child: GetDevice(showroom.mic),
                                  ),
                                ],
                              ),
                              SizedBox(
                                height: 10,
                              ),
                              Row(
                                children: [
                                  Text(
                                    "รองรับจัดประชุมออนไลน์ :",
                                    style: TextStyle(
                                        fontSize: 20,
                                        fontWeight: FontWeight.normal),
                                  ),
                                  SizedBox(
                                    width: 10,
                                  ),
                                  SizedBox(
                                    child: GetDevice(showroom.online),
                                  ),
                                ],
                              ),
                            ],
                            mainAxisAlignment: MainAxisAlignment.start,
                          ),
                        ),
                      ),
                    )
                  ],
                ),
              )),
            ),
          ),
        ),
      );
}

Widget GetDevice(String Device) {
  var Status = "";
  if (Device == "1") {
    Status = "มี";
  } else {
    Status = "ไม่มี";
  }
  return Text(
    Status,
    style: TextStyle(fontSize: 17),
  );
}

จาก Code ด้านบน จะเป็นการแสดงรายละเอียดข้อมูลที่ดึงมาจาก Web Service

2.8 ไฟล์ room3_api.php

<?php
header('Content-Type: application/json');
	ini_set('display_errors', 1);
	error_reporting(~0);

	$serverName = "localhost";
	$userName = "root";
	$userPassword = "toor";
	$dbName = "db_room";

	$conn = mysqli_connect($serverName,$userName,$userPassword,$dbName);
	$sql = "SELECT tb_room.room_name,tb_room.locations,tb_room.picture,tb_room.capacity,
	tb_room.projector,tb_room.tv,tb_room.wifi,tb_room.mic,tb_room.online FROM tb_room";
	mysqli_query($conn,"SET CHARACTER SET UTF8");	
	mysqli_query($conn,"SET character_set_client = UTF8");
	mysqli_query($conn,"SET character_set_results = UTF8");
	mysqli_query($conn,"SET character_set_connection = UTF8");
	$query = mysqli_query($conn,$sql);
	if (!$query) {
		printf("Error: %s\n", $conn->error);
		exit();
	}
	$resultArray = array();
	while($result = mysqli_fetch_array($query,MYSQLI_ASSOC))
	{
		array_push($resultArray,$result);
	}
	mysqli_close($conn);

	echo json_encode($resultArray);
?>

จาก Code ด้านบนเป็น code php สำหรับดึงข้อมูลจาก Database ส่งรายการ Table เข้า Array แล้วส่งกลับเป็น json ให้ App นำไปใช้งาน

หาก Login ผ่านจะแสดงรายการ
เมื่อกดดูรายการจะแสดงข้อมูล

1 thought on “การสร้าง Login Page & Authentication ผ่าน json API โดย Flutter และ PHP

Leave a Reply

Your email address will not be published. Required fields are marked *