# Dart / Flutter

`turnix_dart` wraps [`POST /credentials/ice`](https://turnix.io/docs/api-reference/credentials) and [`GET /regions`](https://turnix.io/docs/api-reference/regions) for Dart. It returns typed objects, including the expiry time.

Package: [turnix_dart on pub.dev](https://pub.dev/packages/turnix_dart) · Source: [github.com/turnixio/turnix_dart](https://github.com/turnixio/turnix_dart)

> **Tip: Integrate in seconds with your AI agent**
>
> Building a Flutter app? The [Flutter integration guide](https://turnix.io/docs/integrations/flutter) has a ready-made prompt for Claude Code, Cursor or Copilot that adds the backend endpoint and the app side in one go.

> **Warning: Use it on your server, not in the app**
>
> The SDK needs your API token. If you ship the token inside a Flutter app, anyone can extract it. Run the SDK in a Dart backend (for example with `shelf` or `dart_frog`), and let the app fetch only `iceServers` from that backend.

## Install

```bash
dart pub add turnix_dart
```

```dart
import 'package:turnix_dart/turnix_dart.dart';
```

## Request credentials

```dart
import 'dart:io';
import 'package:turnix_dart/turnix_dart.dart';

final creds = await TurnixIO.getIceCredentials(
  apiToken: Platform.environment['TURNIX_API_TOKEN']!,
  ttl: 3600,
  room: 'room-42',
  initiatorClient: 'user-1842',
  clientIp: '203.0.113.42', // the end user's public IP, for geo-routing
);

print(creds.expiresAt); // DateTime (UTC)
```

Return the servers to your app as JSON, for example:

```dart
final body = {
  'iceServers': creds.iceServers
      .map((s) => {
            'urls': s.urls,
            if (s.username != null) 'username': s.username,
            if (s.credential != null) 'credential': s.credential,
          })
      .toList(),
  'expiresAt': creds.expiresAt.toIso8601String(),
};
```

In the Flutter app, with [`flutter_webrtc`](https://pub.dev/packages/flutter_webrtc):

```dart
final pc = await createPeerConnection({'iceServers': json['iceServers']});
```

For the complete app side (fetching, refreshing before ICE restarts, verifying the relay), see the [Flutter integration guide](https://turnix.io/docs/integrations/flutter).

## API

### `TurnixIO.getIceCredentials(...)`

Returns `Future<IceCredentials>`. Only `apiToken` is required.

| Parameter | Type | Sent as |
|-----------|------|---------|
| `apiToken` | `String` | `Authorization: Bearer …` header |
| `ttl` | `int?` | `ttl` |
| `room` | `String?` | `room` |
| `initiatorClient` | `String?` | `initiator_client` |
| `receiverClient` | `String?` | `receiver_client` |
| `preferredRegion` | `String?` | `preferred_region` |
| `fixedRegion` | `String?` | `fixed_region` |
| `clientIp` | `String?` | `X-TURN-CLIENT-IP` header |

`IceCredentials` has `iceServers` (`List<IceServer>`, each with `urls`, `username?`, `credential?`) and `expiresAt` (`DateTime`).

Throws `http.ClientException` with the status code (for example `Failed to fetch ICE credentials (429)`) for any non-200 response.

### `TurnixIO.getAvailableRegions(apiToken: ...)`

Returns `Future<List<Region>>` with `slug`, `nat2` (country code), `city`, `name`, `lat`, `lon`, `isOnline` and `isOperational`.

```dart
final regions = await TurnixIO.getAvailableRegions(apiToken: token);
final usable = regions.where((r) => r.isOnline && r.isOperational);
```

## Refreshing before expiry

```dart
if (creds.expiresAt.difference(DateTime.now().toUtc()) < const Duration(seconds: 60)) {
  creds = await TurnixIO.getIceCredentials(apiToken: token, ttl: 3600);
}
```

See [Credentials & TTL](https://turnix.io/docs/concepts/credentials) for when to refresh.
