feat: integrate Google Maps Places Autocomplete for hub address validation
This commit is contained in:
3
apps/mobile/config.dev.json
Normal file
3
apps/mobile/config.dev.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"GOOGLE_PLACES_API_KEY": "AIzaSyAS9yTf4q51_CNSZ7mbmeS9V3l_LZR80lU"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<AddHubDialog> {
|
||||
),
|
||||
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(
|
||||
|
||||
@@ -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: <Widget>[
|
||||
const Icon(UiIcons.mapPin, color: UiColors.iconSecondary),
|
||||
Expanded(
|
||||
child: Text(
|
||||
prediction.description ?? "",
|
||||
style: UiTypography.body1r.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
textStyle: UiTypography.body1r.textPrimary,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class HubsConstants {
|
||||
static const String googlePlacesApiKey = String.fromEnvironment('GOOGLE_PLACES_API_KEY');
|
||||
static const List<String> supportedCountries = <String>['us'];
|
||||
}
|
||||
@@ -11,10 +11,6 @@ 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:
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user