Fix sheet peek for async content, and rework several screens' details
showNooSheet's peek detection previously measured content once, right
after the first frame - Details' Versions/Activity tabs fetch their
data asynchronously, so that one-shot measurement only ever saw the
initial loading-spinner height, never the eventual (often long) list,
and the sheet still snapped to full height once data arrived. Replaced
it with a ScrollMetricsNotification-based check that keeps content
capped at the peek height from frame one and upgrades to a draggable
sheet whenever it actually overflows that cap, however and whenever
that happens - fixed for good this time, with a regression test that
mirrors the async-load shape.
Also, in the same pass:
- Upload/Move/Copy's bottom bar meta line now always reads as a count
("Uploading 3 items"), never a filename for the single-item case,
and is centered rather than left-aligned - matches the approved
design and removes a bespoke marquee/chip implementation that only
Upload had.
- Upload/Move/Copy now show the same FilesControlsRow (sort, filter,
List/Grid) Files itself uses, including a folder-grid rendering
path for when List/Grid is set to grid.
- Files' filter chip reads "Filters" instead of "All types" when idle.
- Trash's overflow button - whose only action is "Delete forever" -
is now a direct delete icon (mobile) / "Delete" danger pill
(desktop) instead of a generic "..." menu that hid a single action
behind an extra tap.
- The media viewer's floating chrome (top bar, bottom action bar,
video transport row) now follows the app's light/dark theme instead
of a fixed dark panel - a dark panel read as out of place over a
light-theme stage. Panel tint and icon/text colors come from the
theme's own surface/fg1 tokens (at a higher blur opacity to stay
legible over arbitrary photo/video brightness), and the edge scrim
gradients fade toward the panel's own tint instead of a fixed black.
Mirrored the same change into the design canvas's Main.dc.html/
MainAndroid.dc.html mockups (now with a theme prop) so they match.
This commit is contained in:
@@ -18,6 +18,43 @@ Widget _launcher(void Function(BuildContext) open) => Builder(
|
||||
TextButton(onPressed: () => open(context), child: const Text('open')),
|
||||
);
|
||||
|
||||
/// A short placeholder that swaps in a long list one microtask later -
|
||||
/// standing in for a sheet tab that fetches its content on first build
|
||||
/// (`_requested`, as `DetailsVersionsTab`/`DetailsActivityTab` do) rather
|
||||
/// than having it all synchronously on the very first frame.
|
||||
class _AsyncGrowingContent extends StatefulWidget {
|
||||
const _AsyncGrowingContent();
|
||||
|
||||
@override
|
||||
State<_AsyncGrowingContent> createState() => _AsyncGrowingContentState();
|
||||
}
|
||||
|
||||
class _AsyncGrowingContentState extends State<_AsyncGrowingContent> {
|
||||
bool _loaded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (mounted) setState(() => _loaded = true);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_loaded) {
|
||||
return const SizedBox(height: 40, child: Center(child: Text('Loading')));
|
||||
}
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (var i = 0; i < 30; i++)
|
||||
SizedBox(height: 40, child: Text('Row $i')),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Finder _dialogCard() => find.byWidgetPredicate(
|
||||
(w) =>
|
||||
w is Container &&
|
||||
@@ -136,6 +173,38 @@ void main() {
|
||||
expect(sheetHeight, lessThan(500));
|
||||
expect(sheetHeight, greaterThan(150));
|
||||
});
|
||||
|
||||
testNooWidgets(
|
||||
'content that grows after an async load still switches to peek',
|
||||
(tester, theme, c) async {
|
||||
// Mirrors DetailsVersionsTab/DetailsActivityTab: a small placeholder
|
||||
// first, then a long list once a fetch "completes" - the sheet has
|
||||
// to catch the overflow whenever it happens, not just on its first
|
||||
// frame (that one-shot approach is what shipped the original bug).
|
||||
await pumpNoo(
|
||||
tester,
|
||||
_launcher(
|
||||
(ctx) => showNooSheet<void>(
|
||||
ctx,
|
||||
children: [const _AsyncGrowingContent()],
|
||||
),
|
||||
),
|
||||
theme: theme,
|
||||
surfaceSize: const Size(400, 700),
|
||||
);
|
||||
await tester.tap(find.text('open'));
|
||||
await tester.pump();
|
||||
expect(find.byType(DraggableScrollableSheet), findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(DraggableScrollableSheet), findsOneWidget);
|
||||
final sheetHeight = tester
|
||||
.getSize(find.byType(DraggableScrollableSheet))
|
||||
.height;
|
||||
expect(sheetHeight, lessThan(500));
|
||||
expect(sheetHeight, greaterThan(150));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('NooDialog', () {
|
||||
|
||||
Reference in New Issue
Block a user