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

Flutter中如何基于异步函数返回的用户距离对列表排序?

解决异步距离计算下的Locations列表排序问题

你遇到的核心问题是Geolocator().distanceBetween是异步方法,而列表排序需要同步的、已确定的距离值才能做比较——直接在排序逻辑里调用异步方法行不通,因为排序函数没法等待Future完成。下面是具体的解决思路和代码实现:

核心思路

  1. 预计算所有距离:先批量算出每个Locations对象到mylocation的距离,把距离和对应对象绑定在一起
  2. 基于预计算值排序:拿到所有距离后,对绑定了距离的临时列表做排序
  3. 在合适的生命周期处理异步逻辑:别在build方法里做异步操作(build会频繁触发),应该在initState里完成距离计算和排序,再更新状态刷新UI

修改后的代码实现

第一步:重构LocationList的State,处理异步排序

class _LocationListState extends State<LocationList> {
  List<Locations> sortedLocations = [];
  GeoPoint mylocation = GeoPoint(2, 2); // 当前用户位置

  @override
  void initState() {
    super.initState();
    _sortLocationsByDistance(); // 初始化时执行排序逻辑
  }

  // 异步排序方法
  Future<void> _sortLocationsByDistance() async {
    final List<Locations> locationlist = [
      Locations(location: GeoPoint(1, 2)),
      Locations(location: GeoPoint(2, 2)),
      Locations(location: GeoPoint(3, 2)),
      Locations(location: GeoPoint(4, 1))
    ];

    // 1. 批量计算每个位置到用户位置的距离,生成带距离的临时列表
    List<Map<String, dynamic>> locationWithDistance = [];
    for (var location in locationlist) {
      double distance = await Geolocator().distanceBetween(
        mylocation.latitude,
        mylocation.longitude,
        location.location.latitude,
        location.location.longitude,
      ) / 1000; // 转换成公里单位

      locationWithDistance.add({
        'location': location,
        'distance': distance,
      });
    }

    // 2. 根据距离从小到大排序
    locationWithDistance.sort((a, b) => a['distance'].compareTo(b['distance']));

    // 3. 提取排序后的Locations列表
    sortedLocations = locationWithDistance.map((item) => item['location'] as Locations).toList();

    // 更新UI
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    // 排序完成前显示加载状态
    if (sortedLocations.isEmpty) {
      return const Center(child: CircularProgressIndicator());
    }

    return ListView.builder(
      scrollDirection: Axis.horizontal,
      itemCount: sortedLocations.length,
      itemBuilder: (context, index) {
        return LocationCard(
          location: sortedLocations[index],
          userLocation: mylocation,
        );
      },
    );
  }
}

第二步:重构LocationCard,直接接收目标位置和用户位置

原来通过索引传递数据的方式不够直观,直接传递对应对象更清晰:

class LocationCard extends StatelessWidget {
  final Locations location;
  final GeoPoint userLocation;

  const LocationCard({
    Key? key,
    required this.location,
    required this.userLocation,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Future<double> _calculateDistance() async {
      double distance = await Geolocator().distanceBetween(
        userLocation.latitude,
        userLocation.longitude,
        location.location.latitude,
        location.location.longitude,
      ) / 1000;
      return distance;
    }

    return Container(
      child: Card(
        child: Center(
          child: FutureBuilder<double>(
            future: _calculateDistance(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text('${snapshot.data!.toStringAsFixed(2)} km');
              } else if (snapshot.hasError) {
                return Text('距离计算失败');
              }
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

额外优化建议

  • 避免重复计算:如果locationlist是从网络/本地获取的,建议只在数据更新时重新计算距离,不要每次初始化都创建新列表
  • 用compute处理大量计算:如果位置数量很多,距离计算可能阻塞UI,可以把计算逻辑放到后台隔离区:
    // 抽成顶层函数(compute要求顶层或static函数)
    double calculateDistance(List<GeoPoint> points) {
      return Geolocator.distanceBetween(
        points[0].latitude,
        points[0].longitude,
        points[1].latitude,
        points[1].longitude,
      ) / 1000;
    }
    
    // 在_sortLocationsByDistance里替换计算逻辑:
    double distance = await compute(calculateDistance, [mylocation, location.location]);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 17:37:56