Skip to content

feat(ui): floating header#115

Open
renefloor wants to merge 5 commits into
mainfrom
feat/floating-header
Open

feat(ui): floating header#115
renefloor wants to merge 5 commits into
mainfrom
feat/floating-header

Conversation

@renefloor
Copy link
Copy Markdown
Collaborator

@renefloor renefloor commented May 27, 2026

Submit a pull request

Linear: FLU-502

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

This adds the option to create a floating appbar. It automatically adjusts the background from a color to a gradient and removes the border. Also makes the back button floating by default.

Screenshots / Videos

image

Summary by CodeRabbit

  • New Features

    • Floating app bar mode with gradient fade over content and adaptive button styling
    • Theme-level app style (regular vs floating) with a new floating background color option and top-level export
    • Playground knob and a new showcase preview for the floating — gradient fade effect
    • New scaffold and floating bottom navigation bar variants to support floating app/bottom behaviors
  • Tests

    • Added golden and unit tests covering floating vs regular app-bar visuals and button behavior

Review Change Stack

@renefloor renefloor requested a review from a team as a code owner May 27, 2026 12:52
@renefloor renefloor changed the title Feat/floating header feat(ui): floating header May 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

Warning

Review limit reached

@renefloor, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 52 minutes and 22 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ea47f813-66ce-4017-9744-58eedf23a210

📥 Commits

Reviewing files that changed from the base of the PR and between 61b0dfb and 3bd5156.

📒 Files selected for processing (4)
  • packages/stream_core_flutter/lib/src/components.dart
  • packages/stream_core_flutter/lib/src/components/scaffold/stream_scaffold.dart
  • packages/stream_core_flutter/lib/src/components/toolbar/stream_bottom_nav_bar.dart
  • packages/stream_core_flutter/lib/src/theme/stream_app_style.dart
📝 Walkthrough

Walkthrough

Adds a floating mode for StreamAppBar that renders a safe-area-aware vertical gradient fade instead of a solid background+bottom border, adapts leading button styling, and exposes/app-uses theme/appStyle tokens; updates the gallery and adds unit and golden tests.

Changes

Floating Mode Feature for StreamAppBar

Layer / File(s) Summary
Theme contract and floating background color property
packages/stream_core_flutter/lib/src/theme/components/stream_app_bar_theme.dart, packages/stream_core_flutter/lib/src/theme/components/stream_app_bar_theme.g.theme.dart
StreamAppBarStyle gains an optional floatingBackgroundColor propagated through lerp, copyWith, merge, equality, and hashCode.
Core StreamAppBar floating feature
packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart
Add appBarBehavior parameter and StreamAppBarProps.appBarBehavior, resolve effective behavior from props or theme, compute effective background color, switch DecoratedBox between solid+border and safe-area-aware vertical gradient fade, and adapt auto-implied leading StreamButton type and isFloating flag.
Theme wiring and app-style token
packages/stream_core_flutter/lib/src/theme/stream_app_style.dart, packages/stream_core_flutter/lib/src/theme/stream_theme.dart, packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart, packages/stream_core_flutter/lib/src/theme.dart
Introduce StreamAppStyle and enums (ComposerLocation, AppBarBehavior, BottomBarBehavior), export the new token, add appStyle to StreamTheme, and propagate appStyle through generated theme helpers.
Components and scaffold
packages/stream_core_flutter/lib/src/components/scaffold/stream_scaffold.dart, packages/stream_core_flutter/lib/src/components.dart
Add StreamScaffold and StreamScaffoldInsets with custom layout and inset publishing for floating bars; update barrel exports to include scaffold and reorder toolbar exports.
Bottom navigation component
packages/stream_core_flutter/lib/src/components/toolbar/stream_bottom_nav_bar.dart
Add StreamBottomNavBarItem and StreamBottomNavBar, rendering floating pill-style or regular docked bar based on behavior/appStyle.
Gallery playground and showcase
apps/design_system_gallery/lib/components/toolbar/stream_app_bar.dart
Playground adds a floating knob that builds the bar into a variable and conditionally wraps it in a gradient-fade preview; showcase adds a "Floating — gradient fade over content" example and _FloatingAppBarPreview widget.
Unit and golden test coverage
packages/stream_core_flutter/test/components/toolbar/stream_app_bar_test.dart, packages/stream_core_flutter/test/components/toolbar/stream_app_bar_golden_test.dart
Add widget tests asserting background/border vs gradient behavior and leading button type selection; add golden tests for light/dark × normal/floating and helpers that render floating bar over simulated content.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • GetStream/stream-core-flutter#113: Both PRs modify packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart — this earlier PR refactors StreamAppBar layout/height constants while the current PR adds behavior-driven floating styling.

Poem

