Hub moves, focus flips and item selects now flow through the rail focus model + ListenableSelectors (two headers/cards repaint instead of every visible card rebuilding). DiscoverScreen memoizes the hubs list, gates provider notifies on a render signature and caches the rail widget so no-op refreshes skip the subtree entirely. The whole-rail 0.6 and inactive-row 0.7 AnimatedOpacity layers are replaced by AnimatedDimScrim quads, removing the permanent per-frame saveLayers that kept low-end TV GPUs raster-bound.
41 lines
1.6 KiB
Dart
41 lines
1.6 KiB
Dart
import 'package:flutter/widgets.dart';
|
|
|
|
import '../focus/focus_theme.dart';
|
|
|
|
/// Dims the content beneath it by drawing a translucent [color] quad on top,
|
|
/// as a saveLayer-free replacement for wrapping the content in
|
|
/// [AnimatedOpacity]. Widget opacity below 1.0 forces an offscreen render
|
|
/// pass every frame — a permanent GPU cost on the weak GLES devices most
|
|
/// Android TVs are — while a color-blended quad is free. Over a
|
|
/// [color]-colored underlay the result is mathematically identical to
|
|
/// `Opacity(opacity: 1 - alpha)`; over artwork it darkens toward [color]
|
|
/// instead of turning translucent.
|
|
///
|
|
/// Stack this above the content (with `IgnorePointer` built in so taps pass
|
|
/// through). Animates with [FocusTheme.getAnimationDuration], so the full
|
|
/// tier fades and the reduced tier snaps, matching the [AnimatedOpacity]
|
|
/// behavior it replaces.
|
|
class AnimatedDimScrim extends StatelessWidget {
|
|
const AnimatedDimScrim({super.key, required this.dimmed, required this.color, required this.alpha});
|
|
|
|
final bool dimmed;
|
|
final Color color;
|
|
|
|
/// Scrim strength while [dimmed]; visually equivalent to
|
|
/// `Opacity(opacity: 1 - alpha)` over a [color] underlay.
|
|
final double alpha;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return IgnorePointer(
|
|
child: TweenAnimationBuilder<double>(
|
|
tween: Tween(end: dimmed ? alpha : 0.0),
|
|
duration: FocusTheme.getAnimationDuration(context),
|
|
curve: Curves.easeOutCubic,
|
|
builder: (context, value, _) =>
|
|
value == 0 ? const SizedBox.shrink() : ColoredBox(color: color.withValues(alpha: value)),
|
|
),
|
|
);
|
|
}
|
|
}
|