form_field_row.dart 2.9 KB

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