From c212e8be00aa59542a8eda0d944b5342a98386cb Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Thu, 29 Jan 2026 03:01:14 -0500 Subject: [PATCH] feat: integrate Google Maps Places Autocomplete for hub address validation --- apps/mobile/config.dev.json | 3 + .../lib/src/l10n/strings.g.dart | 2 +- .../presentation/widgets/add_hub_dialog.dart | 9 ++- .../widgets/hub_address_autocomplete.dart | 55 +++++++++++++++++++ .../hubs/lib/src/util/hubs_constants.dart | 4 ++ .../features/client/hubs/pubspec.yaml | 12 ++-- apps/mobile/pubspec.lock | 32 +++++++++++ 7 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 apps/mobile/config.dev.json create mode 100644 apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_address_autocomplete.dart create mode 100644 apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart diff --git a/apps/mobile/config.dev.json b/apps/mobile/config.dev.json new file mode 100644 index 00000000..d2d5fa4c --- /dev/null +++ b/apps/mobile/config.dev.json @@ -0,0 +1,3 @@ +{ + "GOOGLE_PLACES_API_KEY": "AIzaSyAS9yTf4q51_CNSZ7mbmeS9V3l_LZR80lU" +} diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart b/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart index bdcfdea1..0ff6481d 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart +++ b/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 1038 (519 per locale) /// -/// Built on 2026-01-29 at 04:15 UTC +/// Built on 2026-01-29 at 06:58 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/add_hub_dialog.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/add_hub_dialog.dart index 2a4dd8e9..f9124071 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/add_hub_dialog.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/add_hub_dialog.dart @@ -2,6 +2,8 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:core_localization/core_localization.dart'; +import 'hub_address_autocomplete.dart'; + /// A dialog for adding a new hub. class AddHubDialog extends StatefulWidget { /// Callback when the "Create Hub" button is pressed. @@ -74,12 +76,9 @@ class _AddHubDialogState extends State { ), const SizedBox(height: UiConstants.space4), _buildFieldLabel(t.client_hubs.add_hub_dialog.address_label), - TextField( + HubAddressAutocomplete( controller: _addressController, - style: UiTypography.body1r.textPrimary, - decoration: _buildInputDecoration( - t.client_hubs.add_hub_dialog.address_hint, - ), + hintText: t.client_hubs.add_hub_dialog.address_hint, ), const SizedBox(height: UiConstants.space8), Row( diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_address_autocomplete.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_address_autocomplete.dart new file mode 100644 index 00000000..ef269f50 --- /dev/null +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_address_autocomplete.dart @@ -0,0 +1,55 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:google_places_flutter/google_places_flutter.dart'; +import 'package:google_places_flutter/model/prediction.dart'; + +import '../../util/hubs_constants.dart'; + +class HubAddressAutocomplete extends StatelessWidget { + const HubAddressAutocomplete({ + required this.controller, + required this.hintText, + super.key, + }); + + final TextEditingController controller; + final String hintText; + + @override + Widget build(BuildContext context) { + return GooglePlaceAutoCompleteTextField( + textEditingController: controller, + googleAPIKey: HubsConstants.googlePlacesApiKey, + debounceTime: 500, + countries: HubsConstants.supportedCountries, + isLatLngRequired: false, + getPlaceDetailWithLatLng: (Prediction prediction) { + // Handle lat/lng if needed in the future + }, + itemClick: (Prediction prediction) { + controller.text = prediction.description ?? ''; + controller.selection = TextSelection.fromPosition( + TextPosition(offset: controller.text.length), + ); + }, + itemBuilder: (_, _, Prediction prediction) { + return Padding( + padding: const EdgeInsets.all(UiConstants.space2), + child: Row( + spacing: UiConstants.space1, + children: [ + const Icon(UiIcons.mapPin, color: UiColors.iconSecondary), + Expanded( + child: Text( + prediction.description ?? "", + style: UiTypography.body1r.textSecondary, + ), + ), + ], + ), + ); + }, + textStyle: UiTypography.body1r.textPrimary, + ); + } +} diff --git a/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart b/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart new file mode 100644 index 00000000..23d706bc --- /dev/null +++ b/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart @@ -0,0 +1,4 @@ +class HubsConstants { + static const String googlePlacesApiKey = String.fromEnvironment('GOOGLE_PLACES_API_KEY'); + static const List supportedCountries = ['us']; +} diff --git a/apps/mobile/packages/features/client/hubs/pubspec.yaml b/apps/mobile/packages/features/client/hubs/pubspec.yaml index 3c578989..a5394d89 100644 --- a/apps/mobile/packages/features/client/hubs/pubspec.yaml +++ b/apps/mobile/packages/features/client/hubs/pubspec.yaml @@ -11,11 +11,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_bloc: ^8.1.0 - flutter_modular: ^6.3.2 - equatable: ^2.0.5 - lucide_icons: ^0.257.0 - + # Architecture Packages krow_core: path: ../../../core @@ -27,8 +23,14 @@ dependencies: path: ../../../design_system core_localization: path: ../../../core_localization + + flutter_bloc: ^8.1.0 + flutter_modular: ^6.3.2 + equatable: ^2.0.5 + lucide_icons: ^0.257.0 firebase_auth: ^6.1.4 firebase_data_connect: ^0.2.2+2 + google_places_flutter: ^2.1.1 dev_dependencies: flutter_test: diff --git a/apps/mobile/pubspec.lock b/apps/mobile/pubspec.lock index 594c00bd..9abb77a4 100644 --- a/apps/mobile/pubspec.lock +++ b/apps/mobile/pubspec.lock @@ -295,6 +295,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.1" + dio: + dependency: transitive + description: + name: dio + sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25 + url: "https://pub.dev" + source: hosted + version: "5.9.1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" equatable: dependency: transitive description: @@ -515,6 +531,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.3+1" + google_places_flutter: + dependency: transitive + description: + name: google_places_flutter + sha256: "37bd64221cf4a5aa97eb3a33dc2d40f6326aa5ae4e2f2a9a7116bdc1a14f5194" + url: "https://pub.dev" + source: hosted + version: "2.1.1" googleapis_auth: dependency: transitive description: @@ -939,6 +963,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" shared_preferences: dependency: transitive description: