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

Flutter SearchDelegate搜索位置时出现重复结果求助

搜索位置时列表重复条目问题修复

我使用SearchDelegate实现搜索功能,支持搜索序列号或站点位置。搜索序列号功能正常,但搜索位置时会出现结果重复的情况,列表重建后显示重复条目。

搜索栏显示重复结果

问题根源

  1. 双重循环嵌套:在ListView.builder的itemBuilder回调中,又嵌套了一个for循环遍历整个suggestionList,导致每个itemBuilder索引都会渲染一遍完整的搜索结果列表,这是重复条目的直接原因。
  2. 冗余的嵌套滚动组件:代码中多层嵌套SingleChildScrollView和ListView,不仅会导致滚动冲突,还加剧了重复渲染的问题。

修复后的代码

class Search extends SearchDelegate<String> {
  final List stationList;
  Search(this.stationList);

  ScrollController _scrollController = ScrollController();

  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      Padding(
        padding: EdgeInsets.only(right: 2),
        child: IconButton(
            onPressed: () => buildResults(context),
            icon: Icon(Icons.search)),
      ),
      Padding(
        padding: EdgeInsets.only(left: 2),
        child: IconButton(
            onPressed: () => query = '',
            icon: Icon(Icons.clear)),
      ),
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      onPressed: () => Navigator.pop(context),
      icon: Icon(Icons.arrow_back_ios),
    );
  }

  // 提取公共的列表项构建方法,避免代码重复
  Widget _buildStationItem(BuildContext context, int index, List suggestionList) {
    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => StationData(
              stationSerialNumber: suggestionList[index]['serial_number'],
            ),
          ),
        );
      },
      child: Container(
        margin: EdgeInsets.only(bottom: ScreenSize.blockSizeVertical * 1),
        color: ColorTheme.white,
        child: Padding(
          padding: const EdgeInsets.all(15),
          child: Row(
            children: [
              Expanded(
                flex: 1,
                child: LayoutBuilder(
                  builder: (context, constraints) {
                    return Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        SingleChildScrollView(
                          scrollDirection: Axis.horizontal,
                          child: Row(
                            children: [
                              Icon(
                                Icons.wifi_tethering,
                                color: ColorTheme.secondary,
                                size: constraints.maxWidth * 10 / 100,
                              ),
                              SizedBox(width: ScreenSize.blockSizeVertical * 1.5),
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Row(
                                    children: [
                                      SizedBox(width: constraints.maxWidth * 0.02),
                                      Text(
                                        '${suggestionList[index]['serial_number']}',
                                        style: TextStyle(
                                          fontSize: ScreenSize.blockSizeVertical * 2.5,
                                        ),
                                      ),
                                      SizedBox(width: constraints.maxWidth * 0.05),
                                      TextButton(
                                        onPressed: null,
                                        child: Text(
                                          '${suggestionList[index]['status']}',
                                          style: TextStyle(
                                            fontSize: ScreenSize.blockSizeVertical * 1.8,
                                            color: suggestionList[index]['status'] == 'Under QC'
                                                ? Colors.redAccent
                                                : Colors.greenAccent,
                                            fontWeight: FontWeight.bold,
                                          ),
                                        ),
                                      ),
                                    ],
                                  ),
                                  SizedBox(height: ScreenSize.blockSizeVertical * 1),
                                  Row(
                                    children: [
                                      Icon(
                                        Icons.location_on,
                                        color: ColorTheme.secondary,
                                        size: constraints.maxWidth * 4 / 100,
                                      ),
                                      SizedBox(width: constraints.maxWidth * 1 / 100),
                                      Text(
                                        '${suggestionList[index]['location']}',
                                        style: TextStyle(
                                          fontSize: ScreenSize.blockSizeVertical * 2,
                                          color: ColorTheme.black,
                                        ),
                                      ),
                                      SizedBox(width: constraints.maxWidth * 5 / 100),
                                      Icon(
                                        Icons.access_time,
                                        color: ColorTheme.secondary,
                                        size: constraints.maxWidth * 4 / 100,
                                      ),
                                      SizedBox(width: constraints.maxWidth * 1 / 100),
                                      Text(
                                        'Created at ${suggestionList[index]['date_added'].split(' ')[0]}',
                                        style: TextStyle(
                                          fontSize: ScreenSize.blockSizeVertical * 2,
                                          color: ColorTheme.black,
                                        ),
                                      ),
                                      SizedBox(width: constraints.maxWidth * 10 / 100),
                                    ],
                                  ),
                                ],
                              ),
                            ],
                          ),
                        ),
                      ],
                    );
                  },
                ),
              ),
              Align(
                alignment: Alignment.centerRight,
                child: IconButton(
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => StationData(
                          stationSerialNumber: suggestionList[index]['serial_number'],
                        ),
                      ),
                    );
                  },
                  icon: Icon(
                    Icons.navigate_next,
                    color: ColorTheme.grey,
                    size: ScreenSize.blockSizeVertical * 6,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    final suggestionList = query.isEmpty
        ? stationList
        : stationList
            .where((element) => element['serial_number']
                .toString()
                .toUpperCase()
                .startsWith(query.toUpperCase()) ||
                element['location']
                    .toString()
                    .toUpperCase()
                    .startsWith(query.toUpperCase()))
            .toList();

    return Scaffold(
      backgroundColor: Color.fromARGB(255, 255, 246, 247),
      body: ListView.builder(
        controller: _scrollController,
        physics: ClampingScrollPhysics(),
        itemCount: suggestionList.length,
        itemBuilder: (context, index) => _buildStationItem(context, index, suggestionList),
      ),
    );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    final suggestionList = query.isEmpty
        ? stationList
        : stationList
            .where((element) => element['serial_number']
                .toString()
                .toUpperCase()
                .startsWith(query.toUpperCase()) ||
                element['location']
                    .toString()
                    .toUpperCase()
                    .startsWith(query.toUpperCase()))
            .toList();

    return Scaffold(
      backgroundColor: Color.fromARGB(255, 255, 246, 247),
      body: ListView.builder(
        controller: _scrollController,
        physics: ClampingScrollPhysics(),
        itemCount: suggestionList.length,
        itemBuilder: (context, index) => _buildStationItem(context, index, suggestionList),
      ),
    );
  }
}

修复说明

  1. 移除双重循环:删除itemBuilder内部的for循环,直接使用itemBuilder提供的index渲染单个条目,彻底解决重复显示问题。
  2. 提取公共组件:将重复的列表项构建逻辑提取为_buildStationItem方法,减少代码冗余,方便后续维护。
  3. 简化滚动结构:移除多余的SingleChildScrollView、Stack和嵌套ListView,只保留最外层的ListView.builder,避免滚动冲突。
  4. 优化代码风格:将部分匿名函数简化为箭头函数,提升代码可读性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:48:21