import 'package:flutter/material.dart'; import '../../about/internal/application/TextType.dart'; import '../../utils/Colors.dart'; import '../text/Text.dart'; /// The standard labelled text field. Every form in the app uses this rather /// than building a bare [TextFormField], so labels, spacing and validation /// messages stay identical everywhere. Widget inputField({ required String label, required String initial, required void Function(String) onSaved, String? hint, bool required = false, bool obscure = false, TextInputType? keyboardType, Widget? suffix, String? Function(String?)? validator, void Function(String)? onChanged, TextEditingController? controller, int? maxLines = 1, bool enabled = true, }) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: TextFormField( initialValue: controller == null ? initial : null, controller: controller, obscureText: obscure, keyboardType: keyboardType, maxLines: obscure ? 1 : maxLines, enabled: enabled, onChanged: onChanged, decoration: InputDecoration( labelText: label, hintText: hint, suffixIcon: suffix, ), validator: validator ?? (required ? (v) => (v == null || v.trim().isEmpty) ? 'Required' : null : null), onSaved: (v) => onSaved(v ?? ''), ), ); } /// A section heading inside a form. Widget inputSection(BuildContext context, String title) => Padding( padding: const EdgeInsets.only(top: 20, bottom: 10), child: text( title.toUpperCase(), 12, TextType.Bold, color: context.jp.textDim, letterSpacing: 1.2, ), ); /// A one-line explanatory note under a field. Widget inputHint(BuildContext context, IconData icon, String message) => Padding( padding: const EdgeInsets.only(left: 4, top: 2, bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 13, color: context.jp.textFaint), const SizedBox(width: 6), Expanded( child: text( message, 12, TextType.Regular, color: context.jp.textFaint, ), ), ], ), ); /// A labelled switch row, matching the field spacing. Widget inputSwitch({ required BuildContext context, required String title, required String subtitle, required bool value, required void Function(bool) onChanged, }) => SwitchListTile( contentPadding: EdgeInsets.zero, value: value, onChanged: onChanged, title: text(title, 14, TextType.Regular, color: context.jp.text), subtitle: text(subtitle, 12, TextType.Light, color: context.jp.textDim), );