step_card.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import '../../../resource/colors.gen.dart';
  4. import '../../../widget/gradient_text.dart';
  5. import '../intimacy_analyse_upload/widget/step_label_widget.dart';
  6. /// 步骤卡片,支持设置卡片背景、步骤标题、步骤描述、顶部图标和内容区域
  7. class StepCard extends StatelessWidget {
  8. /// 卡片背景
  9. final ImageProvider? bgImageProvider;
  10. /// 步骤标签
  11. final String? stepLabel;
  12. /// 步骤标题
  13. final String? stepTitle;
  14. /// 步骤描述
  15. final String? stepDesc;
  16. /// 顶部图标
  17. final Image? topIconWidget;
  18. /// 内容组件
  19. final Widget contentWidget;
  20. /// 是否有步骤
  21. final bool hasStep;
  22. const StepCard({
  23. super.key,
  24. this.bgImageProvider,
  25. this.stepLabel,
  26. this.stepTitle,
  27. this.stepDesc,
  28. this.hasStep = true,
  29. required this.contentWidget,
  30. this.topIconWidget,
  31. });
  32. @override
  33. Widget build(BuildContext context) {
  34. BoxDecoration decoration;
  35. // 没有背景图,使用渐变背景
  36. if (bgImageProvider == null) {
  37. decoration = BoxDecoration(
  38. gradient: LinearGradient(
  39. colors: [
  40. Color(0xFFEFE9FF),
  41. Color(0xFFFBFAFF),
  42. ],
  43. begin: Alignment.topCenter,
  44. end: Alignment.bottomCenter,
  45. ),
  46. shape: BoxShape.rectangle,
  47. border: Border.all(color: ColorName.white80, width: 1.w),
  48. borderRadius: BorderRadius.all(Radius.circular(20.r)),
  49. );
  50. } else {
  51. // 背景图
  52. decoration = BoxDecoration(
  53. image: DecorationImage(image: bgImageProvider!, fit: BoxFit.fill),
  54. );
  55. }
  56. return Container(
  57. margin: EdgeInsets.only(left: 12.w, top: 10.h, right: 12.w),
  58. child: Stack(
  59. // 不裁切超出区域的子组件,例如下面的顶部的图标
  60. clipBehavior: Clip.none,
  61. children: [
  62. // 顶部的图标
  63. topIconWidget == null
  64. ? SizedBox()
  65. : Positioned(top: -11.h, right: 0, child: topIconWidget!),
  66. // 卡片背景
  67. Container(
  68. decoration: decoration,
  69. child: Column(
  70. // 左对齐
  71. crossAxisAlignment: CrossAxisAlignment.start,
  72. children: [
  73. _buildStepLayout(),
  74. // 内容区域
  75. contentWidget,
  76. SizedBox(height: 12.h),
  77. ],
  78. ),
  79. ),
  80. ],
  81. ),
  82. );
  83. }
  84. /// 步骤
  85. Widget _buildStepLayout() {
  86. if (!hasStep) {
  87. return SizedBox();
  88. }
  89. String stepDescStr = stepDesc ?? "";
  90. return Column(
  91. children: [
  92. // 步骤标题
  93. Container(
  94. margin: EdgeInsets.only(
  95. // 存在步骤标签时,才有左边距
  96. left: _hasStepLabel() ? 12.w : 0,
  97. top: 16.h,
  98. bottom: 4.h,
  99. ),
  100. child: _buildStepTitle(),
  101. ),
  102. SizedBox(height: 4.h),
  103. // 步骤描述
  104. stepDescStr.isNotEmpty
  105. ? _buildStepDesc(stepDescStr)
  106. : SizedBox(height: 6.h),
  107. SizedBox(height: 14.h),
  108. ],
  109. );
  110. }
  111. /// 是否有步骤标签
  112. bool _hasStepLabel() {
  113. String stepLabelStr = stepLabel ?? "";
  114. return stepLabelStr.isNotEmpty == true;
  115. }
  116. /// 是否有步骤标题
  117. bool _hasStepTitle() {
  118. String stepTitleStr = stepTitle ?? "";
  119. return stepTitleStr.isNotEmpty == true;
  120. }
  121. /// 是否有步骤标题
  122. bool _hasStepDesc() {
  123. String stepDescStr = stepDesc ?? "";
  124. return stepDescStr.isNotEmpty == true;
  125. }
  126. /// 步骤标题
  127. Widget _buildStepTitle() {
  128. String stepTitleStr = stepTitle ?? "";
  129. // 步骤标签
  130. Widget stepLabelWidget;
  131. if (_hasStepLabel()) {
  132. stepLabelWidget = StepLabelWidget(label: stepLabel ?? "");
  133. } else {
  134. stepLabelWidget = SizedBox();
  135. }
  136. return Row(
  137. children: [
  138. // 步骤标签
  139. stepLabelWidget,
  140. SizedBox(width: 10.w),
  141. // 标题
  142. GradientText(
  143. // 渐变颜色
  144. colors: [ColorName.stepTitleColor1, ColorName.stepTitleColor2],
  145. child: Text(
  146. stepTitleStr,
  147. style: TextStyle(fontSize: 20.sp, fontWeight: FontWeight.w700),
  148. ),
  149. ),
  150. ],
  151. );
  152. }
  153. /// 构建步骤描述
  154. Widget _buildStepDesc(String desc) {
  155. if (desc.isEmpty) {
  156. return SizedBox();
  157. }
  158. return Row(
  159. children: [
  160. Expanded(
  161. child: Container(
  162. margin: EdgeInsets.only(left: 12.w, right: 12.w),
  163. child: Text(
  164. desc,
  165. // 超过时显示省略号
  166. overflow: TextOverflow.ellipsis,
  167. // 最多多少行
  168. maxLines: 2,
  169. style: TextStyle(
  170. fontSize: 12.sp,
  171. fontWeight: FontWeight.w400,
  172. color: ColorName.black60,
  173. ),
  174. ),
  175. ),
  176. ),
  177. SizedBox(height: 16.h),
  178. ],
  179. );
  180. }
  181. }