Files
buzz/mobile/test/shared/widgets/modal_presentation_test.dart
klopez4212 39b8b2b6a6 feat(mobile): unify Messages navigation with Solo and Team filters (#8649)
## Summary

Unify mobile conversations in Messages with All / Solo / Team filters, a
collapsing header, and separate Search, New, and default-agent
shortcuts. Move workspace actions into the community menu and keep
collapsed sections intact across filters.

Align Inbox and list typography, spacing, and muted controls. Preserve
native glass layering through navigation and give every filtered list
enough clearance above the footer. The default-agent shortcut reads the
shared encrypted preference and opens an existing chat immediately. When
unset, it opens Agents; a star action selects the default through a
production signed, self-encrypted preference writer. Save failures
remain visible and retryable.

Android now uses rounded navigation controls and filters, optically
balanced search icons, and expanding New/community menus with leading
Tabler icons matching iOS. Preset avatars retain transparent WebM
animation through an Android WebView renderer, with lifecycle pausing
and poster fallback.

### Related issue

None found in searches for mobile navigation issues and open PRs.

### Testing

- Android follow-up: ARM64 debug build, Dart analysis, and emulator
checks passed for menu placement, selection, icon alignment, search, and
avatar playback/pause/resume. The avatar implementation passed 3,840
Flutter tests and native Android tests before subsequent styling
iterations. Automated tests for the later menu/icon iterations were
skipped at the author’s request; those earlier results do not cover the
final styling changes.

- Mobile formatting/analysis and full test suite (3,838 passed, 23
skipped, plus 4 push-configuration tests); regression journey covers
filtering, collapse retention, and scrolling the last Solo row above the
footer.
- iPhone 18 Pro simulator (Honeycomb): filters, menus, Inbox,
Invite/Back, Switch Community, and default-agent chat/Back exercised.
iOS release build and Pairing Preview install/launch passed during
iteration, preserving the saved profile.
- P1 follow-up: production writer-to-reader widget journey covers
selection, publish failure/retry, and the unset footer opening Agents.
Simulator Agents open/close passes; its saved community has no
selectable agents, so live selection was not verified there.
- Independent agent review completed; its Solo footer-clearance finding
is fixed. The final clearance adjustment is covered by widget tests;
physical-device acceptance of that adjustment and hosted CI remain
pending.

Final Messages layout:

![Messages with All, Solo and Team filters and separate bottom
shortcuts](https://github.com/user-attachments/assets/562ddc3e-945b-4945-bd58-217a2c62b570)

Switcher layering during iteration (before → after):

| Before: filter edge showing through | After: Home hidden behind
switcher |
| --- | --- |
| ![Filter edge visible beside Switch
Community](https://github.com/user-attachments/assets/b32a0157-8b17-4f01-9a23-29b12ce3b1af)
| ![Clean Switch Community
screen](https://github.com/user-attachments/assets/222956e4-9989-4c8f-a162-65de45029845)
|

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
2026-10-29 14:27:22 +00:00

825 lines
27 KiB
Dart

import 'package:buzz/shared/widgets/concentric_sheet_surface.dart';
import 'package:buzz/shared/theme/theme.dart';
import 'package:buzz/shared/widgets/modal_presentation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
for (final platform in [TargetPlatform.android, TargetPlatform.iOS]) {
for (final titled in [false, true]) {
for (final ownSafeArea in [false, true]) {
testWidgets(
'fixed bottom clearance $platform titled=$titled ownSafeArea=$ownSafeArea',
(tester) async {
debugDefaultTargetPlatformOverride = platform;
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
final systemInset = platform == TargetPlatform.iOS ? 34.0 : 24.0;
tester.view.padding = FakeViewPadding(bottom: systemInset);
tester.view.viewPadding = FakeViewPadding(bottom: systemInset);
addTearDown(() {
tester.view.reset();
debugDefaultTargetPlatformOverride = null;
});
try {
double? contentInset;
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Builder(
builder: (context) => Scaffold(
body: TextButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
title: titled ? 'People' : null,
showCloseButton: titled,
isScrollControlled: true,
builder: (context) {
contentInset = MediaQuery.paddingOf(context).bottom;
final list = SizedBox(
height: 300,
child: ListView.builder(
key: const ValueKey('clearance-scroll'),
padding: EdgeInsets.zero,
itemCount: 30,
itemBuilder: (_, index) => SizedBox(
height: 48,
child: Text('Person $index'),
),
),
);
return ownSafeArea
? SafeArea(top: false, child: list)
: list;
},
),
child: const Text('Open'),
),
),
),
),
);
await tester.tap(find.text('Open'));
await tester.pumpAndSettle();
final list = find.byKey(const ValueKey('clearance-scroll'));
final viewportBottom = tester.getRect(list).bottom;
// This is outside the scrolling viewport, not trailing list space.
expect(
viewportBottom,
lessThanOrEqualTo(844 - systemInset - Grid.half),
);
expect(contentInset, 0);
await tester.drag(list, const Offset(0, -2000));
await tester.pumpAndSettle();
expect(find.text('Person 29'), findsOneWidget);
expect(tester.getRect(list).bottom, viewportBottom);
expect(
tester.getRect(find.text('Person 29')).bottom,
lessThanOrEqualTo(viewportBottom),
);
expect(tester.takeException(), isNull);
} finally {
debugDefaultTargetPlatformOverride = null;
}
},
);
}
}
}
Border sheetHeaderBorder(WidgetTester tester) {
final decoration =
tester
.widget<DecoratedBox>(
find.byKey(const ValueKey('buzz-sheet-scroll-divider')),
)
.decoration
as BoxDecoration;
return decoration.border! as Border;
}
testWidgets(
'keeps an opaque Flutter surface when iOS native support is unavailable',
(tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: const ConcentricSheetSurface(
enabled: true,
color: Colors.red,
child: SizedBox(height: 80, child: Text('Sheet body')),
),
),
);
await tester.pumpAndSettle();
expect(
find.byWidgetPredicate(
(widget) => widget is Material && widget.color == Colors.red,
),
findsOneWidget,
);
final contentClip = tester.widget<ClipRSuperellipse>(
find.byKey(const ValueKey('concentric-sheet-content-clip')),
);
expect(contentClip.borderRadius, BorderRadius.circular(Radii.dialog));
} finally {
debugDefaultTargetPlatformOverride = null;
}
},
);
testWidgets(
'replaces the Flutter fallback when native support is available',
(tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const surfaceChannel = MethodChannel('buzz/concentric_sheet_surface');
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
(call) async => call.method == 'isSupported' ? true : null,
);
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: const ConcentricSheetSurface(
enabled: true,
color: Colors.red,
child: SizedBox(height: 80, child: Text('Sheet body')),
),
),
);
await tester.pump();
expect(find.byType(UiKitView), findsOneWidget);
expect(
tester.widget<UiKitView>(find.byType(UiKitView)).hitTestBehavior,
PlatformViewHitTestBehavior.translucent,
);
expect(
find.ancestor(
of: find.byType(UiKitView),
matching: find.byWidgetPredicate(
(widget) => widget is IgnorePointer && widget.ignoring,
),
),
findsNothing,
);
expect(
find.byWidgetPredicate(
(widget) => widget is Material && widget.color == Colors.red,
),
findsNothing,
);
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
null,
);
debugDefaultTargetPlatformOverride = null;
}
},
);
testWidgets(
'native surfaces can limit concentric clipping to bottom corners',
(tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const surfaceChannel = MethodChannel('buzz/concentric_sheet_surface');
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
(call) async => call.method == 'isSupported' ? true : null,
);
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: const ConcentricSheetSurface(
enabled: true,
color: Colors.red,
backdropColor: Colors.black,
corners: ConcentricSurfaceCorners.bottom,
padding: EdgeInsets.zero,
providesSheetSurface: false,
child: SizedBox(height: 80, child: Text('App surface')),
),
),
);
await tester.pump();
final nativeSurface = tester.widget<UiKitView>(find.byType(UiKitView));
expect(nativeSurface.creationParams, containsPair('corners', 'bottom'));
expect(
nativeSurface.creationParams,
containsPair('backdropColor', Colors.black.toARGB32()),
);
final contentClip = tester.widget<ClipRSuperellipse>(
find.byKey(const ValueKey('concentric-sheet-content-clip')),
);
expect(
contentClip.borderRadius,
BorderRadius.vertical(bottom: Radius.circular(Radii.dialog * 2)),
);
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
null,
);
debugDefaultTargetPlatformOverride = null;
}
},
);
testWidgets('native surface colors follow live theme changes', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const supportChannel = MethodChannel('buzz/concentric_sheet_surface');
const viewChannel = MethodChannel('buzz/concentric_sheet_surface/42');
final colorUpdates = <MethodCall>[];
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
supportChannel,
(call) async => call.method == 'isSupported' ? true : null,
);
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
viewChannel,
(call) async {
colorUpdates.add(call);
return null;
},
);
Widget themedSurface(ThemeData theme) => MaterialApp(
theme: theme,
home: Builder(
builder: (context) => ConcentricSheetSurface(
enabled: true,
color: context.colors.surface,
backdropColor: context.appColors.huddleDrawerSurface,
corners: ConcentricSurfaceCorners.bottom,
padding: EdgeInsets.zero,
providesSheetSurface: false,
child: const SizedBox(height: 80, child: Text('App surface')),
),
),
);
try {
final darkTheme = AppTheme.dark();
await tester.pumpWidget(themedSurface(darkTheme));
await tester.pumpAndSettle();
tester.widget<UiKitView>(find.byType(UiKitView)).onPlatformViewCreated!(
42,
);
await tester.pump();
final initialColorUpdates = colorUpdates
.where((call) => call.method == 'updateColors')
.toList();
expect(initialColorUpdates, hasLength(1));
expect(
initialColorUpdates.single.arguments,
containsPair('color', darkTheme.colorScheme.surface.toARGB32()),
);
expect(
initialColorUpdates.single.arguments,
containsPair(
'backdropColor',
darkTheme.extension<AppColors>()!.huddleDrawerSurface.toARGB32(),
),
);
final lightTheme = AppTheme.light();
await tester.pumpWidget(themedSurface(lightTheme));
await tester.pumpAndSettle();
final updatedColorCalls = colorUpdates
.where((call) => call.method == 'updateColors')
.toList();
expect(updatedColorCalls.length, greaterThanOrEqualTo(2));
expect(
updatedColorCalls.last.arguments,
containsPair('color', lightTheme.colorScheme.surface.toARGB32()),
);
expect(
updatedColorCalls.last.arguments,
containsPair(
'backdropColor',
lightTheme.extension<AppColors>()!.huddleDrawerSurface.toARGB32(),
),
);
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
supportChannel,
null,
);
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
viewChannel,
null,
);
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets('native glass surfaces receive concentric composer geometry', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const supportChannel = MethodChannel('buzz/concentric_sheet_surface');
const viewChannel = MethodChannel('buzz/concentric_sheet_surface/84');
final updates = <MethodCall>[];
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
supportChannel,
(call) async => call.method == 'isSupported' ? true : null,
);
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
viewChannel,
(call) async {
updates.add(call);
return null;
},
);
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: const ConcentricSheetSurface(
enabled: true,
usesGlass: true,
minimumRadius: 26,
contentClipRadius: 18,
padding: EdgeInsets.zero,
providesSheetSurface: false,
child: SizedBox(height: 80, child: Text('Composer')),
),
),
);
await tester.pump();
final nativeSurface = tester.widget<UiKitView>(find.byType(UiKitView));
expect(nativeSurface.creationParams, containsPair('usesGlass', true));
expect(nativeSurface.creationParams, containsPair('minimumRadius', 26));
final contentClip = tester.widget<ClipRSuperellipse>(
find.byKey(const ValueKey('concentric-sheet-content-clip')),
);
expect(contentClip.borderRadius, BorderRadius.circular(18));
nativeSurface.onPlatformViewCreated!(84);
await tester.pump();
final geometryUpdate = updates.singleWhere(
(call) => call.method == 'updateGeometry',
);
expect(geometryUpdate.arguments, containsPair('minimumRadius', 26.0));
expect(geometryUpdate.arguments, containsPair('brightness', 'light'));
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
supportChannel,
null,
);
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
viewChannel,
null,
);
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets('native titled sheets leave the concentric surface unobscured', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const surfaceChannel = MethodChannel('buzz/concentric_sheet_surface');
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
(call) async => call.method == 'isSupported' ? true : null,
);
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
title: 'Members',
builder: (sheetContext) => ColoredBox(
key: const ValueKey('sheet-container-surface'),
color: Theme.of(
sheetContext,
).colorScheme.surfaceContainerHighest,
child: const Text('Sheet body'),
),
),
child: const Text('Open sheet'),
),
),
),
),
);
await tester.tap(find.text('Open sheet'));
await tester.pumpAndSettle();
final nativeSurface = tester.widget<UiKitView>(
find.byWidgetPredicate(
(widget) =>
widget is UiKitView &&
widget.viewType == 'buzz/concentric_sheet_surface',
),
);
expect(
nativeSurface.creationParams,
containsPair(
'color',
lightColorScheme.surfaceContainerHighest.toARGB32(),
),
);
expect(
tester
.widget<ColoredBox>(
find.byKey(const ValueKey('sheet-container-surface')),
)
.color,
lightColorScheme.surface,
);
expect(nativeSurface.creationParams, isNot(contains('headerGradient')));
expect(
find.byKey(const ValueKey('buzz-sheet-header-gradient')),
findsNothing,
);
expect(
find.byKey(const ValueKey('buzz-sheet-surface-clip')),
findsNothing,
);
final contentClip = tester.widget<ClipRSuperellipse>(
find.byKey(const ValueKey('concentric-sheet-content-clip')),
);
expect(
contentClip.borderRadius,
const BorderRadius.vertical(
top: Radius.circular(ConcentricSheetSurface.sheetTopRadius),
bottom: Radius.circular(Radii.dialog * 2),
),
);
expect((nativeSurface.creationParams as Map)['topRadius'], 40.0);
expect(contentClip.clipBehavior, Clip.antiAlias);
expect(find.text('Members'), findsOneWidget);
final nativeClose = tester.widget<UiKitView>(
find.byWidgetPredicate(
(widget) =>
widget is UiKitView && widget.viewType == 'buzz/navigation_glass',
),
);
expect(nativeClose.creationParams, containsPair('icon', 'close'));
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
surfaceChannel,
null,
);
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets(
'non-iOS sheets use Flutter drag handle and shared close control',
(tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.android;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
title: 'Sheet title',
showDragHandle: true,
builder: (_) => const Text('Sheet body'),
),
child: const Text('Open sheet'),
),
),
),
),
);
await tester.tap(find.text('Open sheet'));
await tester.pumpAndSettle();
final closeButton = find.byTooltip('Close sheet');
final title = find.byKey(const ValueKey('buzz-sheet-title'));
expect(closeButton, findsOneWidget);
expect(title, findsOneWidget);
expect(
find.byKey(const ValueKey('buzz-sheet-surface-clip')),
findsOneWidget,
);
expect(tester.getSize(closeButton), const Size.square(44));
expect(tester.widget<Text>(title).style?.fontSize, 16);
expect(
find.byKey(const ValueKey('buzz-sheet-header-gradient')),
findsNothing,
);
expect(
tester
.widget<ColoredBox>(
find.byKey(const ValueKey('buzz-sheet-surface')),
)
.color,
lightColorScheme.surfaceContainerHighest,
);
expect(
tester.getTopLeft(find.text('Sheet body')).dy -
tester
.getTopLeft(find.byKey(const ValueKey('buzz-sheet-surface')))
.dy,
80,
);
expect(find.byType(BackdropFilter), findsNothing);
expect(
tester.widget<BottomSheet>(find.byType(BottomSheet)).backgroundColor,
Colors.transparent,
);
expect(
tester.getCenter(title).dx,
closeTo(tester.getCenter(find.byType(BottomSheet)).dx, 0.01),
);
expect(
tester.getCenter(title).dy,
closeTo(tester.getCenter(closeButton).dy, 0.01),
);
expect(
tester.getCenter(closeButton).dx,
greaterThan(tester.getCenter(title).dx),
);
final closeGutter = find.ancestor(
of: closeButton,
matching: find.byWidgetPredicate(
(widget) =>
widget is Padding &&
widget.padding ==
const EdgeInsets.only(
top: Grid.xxs,
left: Grid.gutter,
right: Grid.gutter,
bottom: Grid.xs,
),
),
);
expect(closeGutter, findsOneWidget);
final gutterRect = tester.getRect(closeGutter);
final closeRect = tester.getRect(closeButton);
expect(closeRect.top - gutterRect.top, Grid.gutter);
expect(gutterRect.right - closeRect.right, Grid.gutter);
expect(
tester.widget<BottomSheet>(find.byType(BottomSheet)).showDragHandle,
isFalse,
);
expect(
find.byKey(const ValueKey('buzz-sheet-drag-handle')),
findsOneWidget,
);
expect(
tester.getTopLeft(closeButton).dy -
tester.getTopLeft(find.byType(BottomSheet)).dy,
Grid.gutter,
);
final dismissHandle = find.bySemanticsLabel('Dismiss').first;
expect(dismissHandle, findsOneWidget);
final semantics = tester.getSemantics(dismissHandle);
expect(semantics.flagsCollection.isButton, isTrue);
expect(
semantics.getSemanticsData().hasAction(SemanticsAction.tap),
isTrue,
);
tester.binding.performSemanticsAction(
SemanticsActionEvent(
type: SemanticsAction.tap,
viewId: tester.view.viewId,
nodeId: semantics.id,
),
);
await tester.pumpAndSettle();
expect(find.text('Sheet body'), findsNothing);
} finally {
debugDefaultTargetPlatformOverride = null;
}
},
);
testWidgets('untitled Android sheets paint the utility route surface', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.android;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
builder: (sheetContext) => ColoredBox(
key: const ValueKey('untitled-sheet-container'),
color: Theme.of(
sheetContext,
).colorScheme.surfaceContainerHighest,
child: const SizedBox(
height: 80,
child: Text('Sheet body'),
),
),
),
child: const Text('Open sheet'),
),
),
),
),
);
await tester.tap(find.text('Open sheet'));
await tester.pumpAndSettle();
expect(
tester.widget<BottomSheet>(find.byType(BottomSheet)).backgroundColor,
lightColorScheme.surfaceContainerHighest,
);
expect(
tester
.widget<ColoredBox>(
find.byKey(const ValueKey('untitled-sheet-container')),
)
.color,
lightColorScheme.surface,
);
} finally {
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets('sheet divider appears only when content scrolls under header', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.android;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
title: 'Theme',
isScrollControlled: true,
builder: (_) => SizedBox(
height: 360,
child: ListView.builder(
key: const ValueKey('sheet-scroll-view'),
itemCount: 30,
itemBuilder: (_, index) =>
SizedBox(height: 44, child: Text('Option $index')),
),
),
),
child: const Text('Open sheet'),
),
),
),
),
);
await tester.tap(find.text('Open sheet'));
await tester.pumpAndSettle();
expect(sheetHeaderBorder(tester).bottom.color.a, 0);
await tester.drag(
find.byKey(const ValueKey('sheet-scroll-view')),
const Offset(0, -100),
);
await tester.pumpAndSettle();
expect(
tester
.state<ScrollableState>(
find.descendant(
of: find.byKey(const ValueKey('sheet-scroll-view')),
matching: find.byType(Scrollable),
),
)
.position
.pixels,
greaterThan(0),
);
expect(sheetHeaderBorder(tester).bottom.color.a, greaterThan(0));
await tester.drag(
find.byKey(const ValueKey('sheet-scroll-view')),
const Offset(0, 500),
);
await tester.pumpAndSettle();
expect(sheetHeaderBorder(tester).bottom.color.a, 0);
} finally {
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets('iOS paints the drag handle inside the concentric surface', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
showDragHandle: true,
builder: (_) => const Text('Sheet body'),
),
child: const Text('Open sheet'),
),
),
),
),
);
await tester.tap(find.text('Open sheet'));
await tester.pumpAndSettle();
expect(
tester.widget<BottomSheet>(find.byType(BottomSheet)).showDragHandle,
isFalse,
);
final internalHandle = find.byKey(
const ValueKey('buzz-sheet-drag-handle'),
);
expect(internalHandle, findsOneWidget);
expect(tester.getSize(internalHandle), const Size(32, 4));
final dismissHandle = find.bySemanticsLabel('Dismiss');
expect(dismissHandle, findsOneWidget);
final semantics = tester.getSemantics(dismissHandle);
expect(semantics.flagsCollection.isButton, isTrue);
expect(
semantics.getSemanticsData().hasAction(SemanticsAction.tap),
isTrue,
);
expect(find.byTooltip('Close sheet'), findsOneWidget);
expect(find.text('Sheet body'), findsOneWidget);
} finally {
debugDefaultTargetPlatformOverride = null;
}
});
testWidgets('iOS compact sheets can omit X but retain the inside handle', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
try {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Scaffold(
body: Builder(
builder: (context) => FilledButton(
onPressed: () => showBuzzModalBottomSheet<void>(
context: context,
showDragHandle: true,
showCloseButton: false,
builder: (_) => const Text('Compact sheet body'),
),
child: const Text('Open compact sheet'),
),
),
),
),
);
await tester.tap(find.text('Open compact sheet'));
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('buzz-sheet-drag-handle')),
findsOneWidget,
);
expect(find.byTooltip('Close sheet'), findsNothing);
expect(find.text('Compact sheet body'), findsOneWidget);
} finally {
debugDefaultTargetPlatformOverride = null;
}
});
}