# FlutterFlow Setup Guide

This guide will walk you through setting up the FlutterFlow-Supabase Sync Middleware with your FlutterFlow project.

## 📋 Prerequisites

Before starting, ensure you have:

- ✅ FlutterFlow account and project
- ✅ Supabase project set up
- ✅ Middleware server running (see main README)
- ✅ Basic understanding of FlutterFlow custom actions

## 🚀 Step 1: FlutterFlow Project Setup

### 1.1 Create New FlutterFlow Project

1. Log into your FlutterFlow account
2. Create a new project or use existing one
3. Choose your preferred template (we recommend starting with a blank project)

### 1.2 Configure Supabase in FlutterFlow

1. In FlutterFlow, go to **Settings** → **Integrations**
2. Click **Add Integration** → **Supabase**
3. Enter your Supabase credentials:
   - **Project URL**: `https://your-project.supabase.co`
   - **Anon Key**: Your Supabase anon key
   - **Service Role Key**: Your Supabase service role key (for admin operations)

## 🔧 Step 2: Add Custom Actions

### 2.1 Create Sync Initialization Action

1. In FlutterFlow, go to **Custom Code** → **Actions**
2. Click **Create Action**
3. Name it: `InitializeSync`
4. Add the following code:

```dart
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

Future<void> initializeSync() async {
  try {
    await FlutterFlowSyncActions.initializeSync(
      serverUrl: 'http://your-server-url:3000', // Replace with your server URL
      supabaseUrl: 'https://your-project.supabase.co', // Your Supabase URL
      supabaseAnonKey: 'your-supabase-anon-key', // Your Supabase anon key
    );
    
    // Show success message
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Sync initialized successfully!')),
    );
  } catch (e) {
    // Show error message
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Sync initialization failed: $e')),
    );
  }
}
```

### 2.2 Create Data Sync Action

1. Create another action named: `SyncData`
2. Add the following code:

```dart
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

Future<void> syncData() async {
  try {
    await FlutterFlowSyncActions.performSync();
    
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Data sync completed!')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Sync failed: $e')),
    );
  }
}
```

### 2.3 Create Data Read Action

1. Create action named: `ReadSyncData`
2. Add parameters:
   - `tableName` (String, required)
   - `where` (String, optional)
   - `limit` (int, optional)
3. Add the following code:

```dart
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

Future<List<dynamic>> readSyncData(String tableName, String? where, int? limit) async {
  try {
    final data = await FlutterFlowSyncActions.readData(
      tableName: tableName,
      where: where,
      limit: limit,
    );
    
    return data;
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Failed to read data: $e')),
    );
    return [];
  }
}
```

### 2.4 Create Data Write Action

1. Create action named: `WriteSyncData`
2. Add parameters:
   - `tableName` (String, required)
   - `operation` (String, required) - "INSERT", "UPDATE", or "DELETE"
   - `data` (Map<String, dynamic>, required)
3. Add the following code:

```dart
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

Future<void> writeSyncData(String tableName, String operation, Map<String, dynamic> data) async {
  try {
    await FlutterFlowSyncActions.writeData(
      tableName: tableName,
      operation: operation,
      data: data,
    );
    
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Data saved successfully!')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Failed to save data: $e')),
    );
  }
}
```

### 2.5 Create Image Upload Action

1. Create action named: `UploadImage`
2. Add parameters:
   - `imageFile` (File, required)
3. Add the following code:

```dart
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';
import 'dart:io';

Future<Map<String, dynamic>?> uploadImage(File imageFile) async {
  try {
    final result = await FlutterFlowSyncActions.uploadImage(
      imageFile: imageFile,
    );
    
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Image uploaded successfully!')),
    );
    
    return result;
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Image upload failed: $e')),
    );
    return null;
  }
}
```

## 📱 Step 3: Add Custom Widgets

### 3.1 Create Sync Status Widget

1. Go to **Custom Code** → **Widgets**
2. Click **Create Widget**
3. Name it: `SyncStatusWidget`
4. Add the following code:

```dart
import 'package:flutter/material.dart';
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

class SyncStatusWidget extends StatelessWidget {
  const SyncStatusWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<SyncEvent>(
      stream: FlutterFlowSyncClient.instance.eventStream,
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const SizedBox.shrink();
        }
        
        final event = snapshot.data!;
        IconData icon;
        Color color;
        String text;
        
        switch (event.runtimeType) {
          case SyncConnected:
            icon = Icons.cloud_done;
            color = Colors.green;
            text = 'Connected';
            break;
          case SyncDisconnected:
            icon = Icons.cloud_off;
            color = Colors.red;
            text = 'Disconnected';
            break;
          case SyncStarted:
            icon = Icons.sync;
            color = Colors.orange;
            text = 'Syncing...';
            break;
          case SyncCompleted:
            icon = Icons.check_circle;
            color = Colors.green;
            text = 'Synced';
            break;
          case SyncError:
            icon = Icons.error;
            color = Colors.red;
            text = 'Error';
            break;
          default:
            icon = Icons.help;
            color = Colors.grey;
            text = 'Unknown';
        }
        
        return Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, color: color, size: 16),
            const SizedBox(width: 4),
            Text(
              text,
              style: TextStyle(color: color, fontSize: 12),
            ),
          ],
        );
      },
    );
  }
}
```

### 3.2 Create Sync Button Widget

1. Create widget named: `SyncButtonWidget`
2. Add the following code:

```dart
import 'package:flutter/material.dart';
import 'package:flutterflow_sync_client/flutterflow_sync_client.dart';

class SyncButtonWidget extends StatelessWidget {
  const SyncButtonWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<SyncEvent>(
      stream: FlutterFlowSyncClient.instance.eventStream,
      builder: (context, snapshot) {
        final isSyncing = snapshot.data is SyncStarted;
        
        return ElevatedButton.icon(
          onPressed: isSyncing ? null : () async {
            try {
              await FlutterFlowSyncClient.instance.triggerSync();
            } catch (e) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Sync failed: $e')),
              );
            }
          },
          icon: isSyncing 
            ? const SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2),
              )
            : const Icon(Icons.sync),
          label: Text(isSyncing ? 'Syncing...' : 'Sync Now'),
        );
      },
    );
  }
}
```

## 🎨 Step 4: Create Your App Pages

### 4.1 Create Main Page with Sync Controls

1. Create a new page called `HomePage`
2. Add the following components:

**App Bar:**
- Add `SyncStatusWidget` to the app bar actions
- Add `SyncButtonWidget` to the app bar actions

