MaterialApp(
theme: ThemeData(
extensions: [
StreamTheme.light(), // or StreamTheme.dark() for dark mode
],
),
home: StreamChat(
client: client,
child: const MyHomePage(),
),
)Theming
Customizing Widgets Using StreamTheme
Background
Stream's UI SDK makes it easy for developers to add custom styles and attributes to widgets. Starting with the design-refresh release, Stream uses StreamTheme — a Flutter ThemeExtension — instead of a dedicated wrapper widget.
StreamTheme is passed via MaterialApp.theme.extensions (or MaterialApp.darkTheme.extensions) and is resolved from Theme.of(context) anywhere in the widget tree.
Setting Up StreamTheme
Add StreamTheme as a theme extension to your MaterialApp:
If no StreamTheme is provided, a default theme is automatically created based on Theme.of(context).brightness (light or dark mode).
Customizing StreamTheme
Use the StreamTheme constructor (or StreamTheme.light() / StreamTheme.dark() factories) with copyWith to customize only the properties you need:
MaterialApp(
theme: ThemeData(
extensions: [
StreamTheme(
brightness: Brightness.light,
colorScheme: StreamColorScheme.light().copyWith(
primary: Colors.indigo,
secondary: Colors.indigoAccent,
),
avatarTheme: const StreamAvatarThemeData(
// Customize avatar defaults...
),
),
],
),
home: ...,
)Reading the Theme in Widgets
Access the current StreamTheme anywhere in the widget tree:
final streamTheme = StreamTheme.of(context);
final colorScheme = streamTheme.colorScheme;
final avatarTheme = streamTheme.avatarTheme;Two-Layer Theme Architecture
Stream Chat uses two complementary theme layers:
StreamTheme(design-system tokens) — shared across all Stream products. Controls color scheme, typography, avatar sizing, badges, reaction picker appearance, and other low-level primitives. Provided as aThemeExtensiononMaterialApp.theme.StreamChatThemeData(chat-specific themes) — controls styling for chat components like message bubbles, channel list items, message input, polls, and galleries. Passed toStreamChat.streamChatThemeData.
Both are optional — sensible defaults are applied automatically.
Per-Component Theme Objects
Each component has its own theme data class. Depending on which layer it belongs to, you configure it differently:
Design-system themes (via StreamTheme):
| Component | Theme Class |
|---|---|
| Message items | StreamMessageItemThemeData |
| Reaction picker | StreamReactionPickerThemeData |
| Avatars | StreamAvatarThemeData |
| Badges | StreamBadgeNotificationThemeData |
Chat-specific themes (via StreamChatThemeData):
| Component | Theme Class |
|---|---|
| Channel list items | StreamChannelListItemThemeData |
| Own messages | StreamMessageThemeData |
| Other messages | StreamMessageThemeData |
| Message input | StreamMessageInputThemeData |
| Channel header | StreamChannelHeaderThemeData |
| Polls | StreamPollCreatorThemeData, StreamPollInteractorThemeData |
| Thread list | StreamThreadListTileThemeData |
| Drafts list | StreamDraftListTileThemeData |
| Voice recording | StreamVoiceRecordingAttachmentThemeData |
Example — customizing channel list items globally:
MaterialApp(
theme: ThemeData(
extensions: [
StreamTheme.light(),
],
),
home: StreamChat(
client: client,
streamChatThemeData: StreamChatThemeData(
channelListItemTheme: StreamChannelListItemThemeData(
titleStyle: const TextStyle(fontWeight: FontWeight.bold),
subtitleStyle: const TextStyle(color: Colors.grey),
timestampStyle: const TextStyle(fontSize: 12),
),
),
child: const MyHomePage(),
),
)Subtree Theme Overrides
You can override the theme for a subtree using the StreamChannelListItemTheme, StreamMessageItemTheme, or other inherited widgets:
StreamChannelListItemTheme(
data: StreamChannelListItemThemeData(
titleStyle: const TextStyle(color: Colors.blue),
),
child: StreamChannelListView(controller: controller),
)Light and Dark Mode
Pass different StreamTheme instances to MaterialApp.theme and MaterialApp.darkTheme to support both modes:
MaterialApp(
theme: ThemeData(
extensions: [StreamTheme.light()],
),
darkTheme: ThemeData(
brightness: Brightness.dark,
extensions: [StreamTheme.dark()],
),
themeMode: ThemeMode.system,
home: ...,
)Migration from StreamChatTheme Widget
In previous versions, theming used a StreamChatTheme wrapper widget:
// Old approach (v9 and earlier)
StreamChatTheme(
data: StreamChatThemeData(...),
child: MaterialApp(...),
)In v10 with the design refresh, StreamTheme is passed as a theme extension:
// New approach
MaterialApp(
theme: ThemeData(
extensions: [StreamTheme.light()],
),
home: StreamChat(client: client, child: ...),
)The StreamChatThemeData class still exists for configuring Stream-Chat-specific component themes (passed to StreamChat). StreamTheme handles the design-system visual tokens (colors, typography, avatar sizes, etc.).
Global Configuration
For global configuration options, use StreamChatConfigurationData passed to StreamChat.streamChatConfigData. This controls behavioral and structural settings that are independent of theming:
StreamChat(
client: client,
streamChatConfigData: StreamChatConfigurationData(
reactionIconResolver: const MyReactionIconResolver(),
enforceUniqueReactions: true,
draftMessagesEnabled: true,
imageCDN: const StreamImageCDN(),
attachmentBuilders: [MyCustomAttachmentBuilder(), ...defaultAttachmentBuilders],
),
child: ...,
)| Property | Description |
|---|---|
reactionIconResolver | Maps reaction types to emoji/widgets. Defaults to DefaultReactionIconResolver |
enforceUniqueReactions | Whether a new reaction replaces the existing one. Defaults to true |
draftMessagesEnabled | Enables draft message support. Defaults to false |
imageCDN | Image CDN for generating resized URLs and cache keys. Defaults to StreamImageCDN |
attachmentBuilders | Custom attachment renderers prepended to the defaults |
reactionType | Visual style of reactions display (StreamReactionsType.segmented by default) |
reactionPosition | Where reactions appear relative to the bubble (StreamReactionsPosition.header by default) |
messagePreviewFormatter | Formatter for message previews in channel lists |
defaultUserImage | Widget shown when a user has no avatar image |
placeholderUserImage | Widget shown while a user's avatar is loading |