Add app source and tests

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
alvocool
2026-10-11 10:15:30 +03:00
parent 7e246255c2
commit 38881d1fe1
135 changed files with 21057 additions and 0 deletions

View 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),
);