feat:参照研听的基础工程
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
import '../theme/spacing.dart';
|
||||
|
||||
class SkeletonLineCard extends StatelessWidget {
|
||||
const SkeletonLineCard({
|
||||
super.key,
|
||||
this.lineWidths = const [0.4, 1, 0.8, 0.6],
|
||||
this.height,
|
||||
});
|
||||
|
||||
final List<double> lineWidths;
|
||||
final double? height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = ShadTheme.of(context).colorScheme;
|
||||
return Container(
|
||||
height: height,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(Spacing.cardPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.card,
|
||||
border: Border.all(color: cs.border, width: 1),
|
||||
borderRadius: BorderRadius.circular(Spacing.radiusCard),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (var i = 0; i < lineWidths.length; i++) ...[
|
||||
_SkeletonLine(widthFactor: lineWidths[i]),
|
||||
if (i != lineWidths.length - 1) const SizedBox(height: 10),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SkeletonInstitutionCard extends StatelessWidget {
|
||||
const SkeletonInstitutionCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SkeletonLineCard(lineWidths: [0.4, 1, 0.6]);
|
||||
}
|
||||
}
|
||||
|
||||
class SkeletonListenCard extends StatelessWidget {
|
||||
const SkeletonListenCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SkeletonLineCard(lineWidths: [0.4, 1, 0.6]);
|
||||
}
|
||||
}
|
||||
|
||||
class SkeletonSectionCard extends StatelessWidget {
|
||||
const SkeletonSectionCard({
|
||||
super.key,
|
||||
required this.roman,
|
||||
required this.lineWidths,
|
||||
this.baseColor,
|
||||
this.highlightColor,
|
||||
});
|
||||
|
||||
final String roman;
|
||||
final List<double> lineWidths;
|
||||
final Color? baseColor;
|
||||
final Color? highlightColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = ShadTheme.of(context).colorScheme;
|
||||
final brightness = ShadTheme.of(context).brightness;
|
||||
final resolvedBaseColor =
|
||||
baseColor ??
|
||||
(brightness == Brightness.dark
|
||||
? cs.secondary
|
||||
: cs.border.withValues(alpha: 0.95));
|
||||
final resolvedHighlightColor =
|
||||
highlightColor ??
|
||||
(brightness == Brightness.dark
|
||||
? cs.card
|
||||
: cs.secondary.withValues(alpha: 0.95));
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.cardPadding,
|
||||
vertical: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.card,
|
||||
border: Border.all(color: cs.border, width: 1),
|
||||
borderRadius: BorderRadius.circular(Spacing.radiusCard),
|
||||
),
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: resolvedBaseColor,
|
||||
highlightColor: resolvedHighlightColor,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (var i = 0; i < lineWidths.length; i++) ...[
|
||||
_SkeletonLine(
|
||||
widthFactor: lineWidths[i],
|
||||
height: i == 0 ? 14 : 12,
|
||||
),
|
||||
if (i != lineWidths.length - 1) const SizedBox(height: 10),
|
||||
],
|
||||
const SizedBox(height: 2),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SkeletonLine extends StatelessWidget {
|
||||
const _SkeletonLine({required this.widthFactor, this.height = 12});
|
||||
|
||||
final double widthFactor;
|
||||
final double height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = ShadTheme.of(context).colorScheme;
|
||||
return FractionallySizedBox(
|
||||
widthFactor: widthFactor,
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: cs.secondary,
|
||||
highlightColor: cs.muted,
|
||||
child: Container(
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
color: cs.secondary,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionSkeletonList extends StatelessWidget {
|
||||
const SectionSkeletonList({
|
||||
super.key,
|
||||
required this.count,
|
||||
required this.lineWidthsForIndex,
|
||||
this.romanLabels,
|
||||
});
|
||||
|
||||
final int count;
|
||||
final List<double> Function(int index) lineWidthsForIndex;
|
||||
final List<String>? romanLabels;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final labels =
|
||||
romanLabels ??
|
||||
List<String>.generate(count, (index) => _roman(index + 1));
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.fromLTRB(Spacing.page, 4, Spacing.page, 24),
|
||||
sliver: SliverList.separated(
|
||||
itemCount: count,
|
||||
separatorBuilder: (_, _) => const SizedBox(height: Spacing.cardGap),
|
||||
itemBuilder: (context, index) => SkeletonSectionCard(
|
||||
roman: labels[index],
|
||||
lineWidths: lineWidthsForIndex(index),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionSkeletonListView extends StatelessWidget {
|
||||
const SectionSkeletonListView({
|
||||
super.key,
|
||||
required this.count,
|
||||
required this.lineWidthsForIndex,
|
||||
this.romanLabels,
|
||||
});
|
||||
|
||||
final int count;
|
||||
final List<double> Function(int index) lineWidthsForIndex;
|
||||
final List<String>? romanLabels;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final labels =
|
||||
romanLabels ??
|
||||
List<String>.generate(count, (index) => _roman(index + 1));
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.only(bottom: 72),
|
||||
itemCount: count,
|
||||
separatorBuilder: (_, _) => const SizedBox(height: Spacing.cardGap),
|
||||
itemBuilder: (context, index) => SkeletonSectionCard(
|
||||
roman: labels[index],
|
||||
lineWidths: lineWidthsForIndex(index),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _roman(int value) {
|
||||
const numerals = ['I.', 'II.', 'III.', 'IV.', 'V.', 'VI.', 'VII.', 'VIII.'];
|
||||
if (value <= 0 || value > numerals.length) return '$value.';
|
||||
return numerals[value - 1];
|
||||
}
|
||||
Reference in New Issue
Block a user