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
+198
View File
@@ -0,0 +1,198 @@
import 'package:flutter/material.dart';
import 'media_row_metrics.dart';
import 'media_poster_card.dart';
class SkeletonMediaRow extends StatefulWidget {
final String title;
final MediaCardVariant cardVariant;
final Color? titleColor;
const SkeletonMediaRow({
super.key,
required this.title,
this.cardVariant = MediaCardVariant.poster,
this.titleColor,
});
@override
State<SkeletonMediaRow> createState() => _SkeletonMediaRowState();
}
class _SkeletonMediaRowState extends State<SkeletonMediaRow>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1500),
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
double get _cardAspect =>
widget.cardVariant == MediaCardVariant.landscape ? 16 / 9 : 2 / 3;
bool get _showSubtitlePlaceholder =>
widget.cardVariant == MediaCardVariant.landscape;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isLight = theme.brightness == Brightness.light;
final compact = MediaRowMetrics.isCompact(context);
final rowHeight = MediaRowMetrics.rowHeight(
widget.cardVariant,
compact: compact,
);
final cardWidth = MediaRowMetrics.cardWidth(
widget.cardVariant,
compact: compact,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 12, 24, 10),
child: Text(
widget.title,
style: theme.textTheme.titleMedium?.copyWith(
color: widget.titleColor,
),
),
),
SizedBox(
height: rowHeight,
child: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 24),
itemCount: 6,
separatorBuilder: (_, _) => const SizedBox(width: 14),
itemBuilder: (_, _) => _SkeletonCard(
width: cardWidth,
aspect: _cardAspect,
showSubtitle: _showSubtitlePlaceholder,
isLight: isLight,
animation: _controller,
),
),
),
],
);
}
}
class _SkeletonCard extends StatelessWidget {
final double width;
final double aspect;
final bool showSubtitle;
final bool isLight;
final Animation<double> animation;
const _SkeletonCard({
required this.width,
required this.aspect,
required this.showSubtitle,
required this.isLight,
required this.animation,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: aspect,
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
final base = isLight
? const Color(0xFFF3F4F6)
: const Color(0xFF2A2A2A);
final highlight = isLight
? const Color(0xFFE5E7EB)
: const Color(0xFF3A3A3A);
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
begin: Alignment(-1.0 + 2.0 * animation.value, 0),
end: Alignment(-1.0 + 2.0 * animation.value + 1.0, 0),
colors: [base, highlight, base],
stops: const [0.0, 0.5, 1.0],
),
),
);
},
),
),
const SizedBox(height: 8),
AnimatedBuilder(
animation: animation,
builder: (context, child) {
final base = isLight
? const Color(0xFFF3F4F6)
: const Color(0xFF2A2A2A);
final highlight = isLight
? const Color(0xFFE5E7EB)
: const Color(0xFF3A3A3A);
return Container(
height: 12,
width: width * 0.7,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
gradient: LinearGradient(
begin: Alignment(-1.0 + 2.0 * animation.value, 0),
end: Alignment(-1.0 + 2.0 * animation.value + 1.0, 0),
colors: [base, highlight, base],
stops: const [0.0, 0.5, 1.0],
),
),
);
},
),
if (showSubtitle) ...[
const SizedBox(height: 6),
AnimatedBuilder(
animation: animation,
builder: (context, child) {
final base = isLight
? const Color(0xFFF3F4F6)
: const Color(0xFF2A2A2A);
final highlight = isLight
? const Color(0xFFE5E7EB)
: const Color(0xFF3A3A3A);
return Container(
height: 10,
width: width * 0.5,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
gradient: LinearGradient(
begin: Alignment(-1.0 + 2.0 * animation.value, 0),
end: Alignment(-1.0 + 2.0 * animation.value + 1.0, 0),
colors: [base, highlight, base],
stops: const [0.0, 0.5, 1.0],
),
),
);
},
),
],
],
),
);
}
}