Adding chat to a Flutter app means more than sending text. You may also need channel membership, message history, typing indicators, file uploads, moderation, notifications, and a way to keep credentials out of the app. Stream Chat provides a hosted chat API and Flutter SDK for those pieces; the SDK includes ready-made widgets as well as lower-level packages.
This guide uses the current Stream Chat Flutter 10.x API to outline a secure integration, a channel list, and the decisions to make before shipping. Flutter's cross-platform development overview explains the framework's platform model.
What the Stream Chat Flutter SDK provides
The official stream_chat_flutter package combines a Flutter UI kit with Stream's chat client. Its widgets cover channel and message lists, composing messages, attachments, reactions, threads, typing and read indicators, and other common chat interactions. The package targets Android, iOS, web, macOS, Windows, and Linux, but individual media features can have platform-specific requirements.
Stream splits its Flutter integration into three levels:
stream_chat_flutteris the full UI SDK and the quickest starting point for a conventional chat interface.stream_chat_flutter_coreprovides state, controllers, and builders when you want to make your own UI.stream_chatis the lower-level Dart client when you also want to own more of the state and interface.
These are managed-service SDKs, not a self-hosted chat server. Compare the hosted service's current pricing and usage limits with the cost of operating your own backend, and consider data portability and vendor dependence before you commit.
Create a Flutter project and add the SDK
Install Flutter and confirm the toolchain with flutter doctor. The current stream_chat_flutter release requires Flutter 3.44 or later and Dart 3.12 or later; check the package's published requirements before upgrading an existing app.
Create a project if you do not already have one, then add the UI SDK:
flutter create chat_app
cd chat_app
flutter pub add stream_chat_flutter
flutter pub get
The latest package release verified for this update is 10.6.0. If you manage dependencies in pubspec.yaml yourself, use a compatible 10.x constraint and resolve it with flutter pub get. Review the official installation guide for platform-specific setup, including media and desktop video requirements.
Authenticate chat users securely
Create a Stream app in the Stream dashboard. The API key identifies that app and can be included in the Flutter client. The API secret must stay on a trusted server: use your backend to authenticate the person in your app and issue a Stream user token for the same user ID.
Do not generate tokens in Flutter or ship the Stream API secret in the app. Hardcoded test tokens are not a production authentication design. If you use Firebase, Stream documents a Firebase Authentication and Cloud Functions flow; other backends should generate tokens server-side using a Stream server SDK.
After your normal login flow has obtained the authenticated user's ID and a token from your backend, connect one client for that user:
final client = StreamChatClient(apiKey);
await client.connectUser(
User(id: authenticatedUser.id),
chatTokenFromYourBackend,
);
apiKey, authenticatedUser, and chatTokenFromYourBackend come from your app's configuration, sign-in state, and authenticated backend request. Keep the token tied to that user, refresh it through the backend when needed, and disconnect the client when the app signs out. See Stream's Flutter SDK documentation for client lifecycle and authentication guidance.
Show a channel list and open a conversation
Put StreamChat above the widgets that use Stream's chat state. A StreamChannelListController handles filtering, sorting, and pagination; the example below limits the list to channels the signed-in user belongs to and opens a conversation when a row is tapped.
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class ChannelListPage extends StatefulWidget {
const ChannelListPage({super.key, required this.client});
final StreamChatClient client;
@override
State<ChannelListPage> createState() => _ChannelListPageState();
}
class _ChannelListPageState extends State<ChannelListPage> {
late final _controller = StreamChannelListController(
client: widget.client,
filter: Filter.in_(
'members',
[StreamChat.of(context).currentUser!.id],
),
channelStateSort: const [
SortOption<ChannelState>.desc('last_message_at'),
],
);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: const StreamChannelListHeader(),
body: StreamChannelListView(
controller: _controller,
onChannelTap: (channel) => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const StreamChannelPage(),
),
),
),
),
);
}
Use the connected client when creating the app's widget tree:
MaterialApp(
builder: (context, child) => StreamChat(client: client, child: child),
home: ChannelListPage(client: client),
)
This is the channel-list pattern from Stream's current widget documentation. The channel screen supplies the message list, composer, and thread interactions. Your app still needs to create channels and assign members through an appropriate client or server workflow; an empty list is expected when the signed-in user has not joined any channels.
Choose the features your product needs
Start with the smallest set of chat features that solves a real user need, then add capabilities deliberately:
- Direct and group conversations: Decide how your app creates channels, identifies members, and controls who may invite or remove participants. Keep permission checks on the server rather than trusting client-supplied roles.
- Messages and collaboration: The UI SDK includes common text, typing, presence, read-state, reaction, and thread interactions. Configure channel types and permissions to match the product instead of assuming every conversation should behave the same way.
- Attachments: Images, videos, and files involve device permissions, platform plugins, storage, and moderation decisions. Check Stream's installation notes for your target platforms before enabling them.
- Moderation and integrations: Decide how users report or block abuse and who can remove content. Put privileged actions and bot or third-party integrations behind server-side permissions rather than trusting a mobile client.
- Offline access: If your app must retain channel and message data locally, add the compatible
stream_chat_persistencepackage and configure it before connecting the user. Review the offline support guide and decide how cached data is handled on logout or shared devices. - Push notifications: Push needs a configured provider, such as Firebase Cloud Messaging, plus device registration and platform setup. Stream's Flutter push guide explains the integration; channel membership is required for delivery.
- Branding: The SDK supports theming and widget customization. Check the version-specific Flutter docs when customizing because major SDK releases can change component names and theme APIs.
If you already design in Figma, the original DhiWise guide to converting a Figma design to Flutter code can be one input to the handoff. Treat generated UI as a starting point: review its accessibility, responsive behavior, and maintainability before shipping.
Test the integration before release
Use separate development and production Stream apps when practical. Verify sign-in and sign-out, token refresh, channel membership, empty and error states, pagination, reconnect behavior, attachment permissions, and notification handling on each platform you support. Also confirm that the API secret is absent from the app bundle and that server-side permissions prevent users from joining or modifying channels they should not access.
Stream's Flutter tutorial and GitHub repository provide maintained examples. Recheck the package changelog and compatibility notes when upgrading; avoid copying snippets written for older SDK versions without comparing them with the current API.
For another practical open-source project, see open-source HTML5 games.