fix(ui): make screen backgrounds full-bleed instead of content-sized

The gradient Container in the playlist selection and admin screens sized
itself to its content rather than the viewport: under the loose body
constraints, SingleChildScrollView and Wrap both shrink-wrap, so the
gradient was painted only across a 436 px band (48 padding + 340 card +
48 padding) with the rest of the screen falling through to bare black.
BoxConstraints.expand() forces full coverage.

Also:
- both screens now use AppColors.backgroundGradient. The previous ramp
  ran surfaceContainerLowest -> background -> baseLevel0, i.e. dark to
  lighter to dark, which put a pale band across the middle
- the playlist card was a 3% cream translucent plate, so it took on
  whatever sat behind it instead of resting on it; it is now an opaque
  surface (surfaceContainerLow, surfaceContainerHigh on hover) with the
  lift() shadow at rest and the ember focus halo on hover
- Scaffold backgroundColor pinned to baseLevel0 so route transitions
  never flash pure black

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5 committed 2026-08-10 21:57:35 +02:00
1 parent ad3e970c8d
commit ef744c215e
2 files changed
+27 -33

No files matched your search

+5 -8
View File
@@ -24,16 +24,13 @@ class AdminPanel extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
backgroundColor: AppColors.baseLevel0,
body: Container( body: Container(
// Meme precaution que sur l'ecran de selection de playlist : sans
// contrainte d'expansion, le degrade ne couvre que le contenu.
constraints: const BoxConstraints.expand(),
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: AppColors.backgroundGradient,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.surfaceVariant,
AppColors.background,
],
),
), ),
child: const AdminContent(), child: const AdminContent(),
), ),
@@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart';
import '../../../core/services/playlist_api_service.dart'; import '../../../core/services/playlist_api_service.dart';
import '../../../core/models/playlist_config.dart'; import '../../../core/models/playlist_config.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../auth/providers/auth_provider.dart'; import '../../auth/providers/auth_provider.dart';
import '../providers/xtream_provider.dart'; import '../providers/xtream_provider.dart';
@@ -24,6 +25,9 @@ class PlaylistSelectionScreen extends ConsumerWidget {
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
// Filet de securite : meme pendant une transition de route, le fond
// reste dans la palette au lieu de virer au noir pur.
backgroundColor: AppColors.baseLevel0,
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'Select Playlist', 'Select Playlist',
@@ -57,16 +61,14 @@ class PlaylistSelectionScreen extends ConsumerWidget {
], ],
), ),
body: Container( body: Container(
// `BoxConstraints.expand()` est indispensable ici : sous les
// contraintes laches du body, ce Container se dimensionnait sur son
// contenu (SingleChildScrollView -> Wrap shrink-wrappent tous les
// deux), si bien que le degrade n'etait peint que sur un rectangle
// de la taille des cartes, le reste de l'ecran restant noir.
constraints: const BoxConstraints.expand(),
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: AppColors.backgroundGradient,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.surfaceContainerLowest,
AppColors.background,
AppColors.baseLevel0,
],
),
), ),
child: SafeArea( child: SafeArea(
child: LayoutBuilder( child: LayoutBuilder(
@@ -208,26 +210,21 @@ class _PlaylistCardState extends State<_PlaylistCard> {
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
decoration: BoxDecoration( decoration: BoxDecoration(
// Surface OPAQUE : une plaque a 3 % de creme prenait la couleur du
// fond et flottait sans jamais se poser dessus. Un cran de la
// hierarchie de surfaces ancre la carte, l'ombre fait le relief.
color: _isHovered color: _isHovered
? AppColors.onSurface.withOpacity(0.1) ? AppColors.surfaceContainerHigh
: AppColors.onSurface.withOpacity(0.03), : AppColors.surfaceContainerLow,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(AppTheme.radiusXl),
border: Border.all( border: Border.all(
color: _isHovered color: _isHovered
? AppColors.primary.withOpacity(0.5) ? AppColors.primaryContainer
: AppColors.onSurface.withOpacity(0.08), : AppColors.glassLevel1Border,
width: 1.5, width: _isHovered ? 2 : 1,
), ),
boxShadow: _isHovered boxShadow:
? [ _isHovered ? AppColors.emberFocus() : AppColors.lift(),
BoxShadow(
color: AppColors.primary.withOpacity(0.2),
blurRadius: 30,
spreadRadius: -5,
offset: const Offset(0, 10),
),
]
: [],
), ),
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
child: Column( child: Column(