form_field_row.dart 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import '../../core/theme/app_colors.dart';
  2. import 'package:flutter/material.dart';
  3. import '../../core/theme/app_colors_extension.dart';
  4. /// 表单字段行,左侧标签 + 右侧值 + 可选箭头。
  5. ///
  6. /// [required] 为 true 时标签前显示红色星号。
  7. class FormFieldRow extends StatelessWidget {
  8. final String label;
  9. final String? value;
  10. final String? hint;
  11. final bool showArrow;
  12. final bool readOnly;
  13. final bool required;
  14. final VoidCallback? onTap;
  15. final VoidCallback? onClear;
  16. const FormFieldRow({
  17. super.key,
  18. required this.label,
  19. this.value,
  20. this.hint,
  21. this.showArrow = true,
  22. this.readOnly = false,
  23. this.required = false,
  24. this.onTap,
  25. this.onClear,
  26. });
  27. @override
  28. Widget build(BuildContext context) {
  29. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  30. final hasValue = value != null && value!.isNotEmpty;
  31. return GestureDetector(
  32. onTap: onTap,
  33. behavior: HitTestBehavior.opaque,
  34. child: Container(
  35. padding: EdgeInsets.zero,
  36. child: Row(
  37. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  38. children: [
  39. Text.rich(
  40. TextSpan(
  41. children: [
  42. TextSpan(
  43. text: label,
  44. style: TextStyle(
  45. fontSize: AppFontSizes.subtitle,
  46. color: colors.textSecondary,
  47. ),
  48. ),
  49. if (required)
  50. TextSpan(
  51. text: ' *',
  52. style: TextStyle(
  53. fontSize: AppFontSizes.subtitle,
  54. color: colors.danger,
  55. ),
  56. ),
  57. ],
  58. ),
  59. ),
  60. Row(
  61. mainAxisSize: MainAxisSize.min,
  62. children: [
  63. Text(
  64. hasValue ? value! : (hint ?? '请选择或填写'),
  65. style: TextStyle(
  66. fontSize: AppFontSizes.subtitle,
  67. color: hasValue
  68. ? (readOnly ? colors.textPrimary : colors.textPrimary)
  69. : colors.textPlaceholder,
  70. ),
  71. ),
  72. if (hasValue && onClear != null)
  73. GestureDetector(
  74. onTap: onClear,
  75. child: const Padding(
  76. padding: EdgeInsets.only(left: 8),
  77. child: Icon(Icons.close, size: 16, color: Color(0xFFBBBBBB)),
  78. ),
  79. )
  80. else if (showArrow && !readOnly) ...[
  81. const SizedBox(width: 4),
  82. Icon(
  83. Icons.chevron_right,
  84. size: 14,
  85. color: colors.textPlaceholder,
  86. ),
  87. ],
  88. ],
  89. ),
  90. ],
  91. ),
  92. ),
  93. );
  94. }
  95. }