skeleton_list_card.dart 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import 'package:flutter/material.dart';
  2. import 'package:tdesign_flutter/tdesign_flutter.dart';
  3. import '../../core/theme/app_colors.dart';
  4. /// Skeleton 占位卡片,匹配 [ListCard] 布局:
  5. /// ┌────────────────────────┐
  6. /// │ cardNo amount │ ← Row: 单号(14/600) + 金额(16/700)
  7. /// │ │
  8. /// │ description │ ← 描述(14)
  9. /// │ │
  10. /// │ date statusTag │ ← Row: 日期(12) + 状态标签
  11. /// └────────────────────────┘
  12. class SkeletonListCard extends StatelessWidget {
  13. const SkeletonListCard({super.key});
  14. @override
  15. Widget build(BuildContext context) {
  16. return Container(
  17. padding: const EdgeInsets.all(12),
  18. decoration: BoxDecoration(
  19. color: AppColors.bgCard,
  20. borderRadius: BorderRadius.circular(8),
  21. ),
  22. child: TDSkeleton.fromRowCol(
  23. animation: TDSkeletonAnimation.flashed,
  24. rowCol: TDSkeletonRowCol(
  25. objects: const [
  26. // R1: cardNo + spacer + amount
  27. [
  28. TDSkeletonRowColObj.text(width: 120, height: 14),
  29. TDSkeletonRowColObj.spacer(),
  30. TDSkeletonRowColObj.text(width: 80, height: 16),
  31. ],
  32. // R2: description
  33. [
  34. TDSkeletonRowColObj.text(height: 14),
  35. ],
  36. // R3: date + spacer + statusTag
  37. [
  38. TDSkeletonRowColObj.text(width: 100, height: 12),
  39. TDSkeletonRowColObj.spacer(),
  40. TDSkeletonRowColObj.rect(width: 48, height: 20),
  41. ],
  42. ],
  43. style: TDSkeletonRowColStyle(
  44. rowSpacing: (_) => 8,
  45. ),
  46. ),
  47. ),
  48. );
  49. }
  50. }
  51. /// Skeleton 占位卡片,匹配车辆列表卡片布局:
  52. /// ┌────────────────────────┐
  53. /// │ 车牌号 状态徽章 │ ← Row: 车牌(16/700) + 徽章(12)
  54. /// │ │
  55. /// │ 申请单号 用途标签 │ ← Row: 单号(12) + 标签(10)
  56. /// │ │
  57. /// │ 路线 ... 时间 │ ← Row: 路线(13/ellipsis) + 时间(12)
  58. /// └────────────────────────┘
  59. class SkeletonVehicleCard extends StatelessWidget {
  60. const SkeletonVehicleCard({super.key});
  61. @override
  62. Widget build(BuildContext context) {
  63. return Container(
  64. padding: const EdgeInsets.all(12),
  65. decoration: BoxDecoration(
  66. color: AppColors.bgCard,
  67. borderRadius: BorderRadius.circular(8),
  68. ),
  69. child: TDSkeleton.fromRowCol(
  70. animation: TDSkeletonAnimation.flashed,
  71. rowCol: TDSkeletonRowCol(
  72. objects: const [
  73. // R1: licensePlate + spacer + status badge
  74. [
  75. TDSkeletonRowColObj.text(width: 100, height: 16),
  76. TDSkeletonRowColObj.spacer(),
  77. TDSkeletonRowColObj.rect(width: 48, height: 20),
  78. ],
  79. // R2: applicationNo + spacer + purpose tag
  80. [
  81. TDSkeletonRowColObj.text(width: 140, height: 12),
  82. TDSkeletonRowColObj.spacer(),
  83. TDSkeletonRowColObj.rect(width: 40, height: 16),
  84. ],
  85. // R3: route (flex) + spacer + date
  86. [
  87. TDSkeletonRowColObj.text(flex: 2, height: 13),
  88. TDSkeletonRowColObj.spacer(flex: 1),
  89. TDSkeletonRowColObj.text(width: 120, height: 12),
  90. ],
  91. ],
  92. style: TDSkeletonRowColStyle(
  93. rowSpacing: (_) => 6,
  94. ),
  95. ),
  96. ),
  97. );
  98. }
  99. }
  100. /// 外勤日志 Skeleton 占位卡片
  101. ///
  102. /// 匹配外勤日志卡片布局:visitNo → customerName+状态 → address → summary+date
  103. class SkeletonOutingLogCard extends StatelessWidget {
  104. const SkeletonOutingLogCard({super.key});
  105. @override
  106. Widget build(BuildContext context) {
  107. return Container(
  108. padding: const EdgeInsets.all(12),
  109. decoration: BoxDecoration(
  110. color: AppColors.bgCard,
  111. borderRadius: BorderRadius.circular(8),
  112. ),
  113. child: TDSkeleton.fromRowCol(
  114. animation: TDSkeletonAnimation.flashed,
  115. rowCol: TDSkeletonRowCol(
  116. objects: const [
  117. // R1: visitNo
  118. [TDSkeletonRowColObj.text(width: 100, height: 11)],
  119. // R2: customerName + spacer + statusTag
  120. [
  121. TDSkeletonRowColObj.text(flex: 3, height: 15),
  122. TDSkeletonRowColObj.spacer(flex: 1),
  123. TDSkeletonRowColObj.rect(width: 48, height: 20),
  124. ],
  125. // R3: checkInAddress
  126. [TDSkeletonRowColObj.text(height: 12)],
  127. // R4: summary + spacer + date
  128. [
  129. TDSkeletonRowColObj.text(flex: 2, height: 12),
  130. TDSkeletonRowColObj.spacer(flex: 1),
  131. TDSkeletonRowColObj.text(width: 80, height: 11),
  132. ],
  133. ],
  134. style: TDSkeletonRowColStyle(rowSpacing: (_) => 4),
  135. ),
  136. ),
  137. );
  138. }
  139. }
  140. /// 公告 Skeleton 占位卡片
  141. ///
  142. /// 匹配公告卡片布局:title 行 + typeTag/publisher/date 行
  143. class SkeletonAnnouncementCard extends StatelessWidget {
  144. const SkeletonAnnouncementCard({super.key});
  145. @override
  146. Widget build(BuildContext context) {
  147. return Container(
  148. padding: const EdgeInsets.all(12),
  149. decoration: BoxDecoration(
  150. color: AppColors.bgCard,
  151. borderRadius: BorderRadius.circular(8),
  152. ),
  153. child: TDSkeleton.fromRowCol(
  154. animation: TDSkeletonAnimation.flashed,
  155. rowCol: TDSkeletonRowCol(
  156. objects: const [
  157. // R1: title (full width)
  158. [TDSkeletonRowColObj.text(height: 15)],
  159. // R2: typeTag + spacer + date
  160. [
  161. TDSkeletonRowColObj.rect(width: 56, height: 20),
  162. TDSkeletonRowColObj.spacer(width: 8),
  163. TDSkeletonRowColObj.text(width: 60, height: 12),
  164. TDSkeletonRowColObj.spacer(),
  165. TDSkeletonRowColObj.text(width: 120, height: 12),
  166. ],
  167. ],
  168. style: TDSkeletonRowColStyle(rowSpacing: (_) => 8),
  169. ),
  170. ),
  171. );
  172. }
  173. }
  174. /// 列表加载态:骨架卡片占位
  175. ///
  176. /// [cardCount] 骨架卡片数量,默认 5
  177. /// [cardBuilder] 骨架卡片构建器,默认 [SkeletonListCard]
  178. class SkeletonLoadingList extends StatelessWidget {
  179. final int cardCount;
  180. final Widget Function() cardBuilder;
  181. const SkeletonLoadingList({
  182. super.key,
  183. this.cardCount = 5,
  184. this.cardBuilder = _defaultBuilder,
  185. });
  186. static Widget _defaultBuilder() => const SkeletonListCard();
  187. @override
  188. Widget build(BuildContext context) {
  189. return ListView(
  190. padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
  191. physics: const NeverScrollableScrollPhysics(),
  192. children: List.generate(
  193. cardCount,
  194. (_) => Padding(
  195. padding: const EdgeInsets.only(bottom: 16),
  196. child: cardBuilder(),
  197. ),
  198. ),
  199. );
  200. }
  201. }