diff --git a/lib/api/mapflow_api.dart b/lib/api/mapflow_api.dart index 02881d7..d7d4d04 100644 --- a/lib/api/mapflow_api.dart +++ b/lib/api/mapflow_api.dart @@ -160,6 +160,8 @@ class MapflowApi { name latitude longitude + googlePrimaryType + googleTypes experiences { id status @@ -184,6 +186,10 @@ class MapflowApi { (place['longitude'] as num).toDouble(), ), traits: _traitsFromExperiences(place['experiences'] as List), + googlePrimaryType: place['googlePrimaryType'] as String?, + googleTypes: (place['googleTypes'] as List) + .map((type) => type as String) + .toList(), ); }).toList(); } @@ -201,6 +207,8 @@ class MapflowApi { name latitude longitude + googlePrimaryType + googleTypes experiences { id status @@ -233,6 +241,10 @@ class MapflowApi { (place['longitude'] as num).toDouble(), ), traits: _traitsFromExperiences(place['experiences'] as List), + googlePrimaryType: place['googlePrimaryType'] as String?, + googleTypes: (place['googleTypes'] as List) + .map((type) => type as String) + .toList(), ); }).toList(); } diff --git a/lib/models/place_models.dart b/lib/models/place_models.dart index b62719c..d8adc23 100644 --- a/lib/models/place_models.dart +++ b/lib/models/place_models.dart @@ -90,6 +90,8 @@ class PlaceRecommendation { required this.photoUrls, required this.coordinate, required this.traits, + required this.googlePrimaryType, + required this.googleTypes, }); final String id; @@ -99,6 +101,8 @@ class PlaceRecommendation { final List photoUrls; final LatLng coordinate; final Set traits; + final String? googlePrimaryType; + final List googleTypes; String get coverPhotoUrl => photoUrls.first; } diff --git a/lib/screens/mapflow_shell.dart b/lib/screens/mapflow_shell.dart index 291300d..9993674 100644 --- a/lib/screens/mapflow_shell.dart +++ b/lib/screens/mapflow_shell.dart @@ -963,21 +963,10 @@ class _PlaceStep extends StatelessWidget { separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (context, index) { final place = places[index]; - return ListTile( - onTap: isSubmitting ? null : () => onSelect(place), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 8, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - tileColor: const Color(0xFFFFFBF5), - leading: const Icon(Icons.place_outlined), - title: Text( - place.name, - style: const TextStyle(fontWeight: FontWeight.w800), - ), + return _NearbyPlaceCard( + place: place, + disabled: isSubmitting, + onTap: () => onSelect(place), ); }, ); @@ -990,6 +979,113 @@ class _PlaceStep extends StatelessWidget { } } +class _NearbyPlaceCard extends StatelessWidget { + const _NearbyPlaceCard({ + required this.place, + required this.disabled, + required this.onTap, + }); + + final PlaceRecommendation place; + final bool disabled; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final primaryType = _formatType(place.googlePrimaryType); + final visibleTypes = place.googleTypes + .where((type) => type != place.googlePrimaryType) + .map(_formatType) + .nonNulls + .take(4) + .toList(); + + return Material( + color: const Color(0xFFFFFBF5), + borderRadius: BorderRadius.circular(8), + child: InkWell( + onTap: disabled ? null : onTap, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.place_outlined), + const SizedBox(width: 10), + Expanded( + child: Text( + place.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.w900, + height: 1.1, + ), + ), + ), + ], + ), + if (primaryType != null || visibleTypes.isNotEmpty) ...[ + const SizedBox(height: 12), + Wrap( + spacing: 6, + runSpacing: 6, + children: [ + if (primaryType != null) + _PlaceTypeChip(label: primaryType, primary: true), + for (final type in visibleTypes) + _PlaceTypeChip(label: type, primary: false), + ], + ), + ], + ], + ), + ), + ), + ); + } + + String? _formatType(String? type) { + if (type == null || type.isEmpty) { + return null; + } + return type.replaceAll('_', ' '); + } +} + +class _PlaceTypeChip extends StatelessWidget { + const _PlaceTypeChip({required this.label, required this.primary}); + + final String label; + final bool primary; + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + color: primary ? const Color(0xFF111827) : const Color(0xFFECE5D8), + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5), + child: Text( + label, + style: TextStyle( + color: primary ? Colors.white : const Color(0xFF3A332A), + fontSize: 12, + fontWeight: FontWeight.w700, + height: 1, + ), + ), + ), + ); + } +} + class _VoiceStep extends StatelessWidget { const _VoiceStep({ required this.placeName,