Add app source and tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
96
lib/Peach/designs/input/InputFields.dart
Normal file
96
lib/Peach/designs/input/InputFields.dart
Normal file
@@ -0,0 +1,96 @@
|
||||
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),
|
||||
);
|
||||
Reference in New Issue
Block a user