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

Flutter GPS追踪器开发求助:位置更新与地址时间列表展示

解决方案

问题分析

原代码存在几个关键问题:

  • 位置流订阅被定义为局部变量,未赋值给类成员positionStream,可能导致无法正确管理订阅,且状态更新仅更新单个经纬度,未存储历史记录
  • 缺少地址解析逻辑,无法将经纬度转换为实际地址
  • 未使用列表存储历史位置数据,无法实现ListView展示需求

完整修改后的代码

首先在pubspec.yaml中添加地址解析依赖:

dependencies:
  flutter:
    sdk: flutter
  geolocator: ^10.1.0
  geocoding: ^2.1.1

然后替换原代码为:

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart';
import 'package:intl/intl.dart';

// 位置记录模型类
class LocationRecord {
  final String latitude;
  final String longitude;
  final String address;
  final String timestamp;

  LocationRecord({
    required this.latitude,
    required this.longitude,
    required this.address,
    required this.timestamp,
  });
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: HomePage());
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool servicestatus = false;
  bool haspermission = false;
  late LocationPermission permission;
  final List<LocationRecord> _locationHistory = [];
  StreamSubscription<Position>? positionStream;

  @override
  void initState() {
    super.initState();
    checkGps();
  }

  @override
  void dispose() {
    // 取消位置流订阅,避免内存泄漏
    positionStream?.cancel();
    super.dispose();
  }

  checkGps() async {
    servicestatus = await Geolocator.isLocationServiceEnabled();
    if (!servicestatus) {
      print("GPS服务未开启,请打开GPS");
      setState(() {});
      return;
    }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        print("位置权限被拒绝");
      } else if (permission == LocationPermission.deniedForever) {
        print("位置权限被永久拒绝");
      } else {
        haspermission = true;
      }
    } else {
      haspermission = true;
    }

    if (haspermission) {
      setState(() {});
      startLocationTracking();
    }
    setState(() {});
  }

  // 转换经纬度为地址
  Future<String> getAddressFromLatLng(double lat, double lng) async {
    try {
      List<Placemark> placemarks = await placemarkFromCoordinates(lat, lng);
      Placemark place = placemarks[0];
      return "${place.street}, ${place.locality}, ${place.postalCode}, ${place.country}";
    } catch (e) {
      return "无法获取地址";
    }
  }

  // 启动位置追踪
  void startLocationTracking() async {
    // 获取初始位置
    Position initialPosition = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high);
    String address = await getAddressFromLatLng(
        initialPosition.latitude, initialPosition.longitude);
    String time = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());

    setState(() {
      _locationHistory.add(LocationRecord(
        latitude: initialPosition.latitude.toString(),
        longitude: initialPosition.longitude.toString(),
        address: address,
        timestamp: time,
      ));
    });

    // 设置位置更新监听(移动超过100米触发更新)
    LocationSettings locationSettings = const LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 100,
    );

    // 赋值给类成员变量,方便后续取消订阅
    positionStream = Geolocator.getPositionStream(locationSettings: locationSettings)
        .listen((Position position) async {
      String address = await getAddressFromLatLng(position.latitude, position.longitude);
      String time = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());

      setState(() {
        _locationHistory.add(LocationRecord(
          latitude: position.latitude.toString(),
          longitude: position.longitude.toString(),
          address: address,
          timestamp: time,
        ));
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GPS追踪器'),
        centerTitle: true,
      ),
      body: Column(
        children: [
          // 状态提示
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: [
                Text(servicestatus ? "GPS已开启" : "GPS未开启"),
                Text(haspermission ? "已获取位置权限" : "未获取位置权限"),
              ],
            ),
          ),
          // 历史位置列表
          Expanded(
            child: ListView.builder(
              itemCount: _locationHistory.length,
              itemBuilder: (context, index) {
                LocationRecord record = _locationHistory[index];
                return Card(
                  margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text("时间: ${record.timestamp}", style: const TextStyle(fontWeight: FontWeight.bold)),
                        const SizedBox(height: 8),
                        Text("纬度: ${record.latitude}"),
                        Text("经度: ${record.longitude}"),
                        const SizedBox(height: 8),
                        Text("地址: ${record.address}"),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

修改说明

  • 位置自动更新修复:将位置流订阅赋值给类成员positionStream,并在dispose方法中取消订阅,避免内存泄漏;每次位置变化时将新记录添加到历史列表,通过setState触发UI更新
  • 地址解析实现:使用geocoding包将经纬度转换为实际地址,处理异常情况避免崩溃
  • ListView展示:使用ListView.builder实现可滚动的历史位置列表,每个列表项展示时间、经纬度和地址信息
  • 数据管理:创建LocationRecord模型类统一管理位置相关数据,使代码结构更清晰

内容的提问来源于stack exchange,提问作者Sri Malayitha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:05:04