import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/services/geocoding_service.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/theme/text.dart'; import 'package:flutter/material.dart'; import 'info_row.dart'; class MapAddressRow extends StatefulWidget { final AvesEntry? entry; const MapAddressRow({ super.key, required this.entry, }); @override State createState() => _MapAddressRowState(); } class _MapAddressRowState extends State { final ValueNotifier _addressLineNotifier = ValueNotifier(null); @override void initState() { super.initState(); _updateAddress(); } @override void didUpdateWidget(covariant MapAddressRow oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.entry != widget.entry) { _updateAddress(); } } @override void dispose() { _addressLineNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final textScaler = MediaQuery.textScalerOf(context); return Container( alignment: AlignmentDirectional.centerStart, // addresses can include non-latin scripts with inconsistent line height, // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout height: textScaler.scale(Theme.of(context).textTheme.bodyMedium!.fontSize!) * 2, child: ValueListenableBuilder( valueListenable: _addressLineNotifier, builder: (context, addressLine, child) { final entry = widget.entry; final location = addressLine ?? (entry == null ? AText.valueNotAvailable : entry.hasAddress ? entry.shortAddress : settings.coordinateFormat.format(context, entry.latLng!)); return Text.rich( TextSpan( children: [ WidgetSpan( child: Padding( padding: const EdgeInsets.symmetric(horizontal: MapInfoRow.iconPadding), child: Icon(AIcons.location, size: MapInfoRow.getIconSize(context)), ), alignment: PlaceholderAlignment.middle, ), TextSpan(text: location), ], ), softWrap: false, overflow: TextOverflow.fade, maxLines: 1, ); }, ), ); } Future _updateAddress() async { final entry = widget.entry; final addressLine = await _getAddressLine(entry); if (!mounted) return; if (entry == widget.entry) { _addressLineNotifier.value = addressLine; } } Future _getAddressLine(AvesEntry? entry) async { if (entry != null && await availability.canLocatePlaces) { final addresses = await GeocodingService.getAddress(entry.latLng!, settings.appliedLocale); if (addresses.isNotEmpty) { final address = addresses.first; return address.addressLine; } } return null; } }