This is beta documentation for Stream Chat Flutter SDK v10. For the latest stable version, see the latest version (v9).

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:

MaterialApp(
  theme: ThemeData(
    extensions: [
      StreamTheme.light(), // or StreamTheme.dark() for dark mode
    ],
  ),
  home: StreamChat(
    client: client,
    child: const MyHomePage(),
  ),
)

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 a ThemeExtension on MaterialApp.theme.
  • StreamChatThemeData (chat-specific themes) — controls styling for chat components like message bubbles, channel list items, message input, polls, and galleries. Passed to StreamChat.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):

ComponentTheme Class
Message itemsStreamMessageItemThemeData
Reaction pickerStreamReactionPickerThemeData
AvatarsStreamAvatarThemeData
BadgesStreamBadgeNotificationThemeData

Chat-specific themes (via StreamChatThemeData):

ComponentTheme Class
Channel list itemsStreamChannelListItemThemeData
Own messagesStreamMessageThemeData
Other messagesStreamMessageThemeData
Message inputStreamMessageInputThemeData
Channel headerStreamChannelHeaderThemeData
PollsStreamPollCreatorThemeData, StreamPollInteractorThemeData
Thread listStreamThreadListTileThemeData
Drafts listStreamDraftListTileThemeData
Voice recordingStreamVoiceRecordingAttachmentThemeData

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: ...,
)
PropertyDescription
reactionIconResolverMaps reaction types to emoji/widgets. Defaults to DefaultReactionIconResolver
enforceUniqueReactionsWhether a new reaction replaces the existing one. Defaults to true
draftMessagesEnabledEnables draft message support. Defaults to false
imageCDNImage CDN for generating resized URLs and cache keys. Defaults to StreamImageCDN
attachmentBuildersCustom attachment renderers prepended to the defaults
reactionTypeVisual style of reactions display (StreamReactionsType.segmented by default)
reactionPositionWhere reactions appear relative to the bubble (StreamReactionsPosition.header by default)
messagePreviewFormatterFormatter for message previews in channel lists
defaultUserImageWidget shown when a user has no avatar image
placeholderUserImageWidget shown while a user's avatar is loading