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

Flutter中API加载加密货币列表的搜索/过滤功能实现及代码位置咨询

加密货币列表搜索/过滤功能实现方案

我来帮你梳理下这个搜索功能的实现,先说说你写的代码里的几个小问题,再给你调整好的完整代码:

一、你的搜索代码存在的问题

  • 类型不匹配:你定义的foundAssetList是List<Map<String, dynamic>>,但assetList是List<Asset>类型,直接赋值会报错
  • 初始化时机不对:initState里初始化foundAssetList = assetList时,API请求还没完成,assetList是空的,会导致初始列表为空
  • 数据刷新不同步:你用Timer.periodic每1秒刷新一次加密货币数据,但刷新后不会同步更新搜索结果列表

二、修正后的完整代码(搜索功能整合到AddCryptoAssetScreen中)

把搜索相关的变量和方法全部放到_AddCryptoAssetScreenState类内部,下面是调整后的完整代码:

class AddCryptoAssetScreen extends StatefulWidget {
  @override
  _AddCryptoAssetScreenState createState() => _AddCryptoAssetScreenState();
}

class _AddCryptoAssetScreenState extends State<AddCryptoAssetScreen> {
  // 原始资产列表
  List<Asset> assetList = [];
  // 过滤后的资产列表(用于搜索展示)
  List<Asset> foundAssetList = [];
  // 搜索关键词控制器,用于监听输入和管理状态
  final TextEditingController _searchController = TextEditingController();

  Future<List<Asset>> fetchCoin() async {
    final response = await http.get(Uri.parse(
        'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'));
    if (response.statusCode == 200) {
      List<dynamic> values = json.decode(response.body);
      List<Asset> newAssetList = [];
      if (values.length > 0) {
        for (int i = 0; i < values.length; i++) {
          if (values[i] != null) {
            Map<String, dynamic> map = values[i];
            newAssetList.add(Asset.fromJson(map));
          }
        }
      }
      setState(() {
        assetList = newAssetList;
        // 刷新数据后,根据当前搜索状态更新展示列表
        if (_searchController.text.isEmpty) {
          foundAssetList = List.from(assetList);
        } else {
          _runFilter(_searchController.text);
        }
      });
      return assetList;
    } else {
      throw Exception('Failed to load coins');
    }
  }

  @override
  void initState() {
    super.initState();
    // 初始加载数据
    fetchCoin();
    // 定时刷新数据
    Timer.periodic(const Duration(seconds: 1), (timer) => fetchCoin());
    // 监听搜索输入变化
    _searchController.addListener(() {
      _runFilter(_searchController.text);
    });
  }

  @override
  void dispose() {
    // 销毁控制器,避免内存泄漏
    _searchController.dispose();
    super.dispose();
  }

  // 修正后的过滤方法,适配Asset实体类
  void _runFilter(String enteredKeyword) {
    List<Asset> results = [];
    if (enteredKeyword.isEmpty) {
      // 关键词为空时展示全部资产
      results = List.from(assetList);
    } else {
      // 根据名称模糊匹配,忽略大小写
      results = assetList
          .where((asset) => asset.name.toLowerCase().contains(enteredKeyword.toLowerCase()))
          .toList();
    }
    // 更新过滤后的列表状态
    setState(() {
      foundAssetList = results;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () => Navigator.pop(context),
      child: DraggableScrollableSheet(
        builder: (_, controller) => Container(
          decoration: const BoxDecoration(),
          clipBehavior: Clip.antiAlias,
          child: Scaffold(
            appBar: AppBar(),
            body: Column(
              children: [
                Container(
                  margin: const EdgeInsets.all(16),
                  child: TextField(
                    controller: _searchController,
                    keyboardType: TextInputType.text,
                    textAlign: TextAlign.start,
                    decoration: InputDecoration(
                        prefixIcon: const Icon(Icons.search),
                        hintText: 'Search',
                        border: OutlineInputBorder(),
                        contentPadding: const EdgeInsets.symmetric(vertical: 12)),
                  ),
                ),
                Expanded(
                  child: ListView.builder(
                    controller: controller,
                    scrollDirection: Axis.vertical,
                    itemCount: foundAssetList.length,
                    itemBuilder: (context, index) {
                      return AssetCryptoCard(
                        name: foundAssetList[index].name,
                        image: foundAssetList[index].image,
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

三、关键改动说明

  1. 变量调整:

    • 将全局的assetList移到State类内部,避免全局状态污染
    • foundAssetList改为List<Asset>类型,和原始列表类型统一
    • 添加TextEditingController来监听搜索输入,同时方便后续清空输入等操作
  2. 过滤方法修正:

    • _runFilter方法直接操作Asset对象的name属性,不再用Map键值对的方式
    • 使用List.from()创建列表副本,避免直接引用导致的状态异常
  3. 数据同步:

    • 在fetchCoin获取新数据后,根据当前搜索关键词决定是直接更新展示列表还是重新过滤
    • 给搜索控制器添加监听,实时响应输入变化
  4. 内存管理:

    • 在dispose方法里销毁TextEditingController,防止内存泄漏

四、代码可行性验证

调整后的代码完全可以实现你需要的搜索/过滤功能:

  • 打开页面后,初始加载全部加密货币列表
  • 在搜索框输入关键词时,列表会实时过滤出名称包含关键词的资产
  • 每1秒刷新数据时,如果搜索框为空,列表会同步更新最新数据;如果有搜索关键词,会自动用新数据重新过滤,保持搜索结果的时效性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 15:54:08