Initial commit

This commit is contained in:
admin1
2026-07-14 11:11:36 +08:00
commit 656499cf94
604 changed files with 119518 additions and 0 deletions
@@ -0,0 +1,201 @@
import 'dart:io' show Platform;
import 'dart:ui' show ImageFilter;
import 'package:flutter/material.dart';
import '../../../shared/widgets/smart_image.dart';
class DetailBackdrop extends StatefulWidget {
const DetailBackdrop({
super.key,
required this.scrollProgress,
required this.backdropUrl,
required this.fallbackUrls,
required this.embyBaseUrl,
required this.imageHeaders,
this.gradientEndColor = const Color(0xFF08090c),
});
final ValueNotifier<double> scrollProgress;
final String backdropUrl;
final List<String> fallbackUrls;
final String embyBaseUrl;
final Map<String, String>? imageHeaders;
final Color gradientEndColor;
@override
State<DetailBackdrop> createState() => _DetailBackdropState();
}
class _DetailBackdropState extends State<DetailBackdrop> {
bool _backdropFailed = false;
@override
void didUpdateWidget(DetailBackdrop oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.backdropUrl != widget.backdropUrl) {
_backdropFailed = false;
}
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: _backdropFailed
? _buildFallbackGradient()
: RepaintBoundary(
child: ValueListenableBuilder<double>(
valueListenable: widget.scrollProgress,
builder: (_, p, child) {
final maxSigma = Platform.isAndroid ? 6.0 : 10.0;
var sigma = p > 0.01 ? p * maxSigma : 0.0;
if (Platform.isAndroid && sigma > 0) {
sigma = (sigma / 1.5).roundToDouble() * 1.5;
}
final blurred = ImageFiltered(
imageFilter: ImageFilter.blur(
sigmaX: sigma,
sigmaY: sigma,
),
child: child,
);
if (Platform.isAndroid) return blurred;
return Transform.scale(
scale: 1.0 + p * 0.15,
child: blurred,
);
},
child: _buildImageStack(),
),
),
),
Positioned.fill(
child: ValueListenableBuilder<double>(
valueListenable: widget.scrollProgress,
builder: (_, p, _) {
return IgnorePointer(
child: ColoredBox(
color: Colors.black.withValues(alpha: 0.10 + p * 0.3),
),
);
},
),
),
Positioned.fill(
child: IgnorePointer(child: _buildBottomGradient(context)),
),
],
);
}
Widget _buildBottomGradient(BuildContext context) {
final size = MediaQuery.sizeOf(context);
final coverage = (size.width * 9.0 / 16.0 / size.height).clamp(0.0, 1.0);
final rampStart = coverage >= 0.95
? 0.5
: (coverage * 0.55).clamp(0.1, 0.5);
final rampEnd = coverage >= 0.95
? 0.75
: (coverage * 0.85).clamp(0.25, 0.75);
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.0, rampStart, rampEnd, 1.0],
colors: [
Colors.transparent,
Colors.transparent,
widget.gradientEndColor.withValues(alpha: 0.85),
widget.gradientEndColor,
],
),
),
);
}
Widget _buildFallbackGradient() {
return IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.0, 0.6, 1.0],
colors: [
widget.gradientEndColor.withValues(alpha: 0.5),
widget.gradientEndColor.withValues(alpha: 0.85),
widget.gradientEndColor,
],
),
),
),
);
}
Widget _buildImageStack() {
return _buildArtworkImage(
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter,
);
}
Widget _buildArtworkImage({
required BoxFit fit,
required Alignment alignment,
int? memCacheWidth,
}) {
final placeholder = DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
widget.gradientEndColor.withValues(alpha: 0.3),
widget.gradientEndColor,
],
),
),
);
final imageStack = Stack(
fit: StackFit.expand,
children: [
ColoredBox(color: widget.gradientEndColor),
SmartImage(
url: widget.backdropUrl,
fallbackUrls: widget.fallbackUrls,
fit: fit,
alignment: alignment,
borderRadius: 0,
memCacheWidth: memCacheWidth,
placeholder: placeholder,
onError: () {
if (mounted && !_backdropFailed) {
setState(() => _backdropFailed = true);
}
},
resolveHttpHeaders: (url) =>
widget.embyBaseUrl.isNotEmpty &&
url.startsWith(widget.embyBaseUrl)
? widget.imageHeaders
: null,
),
],
);
return imageStack;
}
}