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.
61 lines
2.2 KiB
Dart
61 lines
2.2 KiB
Dart
import 'dart:ui';
|
|
import 'package:flutter/material.dart';
|
|
import '../theme/design_tokens.dart';
|
|
|
|
/// The blurred/translucent chrome for the media viewer's overlay bars
|
|
/// (`file_viewer_screen.dart`'s top bar, `MediaActionBar`, the video
|
|
/// transport row) - the one deliberate exception to the design system's
|
|
/// flat, no-shadow product UI (DESIGN_SYSTEM.md 1.4), since this chrome
|
|
/// floats over photo/video content rather than over the app's own
|
|
/// surfaces. No shadow, no outline: [color] defaults to the app's own
|
|
/// `surface` token (so this panel reads as light or dark to match the
|
|
/// active theme, like the rest of the media viewer, instead of a fixed
|
|
/// dark tone regardless of theme) at a higher-than-usual [opacity], since
|
|
/// a light tint needs denser coverage than a near-black one did to stay
|
|
/// legible over arbitrary photo/video brightness underneath.
|
|
class FrostedGlassContainer extends StatelessWidget {
|
|
final Widget child;
|
|
final double borderRadius;
|
|
|
|
/// Blur sigma for the backdrop filter. Defaults to a fixed value; pass an
|
|
/// explicit value (e.g. from user settings) to make it adjustable.
|
|
final double blurSigma;
|
|
|
|
/// Opacity (0-1) of the tonal fill behind the blur. Defaults to a fixed
|
|
/// value; pass an explicit value (e.g. from user settings) to make it
|
|
/// adjustable.
|
|
final double opacity;
|
|
|
|
/// The tint under the blur. Defaults to [NooColors.surface] (resolved at
|
|
/// build time, so it always matches the active theme) - pass an explicit
|
|
/// color only to override that, e.g. for a fixed-color panel elsewhere.
|
|
final Color? color;
|
|
|
|
const FrostedGlassContainer({
|
|
super.key,
|
|
required this.child,
|
|
this.borderRadius = 0,
|
|
this.blurSigma = 20,
|
|
this.opacity = 0.8,
|
|
this.color,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final tint = color ?? context.nooColors.surface;
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(borderRadius),
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(sigmaX: blurSigma, sigmaY: blurSigma),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: tint.withValues(alpha: opacity),
|
|
borderRadius: BorderRadius.circular(borderRadius),
|
|
),
|
|
child: child,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|