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

Flutter/Dart:带懒加载的列表如何按距离排序?

问题描述

我有一个包含纬度(lat)和经度(lng)的列表,希望按距离对其进行排序,但由于给列表添加了懒加载功能,实现起来十分困难,以下是我的完整代码:

class ListPage extends StatefulWidget {
  late final AppController appController;
  ListPage(appController){
    this.appController = appController;
  }

  @override
  _ListPageState createState() => _ListPageState();
}

class _ListPageState extends State<ListPage> {
  late double unitHeightValue;
  final myController = TextEditingController();
  bool firstTime = false;
  String evseSearchField = "";
  Evse evse = new Evse(evseID: 0, friendlyName: "", street: "", streetNumber: "", region: "", lat: 0.0, lng: 0.0, connectorList: [], cpSerial: "", img: "");
  late List<EvsePanel> evsePanels = [new EvsePanel(evse, widget.appController, true)];
  late TextField searchTextField = TextField(
    controller: myController,
    decoration: InputDecoration(
      border: InputBorder.none,
      hintText: 'Search',
      fillColor: Colors.white,
    ),
  );

  final controller = ScrollController();
  List<String> items = [];
  bool hasMore = true;
  int page = 1;
  bool isLoading = false;

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

    fetch();

    controller.addListener(() {
      if (controller.position.maxScrollExtent == controller.offset) {
        fetch();
      }
    });
  }

  @override
  void dispose() {
    myController.dispose();
    controller.dispose();
    super.dispose();
  }

  void refreshEvsePanels() async{
    const refreshSeconds = const Duration(seconds: 5);
    Timer.periodic(refreshSeconds, (Timer t) {
      if(!mounted) {
        t.cancel();
        return;
      }
      showEvsePanels();
    });
  }
  void showEvsePanels() async{
widget.appController.evseList.sort((a, b){
      int nameComp = a.sort_availability.compareTo(b.sort_availability);
      if (nameComp == stationAvailability.available.index) {
        return a.distance.compareTo(b.distance); // '-' for descending
      }
      return nameComp;
});

    setState(() {
      if (evseSearchField.isEmpty){
        int i = 0;
        if (tempEvses.length != 0){
          evsePanels.clear();
          while (i < tempEvses.length){
            evsePanels.add(new EvsePanel(tempEvses[i], widget.appController, false));
            i++;
          }
        }
      }
      else{
        int i = 0;
        if (tempEvses.length != 0){
          evsePanels.clear();
          while (i < tempEvses.length){
            if (containsIgnoreCase(tempEvses[i].friendlyName, evseSearchField))
              evsePanels.add(new EvsePanel(
                  tempEvses[i], widget.appController, false));
            i++;
          }
        }
      }
    });
  }
  List<Evse> tempEvses = [];

  Future fetch() async {
    if (isLoading) return;
    isLoading = true;

    const limit = 10;
    final url = Uri.parse('https://everywhere-dev.iccs.gr:18083/evse/getEvsesPaged?page=$page&pageLimit=$limit');
    final response = await http.get(url);
    if (response.statusCode == 200) {
      final List newItems = json.decode(response.body)["results"];

      setState(() {
        page++;
        isLoading = false;

        if (newItems.length <limit) {
          hasMore = false;
        }
        tempEvses.addAll(newItems.map<Evse>((items) {
          final evseID = items['evseID'];
          final friendlyName = items['friendlyName'];
          final street = items['street'];
          final streetNumber = items['streetNumber'];
          final region = items['region'];
          final lat = items['lat'] ?? 0.0;
          final lng = items['lng'] ?? 0.0;
          final connectorList = items['connectorList'];
          final cpSerial = items['cpSerial'];
          final img = items['img'];


          return new Evse(evseID: evseID, friendlyName: friendlyName, street: street, streetNumber: streetNumber, region: region, lat: lat, lng: lng, connectorList: [connectorList], cpSerial: cpSerial, img: img);


        }).toList());
        for(int i=0 ; i<tempEvses.length; i++  ){
          this.tempEvses[i].calculateDistance(widget.appController.currentLocation);
        //  this.tempEvses[i].calculateAvailability();
          this.tempEvses[i].isEvseFavorite(widget.appController.favoritesList);
          this.tempEvses[i].storePlugCounters();
        }
        showEvsePanels();
      });
    }
  }

  Future refresh() async{
    setState(() {
      isLoading = false;
      hasMore =true;
      page = 0;
      tempEvses.clear();
    });
    fetch();
  }

  @override
  Widget build(BuildContext context) {
    if(!firstTime){
      this.unitHeightValue = widget.appController.queryData.size.height * 0.001;
      showEvsePanels();
      //refreshEvsePanels();
      firstTime = true;
    }

    return  SizedBox(
        height: widget.appController.queryData.size.height,
        width : widget.appController.queryData.size.width,

        child: Column(
            children: [
              Container(
                height: widget.appController.queryData.size.height/12,
                width: widget.appController.queryData.size.width * (9/10),
                margin: EdgeInsets.all(5.0),
                decoration: BoxDecoration(
                    color: widget.appController.themeController.appWhiteLightColor,
                    boxShadow: [
                      BoxShadow(
                        color: widget.appController.themeController.appGreyLightColor.withOpacity(0.6),
                        spreadRadius: 1,
                        blurRadius: 1,
                        offset: Offset(0, 2), // changes position of shadow
                      ),
                    ],
                    shape: BoxShape.rectangle,
                    border: Border.all(
                      color: Colors.transparent,
                    ),
                    borderRadius: BorderRadius.all(Radius.circular(10))
                ),
                child:Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children:[
                      Container(
                        width: widget.appController.queryData.size.width * (7/10),
                        child: searchTextField,
                      ),
                      Container(
                        child: IconButton(
                          color: widget.appController.themeController.appBlackDeepColor,
                          tooltip: 'Search',
                          icon: const Icon(Icons.search),
                          iconSize: 32 * this.unitHeightValue,
                          onPressed: () {
                            this.evseSearchField = myController.text;
                            showEvsePanels();
                          },
                        ),
                      ),
                    ]
                ),
              ),
              Expanded(
                  child: RefreshIndicator(
                    onRefresh: refresh,
                    child: ListView.separated(
                        separatorBuilder: (context, index) => Divider(),
                        controller: controller,
                        padding: const EdgeInsets.all(8),
                        itemCount: evsePanels.length + 1,
                        itemBuilder: (context, index) {
                          if (index < evsePanels.length) {
                            return evsePanels[index];
                          } else {
                            return  Padding(
                              padding: const EdgeInsets.symmetric(vertical: 32),
                              child: Center(
                                child: hasMore
                                    ? const CircularProgressIndicator()
                                    : const Text('No more data to load'),
                              ),
                            );
                          }
                        }
                    ),
                  )
              )
            ]
        )
    );
  }
  bool containsIgnoreCase(String str1, String str2) {
    return str1.toLowerCase().contains(str2.toLowerCase());
  }

}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:21:58