🐰 I float a ribbon soft and bright,
A fuzzy gradient edge of light,
Themes and tests I hop along,
Buttons change their little song,
Gallery shows my airy flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(ui): floating header' directly summarizes the main feature: adding a floating header/appbar with gradient background styling.
Description check ✅ Passed The description provides a clear explanation of the feature, references the Linear ticket (FLU-502), confirms CLA signing and testing, and includes a screenshot demonstrating the floating header functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/floating-header

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/design_system_gallery/lib/components/toolbar/stream_app_bar.dart (1)

100-100: ⚡ Quick win

Replace hardcoded margin with spacing token.

The horizontal margin of 32 should use a spacing token from context.streamSpacing rather than a hardcoded value. As per coding guidelines, always use spacing tokens from the theme context instead of hardcoded values.

♻️ Proposed fix
               margin: const EdgeInsets.symmetric(horizontal: 32),
+              // Replace with appropriate spacing token, e.g.:
+              // margin: EdgeInsets.symmetric(horizontal: spacing.xl),

Note: Choose the spacing token that best matches the intended 32-pixel spacing (likely spacing.xl or similar).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/design_system_gallery/lib/components/toolbar/stream_app_bar.dart` at
line 100, Replace the hardcoded horizontal margin of 32 in StreamAppBar with the
theme spacing token: locate the margin: const EdgeInsets.symmetric(horizontal:
32) in stream_app_bar.dart (the AppBar/Container/widget that sets margin) and
use the spacing token from context.streamSpacing (e.g.,
EdgeInsets.symmetric(horizontal: context.streamSpacing.xl) or the appropriate
token that maps to 32px) so the layout uses the theme spacing instead of a
literal value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/design_system_gallery/lib/components/toolbar/stream_app_bar.dart`:
- Line 100: Replace the hardcoded horizontal margin of 32 in StreamAppBar with
the theme spacing token: locate the margin: const
EdgeInsets.symmetric(horizontal: 32) in stream_app_bar.dart (the
AppBar/Container/widget that sets margin) and use the spacing token from
context.streamSpacing (e.g., EdgeInsets.symmetric(horizontal:
context.streamSpacing.xl) or the appropriate token that maps to 32px) so the
layout uses the theme spacing instead of a literal value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f9bb118-2547-48c3-ab91-fa8c2e73b2eb

📥 Commits

Reviewing files that changed from the base of the PR and between 333f7b7 and 84123de.

📒 Files selected for processing (6)
  • apps/design_system_gallery/lib/components/toolbar/stream_app_bar.dart
  • packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_app_bar_theme.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_app_bar_theme.g.theme.dart
  • packages/stream_core_flutter/test/components/toolbar/stream_app_bar_golden_test.dart
  • packages/stream_core_flutter/test/components/toolbar/stream_app_bar_test.dart

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/stream_core_flutter/lib/src/theme/stream_app_style.dart (1)

7-11: 💤 Low value

Clarify naming: StreamAppStyle.floating() keeps appBarBehavior as regular.

The .floating() constructor sets composerLocation = floating but appBarBehavior = AppBarBehavior.regular. This may confuse consumers who expect "floating" to also affect the app bar. Consider either:

  • Adding a doc comment explaining this distinction, or
  • Renaming to StreamAppStyle.floatingComposer() for clarity
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stream_core_flutter/lib/src/theme/stream_app_style.dart` around
lines 7 - 11, The floating constructor StreamAppStyle.floating() sets
composerLocation = ComposerLocation.floating but leaves appBarBehavior =
AppBarBehavior.regular which can confuse callers; update the API to make intent
explicit by either adding a doc comment on StreamAppStyle.floating() that
documents that only the composerLocation changes (appBarBehavior remains
regular), or rename the constructor to StreamAppStyle.floatingComposer() and
update all usages to the new name so it clearly indicates only the composer is
floating while composerLocation and appBarBehavior fields remain explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart`:
- Around line 1-2: Remove the unused JavaScript interop import: delete the line
"import 'dart:js_interop';" from stream_app_bar.dart (the import at the top of
the file) and run the analyzer or build to confirm there are no remaining
references to dart:js_interop in this file; this file's symbols like the
StreamAppBar widget/constructor do not require JS interop so the import is safe
to remove.

---

Nitpick comments:
In `@packages/stream_core_flutter/lib/src/theme/stream_app_style.dart`:
- Around line 7-11: The floating constructor StreamAppStyle.floating() sets
composerLocation = ComposerLocation.floating but leaves appBarBehavior =
AppBarBehavior.regular which can confuse callers; update the API to make intent
explicit by either adding a doc comment on StreamAppStyle.floating() that
documents that only the composerLocation changes (appBarBehavior remains
regular), or rename the constructor to StreamAppStyle.floatingComposer() and
update all usages to the new name so it clearly indicates only the composer is
floating while composerLocation and appBarBehavior fields remain explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac5ca1cf-6f3b-4b23-a425-08b5d6d077c5

📥 Commits

Reviewing files that changed from the base of the PR and between 84123de and 234103c.

