# Going Live Checklist

Before releasing your Stream Chat Flutter integration to production, work through this checklist to avoid common pitfalls and ensure a smooth launch.

### Authentication

**Use server-side token generation.** Development tokens (generated client-side or hardcoded) are only suitable for local testing. Before going live, your backend must generate user tokens using your Stream API secret. Never ship your API secret in the app binary.

**Use expiring tokens.** Tokens with an expiry date limit the blast radius if a token is compromised. Implement a `tokenProvider` callback on `StreamChatClient` so the SDK can refresh tokens automatically:

```dart
final client = StreamChatClient('your-api-key');

await client.connectUserWithProvider(
  User(id: 'user-id'),
  (String userId) async => await fetchToken(userId),
);
```

### Sensitive data

**Never store your Stream API secret in the app.** It can be extracted from the binary by attackers. Keep it on your server only.

**Review local storage.** If you have offline support enabled, message content is persisted to the device. Confirm this meets your privacy and compliance requirements.

### Logout and user switching

**Always await `disconnect` before switching users.** The SDK uses persistent local storage. Switching users without waiting for `disconnect` to complete can throw `User already getting connected, try calling disconnectUser`:

```dart
await client.disconnectUser();
// now it is safe to connect a different user
await client.connectUser(newUser, newToken);
```

### Performance

**Test under realistic load.** For channels with many concurrent users (livestreams, large groups), run load tests before launch. Stream provides the [benchat](https://github.com/GetStream/benchat) repository with scripts that simulate many users sending messages simultaneously.

**Avoid rebuilding controllers in widget `build` methods.** Creating a `StreamChannelListController` or other controller inside `build` causes it to be recreated on every rebuild. Define controllers in `initState` or `State` fields:

```dart
// Correct
class _MyPageState extends State<MyPage> {
  late final _controller = StreamChannelListController(...);

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
```

**Dispose controllers.** Always call `dispose()` on `StreamChannelListController`, `StreamMessageComposerController`, and other controllers when the widget is removed from the tree to free resources and cancel subscriptions.

### Push notifications

**Test on a physical device.** Push notifications do not work on iOS simulators. Verify that your APNs / FCM configuration works end-to-end on a real device before release.

**Handle background messages.** Ensure your background message handler processes Stream payloads and avoids performing UI operations, since no UI context is available in the background.

### Logging

**Reduce the log level in production.** Use `Level.WARNING` or `Level.SEVERE` rather than `Level.ALL` to minimise performance overhead and avoid leaking sensitive information to log aggregators:

```dart
final client = StreamChatClient(
  'your-api-key',
  logLevel: kDebugMode ? Level.ALL : Level.WARNING,
);
```

### App store submissions

**iOS Info.plist permissions.** If your app uses the attachment picker, push notifications, or location sharing, ensure all required `NSUsageDescription` keys are present in `Info.plist`. Missing descriptions cause App Store rejection.

**Android permissions.** Review `AndroidManifest.xml` for all required permissions (storage, camera, microphone for voice recording). Remove any permissions your app does not actually use.

If you have questions about your integration or encounter issues, contact [Stream support](https://support.getstream.io/hc/en-us).


---

This page was last updated at 2026-06-09T15:44:03.959Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/flutter/guides/go-live-checklist/](https://getstream.io/chat/docs/sdk/flutter/guides/go-live-checklist/).