607 lines
14 KiB
Markdown
607 lines
14 KiB
Markdown
# 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`:
|
|
|
|
```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:
|
|
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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)
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
final result = await bookingService.cancelBooking(1);
|
|
|
|
if (result['success']) {
|
|
print('Booking cancelled!');
|
|
} else {
|
|
print('Error: ${result['message']}');
|
|
}
|
|
```
|
|
|
|
### 10. Add Review
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
final favorites = await favoriteService.getFavorites();
|
|
|
|
print('Kontrakan favorites: ${favorites['kontrakan']}');
|
|
print('Laundry favorites: ${favorites['laundry']}');
|
|
```
|
|
|
|
### 13. Get Laundry Services
|
|
```dart
|
|
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
|
|
```dart
|
|
final result = await authService.updateProfile(
|
|
name: 'John Doe',
|
|
phone: '081234567890',
|
|
address: 'Jalan ABC No 123',
|
|
);
|
|
|
|
if (result['success']) {
|
|
print('Profile updated!');
|
|
}
|
|
```
|
|
|
|
### 15. Logout
|
|
```dart
|
|
await authService.logout();
|
|
print('Logged out. Token: ${authService.token}');
|
|
```
|
|
|
|
---
|
|
|
|
## Using in Widgets
|
|
|
|
### Example: Login Screen
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
|
|
```dart
|
|
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:
|
|
|
|
```dart
|
|
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.baseUrl` dengan 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 requests
|
|
- `shared_preferences` - Local storage for token
|
|
- `image_picker` - Pick images dari gallery/camera
|
|
- `file` - File handling
|
|
|
|
Tambahan yang di-recommend:
|
|
```yaml
|
|
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)
|
|
```json
|
|
{
|
|
"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)
|
|
```json
|
|
{
|
|
"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)
|
|
```json
|
|
{
|
|
"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.
|