📒 Files selected for processing (5)
  • packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart
  • packages/stream_core_flutter/lib/src/theme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_app_style.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart
✅ Files skipped from review due to trivial changes (1)
  • packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart

Comment thread packages/stream_core_flutter/lib/src/components/toolbar/stream_app_bar.dart Outdated
@renefloor renefloor force-pushed the feat/floating-header branch from 234103c to 24ae763 Compare May 28, 2026 15:06
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@renefloor renefloor force-pushed the feat/floating-header branch from 61b0dfb to 3bd5156 Compare May 29, 2026 11:19
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/stream_core_flutter/lib/src/components/toolbar/stream_bottom_nav_bar.dart`:
- Around line 203-224: Wrap the floating nav item in a Semantics widget (around
the existing GestureDetector or its child) and set semantics properties to
expose it as a button with selection state: provide button: true, selected:
selected, label: item.label (and optionally a meaningful onTapHint), and include
the onTap callback via the Semantics onTap so assistive tech announces role and
selected/unselected state while preserving the existing onTap behavior; update
the build that uses item, selected, and onTap accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a1618e8-8268-4f3e-a9c4-cd1534d3b36c

📥 Commits

Reviewing files that changed from the base of the PR and between 24ae763 and 61b0dfb.

📒 Files selected for processing (4)
  • packages/stream_core_flutter/lib/src/components.dart
  • packages/stream_core_flutter/lib/src/components/scaffold/stream_scaffold.dart
  • packages/stream_core_flutter/lib/src/components/toolbar/stream_bottom_nav_bar.dart
  • packages/stream_core_flutter/lib/src/theme/stream_app_style.dart
✅ Files skipped from review due to trivial changes (1)
  • packages/stream_core_flutter/lib/src/components.dart

Comment on lines +203 to +224
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: spacing.md, vertical: spacing.xxs),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: spacing.xxxs,
children: [
IconTheme(
data: IconThemeData(color: color, size: 20),
child: selected ? item.selectedIcon : item.icon,
),
Text(
item.label,
style: textTheme.metadataEmphasis.copyWith(color: color),
),
],
),
),
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add semantics to the floating nav item.

The regular variant uses BottomNavigationBar, which exposes each tab to assistive tech as a selectable button with a selected state. The floating variant renders raw GestureDetector + Icon + Text, so screen readers announce only the label text — not the button role nor the selected/unselected state. Wrap the item in Semantics to match the regular variant's accessibility.

♿ Proposed fix to expose button role and selected state
-    return GestureDetector(
-      behavior: HitTestBehavior.opaque,
-      onTap: onTap,
-      child: Padding(
-        padding: EdgeInsets.symmetric(horizontal: spacing.md, vertical: spacing.xxs),
-        child: Column(
-          mainAxisSize: MainAxisSize.min,
-          spacing: spacing.xxxs,
-          children: [
-            IconTheme(
-              data: IconThemeData(color: color, size: 20),
-              child: selected ? item.selectedIcon : item.icon,
-            ),
-            Text(
-              item.label,
-              style: textTheme.metadataEmphasis.copyWith(color: color),
-            ),
-          ],
-        ),
-      ),
-    );
+    return Semantics(
+      button: true,
+      selected: selected,
+      label: item.label,
+      child: GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onTap: onTap,
+        child: Padding(
+          padding: EdgeInsets.symmetric(horizontal: spacing.md, vertical: spacing.xxs),
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            spacing: spacing.xxxs,
+            children: [
+              IconTheme(
+                data: IconThemeData(color: color, size: 20),
+                child: selected ? item.selectedIcon : item.icon,
+              ),
+              Text(
+                item.label,
+                style: textTheme.metadataEmphasis.copyWith(color: color),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: spacing.md, vertical: spacing.xxs),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: spacing.xxxs,
children: [
IconTheme(
data: IconThemeData(color: color, size: 20),
child: selected ? item.selectedIcon : item.icon,
),
Text(
item.label,
style: textTheme.metadataEmphasis.copyWith(color: color),
),
],
),
),
);
}
return Semantics(
button: true,
selected: selected,
label: item.label,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: spacing.md, vertical: spacing.xxs),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: spacing.xxxs,
children: [
IconTheme(
data: IconThemeData(color: color, size: 20),
child: selected ? item.selectedIcon : item.icon,
),
Text(
item.label,
style: textTheme.metadataEmphasis.copyWith(color: color),
),
],
),
),
),
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/stream_core_flutter/lib/src/components/toolbar/stream_bottom_nav_bar.dart`
around lines 203 - 224, Wrap the floating nav item in a Semantics widget (around
the existing GestureDetector or its child) and set semantics properties to
expose it as a button with selection state: provide button: true, selected:
selected, label: item.label (and optionally a meaningful onTapHint), and include
the onTap callback via the Semantics onTap so assistive tech announces role and
selected/unselected state while preserving the existing onTap behavior; update
the build that uses item, selected, and onTap accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant