info_card.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:nomo/app/widgets/ix_image.dart';
  4. import 'package:nomo/config/theme/dark_theme_colors.dart';
  5. /// 信息卡片组件 - 用于显示带图标的信息列表
  6. /// 常用于订阅说明、功能介绍等场景
  7. class InfoCard extends StatelessWidget {
  8. final String? title;
  9. final List<InfoItem> items;
  10. final Color? backgroundColor;
  11. final EdgeInsets? padding;
  12. const InfoCard({
  13. super.key,
  14. this.title,
  15. required this.items,
  16. this.backgroundColor,
  17. this.padding,
  18. });
  19. @override
  20. Widget build(BuildContext context) {
  21. return Column(
  22. crossAxisAlignment: CrossAxisAlignment.start,
  23. children: [
  24. if (title != null) ...[
  25. Text(
  26. title!,
  27. style: TextStyle(
  28. fontSize: 16.sp,
  29. color: DarkThemeColors.hintTextColor,
  30. fontWeight: FontWeight.w500,
  31. ),
  32. ),
  33. 16.verticalSpace,
  34. ],
  35. Container(
  36. padding: padding ?? EdgeInsets.all(16.w),
  37. decoration: BoxDecoration(
  38. color: backgroundColor ?? DarkThemeColors.bgDisable,
  39. borderRadius: BorderRadius.circular(12.r),
  40. ),
  41. child: Column(
  42. children: items
  43. .asMap()
  44. .entries
  45. .map(
  46. (entry) => _InfoItemWidget(
  47. item: entry.value,
  48. isLast: entry.key == items.length - 1,
  49. ),
  50. )
  51. .toList(),
  52. ),
  53. ),
  54. ],
  55. );
  56. }
  57. }
  58. /// 信息项数据模型
  59. class InfoItem {
  60. final IconData? icon;
  61. final String? imageSource;
  62. final String title;
  63. final String description;
  64. final Color? iconColor;
  65. final Gradient? iconGradient;
  66. final double? iconSize;
  67. final double? imageWidth;
  68. final double? imageHeight;
  69. const InfoItem({
  70. this.icon,
  71. this.imageSource,
  72. required this.title,
  73. required this.description,
  74. this.iconColor,
  75. this.iconGradient,
  76. this.iconSize,
  77. this.imageWidth,
  78. this.imageHeight,
  79. }) : assert(
  80. icon != null || imageSource != null,
  81. 'Either icon or imageSource must be provided',
  82. );
  83. }
  84. /// 信息项组件(内部使用)
  85. class _InfoItemWidget extends StatelessWidget {
  86. final InfoItem item;
  87. final bool isLast;
  88. const _InfoItemWidget({required this.item, this.isLast = false});
  89. @override
  90. Widget build(BuildContext context) {
  91. return IntrinsicHeight(
  92. child: Row(
  93. crossAxisAlignment: CrossAxisAlignment.start,
  94. children: [
  95. // 左侧时间线(图标/图片 + 竖线)
  96. Column(
  97. children: [
  98. // 图标或图片容器
  99. _buildIconOrImage(),
  100. // 连接线(最后一个不显示)
  101. if (!isLast)
  102. Expanded(
  103. child: Container(
  104. width: 1,
  105. margin: EdgeInsets.only(top: 8.h, bottom: 8.h),
  106. color: Colors.white.withValues(alpha: 0.1),
  107. ),
  108. ),
  109. ],
  110. ),
  111. 12.horizontalSpace,
  112. // 文本内容
  113. Expanded(
  114. child: Padding(
  115. padding: EdgeInsets.only(bottom: isLast ? 0 : 10.w),
  116. child: Column(
  117. crossAxisAlignment: CrossAxisAlignment.start,
  118. children: [
  119. Text(
  120. item.title,
  121. style: TextStyle(
  122. fontSize: 14.sp,
  123. height: 1.6,
  124. color: DarkThemeColors.bodyTextColor,
  125. fontWeight: FontWeight.w500,
  126. ),
  127. ),
  128. 4.verticalSpace,
  129. Text(
  130. item.description,
  131. style: TextStyle(
  132. fontSize: 12.sp,
  133. color: DarkThemeColors.hintTextColor,
  134. height: 1.7,
  135. ),
  136. ),
  137. ],
  138. ),
  139. ),
  140. ),
  141. ],
  142. ),
  143. );
  144. }
  145. /// 构建图标或图片
  146. Widget _buildIconOrImage() {
  147. if (item.imageSource != null) {
  148. // 显示图片
  149. return IXImage(
  150. source: item.imageSource!,
  151. width: item.imageWidth ?? 20.w,
  152. height: item.imageHeight ?? 20.w,
  153. sourceType: _getImageSourceType(item.imageSource!),
  154. );
  155. } else {
  156. // 显示图标
  157. return Icon(
  158. item.icon,
  159. size: item.iconSize ?? 20.w,
  160. color: item.iconColor ?? Colors.white,
  161. );
  162. }
  163. }
  164. /// 判断图片资源类型
  165. ImageSourceType _getImageSourceType(String source) {
  166. if (source.startsWith('http://') || source.startsWith('https://')) {
  167. return ImageSourceType.network;
  168. } else if (source.startsWith('assets/')) {
  169. return ImageSourceType.asset;
  170. } else {
  171. return ImageSourceType.asset;
  172. }
  173. }
  174. }