CTK 1.1.0
This commit is contained in:
212
lib/consultorios/address_edit_sheet.dart
Normal file
212
lib/consultorios/address_edit_sheet.dart
Normal file
@@ -0,0 +1,212 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../colors/app_colors.dart';
|
||||
import '../colors/app_gradients.dart';
|
||||
|
||||
import '../main.dart' show supabase;
|
||||
import '../strings/address_strings.dart';
|
||||
import '../strings/common_strings.dart';
|
||||
import '../widgets/tap_bounce.dart';
|
||||
import 'geocoding_service.dart';
|
||||
|
||||
/// Mostra o formulário de morada num modal bottom sheet, no mesmo estilo
|
||||
/// já usado para o formulário de adicionar criança. Devolve `true` se a
|
||||
/// morada foi guardada com sucesso.
|
||||
Future<bool?> showAddressEditSheet(
|
||||
BuildContext context, {
|
||||
String? initialAddress,
|
||||
}) {
|
||||
return showModalBottomSheet<bool>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
showDragHandle: true,
|
||||
backgroundColor: AppColors.pinkBackground,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
builder: (ctx) => AddressEditSheet(initialAddress: initialAddress),
|
||||
);
|
||||
}
|
||||
|
||||
class AddressEditSheet extends StatefulWidget {
|
||||
const AddressEditSheet({super.key, this.initialAddress});
|
||||
|
||||
final String? initialAddress;
|
||||
|
||||
@override
|
||||
State<AddressEditSheet> createState() => _AddressEditSheetState();
|
||||
}
|
||||
|
||||
class _AddressEditSheetState extends State<AddressEditSheet> {
|
||||
late final _addressController = TextEditingController(
|
||||
text: widget.initialAddress ?? '',
|
||||
);
|
||||
bool _saving = false;
|
||||
String? _errorText;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_addressController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _save() async {
|
||||
final address = _addressController.text.trim();
|
||||
if (address.isEmpty) {
|
||||
setState(() => _errorText = AddressStrings.addressRequired);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_saving = true;
|
||||
_errorText = null;
|
||||
});
|
||||
|
||||
final coords = await GeocodingService.geocodeAddress(address);
|
||||
if (!mounted) return;
|
||||
if (coords == null) {
|
||||
setState(() {
|
||||
_saving = false;
|
||||
_errorText = AddressStrings.geocodeFailed;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final uid = supabase.auth.currentUser?.id;
|
||||
if (uid == null) {
|
||||
setState(() => _saving = false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await supabase.from('profiles').upsert({
|
||||
'id': uid,
|
||||
'address': address,
|
||||
'address_lat': coords.lat,
|
||||
'address_lon': coords.lon,
|
||||
});
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop(true);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_saving = false;
|
||||
_errorText = AddressStrings.saveError(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bottomInset = MediaQuery.viewInsetsOf(context).bottom;
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(18, 6, 18, 18 + bottomInset),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text(
|
||||
AddressStrings.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: AppColors.pink,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.82),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.black.withValues(alpha: 0.08)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: _addressController,
|
||||
enabled: !_saving,
|
||||
textInputAction: TextInputAction.done,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
onSubmitted: (_) => _save(),
|
||||
decoration: InputDecoration(
|
||||
labelText: AddressStrings.label,
|
||||
hintText: AddressStrings.hint,
|
||||
errorText: _errorText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
AddressStrings.privacyDisclaimer,
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
height: 1.35,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 44,
|
||||
child: TextButton(
|
||||
onPressed: _saving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(false),
|
||||
child: const Text(CommonStrings.cancel),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: TapBounce(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: kGreenButtonGradient,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 44,
|
||||
child: FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
onPressed: _saving ? null : _save,
|
||||
child: _saving
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Text(AddressStrings.save),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user