import 'dart:io' show Platform; import 'dart:ui' show ImageFilter; import 'package:flutter/material.dart'; import 'detail_backdrop.dart'; class DetailImmersiveScaffold extends StatelessWidget { final ScrollController scrollController; final ValueNotifier scrollProgress; final String? backdropUrl; final List fallbackUrls; final String embyBaseUrl; final Map? imageHeaders; final Widget child; final bool compact; final double horizontalPadding; const DetailImmersiveScaffold({ super.key, required this.scrollController, required this.scrollProgress, required this.child, this.backdropUrl, this.fallbackUrls = const [], this.embyBaseUrl = '', this.imageHeaders, this.compact = false, this.horizontalPadding = 32, }); @override Widget build(BuildContext context) { final screenSize = MediaQuery.sizeOf(context); final screenHeight = screenSize.height; final screenWidth = screenSize.width; final bannerHeight = compact ? screenHeight * 0.30 : (screenWidth * 9.0 / 16.0).clamp(280.0, screenHeight * 0.6); final backdrop = backdropUrl; return Stack( children: [ if (backdrop != null) DetailBackdrop( scrollProgress: scrollProgress, backdropUrl: backdrop, fallbackUrls: fallbackUrls, embyBaseUrl: embyBaseUrl, imageHeaders: imageHeaders, ), SingleChildScrollView( controller: scrollController, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: bannerHeight - 80), Stack( children: [ Positioned.fill(child: _FrostedPanelVeil()), Padding( padding: EdgeInsets.fromLTRB( horizontalPadding, 64, horizontalPadding, 48, ), child: Align( alignment: Alignment.topCenter, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1200), child: child, ), ), ), ], ), ], ), ), ], ); } } class _FrostedPanelVeil extends StatelessWidget { @override Widget build(BuildContext context) { if (Platform.isAndroid) { return IgnorePointer( child: ShaderMask( shaderCallback: (rect) { final ratio = (140.0 / rect.height).clamp(0.02, 0.5); return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: const [Colors.transparent, Colors.black], stops: [0.0, ratio], ).createShader(rect); }, blendMode: BlendMode.dstIn, child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: const [0.0, 0.12, 1.0], colors: [ const Color(0xFF0a0c10).withValues(alpha: 0.0), const Color(0xFF0a0c10).withValues(alpha: 0.88), const Color(0xFF0a0c10).withValues(alpha: 0.96), ], ), ), ), ), ); } return IgnorePointer( child: ShaderMask( shaderCallback: (rect) { final ratio = (140.0 / rect.height).clamp(0.02, 0.5); return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: const [Colors.transparent, Colors.black], stops: [0.0, ratio], ).createShader(rect); }, blendMode: BlendMode.dstIn, child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 40, sigmaY: 40), child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: const [0.0, 0.20, 1.0], colors: [ const Color(0xFF0a0c10).withValues(alpha: 0.0), const Color(0xFF0a0c10).withValues(alpha: 0.12), const Color(0xFF0a0c10).withValues(alpha: 0.20), ], ), ), ), ), ), ); } }