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

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;
  }
}

问题原因及解决方法

核心问题

  1. 未触发状态更新:_getData中修改_assets列表后,未调用setState通知Flutter框架重建UI,导致列表更新无法反映到屏幕上。
  2. Future 处理错误:混合使用async/await与.then,且未正确返回Future链,导致FutureBuilder提前认为数据加载完成,但实际_assets仍为空。
  3. 重复请求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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:05:34