This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Before writing or reviewing code, read
STYLE_GUIDE.md. It is the source of truth for coding conventions, the barrel contract, theming, testing, and changelog policy. SeeTESTING.mdfor guidance on writing effective tests, andEFFECTIVE_DART_DOC.md— a vendored copy of Effective Dart's documentation guide — before writing any dartdoc; the style guide wins where they disagree. This file is a repo overview; the style guide is the rulebook.
A Flutter monorepo managed with Melos containing:
packages/stream_core— Pure Dart SDK (WebSocket, HTTP, models, utilities)packages/stream_core_flutter— Flutter UI component library with a full design systemapps/design_system_gallery— Widgetbook-based interactive component showcase
All commands use Melos and should be run from the repo root.
# Setup
melos bootstrap
# Linting & formatting
melos run lint:all # analyze + format check
melos run analyze
melos run format
melos run format:verify # check only, no changes
melos run check:barrels # validate public-barrel contract (see Architecture)
# Testing
melos run test:all # all tests with coverage
melos run test:dart # stream_core only
melos run test:flutter # stream_core_flutter only
# Golden tests
melos run update:goldens # regenerate golden images
# Code generation (run after model/theme changes)
melos run generate:all
melos run generate:icons # regenerate icon font from SVGs
melos run gen-l10n # regenerate localizationsLine width: 120 characters (set in analysis_options.yaml).
StreamIcons is generated: an icon font (lib/fonts/stream_icons_font.otf) plus
Dart constants, built by melos run generate:icons from the source SVGs in
packages/stream_core_flutter/assets_source/icons/. Those SVGs are copied from the
design-system-tokens
repo. Multicolor file-type icons are not part of the font — they ship as runtime
assets in assets/file_type/ and are resolved by path by StreamFileTypeIcon.
Glyph code points are append-only and recorded in assets_source/icon_log.g.txt,
so adding, renaming or retiring an icon has consequences beyond the file you touch.
Never hand-edit the generated Dart, the font, or the log.
Use the update-icons skill for any icon work — it covers finding an icon
upstream, the naming and size conventions, RTL mirroring, deprecations, and the
file-type assets.
UI components are designed in Figma. When implementing or modifying components, use the Figma MCP to inspect designs directly — check spacing, colors, typography, and component structure from the source rather than guessing.
The policy itself lives in STYLE_GUIDE.md: annotate with @Deprecated('Use X instead.'), add a ### 🛑 Breaking / Removals CHANGELOG entry pointing at the replacement, and keep the deprecated API for at least one minor release.
On top of that, give every deprecated member a migration in packages/stream_core_flutter/lib/fix_data.yaml (format docs) so consumers can move off it with:
dart fix --applyThings worth knowing about that file:
- Dart only reads it at
lib/fix_data.yamlorlib/fix_data/*.yaml.element.urismust list the library that declares the member and every barrel it is exported from (core.dart,chat.dart,stream_core_flutter.dart) — a transform whose uris miss the barrel the consumer actually imported never fires. - Treat it as append-only. A transform's real value is carrying someone across the release that finally deletes the member, so it has to outlive the deprecation that motivated it. Never drop an entry just because the member is gone.
- A member reached only through generated code needs its own transform, and may not warn at all.
StreamIcons.copyWith(more: ...)is the worked example:copyWithis generated onto the private_$StreamIconsmixin, which does not inherit the field's@Deprecated, so the call raises no warning while the field exists — its transform (inMixin: "_$StreamIcons") only fires once the field is deleted and the call becomes anundefined_named_parametererror. - Verify a transform by running it, not by reading the YAML. Write a throwaway file exercising each call shape (bare constant, instance field, constructor argument,
copyWith), rundart fix --dry-run, then re-run with the transform removed to confirm the fix disappears — the analyzer offers generic "did you mean" fixes that are easy to mistake for your own.
The package exposes two narrow public barrels so non-chat Stream SDKs (video, feeds, ...) can pull in just the shared primitives without paying for chat code:
package:stream_core_flutter/core.dart— shared UI primitives, theme tokens, the component factory. Safe for any Stream SDK.package:stream_core_flutter/chat.dart— chat-specific widgets (message bubble, composer attachments, reactions, ...). Chat SDKs import this alongsidecore.dart.package:stream_core_flutter/stream_core_flutter.dart— deprecated convenience barrel that re-exports both. Will be removed at 1.0.0.
Rules enforced by melos run check:barrels (config at packages/stream_core_flutter/check_barrels.yaml, and wired into CI):
- Every public file under
lib/src/must appear in exactly one barrel. No duplicates, no orphans, no dangling exports. - No file under
lib/src/may import a public barrel (core.dart,chat.dart,stream_core_flutter.dart). Use a specific relative import to the source file instead — barrels are for consumers, not internal code. - Anything under an
internal/directory listed incheck_barrels.yaml'sinternal_dirsis excluded from coverage. Use this for figma-generated tokens and other implementation-only artefacts.
When adding a new public widget or theme: create the file under lib/src/..., then add an export 'src/.../my_file.dart'; line to either core.dart or chat.dart. The check fails on PR if you forget.
Uses theme_extensions_builder to generate Material 3 theme extensions. The hierarchy is:
- Primitives — raw design tokens: colors, typography, spacing, radius, icons
- Semantics — semantic mappings (e.g.,
primaryColor,bodyText) - Component themes — per-widget theme classes (50+ components), defined in
theme/components/ - Tokens — light/dark concrete values in
theme/primitives/internal/tokens/, copied by hand from the design-token repo and not part of the public API (see Design tokens)
Generated files have .g.theme.dart extension. After modifying .theme.dart files, run melos run generate:flutter.
Colors originate in design-system-tokens,
the same repo the icons come from. theme/primitives/internal/tokens/{light,dark}/stream_tokens.dart
holds the vendored values; it is maintained by hand, is not part of the public API,
and only stream_colors.dart and stream_color_scheme.dart read it. Dimensions
live beside it in internal/tokens/stream_tokens_dimensions.dart — one
mode-independent copy, read by StreamSpacing, StreamRadius,
StreamLineHeight and StreamFontWeight. Font sizes are the one group that
differs per platform, so they mirror upstream's flavor split in
internal/tokens/{android,ios}/stream_tokens_font_size.dart and feed the two
StreamFontSize scales.
Only the root semantics are mapped to a StreamColorScheme field. Upstream's
derived tokens (badge/*, button/*, avatar/*) get no field — components
re-derive them from colorScheme.* in their own defaults. StreamColorScheme is
exported from core.dart, so every field on it is public API.
melos run check:tokens fails on a vendored constant nothing reads, and on
light/ and dark/ disagreeing about which constants exist — so add a constant
only when a field or class will read it, and let CI catch the rest.
A field's dartdoc comes from the token's own $description in the upstream JSON —
quote it rather than inventing prose, but resolve the aliases first, since a
description tracks the token's own light/dark progression and not a comparison
with the sibling it names.
Use the update-design-tokens skill when syncing a token change or assessing
an upstream PR — it covers the naming rules, how a field default should resolve,
and how to read a change without drowning in the generator's re-sort noise.
Components are organized by category: avatar/, buttons/, badge/, list/, message_composer/, emoji/, context_menu/, controls/, common/, accessories/.
Each component typically has:
- A widget file
- A theme file in
theme/components/ - A golden test in
test/components/<name>/ - A Widgetbook use-case in
apps/design_system_gallery/
Pure Dart. Key modules:
src/ws/— WebSocket client with reconnect/backoff logic (RxDart-based)src/api/— Dio HTTP client with interceptorssrc/attachment/— File upload and CDN clientsrc/query/— Query builders and filter modelssrc/logger/— Structured loggingsrc/user/— User models and token management
Golden tests use Alchemist (^0.13.0). Goldens are stored under:
test/components/<name>/goldens/ci/— for CItest/components/<name>/goldens/macos/— for local macOS development
Golden tests are tagged with golden in dart_test.yaml. Run melos run update:goldens to regenerate after visual changes.
- json_serializable — model serialization (
.g.dartfiles) - build_runner — orchestrates all generation
- theme_extensions_builder — generates theme extension classes (
.g.theme.dart) - widgetbook_generator — auto-generates Widgetbook entries
After any model or theme annotation changes, run the appropriate generate command before running tests.