Initial commit
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../shared/widgets/smart_image.dart';
|
||||
|
||||
|
||||
class AndroidDetailBanner extends StatefulWidget {
|
||||
const AndroidDetailBanner({
|
||||
super.key,
|
||||
required this.scrollProgress,
|
||||
required this.backdropUrl,
|
||||
required this.fallbackUrls,
|
||||
this.embyBaseUrl = '',
|
||||
this.imageHeaders,
|
||||
});
|
||||
|
||||
final ValueListenable<double> scrollProgress;
|
||||
final String backdropUrl;
|
||||
final List<String> fallbackUrls;
|
||||
final String embyBaseUrl;
|
||||
final Map<String, String>? imageHeaders;
|
||||
|
||||
@override
|
||||
State<AndroidDetailBanner> createState() => _AndroidDetailBannerState();
|
||||
}
|
||||
|
||||
class _AndroidDetailBannerState extends State<AndroidDetailBanner> {
|
||||
bool _allImagesFailed = false;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant AndroidDetailBanner oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.backdropUrl != widget.backdropUrl ||
|
||||
!listEquals(oldWidget.fallbackUrls, widget.fallbackUrls)) {
|
||||
_allImagesFailed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (!_allImagesFailed)
|
||||
ShaderMask(
|
||||
shaderCallback: (bounds) => const LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0.0, 0.68, 1.0],
|
||||
colors: [Colors.black, Colors.black, Colors.transparent],
|
||||
).createShader(bounds),
|
||||
blendMode: BlendMode.dstIn,
|
||||
child: SmartImage(
|
||||
url: widget.backdropUrl,
|
||||
fallbackUrls: widget.fallbackUrls,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
borderRadius: 0,
|
||||
placeholder: const SizedBox.expand(),
|
||||
onError: () {
|
||||
if (mounted && !_allImagesFailed) {
|
||||
setState(() => _allImagesFailed = true);
|
||||
}
|
||||
},
|
||||
resolveHttpHeaders: (url) =>
|
||||
widget.embyBaseUrl.isNotEmpty &&
|
||||
url.startsWith(widget.embyBaseUrl)
|
||||
? widget.imageHeaders
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 120,
|
||||
child: IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.black.withValues(alpha: 0.38),
|
||||
Colors.transparent,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ValueListenableBuilder<double>(
|
||||
valueListenable: widget.scrollProgress,
|
||||
builder: (context, rawProgress, child) {
|
||||
final progress = rawProgress.clamp(0.0, 1.0);
|
||||
return IgnorePointer(
|
||||
child: ColoredBox(
|
||||
color: Colors.black.withValues(alpha: progress * 0.5),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user