188 lines
6.3 KiB
Dart
188 lines
6.3 KiB
Dart
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:get/get.dart';
|
||
|
|
|
||
|
|
import '../../../../models/contact.dart';
|
||
|
|
import '../../../../services/contact_service.dart';
|
||
|
|
|
||
|
|
/// Kapcsolat szerkesztő — TELJES OLDAL (Scaffold), nem bottom sheet.
|
||
|
|
///
|
||
|
|
/// Miért oldal: a bottom sheet a szoftveres billentyűzettel rosszul
|
||
|
|
/// viselkedik (a mezőket eltakarja / a lapot feltolja). A Scaffold body
|
||
|
|
/// automatikusan a billentyűzet fölé görget, a mentés gomb pedig fixen
|
||
|
|
/// az appbarban van — kesztyűs, terepi használatra is kényelmes.
|
||
|
|
///
|
||
|
|
/// Új rekord: `Get.to(() => const ContactEditView())`.
|
||
|
|
/// Szerkesztés: `Get.to(() => const ContactEditView(), arguments: contact)`.
|
||
|
|
class ContactEditView extends StatefulWidget {
|
||
|
|
const ContactEditView({super.key});
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<ContactEditView> createState() => _ContactEditViewState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _ContactEditViewState extends State<ContactEditView> {
|
||
|
|
final _formKey = GlobalKey<FormState>();
|
||
|
|
late final Contact? _original;
|
||
|
|
|
||
|
|
late final TextEditingController _name;
|
||
|
|
late final TextEditingController _address;
|
||
|
|
late final TextEditingController _phone;
|
||
|
|
late final TextEditingController _email;
|
||
|
|
late final TextEditingController _note;
|
||
|
|
|
||
|
|
bool _saving = false;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
_original = Get.arguments is Contact ? Get.arguments as Contact : null;
|
||
|
|
_name = TextEditingController(text: _original?.name ?? '');
|
||
|
|
_address = TextEditingController(text: _original?.address ?? '');
|
||
|
|
_phone = TextEditingController(text: _original?.phone ?? '');
|
||
|
|
_email = TextEditingController(text: _original?.email ?? '');
|
||
|
|
_note = TextEditingController(text: _original?.note ?? '');
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_name.dispose();
|
||
|
|
_address.dispose();
|
||
|
|
_phone.dispose();
|
||
|
|
_email.dispose();
|
||
|
|
_note.dispose();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _save() async {
|
||
|
|
if (!(_formKey.currentState?.validate() ?? false)) return;
|
||
|
|
setState(() => _saving = true);
|
||
|
|
try {
|
||
|
|
final contact = (_original ?? const Contact(name: '')).copyWith(
|
||
|
|
name: _name.text,
|
||
|
|
address: _address.text,
|
||
|
|
phone: _phone.text,
|
||
|
|
email: _email.text,
|
||
|
|
note: _note.text,
|
||
|
|
);
|
||
|
|
final queued = await ContactService.to.save(contact);
|
||
|
|
Get.back(result: queued); // a lista frissítéshez visszakapja
|
||
|
|
Get.snackbar(queued ? 'Elmentve (offline)' : 'Mentve',
|
||
|
|
queued ? '${contact.name} - feltöltés, amint van net' : contact.name,
|
||
|
|
snackPosition: SnackPosition.BOTTOM);
|
||
|
|
} catch (e) {
|
||
|
|
Get.snackbar('Hiba', _friendly(e),
|
||
|
|
snackPosition: SnackPosition.BOTTOM,
|
||
|
|
backgroundColor: const Color(0xFFB71C1C),
|
||
|
|
colorText: const Color(0xFFFFFFFF));
|
||
|
|
} finally {
|
||
|
|
if (mounted) setState(() => _saving = false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
String _friendly(Object e) {
|
||
|
|
final s = e.toString();
|
||
|
|
if (s.contains('permission') ||
|
||
|
|
s.contains('policy') ||
|
||
|
|
s.contains('row-level')) {
|
||
|
|
return 'Nincs jogosultságod a művelethez.';
|
||
|
|
}
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final isNew = _original?.id == null;
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(
|
||
|
|
title: Text(isNew ? 'Új kapcsolat' : 'Kapcsolat szerkesztése'),
|
||
|
|
actions: [
|
||
|
|
TextButton.icon(
|
||
|
|
onPressed: _saving ? null : _save,
|
||
|
|
icon: _saving
|
||
|
|
? const SizedBox(
|
||
|
|
width: 18,
|
||
|
|
height: 18,
|
||
|
|
child: CircularProgressIndicator(strokeWidth: 2))
|
||
|
|
: const Icon(Icons.check),
|
||
|
|
label: const Text('Mentés'),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
body: Form(
|
||
|
|
key: _formKey,
|
||
|
|
child: ListView(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
children: [
|
||
|
|
TextFormField(
|
||
|
|
controller: _name,
|
||
|
|
autofocus: isNew,
|
||
|
|
textCapitalization: TextCapitalization.words,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'Név *',
|
||
|
|
prefixIcon: Icon(Icons.person_outline),
|
||
|
|
),
|
||
|
|
textInputAction: TextInputAction.next,
|
||
|
|
validator: (v) => (v == null || v.trim().isEmpty)
|
||
|
|
? 'A név megadása kötelező.'
|
||
|
|
: null,
|
||
|
|
),
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
TextFormField(
|
||
|
|
controller: _address,
|
||
|
|
textCapitalization: TextCapitalization.sentences,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'Cím',
|
||
|
|
prefixIcon: Icon(Icons.location_on_outlined),
|
||
|
|
),
|
||
|
|
textInputAction: TextInputAction.next,
|
||
|
|
),
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
TextFormField(
|
||
|
|
controller: _phone,
|
||
|
|
keyboardType: TextInputType.phone,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'Telefonszám',
|
||
|
|
prefixIcon: Icon(Icons.phone_outlined),
|
||
|
|
),
|
||
|
|
textInputAction: TextInputAction.next,
|
||
|
|
),
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
TextFormField(
|
||
|
|
controller: _email,
|
||
|
|
keyboardType: TextInputType.emailAddress,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'E-mail',
|
||
|
|
prefixIcon: Icon(Icons.email_outlined),
|
||
|
|
),
|
||
|
|
textInputAction: TextInputAction.next,
|
||
|
|
validator: (v) {
|
||
|
|
if (v == null || v.trim().isEmpty) return null;
|
||
|
|
final ok =
|
||
|
|
RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$').hasMatch(v.trim());
|
||
|
|
return ok ? null : 'Érvénytelen e-mail cím.';
|
||
|
|
},
|
||
|
|
),
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
TextFormField(
|
||
|
|
controller: _note,
|
||
|
|
maxLines: 4,
|
||
|
|
textCapitalization: TextCapitalization.sentences,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'Megjegyzés',
|
||
|
|
prefixIcon: Icon(Icons.notes_outlined),
|
||
|
|
alignLabelWithHint: true,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 24),
|
||
|
|
FilledButton.icon(
|
||
|
|
onPressed: _saving ? null : _save,
|
||
|
|
icon: const Icon(Icons.save_outlined),
|
||
|
|
label: const Text('Mentés'),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|