# Phase 3: Mobile App Update — Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Remove all residual Node.js API gateway dependencies from the Flutter mobile app, add notification preferences and device token management, and ensure all features work with the Supabase-only backend.
**Architecture:** The mobile app already uses Supabase directly for most operations (EventApiService, UserApiService, MessagingApiService, SocketService all query Supabase PostgreSQL/Realtime). The main work is: (1) remove Socket.IO dependency (SocketService already uses Supabase Realtime), (2) add notification/device token management, (3) add recommendation fetching from cache, (4) clean up any API gateway base URL references.
**Tech Stack:** Flutter 3.6+, Dart 3.6+, Riverpod, Supabase Flutter SDK, GoRouter
**Spec:** `flow_docs/docs/superpowers/specs/2026-03-29-supabase-only-migration-design.md`
**Depends on:** Phase 1 (schema) and Phase 2 (Edge Functions) must be completed first.
---
## File Structure
| Action | File | Purpose |
|--------|------|---------|
| Modify | `flow_mobile/lib/core/constants/app_constants.dart` | Remove API Gateway base URL references |
| Modify | `flow_mobile/lib/core/network/socket_service.dart` | Remove socket_io_client import if still present |
| Create | `flow_mobile/lib/core/services/notification_service.dart` | Device token registration + notification preferences |
| Create | `flow_mobile/lib/features/notifications/models/notification_preference_model.dart` | Notification preference model |
| Modify | `flow_mobile/lib/features/notifications/providers/notification_provider.dart` | Add preferences management |
| Modify | `flow_mobile/lib/features/settings/screens/notifications_settings_screen.dart` | Wire up preferences to Supabase |
| Create | `flow_mobile/lib/features/home/providers/recommendation_provider.dart` | Fetch recommendations from cache |
| Modify | `flow_mobile/pubspec.yaml` | Remove socket_io_client dependency if unused |
---
## Chunk 1: Remove API Gateway References & Socket.IO Cleanup
### Task 1: Audit and remove API Gateway references
- [ ] **Step 1: Search for API Gateway base URL references**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
grep -r "localhost:3000\|10.0.2.2:3000\|API_BASE_URL\|apiBaseUrl\|api-gateway\|3000/api" lib/ --include="*.dart" -l
```
- [ ] **Step 2: Update app_constants.dart — remove/comment out API Gateway URLs**
Read `flow_mobile/lib/core/constants/app_constants.dart` and remove any references to `http://localhost:3000` or `http://10.0.2.2:3000`. All API calls go through Supabase client directly.
- [ ] **Step 3: Remove socket_io_client dependency from pubspec.yaml (if SocketService doesn't use it)**
Check if `socket_io_client` is actually imported in socket_service.dart. If SocketService already uses only Supabase Realtime (confirmed from exploration), remove the dependency:
```yaml
# In pubspec.yaml, remove this line if present:
# socket_io_client: ^2.0.3+1
```
Then run:
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
flutter pub get
```
- [ ] **Step 4: Commit cleanup**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
git add -A
git commit -m "chore: remove API Gateway references and Socket.IO dependency
All API calls now go through Supabase client directly.
SocketService already uses Supabase Realtime, not Socket.IO.
Part of Supabase-only migration Phase 3."
```
---
## Chunk 2: Notification Service & Device Token Management
### Task 2: Create notification service
**Files:**
- Create: `flow_mobile/lib/core/services/notification_service.dart`
- [ ] **Step 1: Create NotificationService for device token management**
```dart
// lib/core/services/notification_service.dart
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '../services/log_service.dart';
class NotificationService {
static final NotificationService _instance = NotificationService._();
factory NotificationService() => _instance;
NotificationService._();
final _client = Supabase.instance.client;
final _messaging = FirebaseMessaging.instance;
/// Register device token with Supabase for push notifications.
/// Called after login and on token refresh.
Future<void> registerDeviceToken() async {
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return;
final token = await _messaging.getToken();
if (token == null) return;
final platform = Platform.isIOS ? 'ios' : 'android';
await _client.from('device_tokens').upsert(
{
'user_id': userId,
'token': token,
'platform': platform,
},
onConflict: 'user_id,token',
);
// Listen for token refresh
_messaging.onTokenRefresh.listen((newToken) async {
await _client.from('device_tokens').upsert(
{
'user_id': userId,
'token': newToken,
'platform': platform,
},
onConflict: 'user_id,token',
);
});
} catch (e) {
LogService.error('Failed to register device token', error: e);
}
}
/// Remove current device token (call on logout).
Future<void> unregisterDeviceToken() async {
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return;
final token = await _messaging.getToken();
if (token == null) return;
await _client
.from('device_tokens')
.delete()
.eq('user_id', userId)
.eq('token', token);
} catch (e) {
LogService.error('Failed to unregister device token', error: e);
}
}
/// Get user's notification preferences.
Future<Map<String, dynamic>?> getPreferences() async {
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return null;
final response = await _client
.from('notification_preferences')
.select()
.eq('user_id', userId)
.maybeSingle();
return response;
} catch (e) {
LogService.error('Failed to get notification preferences', error: e);
return null;
}
}
/// Update notification preferences.
Future<void> updatePreferences({
bool? pushEnabled,
bool? emailEnabled,
String? quietHoursStart,
String? quietHoursEnd,
List<String>? disabledTypes,
}) async {
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return;
final updates = <String, dynamic>{
'user_id': userId,
};
if (pushEnabled != null) updates['push_enabled'] = pushEnabled;
if (emailEnabled != null) updates['email_enabled'] = emailEnabled;
if (quietHoursStart != null) updates['quiet_hours_start'] = quietHoursStart;
if (quietHoursEnd != null) updates['quiet_hours_end'] = quietHoursEnd;
if (disabledTypes != null) updates['disabled_types'] = disabledTypes;
await _client.from('notification_preferences').upsert(
updates,
onConflict: 'user_id',
);
} catch (e) {
LogService.error('Failed to update notification preferences', error: e);
}
}
}
```
- [ ] **Step 2: Wire device token registration into auth flow**
In `flow_mobile/lib/core/auth/auth_service.dart`, add token registration after successful login/register and unregistration on logout:
After `login()` succeeds:
```dart
await NotificationService().registerDeviceToken();
```
After `register()` succeeds:
```dart
await NotificationService().registerDeviceToken();
```
In `logout()`:
```dart
await NotificationService().unregisterDeviceToken();
```
- [ ] **Step 3: Commit**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
git add lib/core/services/notification_service.dart lib/core/auth/auth_service.dart
git commit -m "feat: add NotificationService for device token and preferences management
Registers FCM tokens with Supabase on login, removes on logout.
Provides notification preference CRUD (push, email, quiet hours, disabled types).
Part of Supabase-only migration Phase 3."
```
---
## Chunk 3: Notification Settings UI & Recommendations Provider
### Task 3: Wire notification settings screen to Supabase
- [ ] **Step 1: Read current notifications_settings_screen.dart**
```bash
# Read the file to understand current implementation
```
Read `flow_mobile/lib/features/settings/screens/notifications_settings_screen.dart`
- [ ] **Step 2: Update the settings screen to use NotificationService**
Update the screen to load preferences from Supabase on init and save changes via `NotificationService().updatePreferences()`. The exact changes depend on the current implementation — update toggle handlers to call the service.
- [ ] **Step 3: Commit**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
git add lib/features/settings/screens/notifications_settings_screen.dart
git commit -m "feat: wire notification settings to Supabase notification_preferences
Toggles now persist to Supabase via NotificationService.
Supports push/email toggle, quiet hours, per-type disable."
```
### Task 4: Create recommendation provider
**Files:**
- Create: `flow_mobile/lib/features/home/providers/recommendation_provider.dart`
- [ ] **Step 1: Create RecommendationProvider**
```dart
// lib/features/home/providers/recommendation_provider.dart
import 'package:flutter/foundation.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '../../../core/services/log_service.dart';
class RecommendationProvider extends ChangeNotifier {
final _client = Supabase.instance.client;
List<Map<String, dynamic>> _recommendations = [];
bool _isLoading = false;
List<Map<String, dynamic>> get recommendations => _recommendations;
bool get isLoading => _isLoading;
/// Fetch personalized recommendations from cache.
/// These are pre-computed by the compute-recommendations Edge Function.
Future<void> loadRecommendations({int limit = 20}) async {
_isLoading = true;
notifyListeners();
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return;
final response = await _client
.from('recommendations_cache')
.select('event_id, score, factors, events!inner(id, title, description, image_url, start_date, end_date, location, category, organizer_id, attendee_count, profiles!organizer_id(username, first_name, profile_picture))')
.eq('user_id', userId)
.order('score', ascending: false)
.limit(limit);
_recommendations = List<Map<String, dynamic>>.from(response ?? []);
} catch (e) {
LogService.error('Failed to load recommendations', error: e);
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Trigger on-demand matchmaking via Edge Function.
Future<List<Map<String, dynamic>>> getMatchmakingSuggestions() async {
try {
final userId = _client.auth.currentUser?.id;
if (userId == null) return [];
final response = await _client.functions.invoke(
'compute-matchmaking',
body: {'user_id': userId},
);
if (response.status == 200) {
final data = response.data as Map<String, dynamic>;
return List<Map<String, dynamic>>.from(data['matches'] ?? []);
}
return [];
} catch (e) {
LogService.error('Failed to get matchmaking suggestions', error: e);
return [];
}
}
}
```
- [ ] **Step 2: Register provider**
Add `RecommendationProvider` to the app's provider tree (in the same pattern used by existing providers — typically in `main.dart` or a providers file via `ChangeNotifierProvider`).
- [ ] **Step 3: Commit**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
git add lib/features/home/providers/recommendation_provider.dart
git commit -m "feat: add RecommendationProvider for personalized event discovery
Fetches pre-computed recommendations from Supabase cache.
Invokes compute-matchmaking Edge Function for crew suggestions.
Part of Supabase-only migration Phase 3."
```
---
## Chunk 4: Integration & Verification
### Task 5: End-to-end testing
- [ ] **Step 1: Verify auth flow works**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
flutter run
```
Test: Login → verify device token appears in `device_tokens` table in Supabase dashboard.
- [ ] **Step 2: Verify event discovery works**
Test: Browse events → events load from Supabase. RSVP to an event → check XP incremented, notification created for organizer.
- [ ] **Step 3: Verify chat works via Supabase Realtime**
Test: Open a chat → send message → message appears in real-time for both users.
- [ ] **Step 4: Verify notification settings persist**
Test: Go to Settings → Notifications → toggle push off → check `notification_preferences` table updated.
- [ ] **Step 5: Run tests**
```bash
cd /c/Users/elia-/Documents/flowproject/flow_mobile
flutter test
flutter analyze
```
- [ ] **Step 6: Update spec and commit**
```
Phase 3: Mobile App Update — ✅ COMPLETED
```
```bash
cd /c/Users/elia-/Documents/flowproject
git add flow_docs/docs/superpowers/specs/2026-03-29-supabase-only-migration-design.md
git commit -m "docs: mark Phase 3 (Mobile App Update) as completed"
```
---
## Summary
| Task | What | Files | Est. |
|------|------|-------|------|
| 1 | Remove API Gateway refs + Socket.IO cleanup | constants, pubspec | 5 min |
| 2 | NotificationService + auth integration | 2 files modified | 5 min |
| 3 | Wire notification settings screen | 1 file modified | 5 min |
| 4 | RecommendationProvider | 1 new file | 5 min |
| 5 | E2E testing + verification | Manual testing | 15 min |
**Total: ~35 minutes**
**Note:** Phase 3 is lighter than expected because the mobile app already uses Supabase directly for all major operations. The EventApiService, UserApiService, MessagingApiService, and SocketService all query Supabase PostgreSQL and Realtime — no API Gateway dependency exists in practice.