Problem: ConversationPolicy was blocking all API requests (returned false for everything)
Solution: Updated app/Policies/ConversationPolicy.php to:
- Allow users to view conversations they're part of
- Allow users to create conversations
- Check participant membership before allowing actions
Problem: Avatar URLs weren't included in API responses
Solution: Added $appends = ['avatar_url'] to User model so it's automatically included in JSON
Problem: No visibility into API failures
Solution: Added logging to:
- Contact search
- Message fetching
- Message sending
cd D:\project\chat-app
php artisan config:clear
php artisan cache:clear
php artisan route:cacheVia Browser/Postman:
GET http://localhost:8000/api/contacts/search?q=admin
Headers:
Authorization: Bearer YOUR_TOKEN
Accept: application/jsonExpected Response:
[
{
"id": 1,
"name": "Admin User",
"email": "admin@example.com",
"avatar_path": null,
"created_at": "2024-01-01T00:00:00.000000Z",
"avatar_url": "https://www.gravatar.com/avatar/..."
}
]Request:
GET http://localhost:8000/api/conversations
Headers:
Authorization: Bearer YOUR_TOKEN
Accept: application/jsonExpected Response:
[
{
"id": 1,
"name": null,
"is_group": false,
"created_by": 1,
"created_at": "...",
"participants": [
{
"id": 1,
"name": "User 1",
"avatar_url": "..."
},
{
"id": 2,
"name": "User 2",
"avatar_url": "..."
}
],
"latest_message": {...}
}
]Request:
GET http://localhost:8000/api/conversations/1/messages?page=1
Headers:
Authorization: Bearer YOUR_TOKEN
Accept: application/jsonExpected Response:
{
"current_page": 1,
"data": [
{
"id": 1,
"conversation_id": 1,
"user_id": 1,
"body": "Hello!",
"type": "text",
"created_at": "...",
"sender": {
"id": 1,
"name": "User 1",
"avatar_url": "..."
},
"media": []
}
],
"total": 10,
"per_page": 50
}Text Message:
POST http://localhost:8000/api/conversations/1/messages
Headers:
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
Accept: application/json
Body:
{
"type": "text",
"body": "Test message from API"
}Image Message (multipart/form-data):
POST http://localhost:8000/api/conversations/1/messages
Headers:
Authorization: Bearer YOUR_TOKEN
Accept: application/json
Form Data:
type: image
body: Optional caption
image: [image file]Update lib/config/app_config.dart:
class AppConfig {
// For Android Emulator (10.0.2.2 = host machine)
static const String baseUrl = 'http://10.0.2.2:8000';
// For iOS Simulator
// static const String baseUrl = 'http://localhost:8000';
// For Physical Device (use your computer's IP)
// static const String baseUrl = 'http://192.168.1.XXX:8000';
static const String apiUrl = '$baseUrl/api';
static const String storageUrl = '$baseUrl/storage';
// WebSocket Configuration
static const String pusherAppKey = 'dzipltzgyda8iewsqt4o';
static const String pusherHost = '10.0.2.2'; // Match baseUrl host
static const int pusherPort = 8080;
static const String pusherScheme = 'http';
static const String pusherCluster = 'mt1';
static const bool enableLogging = true;
}Add this test function to your app (temporarily):
Future<void> testAPI() async {
final api = ApiService();
print('π§ͺ Testing API...');
// Test 1: Get User
try {
final response = await api.get('/user');
print('β
Get User: ${response.statusCode}');
print(' Response: ${response.body}');
} catch (e) {
print('β Get User failed: $e');
}
// Test 2: Search Contacts
try {
final response = await api.get('/contacts/search?q=test');
print('β
Search Contacts: ${response.statusCode}');
print(' Response: ${response.body}');
} catch (e) {
print('β Search failed: $e');
}
// Test 3: Get Conversations
try {
final response = await api.get('/conversations');
print('β
Get Conversations: ${response.statusCode}');
print(' Response: ${response.body}');
} catch (e) {
print('β Get Conversations failed: $e');
}
}Call this from your conversations screen's initState() temporarily.
Monitor in real-time:
# Windows PowerShell
Get-Content "D:\project\chat-app\storage\logs\laravel.log" -Wait -Tail 50Look for:
[2024-01-01 12:00:00] local.INFO: Contact search request {"query":"test","user_id":1}
[2024-01-01 12:00:00] local.INFO: Contact search results {"count":2}
[2024-01-01 12:00:00] local.INFO: Fetching messages {"conversation_id":1,"user_id":1,"page":1}
[2024-01-01 12:00:00] local.INFO: Messages fetched {"count":5}
Cause: Token not sent or expired
Solution:
- Check token is stored:
await StorageService().getToken() - Re-login to get fresh token
- Verify Authorization header is sent
Flutter Debug:
final token = await StorageService().getToken();
print('Token: $token');
if (token == null) {
print('β No token found - need to login');
}Cause: User not authorized (ConversationPolicy blocked)
Solution:
- Verify you're a participant in the conversation
- Check policy was updated correctly
- Clear Laravel cache
Cause: Can't reach Laravel server
Solution:
- Verify Laravel is running:
http://localhost:8000in browser - Check IP address:
- Android Emulator: Use
10.0.2.2(notlocalhost) - iOS Simulator: Use
localhost - Physical Device: Use your computer's IP
- Android Emulator: Use
- Laravel must serve on all interfaces:
php artisan serve --host=0.0.0.0
- Check firewall: Temporarily disable to test
Cause: Validation failed
Solution:
- Check request body format
- View Laravel logs for validation errors
- Ensure Content-Type header is correct
Cause: Messages API failing silently
Solution:
- Check Laravel logs for errors
- Test API endpoint directly with Postman
- Verify conversation ID is correct
- Check user is participant
After fixes, verify:
- Can login via Flutter app
- Conversations list loads
- Search for contacts works (shows results)
- Can create new conversation
- Messages load in chat screen
- Can send text message
- Can send image message
- Messages appear in real-time (WebSocket)
- Images display correctly
Save as test_api.sh (Mac/Linux) or test_api.ps1 (Windows):
Windows PowerShell:
$token = "YOUR_TOKEN_HERE"
$base = "http://localhost:8000/api"
Write-Host "Testing API..." -ForegroundColor Green
# Test User
Write-Host "`n1. Testing GET /user" -ForegroundColor Yellow
$response = Invoke-WebRequest -Uri "$base/user" -Headers @{"Authorization"="Bearer $token"; "Accept"="application/json"}
Write-Host "Status: $($response.StatusCode)" -ForegroundColor Cyan
# Test Search
Write-Host "`n2. Testing GET /contacts/search?q=admin" -ForegroundColor Yellow
$response = Invoke-WebRequest -Uri "$base/contacts/search?q=admin" -Headers @{"Authorization"="Bearer $token"; "Accept"="application/json"}
Write-Host "Status: $($response.StatusCode)" -ForegroundColor Cyan
Write-Host $response.Content
# Test Conversations
Write-Host "`n3. Testing GET /conversations" -ForegroundColor Yellow
$response = Invoke-WebRequest -Uri "$base/conversations" -Headers @{"Authorization"="Bearer $token"; "Accept"="application/json"}
Write-Host "Status: $($response.StatusCode)" -ForegroundColor Cyan
Write-Host "`nAll tests passed! β
" -ForegroundColor GreenLogin via Flutter, then add this debug code:
// After successful login in auth_provider.dart
final token = await _storage.getToken();
print('π AUTH TOKEN: $token');
print('Copy this token for testing');Use the token to test each endpoint manually
Laravel Log:
tail -f storage/logs/laravel.logFlutter Console:
Look for API request logs from ApiService
-- Verify user has conversations
SELECT * FROM conversation_participants WHERE user_id = YOUR_USER_ID;
-- Verify messages exist
SELECT * FROM messages WHERE conversation_id = YOUR_CONVERSATION_ID;
-- Check if user is authenticated
SELECT * FROM personal_access_tokens WHERE tokenable_id = YOUR_USER_ID;When everything works you should see:
Flutter Console:
GET: http://10.0.2.2:8000/api/conversations
Response: 200 - [{"id":1,"name":null,...}]
β
Subscribed to conversation.1
π¨ Message received on conversation 1
Laravel Log:
[INFO] Fetching messages {"conversation_id":1,"user_id":1}
[INFO] Messages fetched {"count":5}
[INFO] Storing message {"conversation_id":1,"type":"text"}
[INFO] Message stored and broadcast {"message_id":15}
App Behavior:
- Conversations load instantly
- Messages display properly
- Search returns users
- Messages send successfully
- Real-time updates work