Files
plezy/lib/widgets/system_bottom_inset.dart
edde746 961e9c0326 feat(automotive): scale the car interface and make it adjustable
A head unit is a large screen sitting an arm's length further away than a phone,
and Plezy drew phone-sized controls on it: the primary button measured 8.3 mm
against the 64 dp a car needs. The whole surface is now scaled - 1.35 by default,
adjustable in Appearance - by giving the app a smaller logical viewport and
scaling the result back, so text, spacing and touch targets grow together instead
of a font size being nudged in isolation.

The scale sits above the messenger and the root Scaffold so snackbars and dialogs
are scaled too, and insets are divided back into the scaled space so a system bar
still reserves its physical size. A scaled surface is also a short one: the setup
screen's fixed offsets and the now-playing transport are laid out to survive it,
and a mistyped scale in a hand-edited settings file is clamped rather than
failing startup.
2026-08-06 03:45:09 +02:00

35 lines
1.7 KiB
Dart

import 'package:flutter/widgets.dart';
/// Reserves the system bottom inset — Android's navigation bar, iOS's home
/// indicator — at the end of a scroll view.
///
/// Plezy always runs edge-to-edge on Android: `targetSdk` is 36, and Android
/// 15 enforces edge-to-edge for apps targeting 35+ while Android 16 removes
/// the opt-out entirely. So `MediaQuery.padding.bottom` is a real overlap on
/// phones (~48dp with 3-button navigation, less with gesture navigation) and
/// every full-screen pushed route has to consume it.
///
/// The convention is to bake the inset into the scroll *content* rather than
/// wrapping the scroll view in a [SafeArea]: content keeps painting under the
/// bar, and only the scroll extent grows so the last row can be scrolled clear
/// of it. See `media_detail_screen.dart` and `discover_screen.dart` for the
/// hand-rolled precedents this widget replaces.
///
/// Collapses to zero height wherever the bottom padding is already zero, so it
/// needs no platform branching: desktop and Android TV report no inset, tvOS
/// has it zeroed by `_FormFactorScale`, and inside `MainScreen`'s tab bodies
/// Flutter's [Scaffold] has already stripped it because a `bottomNavigationBar`
/// is present.
///
/// Reading the padding from this widget's own [BuildContext] — instead of the
/// enclosing screen's — keeps it correct below any ancestor that removes
/// padding, and limits inset-driven rebuilds to the spacer itself.
class SliverSystemBottomInset extends StatelessWidget {
const SliverSystemBottomInset({super.key});
@override
Widget build(BuildContext context) {
return SliverPadding(padding: .only(bottom: MediaQuery.paddingOf(context).bottom));
}
}