14 KiB
14 KiB
API Quick Start Guide
Panduan cepat untuk memulai menggunakan API di Flutter app.
Setup Awal
1. Update Base URL (PENTING!)
Edit file lib/config/app_config.dart:
class AppConfig {
// Ganti sesuai IP komputer Anda
static const String baseUrl = 'http://192.168.18.16:8000/api';
static const String storageUrl = 'http://192.168.18.16:8000/storage';
}
Cek IP komputer Anda:
- Windows: Buka Command Prompt, ketik
ipconfig, cari IPv4 Address - Mac/Linux: Terminal, ketik
ifconfig
2. Load Token on App Startup
Di main.dart atau lib/main.dart, tambahkan:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Load saved token jika ada
final authService = AuthService();
await authService.loadToken();
runApp(const MyApp());
}
Common API Usage Examples
1. Login User
import 'package:spk_mobile/services/auth_service.dart';
final authService = AuthService();
final result = await authService.login('user@email.com', 'password');
if (result['success']) {
print('Login berhasil!');
print('Token: ${authService.token}');
print('User: ${authService.currentUser?.name}');
} else {
print('Error: ${result['message']}');
}
2. Register User
final result = await authService.register(
name: 'John Doe',
email: 'john@email.com',
password: 'password123',
passwordConfirmation: 'password123',
role: 'mahasiswa', // atau 'pemilik_kontrakan'
);
if (result['success']) {
print('Register berhasil!');
} else {
print('Error: ${result['message']}');
print('Errors: ${result['errors']}');
}
3. Get All Kontrakan (with filters)
import 'package:spk_mobile/services/kontrakan_service.dart';
final kontrakanService = KontrakanService();
// Get semua kontrakan
List<Kontrakan> list = await kontrakanService.getKontrakan();
// Get dengan filter
list = await kontrakanService.getKontrakan(
search: 'Kamar mandi pribadi',
hargaMin: 500000,
hargaMax: 1500000,
jumlahKamar: 2,
);
for (var kontrakan in list) {
print('${kontrakan.nama} - Rp ${kontrakan.harga}');
}
4. Get Kontrakan Detail
final detail = await kontrakanService.getKontrakanById(1);
if (detail != null) {
print('Nama: ${detail.nama}');
print('Harga: ${detail.hargaBulanan}');
print('Deskripsi: ${detail.deskripsi}');
}
5. Get Kontrakan Reviews
final reviews = await kontrakanService.getReviews(1);
for (var review in reviews) {
print('Rating: ${review['rating']}');
print('Comment: ${review['comment']}');
print('Author: ${review['user']['name']}');
}
6. Get SAW Recommendations
final recommendations = await kontrakanService.getRecommendations(
hargaMax: 1000000,
jarakMax: 5.0,
fasilitas: 'wifi,ac',
);
// Gunakan recommendations untuk ranking
final ranked = recommendations['ranking'] as List;
for (var item in ranked) {
print('${item['name']} - Score: ${item['score']}');
}
7. Create Booking
import 'package:spk_mobile/services/booking_service.dart';
import 'dart:io';
final bookingService = BookingService();
// Persiapkan file bukti pembayaran
final paymentFile = File('/path/to/payment_proof.jpg');
final result = await bookingService.createBooking(
kontrakanId: 1,
tanggalMulai: DateTime.now(),
durasiBulan: 3,
catatan: 'Butuh AC dan dekat kampus',
paymentProof: paymentFile,
);
if (result['success']) {
final booking = result['booking'] as Booking;
print('Booking created! ID: ${booking.id}');
} else {
print('Error: ${result['message']}');
}
8. Get Booking History
final bookings = await bookingService.getBookingHistory();
for (var booking in bookings) {
print('ID: ${booking.id}');
print('Kontrakan: ${booking.kontrakan['nama']}');
print('Status: ${booking.status}');
print('Total: Rp ${booking.totalBiaya}');
}
9. Cancel Booking
final result = await bookingService.cancelBooking(1);
if (result['success']) {
print('Booking cancelled!');
} else {
print('Error: ${result['message']}');
}
10. Add Review
import 'package:spk_mobile/services/review_service.dart';
final reviewService = ReviewService();
final result = await reviewService.addKontrakanReview(
kontrakanId: 1,
rating: 4.5,
comment: 'Nyaman dan bersih, hanya aja AC agak berisik',
);
if (result['success']) {
print('Review posted!');
} else {
print('Error: ${result['message']}');
}
11. Toggle Favorite
import 'package:spk_mobile/services/favorite_service.dart';
final favoriteService = FavoriteService();
// Add/remove from favorites
final result = await favoriteService.toggleKontrakanFavorite(1);
if (result['success']) {
print('Favorite status: ${result['isFavorite']}');
}
// Check if favorite
final isFav = await favoriteService.isKontrakanFavorite(1);
print('Is favorite: $isFav');
12. Get User's Favorites
final favorites = await favoriteService.getFavorites();
print('Kontrakan favorites: ${favorites['kontrakan']}');
print('Laundry favorites: ${favorites['laundry']}');
13. Get Laundry Services
import 'package:spk_mobile/services/laundry_service.dart';
final laundryService = LaundryService();
// Get semua laundry
List<Laundry> list = await laundryService.getLaundry();
// Dengan filter
list = await laundryService.getLaundry(
search: 'express',
hargaMax: 10000,
);
for (var laundry in list) {
print('${laundry.nama} - Rp ${laundry.harga}');
}
14. Update User Profile
final result = await authService.updateProfile(
name: 'John Doe',
phone: '081234567890',
address: 'Jalan ABC No 123',
);
if (result['success']) {
print('Profile updated!');
}
15. Logout
await authService.logout();
print('Logged out. Token: ${authService.token}');
Using in Widgets
Example: Login Screen
import 'package:flutter/material.dart';
import 'package:spk_mobile/services/auth_service.dart';
class LoginScreen extends StatefulWidget {
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _authService = AuthService();
bool _isLoading = false;
void _login() async {
setState(() => _isLoading = true);
final result = await _authService.login(
_emailController.text,
_passwordController.text,
);
setState(() => _isLoading = false);
if (result['success']) {
// Navigate to home
Navigator.of(context).pushReplacementNamed('/home');
} else {
// Show error
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(result['message'])),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
TextField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
),
SizedBox(height: 20),
_isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: _login,
child: Text('Login'),
),
],
),
),
);
}
}
Example: Kontrakan List Screen
import 'package:flutter/material.dart';
import 'package:spk_mobile/services/kontrakan_service.dart';
import 'package:spk_mobile/models/kontrakan.dart';
class KontrakanScreen extends StatefulWidget {
@override
State<KontrakanScreen> createState() => _KontrakanScreenState();
}
class _KontrakanScreenState extends State<KontrakanScreen> {
late Future<List<Kontrakan>> _kontrakanFuture;
final _kontrakanService = KontrakanService();
@override
void initState() {
super.initState();
_kontrakanFuture = _kontrakanService.getKontrakan();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Kontrakan')),
body: FutureBuilder<List<Kontrakan>>(
future: _kontrakanFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'),
);
}
final kontrakan = snapshot.data ?? [];
return ListView.builder(
itemCount: kontrakan.length,
itemBuilder: (context, index) {
final item = kontrakan[index];
return ListTile(
title: Text(item.nama),
subtitle: Text('Rp ${item.hargaBulanan}'),
onTap: () {
// Navigate to detail
Navigator.of(context).pushNamed(
'/kontrakan-detail',
arguments: item.id,
);
},
);
},
);
},
),
);
}
}
Error Handling Best Practices
try {
final result = await bookingService.createBooking(...);
if (!result['success']) {
// Handle API error
final message = result['message'];
final errors = result['errors'] as Map?;
if (errors != null) {
errors.forEach((key, value) {
print('$key: $value');
});
}
// Show error to user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
} else {
// Handle success
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Success!')),
);
}
} catch (e) {
// Handle exception
print('Exception: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Terjadi kesalahan: $e')),
);
}
Testing API Connection
Tambahkan di main.dart untuk test:
import 'package:spk_mobile/services/auth_service.dart';
import 'package:spk_mobile/services/kontrakan_service.dart';
void testAPIs() async {
print('Testing API connections...');
// Test 1: Load token
final authService = AuthService();
await authService.loadToken();
print('Token loaded: ${authService.token?.substring(0, 10)}...');
// Test 2: Get kontrakan
final kontrakanService = KontrakanService();
final kontrakan = await kontrakanService.getKontrakan();
print('Kontrakan count: ${kontrakan.length}');
// Test 3: Get first kontrakan detail
if (kontrakan.isNotEmpty) {
final firstId = kontrakan.first.id;
final detail = await kontrakanService.getKontrakanById(firstId);
print('First kontrakan: ${detail?.nama}');
}
print('API tests completed!');
}
Checklist Sebelum Deploy
- Update
AppConfig.baseUrldengan IP server yang benar - Pastikan backend API sudah running (
php artisan serve) - Test semua API endpoints
- Implement error handling di semua screens
- Add loading indicators saat fetch data
- Test dengan real device/emulator
- Verify CORS headers (sudah configured di backend)
- Test file uploads (payment proof)
- Test authentication flow
- Test favorites dan reviews
Troubleshooting
"Connection refused"
Problem: API tidak bisa terhubung
Solution:
1. Pastikan backend running (php artisan serve)
2. Check baseUrl di AppConfig (harus sesuai IP)
3. Firewall settings - pastikan port 8000 terbuka
"Unauthorized 401"
Problem: Token invalid atau expired
Solution:
1. Login lagi untuk mendapatkan token baru
2. Pastikan loadToken() dipanggil saat startup
3. Check token expiry time di backend
"File not found"
Problem: Upload file gagal
Solution:
1. Pastikan file path benar
2. Check file permissions
3. Gunakan image_picker package untuk select file
"CORS error"
Problem: Cross-origin request blocked
Solution:
Backend sudah configured CORS, tapi jika masih error:
1. Check konfigurasi di backend (config/cors.php)
2. Pastikan credentials disettings di request
Useful Packages
Sudah ada di pubspec.yaml:
http- HTTP requestsshared_preferences- Local storage for tokenimage_picker- Pick images dari gallery/camerafile- File handling
Tambahan yang di-recommend:
dependencies:
dio: ^5.3.0 # Alternative HTTP client (lebih powerful)
provider: ^6.0.0 # State management
riverpod: ^2.4.0 # Better state management
connectivity_plus: ^5.0.0 # Check internet
API Response Examples
Login Response (Success)
{
"success": true,
"message": "Login successful",
"data": {
"token": "123456|abcdef...",
"user": {
"id": 1,
"name": "John Doe",
"email": "john@email.com",
"role": "mahasiswa"
}
}
}
Get Kontrakan Response (Success)
{
"success": true,
"message": "Data retrieved",
"data": {
"data": [
{
"id": 1,
"nama": "Kontrakan Jaya",
"harga_bulanan": 1000000,
"deskripsi": "Nyaman dan strategis",
"alamat": "Jalan ABC No 123"
}
]
}
}
Error Response (Failed)
{
"success": false,
"message": "Validation failed",
"errors": {
"email": ["Email sudah terdaftar"],
"password": ["Password minimal 8 karakter"]
}
}
Sudah siap menggunakan API! Jika ada pertanyaan, check API_INTEGRATION_GUIDE.md untuk dokumentasi lebih lengkap.