Flutter调用加密货币API后ListView.builder未显示资产列表问题
问题:ListView.builder未显示加密货币资产列表
代码无报错,但ListView.builder渲染的加密货币资产名称列表未在应用屏幕上显示,仅能看到空列表。代码基于某API数据获取教程修改。
原代码
AddAssetScreen 代码
import 'package:flutter/material.dart'; import '../models/asset.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import '../widgets/searchPage.dart'; class AddAssetScreen extends StatefulWidget { @override _AddAssetScreenState createState() => _AddAssetScreenState(); } class _AddAssetScreenState extends State<AddAssetScreen> { List<Asset> _assets = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Hello World'), actions: <Widget>[ IconButton( icon: Icon(Icons.search), onPressed: () { Navigator.of(context).push(MaterialPageRoute( builder: (ctx) => SearchPage( assets: _assets, ))); }, ) ], ), body: FutureBuilder( future: _getData(), builder: (ctx, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) return CircularProgressIndicator(); return ListView.builder( itemBuilder: (ctx, i) { return Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ ListTile( title: Text(_assets[i].name!), ), Divider( height: 0, ) ], ); }, itemCount: _assets.length, ); }, ), ); } Future<void> _getData() async { var url = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'; http.get(Uri.parse(url)).then((data) { return json.decode(data.body); }).then((data) { for (var json in data) { _assets.add(Asset.fromJson(json)); } }).catchError((e) { print(e); }); } }
Asset 模型代码
class Asset { String? id; String? name; String? image; double? currentPrice; double? priceChange24h; double? priceChangePercentage24h; Asset({ required this.id, required this.name, required this.image, required this.currentPrice, required this.priceChange24h, required this.priceChangePercentage24h, }); Asset.fromJson(Map<String, dynamic> json) { id = json['id']; name = json['name']; image = json['image']; currentPrice = json['current_price']; priceChange24h = json['price_change_24h']; priceChangePercentage24h = json['price_change_percentage_24h']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = new Map<String, dynamic>(); data['id'] = this.id; data['name'] = this.name; data['image'] = this.image; data['current_price'] = this.currentPrice; data['price_change_24h'] = this.priceChange24h; data['price_change_percentage_24h'] = this.priceChangePercentage24h; return data; } }
问题原因及解决方法
核心问题
- 未触发状态更新:
_getData中修改_assets列表后,未调用setState通知Flutter框架重建UI,导致列表更新无法反映到屏幕上。 - Future 处理错误:混合使用
async/await与.then,且未正确返回Future链,导致FutureBuilder提前认为数据加载完成,但实际_assets仍为空。 - 重复请求API:直接将
_getData()传给FutureBuilder的future参数,会导致每次UI重建都重新发起API请求。
修改后的代码
方案1:移除FutureBuilder,在initState中加载数据
import 'package:flutter/material.dart'; import '../models/asset.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import '../widgets/searchPage.dart'; class AddAssetScreen extends StatefulWidget { @override _AddAssetScreenState createState() => _AddAssetScreenState(); } class _AddAssetScreenState extends State<AddAssetScreen> { List<Asset> _assets = []; bool _isLoading = true; @override void initState() { super.initState(); _getData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Hello World'), actions: <Widget>[ IconButton( icon: const Icon(Icons.search), onPressed: () { Navigator.of(context).push(MaterialPageRoute( builder: (ctx) => SearchPage( assets: _assets, ))); }, ) ], ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : ListView.builder( itemBuilder: (ctx, i) { return Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ ListTile( title: Text(_assets[i].name!), ), const Divider(height: 0), ], ); }, itemCount: _assets.length, ), ); } Future<void> _getData() async { try { var url = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'; final response = await http.get(Uri.parse(url)); final List data = json.decode(response.body); setState(() { _assets = data.map((json) => Asset.fromJson(json)).toList(); _isLoading = false; }); } catch (e) { print(e); setState(() { _isLoading = false; }); } } }
方案2:保留FutureBuilder,避免重复请求
import 'package:flutter/material.dart'; import '../models/asset.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import '../widgets/searchPage.dart'; class AddAssetScreen extends StatefulWidget { @override _AddAssetScreenState createState() => _AddAssetScreenState(); } class _AddAssetScreenState extends State<AddAssetScreen> { List<Asset> _assets = []; late final Future<void> _fetchDataFuture; @override void initState() { super.initState(); _fetchDataFuture = _getData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Hello World'), actions: <Widget>[ IconButton( icon: const Icon(Icons.search), onPressed: () { Navigator.of(context).push(MaterialPageRoute( builder: (ctx) => SearchPage( assets: _assets, ))); }, ) ], ), body: FutureBuilder( future: _fetchDataFuture, builder: (ctx, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } else { return ListView.builder( itemBuilder: (ctx, i) { return Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ ListTile( title: Text(_assets[i].name!), ), const Divider(height: 0), ], ); }, itemCount: _assets.length, ); } }, ), ); } Future<void> _getData() async { try { var url = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'; final response = await http.get(Uri.parse(url)); final List data = json.decode(response.body); setState(() { _assets = data.map((json) => Asset.fromJson(json)).toList(); }); } catch (e) { print(e); rethrow; // 让FutureBuilder捕获错误 } } }
关键修改点
- 在
initState中初始化数据请求,避免重复调用API。 - 使用
async/await统一异步逻辑,代码更清晰。 - 修改
_assets后调用setState,触发UI重建。 - 增加加载状态和错误处理,提升用户体验。
内容的提问来源于stack exchange,提问作者YSC
相关产品推荐
相关产品推荐

