mirror of
https://github.com/block/buzz.git
synced 2026-11-10 11:21:05 +00:00
fix(mobile): keep Home's header clean under sheets (#9152)
On iOS, opening a sheet over Home (for example a channel's long-press actions) left a hard horizontal edge and a light glow band under the filter chips. Home's header keeps a native UIKit soft-edge view behind its Flutter controls, and the sheet's dim barrier doesn't composite cleanly over it. While another route covers Home, the header now switches to its Flutter backdrop through the existing `FrostedAppBar.nativeViewSuppressed` hook, the same mechanism other headers use while a backdrop covers them. Home already hides its native bottom controls under the same condition. | Before | After | | --- | --- | | <img alt="before" src="https://github.com/user-attachments/assets/a5bf01da-4f88-4055-809d-e347b35408a9" width="300"> | <img alt="after" src="https://github.com/user-attachments/assets/6813bb27-9f76-45d5-81c3-b46d8e0df647" width="300"> | ### Validation - iPhone 17 Pro simulator (iOS 27), Builderlab test community: reproduced the artifact on `main` with Home scrolled and the channel actions sheet open, then confirmed it is gone with this change. Also opened and dismissed the sheet repeatedly and checked the header before, during and after. - Transition timing, frame-stepped from simulator recordings of `main` (top row of each strip) and this PR (bottom row) on the same Home state: - The header and the bottom navigation controls switch on the same frame. A modal sheet doesn't drive Home's secondary animation (`MaterialPageRoute.canTransitionTo` is false for a sheet route), so the bottom controls' `secondaryAnimation == 0` check reduces to the same `isCurrent` gate the header uses. - Known limitation: while the native header view is swapped out, the header blanks for about 2 display frames as the sheet opens and about 3 as it closes (about 30 to 50 ms, seen as the missing chips below). `main` has no blank frames but shows the hard edge. Opening: <img alt="sheet open, main above PR" src="https://github.com/user-attachments/assets/df8f04f2-da72-4889-b9c8-5c47596fe7e3" width="900"> Closing: <img alt="sheet close, main above PR" src="https://github.com/user-attachments/assets/aa234a82-773d-4c9f-bba4-469afd158de8" width="900"> - Extended the iOS Home widget journey: the native header view is removed while the channel actions sheet is open and restored after it is dismissed. Without the fix, the journey fails. - Full mobile Flutter suite passes. buzz-review-completed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Tom Brow <tomb@squareup.com> Co-authored-by: Tom Brow <tomb@squareup.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -192,6 +192,12 @@ class ChannelsPage extends HookConsumerWidget {
|
||||
: filterContentHeight,
|
||||
useNativeNavigation: messagesFilters == null,
|
||||
);
|
||||
// A sheet's dim barrier can't composite over Home's native soft edge, so
|
||||
// use the Flutter backdrop while another route covers this page.
|
||||
final route = ModalRoute.of(context);
|
||||
final headerNativeSuppressed = useValueNotifier(false);
|
||||
headerNativeSuppressed.value =
|
||||
messagesFilters != null && !(route?.isCurrent ?? true);
|
||||
final communityAvatarKey = useMemoized(GlobalKey.new);
|
||||
final nativeCommunityAvatarBounds = useRef<Rect?>(null);
|
||||
final headerKey = useMemoized(GlobalKey.new);
|
||||
@@ -607,6 +613,7 @@ class ChannelsPage extends HookConsumerWidget {
|
||||
onNativeReadyChanged: (ready) => nativeHeaderReady.value = ready,
|
||||
nativeTitle: '',
|
||||
useNativeNavigation: messagesFilters == null,
|
||||
nativeViewSuppressed: headerNativeSuppressed,
|
||||
nativeLeading: messagesFilters != null
|
||||
? null
|
||||
: IosNavigationAction(
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:buzz/features/home/messages_filters.dart';
|
||||
import 'package:buzz/features/home/messages_navigation_bar.dart';
|
||||
import 'package:buzz/shared/widgets/ios_message_create_button.dart';
|
||||
import 'package:buzz/shared/widgets/ios_glass_navigation_button.dart';
|
||||
import 'package:buzz/shared/widgets/ios_navigation_bar.dart';
|
||||
import 'package:buzz/features/search/search_page.dart';
|
||||
import 'package:buzz/shared/theme/theme.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -576,6 +577,20 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.widget<Opacity>(homeOpacity).opacity, 1);
|
||||
expect(find.text('Messages'), findsOneWidget);
|
||||
// A sheet's dim barrier can't composite over the native header edge.
|
||||
final headerNativeView = find.descendant(
|
||||
of: find.byType(FrostedAppBar),
|
||||
matching: find.byType(IosNavigationBar),
|
||||
);
|
||||
expect(headerNativeView, findsOneWidget);
|
||||
await tester.longPress(find.text('General'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Copy channel name'), findsOneWidget);
|
||||
expect(headerNativeView, findsNothing);
|
||||
await tester.tapAt(const Offset(20, 20));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Copy channel name'), findsNothing);
|
||||
expect(headerNativeView, findsOneWidget);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user