> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cello.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Cello for Flutter

<span className="badge-blue">Beta</span>

The Cello Flutter SDK allows you to use Cello for iOS and Cello for Android in your Flutter apps. With a plug-n-play mobile component, your users can easily share their invite link with their friends and network using mobile sharing options convenient for them, receive rewards and get paid out.

## Installation

A basic installation takes around 15 minutes, but will take a little longer if you want to customize the way the Cello Referral Component is launched.

**Compatibility**

* The Cello Flutter SDK supports Flutter **3.3.0** and above (Dart 3+).
* Cello for iOS supports **iOS 15+**.
* Cello for Android supports **API 21+**.

### Install Cello

```bash theme={null}
flutter pub add cello_sdk
```

Or add to your `pubspec.yaml`:

```yaml theme={null}
dependencies:
  flutter:
    sdk: flutter
  cello_sdk: ^0.0.1
```

Then run:

```bash theme={null}
flutter pub get
```

### Android Setup

The Flutter plugin automatically handles linking for Android. Ensure your app's `android/app/build.gradle` has the correct configuration:

```gradle theme={null}
android {
  compileSdk 34

  defaultConfig {
    minSdk 21
    // ... other config
  }
}
```

Ensure your project's `android/build.gradle` (or app-level) includes:

```gradle theme={null}
repositories {
  google()
  mavenCentral()
}
```

#### Internet Permission

Add internet permission in `android/app/src/main/AndroidManifest.xml`:

```xml theme={null}
<uses-permission android:name="android.permission.INTERNET" />
```

### iOS Setup

The Flutter plugin automatically handles linking for iOS via CocoaPods.

From your `ios/` directory, run:

```bash theme={null}
cd ios
pod install
cd ..
```

Ensure your `ios/Podfile` specifies iOS 15.0+:

```ruby theme={null}
platform :ios, '15.0'
```

## Choose an Environment

In your Cello SDK setup, you have the flexibility to select the environment in which your application will run. This feature is especially useful for different stages of development, such as testing in a development or staging environment before going live in production. The available environments are:

* `production` or `prod` (Production) *(default)*
* `sandbox` (Sandbox)

You specify the environment when initializing Cello:

```dart theme={null}
await Cello.initialize(
  CelloInitializeOptions(
    productId: 'your-product-id',
    token: 'your-token',
    environment: 'sandbox', // or 'production'
  ),
);
```

## Customize the Cello Referral Component

The Cello Flutter SDK allows for various levels of customization to better fit into your app's design and flow. One of the main components you might want to customize is the [**Referral component**](https://docs.cello.so/docs/component-overview)

### Choose Your Launcher

The library provides two ways to launch the Referral component:

**Default launcher**

If you choose to go with the default launcher, you can call the `showFab()` method from the Cello library to present a Floating Action Button (FAB) within your app. This FAB is pre-styled but may not perfectly match your app's look and feel.

```dart theme={null}
import 'package:cello_sdk/cello_sdk.dart';

await Cello.showFab();
```

**Custom launcher**

If the default launcher does not fit your needs, you can implement your own custom launcher. This could be any UI element like a button, menu item, or even a gesture. To open the Referral component using a custom launcher, you can call `Cello.openWidget()`.

```dart theme={null}
import 'package:cello_sdk/cello_sdk.dart';

ElevatedButton(
  onPressed: () async {
    await Cello.openWidget();
  },
  child: Text('Open Referral'),
)
```

## Flutter API

### `Cello.initialize(CelloInitializeOptions): Future<CelloConfiguration>`

Initializes the Cello referral component.

### CelloInitializeOptions

| Property           | Type               | Required | Description                                            |
| ------------------ | ------------------ | -------- | ------------------------------------------------------ |
| productId          | String             | yes      | Your product ID from Cello Portal                      |
| token              | String             | yes      | User authentication token                              |
| environment        | String?            | no       | Environment: `"production"` or `"sandbox"`             |
| productUserDetails | ProductUserDetails | no       | User details object (see below)                        |
| language           | String?            | no       | Initial language of the widget                         |
| themeMode          | String?            | no       | Initial theme mode: `"light"`, `"dark"`, or `"system"` |

### ProductUserDetails

Optional object with user information:

| Property  | Type   | Description          |
| --------- | ------ | -------------------- |
| firstName | String | User's first name    |
| lastName  | String | User's last name     |
| fullName  | String | User's full name     |
| email     | String | User's email address |

```dart theme={null}
import 'package:cello_sdk/cello_sdk.dart';

final config = await Cello.initialize(
  CelloInitializeOptions(
    productId: 'your-product-id',
    token: 'your-token',
    environment: 'production',
    productUserDetails: ProductUserDetails(
      firstName: 'John',
      lastName: 'Doe',
      fullName: 'John Doe',
      email: 'john.doe@example.com',
    ),
    language: 'de',
    themeMode: 'system',
  ),
);
```

### `Cello.showFab(): Future<void>`

Shows the default Cello button that launches the Referral Component

```dart theme={null}
await Cello.showFab();
```

### `Cello.hideFab(): Future<void>`

Hides the default Cello button that launches the Referral Component

```dart theme={null}
await Cello.hideFab();
```

### `Cello.openWidget(): Future<void>`

Opens the referral component.

```dart theme={null}
await Cello.openWidget();
```

### `Cello.hideWidget(): Future<void>`

Hides the referral component.

```dart theme={null}
await Cello.hideWidget();
```

### `Cello.getActiveUcc(): Future<Map<String, String>>`

A method to get an active `ucc` and related data for the currently logged in user.

```dart theme={null}
final ucc = await Cello.getActiveUcc();
// Returns: { "ucc": "...", "inviteLink": "..." } or empty map
```

### `Cello.getCampaignConfig(): Future<Map<String, dynamic>>`

A method to get the campaign configuration.

```dart theme={null}
final config = await Cello.getCampaignConfig();
// Returns: Map with campaign settings
```

### `Cello.changeLanguage(String language): Future<void>`

A method to change the language of the Referral component at runtime without re-initialising it.

**Note**: Requires iOS 14+ on iOS platform.

```dart theme={null}
await Cello.changeLanguage('de');
```

### `Cello.setThemeMode(String themeMode): Future<void>`

A method to change the theme mode of the Referral component at runtime without re-initialising it.

**Note**: Requires iOS 14+ on iOS platform.

```dart theme={null}
await Cello.setThemeMode('dark');
```

**Parameters:**

* `themeMode` (*String*): The theme mode to set. Valid values are `"light"`, `"dark"`, or `"system"`.

### `Cello.updateToken(String token): Future<void>`

Updates the user authentication token without re-initializing.

**Note**: Requires iOS 14+ on iOS platform.

```dart theme={null}
await Cello.updateToken('new-token');
```

### `Cello.shutdown(): Future<void>`

Shuts down connection to Cello and unmounts the component

```dart theme={null}
await Cello.shutdown();
```

### `Cello.tokenEvents: Stream<CelloTokenEvent>`

A broadcast stream that emits token lifecycle events.

```dart theme={null}
Cello.tokenEvents.listen((event) {
  switch (event.type) {
    case CelloTokenEventType.tokenAboutToExpire:
      // Refresh your token
      refreshToken();
      break;
    case CelloTokenEventType.tokenHasExpired:
      // Handle expired token
      handleExpiredToken();
      break;
  }
});
```

***

## Error Handling

### Exception types

The Cello Flutter SDK throws `PlatformException` with specific error codes when operations fail:

| Code                      | Platform(s)   | Trigger                                          | Typical causes                                                          |
| ------------------------- | ------------- | ------------------------------------------------ | ----------------------------------------------------------------------- |
| `InitializationError`     | iOS & Android | `Cello.initialize` completes with native failure | Invalid/empty product ID or token, network issues, bad environment      |
| `InitializationException` | Android       | Unexpected exception inside `Cello.initialize`   | Activity finishing, SDK not linked correctly, runtime crash             |
| `ActivityUnavailable`     | Android       | `Cello.initialize` without an active Activity    | Initialization triggered too early (e.g. before app fully ready)        |
| `TokenUpdateError`        | iOS           | `Cello.updateToken` failure                      | Token expired/invalid, network errors                                   |
| `LanguageChangeError`     | iOS           | `Cello.changeLanguage` failure                   | Unsupported language code, network errors                               |
| `ThemeModeChangeError`    | iOS           | `Cello.setThemeMode` failure                     | Invalid theme mode value, network errors                                |
| `Unavailable`             | iOS           | Feature requires iOS 14+                         | Device running \< iOS 14 when calling token, language, or theme updates |
| `ClientUnavailable`       | Android       | Method called before initialization              | SDK not initialized before calling other methods                        |
| `CelloError`              | Android       | Generic SDK error                                | Various SDK-level errors                                                |
| `UccError`                | Android       | `Cello.getActiveUcc` threw                       | SDK not initialized, network or serialization errors                    |
| `CampaignConfigError`     | Android       | `Cello.getCampaignConfig` threw                  | SDK not initialized, configuration not ready                            |
| `InvalidArguments`        | iOS & Android | Missing or invalid method arguments              | Required parameters not provided or have invalid values                 |

> **Tip:** Catch `PlatformException` and inspect `error.code`, `error.message`, and `error.details` for extra context.

### Common error scenarios

#### 1. Initialization exception

**Symptoms:** `InitializationError` or `InitializationException` in the catch block.

**Fix:** Validate credentials, confirm network connectivity, and ensure you selected the right environment (`production` vs `sandbox`). Check native logs for additional details.

#### 2. Methods called before initialization

**Symptoms:** `ClientUnavailable` error on Android or native console warnings.

**Fix:** Ensure `Cello.initialize` completes successfully before calling other methods like `showFab()` or `openWidget()`. Track initialization state in your app.

#### 3. Platform feature limitations

`Cello.changeLanguage`, `Cello.setThemeMode`, and `Cello.updateToken` require iOS 14+ on iOS. On older devices these methods reject with `Unavailable` error. Guard these calls with a platform/version check if needed.

### Error handling best practices

**1. Wrap initialization in try/catch:**

```dart theme={null}
import 'package:flutter/services.dart';
import 'package:cello_sdk/cello_sdk.dart';

Future<void> bootstrapCello() async {
  try {
    if (productId.trim().isEmpty || token.trim().isEmpty) {
      throw Exception('Missing credentials');
    }

    final config = await Cello.initialize(
      CelloInitializeOptions(
        productId: productId,
        token: token,
        productUserDetails: productUserDetails,
      ),
    );

    setState(() {
      celloReady = true;
    });

    print('[Cello] Initialized: ${config.rawResponse}');
  } on PlatformException catch (e) {
    print('[Cello] Initialization failed: ${e.code} - ${e.message}');

    // Show user-friendly error
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Referrals unavailable'),
        content: Text('Please check your connection and try again.'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('OK'),
          ),
        ],
      ),
    );
  } catch (e) {
    print('[Cello] Unexpected error: $e');
  }
}
```

**2. Delay UI interactions:** Only call `Cello.showFab()` or `Cello.openWidget()` after a successful initialization. Keep a dedicated `isCelloReady` state flag in your widgets.

```dart theme={null}
ElevatedButton(
  onPressed: celloReady ? () => Cello.openWidget() : null,
  child: Text('Open Referral'),
)
```

**3. Handle token lifecycle:** Listen to `Cello.tokenEvents` and refresh tokens proactively before they expire:

```dart theme={null}
StreamSubscription<CelloTokenEvent>? _tokenSubscription;

@override
void initState() {
  super.initState();

  _tokenSubscription = Cello.tokenEvents.listen((event) {
    if (event.type == CelloTokenEventType.tokenAboutToExpire) {
      // Refresh token from your backend
      _refreshCelloToken();
    }
  });
}

@override
void dispose() {
  _tokenSubscription?.cancel();
  super.dispose();
}

Future<void> _refreshCelloToken() async {
  try {
    // Get fresh token from your API
    final newToken = await fetchFreshToken();

    // Update Cello with new token
    await Cello.updateToken(newToken);

    print('[Cello] Token refreshed successfully');
  } on PlatformException catch (e) {
    print('[Cello] Token refresh failed: ${e.code} - ${e.message}');
  }
}
```

**4. Retry on transient errors:** For recoverable failures (network timeouts, `InitializationError`), schedule a retry with exponential backoff. Limit retries to avoid looping endlessly.

**5. Log error details:** Capture error codes, messages, and relevant details in your logging/monitoring solution to help support diagnose issues quickly.
