如何在Flutter表单中集成Google Maps实现地点选择?附资源推荐
在Flutter表单中集成Google Maps收集清真寺位置的实现方案
一、前期准备
- 申请Google Maps API密钥:
登录Google Cloud控制台创建项目,启用「Maps SDK for Android」和「Maps SDK for iOS」服务,生成API密钥后记得配置密钥限制(如绑定应用包名、SHA-1指纹),防止密钥被滥用。 - 添加依赖包:
在项目的pubspec.yaml中添加以下依赖,用于地图展示和位置获取:dependencies: flutter: sdk: flutter google_maps_flutter: ^2.5.0 geolocator: ^10.1.0
二、平台权限配置
Android端
在android/app/src/main/AndroidManifest.xml中添加位置权限和API密钥配置:
<!-- 位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- Google Maps API密钥 --> <meta-data android:name="com.google.android.geo.API_KEY" android:value="你的API密钥"/>
iOS端
在ios/Runner/Info.plist中添加位置权限说明:
<key>NSLocationWhenInUseUsageDescription</key> <string>需要获取你的位置来帮助选择清真寺位置</string>
同时在ios/Runner/AppDelegate.swift中配置API密钥(或通过Xcode直接在Info.plist中添加GMSApiKey字段)。
三、表单与地图集成实现
核心逻辑说明
- 用Flutter原生
Form组件构建用户信息输入表单(姓名、联系方式等)。 - 嵌入
GoogleMap组件,支持用户点击地图选择位置,同时提供「获取当前位置」按钮快速定位。 - 选中位置后生成标记,将经纬度与表单数据一同提交。
示例代码
import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:geolocator/geolocator.dart'; class UserInfoForm extends StatefulWidget { @override _UserInfoFormState createState() => _UserInfoFormState(); } class _UserInfoFormState extends State<UserInfoForm> { final _formKey = GlobalKey<FormState>(); late GoogleMapController _mapController; LatLng? _selectedLocation; Marker? _locationMarker; final TextEditingController _nameController = TextEditingController(); final TextEditingController _phoneController = TextEditingController(); // 默认初始相机位置(示例指向麦加) final CameraPosition _initialCameraPosition = CameraPosition( target: LatLng(21.4225, 39.8262), zoom: 12, ); // 获取当前位置并更新地图 Future<void> _getCurrentLocation() async { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请开启位置服务')), ); return; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('位置权限被拒绝')), ); return; } } if (permission == LocationPermission.deniedForever) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('位置权限被永久拒绝,请在设置中开启')), ); return; } Position position = await Geolocator.getCurrentPosition(); LatLng currentLatLng = LatLng(position.latitude, position.longitude); _updateSelectedLocation(currentLatLng); _mapController.animateCamera(CameraUpdate.newLatLng(currentLatLng)); } // 更新选中位置与地图标记 void _updateSelectedLocation(LatLng latLng) { setState(() { _selectedLocation = latLng; _locationMarker = Marker( markerId: const MarkerId('selected-location'), position: latLng, infoWindow: const InfoWindow(title: '选中的清真寺位置'), ); }); } // 表单提交逻辑 void _submitForm() { if (_formKey.currentState!.validate() && _selectedLocation != null) { String name = _nameController.text; String phone = _phoneController.text; double lat = _selectedLocation!.latitude; double lng = _selectedLocation!.longitude; // 此处替换为实际的提交逻辑(如存入数据库、调用后端接口) print('用户姓名:$name'); print('联系方式:$phone'); print('清真寺位置:$lat, $lng'); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('表单提交成功')), ); } else if (_selectedLocation == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请选择清真寺位置')), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('用户信息与清真寺位置')), body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: _nameController, decoration: const InputDecoration(labelText: '姓名'), validator: (value) => value?.isEmpty ?? true ? '请输入姓名' : null, ), TextFormField( controller: _phoneController, decoration: const InputDecoration(labelText: '联系方式'), keyboardType: TextInputType.phone, validator: (value) => value?.isEmpty ?? true ? '请输入联系方式' : null, ), const SizedBox(height: 20), const Text('请选择清真寺位置:'), const SizedBox(height: 10), Container( height: 300, child: GoogleMap( initialCameraPosition: _initialCameraPosition, onMapCreated: (controller) => _mapController = controller, onTap: _updateSelectedLocation, markers: _locationMarker != null ? {_locationMarker!} : {}, ), ), const SizedBox(height: 10), ElevatedButton( onPressed: _getCurrentLocation, child: const Text('获取当前位置'), ), const SizedBox(height: 20), Center( child: ElevatedButton( onPressed: _submitForm, child: const Text('提交信息'), ), ), ], ), ), ), ); } @override void dispose() { _nameController.dispose(); _phoneController.dispose(); super.dispose(); } }
四、需遵循的核心资源
- Google Maps Flutter官方文档:涵盖地图组件的所有API说明、配置步骤、高级功能(如标记、相机控制)。
- Flutter表单开发指南:学习表单验证、输入处理、提交逻辑的最佳实践。
- Geolocator插件文档:掌握位置权限申请、当前位置获取的标准流程。
- Google Cloud API密钥管理文档:了解密钥安全配置、使用限制的规范,避免密钥泄露风险。
内容的提问来源于stack exchange,提问作者Adnan Hussain
相关产品推荐
相关产品推荐

