Flutter dan Rest API laravel
Proyek ini adalah contoh pembuatan REST API menggunakan Laravel 11. Berikut adalah langkah-langkah untuk memulai dan mengembangkan proyek ini.
Langkah-langkah Pembuatan REST API
1. Membuat Proyek Laravel
Pertama, buat proyek Laravel baru dengan menggunakan Composer.
composer create-project --prefer-dist laravel/laravel rest-api-laravel
2. Masuk ke Direktori Proyek
Masuk ke dalam direktori proyek yang baru saja dibuat.
cd rest-api-laravel
3. Konfigurasi Database
Modifikasi file .env
untuk mengatur koneksi ke database. Berikut adalah konfigurasi yang digunakan:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=rest-api-db
DB_USERNAME=root
DB_PASSWORD=password
4. Install API
Jalankan perintah berikut untuk menginstall API:
php artisan install:api
isinya adalah untuk sebelum laravel 11 adalah composer require laravel/sanctum , setting config php artisan vendor:publish — provider=”Laravel\Sanctum\SanctumServiceProvider” dan php artisan migrate , app/Http/Kernel.php → ‘api’ => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.’:api’,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
5. Menjalankan Database Migration
Di akhir proses install, akan muncul prompt untuk menjalankan database migration.
One new database migration has been published. Would you like to run all pending database migrations? (yes/no) [yes]:
Ketika prompt menanyakan apakah ingin membuat database rest-api-db
jika belum ada, pilih Yes
.
WARN The database 'rest-api-db' does not exist on the 'mysql' connection.
┌ Would you like to create it? ────────────────────────────────┐
│ ● Yes / ○ No │
└──────────────────────────────────────────────────────────────┘
Tekan enter untuk melanjutkan. Ini akan menjalankan migration dan membuat tabel yang diperlukan.
6. Modifikasi Model User
Buka file app/Models/User.php
dan tambahkan Laravel\Sanctum\HasApiTokens
pada model User.
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable,HasApiTokens ;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
7. Membuat AuthController
Jalankan perintah berikut untuk membuat controller:
php artisan make:controller Api/AuthController
Setelah perintah ini dijalankan, akan ada file baru AuthController.php
di direktori app/Http/Controllers/Api
. Buka file tersebut dan tambahkan method register()
, login()
, dan logout()
. Contoh kode dapat dilihat di repository ini.
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|max:255|unique:users',
'password' => 'required|string|min:8'
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password)
]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'data' => $user,
'access_token' => $token,
'token_type' => 'Bearer'
]);
}
public function login(Request $request)
{
if (! Auth::attempt($request->only('email', 'password'))) {
return response()->json([
'message' => 'Unauthorized'
], 401);
}
$user = User::where('email', $request->email)->firstOrFail();
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'message' => 'Login success',
'access_token' => $token,
'token_type' => 'Bearer'
]);
}
public function logout()
{
Auth::user()->tokens()->delete();
return response()->json([
'message' => 'logout success'
]);
}
}
8. Mendefinisikan Route
Untuk API, definisikan route di file routes/api.php
. Buka file routes/api.php
dan tambahkan route untuk register
, login
, dan logout
. Contoh kode dapat dilihat di repository ini.
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/register', [\App\Http\Controllers\Api\AuthController::class, 'register']);
Route::post('/login', [\App\Http\Controllers\Api\AuthController::class, 'login']);
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [\App\Http\Controllers\Api\AuthController::class, 'logout']);
});
9. Instalasi Flutter
Buka terminal dan jalankan perintah berikut untuk membuat proyek Flutter baru:
flutter create Flutter_app
cd Flutter_app
Buka file pubspec.yaml
dan tambahkan dependency http
:
dependencies:
flutter:
sdk: flutter
http: ^0.14.0
dio: ^4.0.6
shared_preferences: ^2.0.15
Kemudian, jalankan flutter pub get
untuk menginstal paket.
10. Buat Helpers :
Buat file baru di dalam folder lib/helpers
, yaitu
api_client.dart
import 'package:dio/dio.dart';
// Inisialisasi objek Dio dengan pengaturan dasar
final Dio dio = Dio(BaseOptions(
baseUrl:
'https://example.com/api/', // URL dasar untuk API
connectTimeout:
5000, // Waktu maksimum untuk mencoba menghubungkan (dalam milidetik)
receiveTimeout:
3000, // Waktu maksimum untuk menerima respons (dalam milidetik)
));
// Kelas ApiClient untuk menangani permintaan HTTP
class ApiClient {
// Metode untuk melakukan permintaan GET
Future<Response> get(String path) async {
try {
final response =
await dio.get(Uri.encodeFull(path)); // Melakukan permintaan GET
return response;
} on DioError catch (e) {
throw Exception(e.message); // Menangani kesalahan permintaan
}
}
// Metode untuk melakukan permintaan POST
Future<Response> post(String path, dynamic data) async {
try {
final response = await dio.post(Uri.encodeFull(path),
data: data); // Melakukan permintaan POST dengan data
return response;
} on DioError catch (e) {
throw Exception(e.message); // Menangani kesalahan permintaan
}
}
// Metode untuk melakukan permintaan PUT
Future<Response> put(String path, dynamic data) async {
try {
final response = await dio.put(Uri.encodeFull(path),
data: data); // Melakukan permintaan PUT dengan data
return response;
} on DioError catch (e) {
throw Exception(e.message); // Menangani kesalahan permintaan
}
}
// Metode untuk melakukan permintaan DELETE
Future<Response> delete(String path) async {
try {
final response =
await dio.delete(Uri.encodeFull(path)); // Melakukan permintaan DELETE
return response;
} on DioError catch (e) {
throw Exception(e.message); // Menangani kesalahan permintaan
}
}
}
user_info.dart
import 'package:shared_preferences/shared_preferences.dart';
const String TOKEN = "token"; // Konstanta untuk kunci token
const String EMAIL = "email"; // Konstanta untuk kunci email pengguna
class UserInfo {
// Metode untuk menyimpan token ke shared preferences
Future<void> setToken(String value) async {
final SharedPreferences pref = await SharedPreferences
.getInstance(); // Mendapatkan instance shared preferences
await pref.setString(TOKEN, value); // Menyimpan token sebagai string
}
// Metode untuk mengambil token dari shared preferences
Future<String?> getToken() async {
final SharedPreferences pref = await SharedPreferences
.getInstance(); // Mendapatkan instance shared preferences
return pref.getString(TOKEN); // Mengambil token yang disimpan
}
// Metode untuk menyimpan email pengguna ke shared preferences
Future<void> setEmail(String value) async {
final SharedPreferences pref = await SharedPreferences
.getInstance(); // Mendapatkan instance shared preferences
await pref.setString(EMAIL, value); // Menyimpan email sebagai string
}
// Metode untuk mengambil email pengguna dari shared preferences
Future<String?> getEmail() async {
final SharedPreferences pref = await SharedPreferences
.getInstance(); // Mendapatkan instance shared preferences
return pref.getString(EMAIL); // Mengambil email yang disimpan
}
// Metode untuk menghapus semua data dari shared preferences (logout)
Future<void> logout() async {
final SharedPreferences pref = await SharedPreferences
.getInstance(); // Mendapatkan instance shared preferences
await pref.clear(); // Menghapus semua data yang disimpan
}
}
11. Buat Halaman Model:
Buat dua file baru di dalam folder lib/model
, yaitu user_model.dart
Berikut kode UserModel:
user_model.dart
class UserModel {
final String email;
final String password;
UserModel({
required this.email,
required this.password,
});
// Metode untuk mengubah UserModel menjadi format JSON
Map<String, dynamic> toJson() {
return {
'email': email,
'password': password,
};
}
// Metode factory untuk membuat UserModel dari format JSON
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
email: json['email'],
password: json['password'],
);
}
}
12. Buat Halaman Login dan Register:
Buat dua file baru di dalam folder lib/ui
, yaitu login_view.dart
dan register_view.dart
. Berikut adalah contoh sederhana untuk halaman login:
login_view.dart
import 'package:flutter/material.dart';
import '/controller/login_controller.dart';
import '/model/user_model.dart';
import 'RegisterView.dart';
import 'MainMenuScreen.dart';
class LoginView extends StatefulWidget {
@override
_LoginViewState createState() => _LoginViewState();
}
class _LoginViewState extends State<LoginView> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final LoginController _loginController = LoginController();
bool _loading = false; // Penanganan loading state
// Fungsi untuk menangani tombol login
Future<void> _handleLogin() async {
String email = _emailController.text;
String password = _passwordController.text;
UserModel user = UserModel(email: email, password: password);
setState(() {
_loading = true; // Menampilkan loading indicator
});
bool isLoggedIn = await _loginController.login(user);
setState(() {
_loading = false; // Menyembunyikan loading indicator
});
if (isLoggedIn) {
// Menampilkan pesan sukses dan navigasi ke layar utama
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Login successful!')));
// Navigasi ke layar menu utama (ganti dengan navigasi aktual Anda)
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MainMenuScreen()),
);
} else {
// Menampilkan pesan kesalahan
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Login gagal')));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Kolom input email
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
// Kolom input password
TextField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onSubmitted: (value) =>
_handleLogin(), // Menambahkan onSubmitted agar enter langsung login
),
SizedBox(height: 20),
// Tombol login
_loading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: _handleLogin,
child: Text('Login'),
),
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RegisterView()),
);
},
child: Text('Register'),
),
],
),
),
);
}
}
register_view.dart
import 'package:flutter/material.dart';
import '/controller/register_controller.dart';
class RegisterView extends StatefulWidget {
@override
_RegisterViewState createState() => _RegisterViewState();
}
class _RegisterViewState extends State<RegisterView> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final RegisterController _registerController = RegisterController();
bool _loading = false; // Penanganan loading state
// Fungsi untuk menangani tombol register
Future<void> _handleRegister() async {
String name = _nameController.text;
String email = _emailController.text;
String password = _passwordController.text;
setState(() {
_loading = true; // Menampilkan loading indicator
});
bool isRegistered =
await _registerController.register(name, email, password);
setState(() {
_loading = false; // Menyembunyikan loading indicator
});
if (isRegistered) {
// Tampilkan pesan sukses dan navigasi kembali ke layar login
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Registration successful!')));
Navigator.pop(context);
} else {
// Tampilkan pesan kesalahan
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Registration failed')));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Register'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Kolom input name
TextField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
),
// Kolom input email
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
// Kolom input password
TextField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onSubmitted: (value) =>
_handleRegister(), // Menambahkan onSubmitted
),
SizedBox(height: 20),
// Tombol register
_loading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: _handleRegister,
child: Text('Register'),
),
],
),
),
);
}
}
13. Buat Controller Login dan Register:
Buat dua file baru di dalam folder lib/controller
, yaitu
login_controller.dart
import 'package:dio/dio.dart';
import '../model/user_model.dart';
import '../helpers/api_client.dart';
import '../helpers/user.info.dart';
class LoginController {
final ApiClient _apiClient = ApiClient();
// Fungsi untuk memvalidasi kredensial login dengan API
Future<bool> login(UserModel user) async {
try {
final response = await _apiClient.post('/login', user.toJson());
if (response.statusCode == 200) {
// Memeriksa apakah login berhasil berdasarkan respons API
final jsonData = response.data;
final token = jsonData['access_token'];
final email = jsonData['user']['email'];
// Menyimpan token dan email menggunakan SharedPreferences
await UserInfo().setToken(token);
await UserInfo().setEmail(email);
return true;
} else {
// Menangani respons login yang tidak valid
return false;
}
} catch (e) {
// Menangani kesalahan (misalnya, masalah jaringan)
print('Login error: $e');
return false;
}
}
}
Register_controller.dart
import '../helpers/api_client.dart';
class RegisterController {
final ApiClient _apiClient = ApiClient();
Future<bool> register(String name, String email, String password) async {
try {
final response = await _apiClient.post('register', {
'name': name,
'email': email,
'password': password,
});
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} catch (e) {
print('Registration error: $e');
return false;
}
}
}