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

ActionFilePurpose
Modifyflow_mobile/lib/core/constants/app_constants.dartRemove API Gateway base URL references
Modifyflow_mobile/lib/core/network/socket_service.dartRemove socket_io_client import if still present
Createflow_mobile/lib/core/services/notification_service.dartDevice token registration + notification preferences
Createflow_mobile/lib/features/notifications/models/notification_preference_model.dartNotification preference model
Modifyflow_mobile/lib/features/notifications/providers/notification_provider.dartAdd preferences management
Modifyflow_mobile/lib/features/settings/screens/notifications_settings_screen.dartWire up preferences to Supabase
Createflow_mobile/lib/features/home/providers/recommendation_provider.dartFetch recommendations from cache
Modifyflow_mobile/pubspec.yamlRemove 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
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:

# In pubspec.yaml, remove this line if present:
# socket_io_client: ^2.0.3+1

Then run:

cd /c/Users/elia-/Documents/flowproject/flow_mobile
flutter pub get
  • Step 4: Commit cleanup
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

// 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:

await NotificationService().registerDeviceToken();

After register() succeeds:

await NotificationService().registerDeviceToken();

In logout():

await NotificationService().unregisterDeviceToken();
  • Step 3: Commit
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
# 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
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

// 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
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
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
cd /c/Users/elia-/Documents/flowproject/flow_mobile
flutter test
flutter analyze
  • Step 6: Update spec and commit
Phase 3: Mobile App Update — ✅ COMPLETED
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

TaskWhatFilesEst.
1Remove API Gateway refs + Socket.IO cleanupconstants, pubspec5 min
2NotificationService + auth integration2 files modified5 min
3Wire notification settings screen1 file modified5 min
4RecommendationProvider1 new file5 min
5E2E testing + verificationManual testing15 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.