**Body:**
- Add a `Column` with:
  - Welcome text
  - Sync status indicator
  - Data list (we'll create this next)

### 4.2 Create Data List Page

1. Create a new page called `DataListPage`
2. Add parameters:
   - `tableName` (String)
3. Add the following components:

**App Bar:**
- Title: "Data List"
- Actions: `SyncButtonWidget`

**Body:**
- Add a `ListView.builder` with:
  - Item count: Use `ReadSyncData` action result length
  - Item builder: Create custom list tiles

**List Tile Example:**
```dart
ListTile(
  title: Text(item['name'] ?? 'No Name'),
  subtitle: Text(item['description'] ?? 'No Description'),
  trailing: IconButton(
    icon: Icon(Icons.delete),
    onPressed: () async {
      await WriteSyncData(
        tableName: widget.tableName,
        operation: 'DELETE',
        data: {'id': item['id']},
      );
    },
  ),
)
```

### 4.3 Create Image Upload Page

1. Create a new page called `ImageUploadPage`
2. Add the following components:

**Body:**
- Add an `Image` widget for preview
- Add an `ElevatedButton` for image selection
- Add a `GridView` for uploaded images

**Image Selection Button:**
```dart
ElevatedButton(
  onPressed: () async {
    // This would typically use image_picker
    // For now, we'll show how to handle the result
    final result = await UploadImage(selectedImageFile);
    if (result != null) {
      // Handle successful upload
      setState(() {
        // Update UI
      });
    }
  },
  child: Text('Select Image'),
)
```

## 🔗 Step 5: Connect Actions to UI

### 5.1 Initialize Sync on App Start

1. Go to your main page (usually the first page)
2. Add an `On Page Load` action
3. Call the `InitializeSync` action

### 5.2 Add Sync Controls to Pages

1. Add `SyncStatusWidget` to your app bar
2. Add `SyncButtonWidget` to your app bar
3. Connect buttons to appropriate actions

### 5.3 Connect Data Operations

1. For reading data:
   - Use `ReadSyncData` action in `On Page Load`
   - Store result in a page state variable
   - Use the variable in your UI components

2. For writing data:
   - Connect form submissions to `WriteSyncData` action
   - Add success/error handling

## 📊 Step 6: Test Your Integration

### 6.1 Test Sync Initialization

1. Run your app
2. Check that sync initializes without errors
3. Verify the sync status widget shows "Connected"

### 6.2 Test Data Operations

1. Try reading data from your tables
2. Test creating new records
3. Test updating existing records
4. Test deleting records

### 6.3 Test Image Upload

1. Select an image
2. Upload it using the upload action
3. Verify it appears in your image gallery

## 🐛 Step 7: Troubleshooting

### Common Issues and Solutions

**Issue: Sync initialization fails**
- Check your server URL is correct
- Verify Supabase credentials
- Ensure middleware server is running

**Issue: Data not syncing**
- Check network connection
- Verify sync rules configuration
- Check server logs for errors

**Issue: Images not uploading**
- Check file size limits
- Verify image format is supported
- Check server storage permissions

**Issue: Widgets not showing**
- Ensure custom widgets are properly imported
- Check widget code for syntax errors
- Verify FlutterFlow custom code settings

## 📚 Step 8: Advanced Features

### 8.1 Real-time Updates

To enable real-time updates, add this to your page:

```dart
@override
void initState() {
  super.initState();
  
  // Listen for sync events
  FlutterFlowSyncClient.instance.eventStream.listen((event) {
    if (event is DataUpdated) {
      // Refresh your data
      setState(() {
        // Update UI
      });
    }
  });
}
```

### 8.2 Offline Support

The sync system automatically handles offline scenarios:
- Data is stored locally when offline
- Changes are queued for sync when online
- Automatic retry on connection restore

### 8.3 Custom Sync Rules

To customize what data syncs, modify the `syncConfig.js` file in your middleware server.

## 🎯 Step 9: Best Practices

### 9.1 Error Handling

Always wrap sync operations in try-catch blocks:

```dart
try {
  await FlutterFlowSyncActions.performSync();
} catch (e) {
  // Handle error appropriately
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text('Error: $e')),
  );
}
```

### 9.2 Loading States

Show loading indicators during sync operations:

```dart
bool _isSyncing = false;

Future<void> _syncData() async {
  setState(() {
    _isSyncing = true;
  });
  
  try {
    await FlutterFlowSyncActions.performSync();
  } finally {
    setState(() {
      _isSyncing = false;
    });
  }
}
```

### 9.3 Data Validation

Validate data before syncing:

```dart
bool _validateData(Map<String, dynamic> data) {
  if (data['name'] == null || data['name'].isEmpty) {
    return false;
  }
  return true;
}
```

## 🚀 Step 10: Deployment

### 10.1 Production Configuration

1. Update server URLs to production endpoints
2. Use production Supabase credentials
3. Configure proper CORS settings
4. Set up SSL certificates

### 10.2 App Store Deployment

1. Test thoroughly on physical devices
2. Ensure offline functionality works
3. Test with poor network conditions
4. Verify image upload works on all devices

## 📞 Support

If you encounter issues:

1. Check the main README for troubleshooting
2. Review server logs for errors
3. Test with the dashboard to verify server functionality
4. Check FlutterFlow custom code documentation

## 🎉 Congratulations!

You've successfully set up the FlutterFlow-Supabase Sync Middleware! Your app now has:

- ✅ Offline-first data synchronization
- ✅ Real-time sync status monitoring
- ✅ Image upload and management
- ✅ Conflict resolution
- ✅ Comprehensive error handling

Happy coding! 🚀
