You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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字段)。

三、表单与地图集成实现

核心逻辑说明

  1. 用Flutter原生Form组件构建用户信息输入表单(姓名、联系方式等)。
  2. 嵌入GoogleMap组件,支持用户点击地图选择位置,同时提供「获取当前位置」按钮快速定位。
  3. 选中位置后生成标记,将经纬度与表单数据一同提交。

示例代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 21:05:21