form_section.dart 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import '../../core/theme/app_colors.dart';
  2. import 'package:flutter/material.dart';
  3. import '../../core/theme/app_colors_extension.dart';
  4. /// Pencil Component/FormSection — 表单分组卡片
  5. ///
  6. /// 标题行(16号/600字重) + 右侧可选操作按钮(带plus图标) + 内容区
  7. class FormSection extends StatelessWidget {
  8. final String title;
  9. final IconData? leadingIcon;
  10. final String? actionText;
  11. final IconData? actionIcon;
  12. final VoidCallback? onActionTap;
  13. final bool showAction;
  14. final Widget? trailing;
  15. final List<Widget> children;
  16. const FormSection({
  17. super.key,
  18. required this.title,
  19. this.leadingIcon,
  20. this.actionText,
  21. this.actionIcon,
  22. this.onActionTap,
  23. this.showAction = false,
  24. this.trailing,
  25. required this.children,
  26. });
  27. @override
  28. Widget build(BuildContext context) {
  29. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  30. return Container(
  31. padding: const EdgeInsets.all(16),
  32. decoration: BoxDecoration(
  33. color: colors.bgCard,
  34. borderRadius: BorderRadius.circular(8),
  35. ),
  36. child: Column(
  37. crossAxisAlignment: CrossAxisAlignment.start,
  38. children: [
  39. Row(
  40. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  41. children: [
  42. Row(
  43. mainAxisSize: MainAxisSize.min,
  44. children: [
  45. if (leadingIcon != null) ...[
  46. Icon(leadingIcon, size: 18, color: colors.textPrimary),
  47. const SizedBox(width: 8),
  48. ],
  49. Text(
  50. title,
  51. style: TextStyle(
  52. fontSize: AppFontSizes.title,
  53. fontWeight: FontWeight.w600,
  54. color: colors.textPrimary,
  55. ),
  56. ),
  57. ],
  58. ),
  59. if (trailing != null) trailing!, // ignore: prefer_null_aware_operator
  60. if (showAction)
  61. GestureDetector(
  62. onTap: onActionTap,
  63. behavior: HitTestBehavior.opaque,
  64. child: Padding(
  65. padding: const EdgeInsetsDirectional.only(start: 16, top: 6, bottom: 6),
  66. child: Row(
  67. mainAxisSize: MainAxisSize.min,
  68. children: [
  69. Text(
  70. actionText ?? '',
  71. style: TextStyle(
  72. fontSize: AppFontSizes.body,
  73. color: colors.primary,
  74. ),
  75. ),
  76. const SizedBox(width: 4),
  77. Icon(
  78. actionIcon ?? Icons.add,
  79. size: 20,
  80. color: colors.primary,
  81. ),
  82. ],
  83. ),
  84. ),
  85. ),
  86. ],
  87. ),
  88. const SizedBox(height: 16),
  89. ...children,
  90. ],
  91. ),
  92. );
  93. }
  94. }