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, ); }, ), ), ], ), ), ), ), ); } }
三、关键改动说明
变量调整:
- 将全局的
assetList移到State类内部,避免全局状态污染 foundAssetList改为List<Asset>类型,和原始列表类型统一- 添加
TextEditingController来监听搜索输入,同时方便后续清空输入等操作
- 将全局的
过滤方法修正:
_runFilter方法直接操作Asset对象的name属性,不再用Map键值对的方式- 使用
List.from()创建列表副本,避免直接引用导致的状态异常
数据同步:
- 在
fetchCoin获取新数据后,根据当前搜索关键词决定是直接更新展示列表还是重新过滤 - 给搜索控制器添加监听,实时响应输入变化
- 在
内存管理:
- 在
dispose方法里销毁TextEditingController,防止内存泄漏
- 在
四、代码可行性验证
调整后的代码完全可以实现你需要的搜索/过滤功能:
- 打开页面后,初始加载全部加密货币列表
- 在搜索框输入关键词时,列表会实时过滤出名称包含关键词的资产
- 每1秒刷新数据时,如果搜索框为空,列表会同步更新最新数据;如果有搜索关键词,会自动用新数据重新过滤,保持搜索结果的时效性
内容的提问来源于stack exchange,提问作者YSC
相关产品推荐
相关产品推荐

