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

Dart/Flutter:如何使用异步方法获取的数据构建Widget

用ListView.builder构建异步加载的列表项

嘿,我来帮你搞定这个问题!要实现每个列表项在构建前通过异步方法获取数据,核心是给每个列表项搭配FutureBuilder,它能帮你优雅处理异步数据的加载、成功、失败三种状态。下面一步步来实现:

1. 先定义数据模型

首先我们把要获取的狗狗信息封装成一个类,方便后续统一管理和使用:

class Dog {
  final String name;
  final String breed;

  Dog({required this.name, required this.breed});
}

2. 完善异步数据获取函数

你提供的fetchBasicData函数可以调整成返回Future<Dog>,同时补充异常处理和空值校验,避免意外崩溃:

import 'package:http/http.dart' as http;
import 'package:html/parser.dart' as parser;
import 'package:html/dom.dart' as dom;

Future<Dog> fetchBasicData(String url) async {
  try {
    final response = await http.get(Uri.parse(url));
    // 先检查请求是否成功
    if (response.statusCode != 200) {
      throw Exception('请求失败,状态码:${response.statusCode}');
    }
    
    final document = parser.parse(response.body);
    
    // 解析狗名,做空值校验
    final nameElements = document.getElementsByClassName('datas-nev');
    if (nameElements.isEmpty || nameElements[0].nodes.length < 2) {
      throw Exception('无法解析狗名');
    }
    final dogName = nameElements[0].nodes[1].text.trim();
    
    // 解析狗品种,做空值校验
    final breedElements = document.getElementsByClassName('datas-tipus');
    if (breedElements.isEmpty) {
      throw Exception('无法解析狗品种');
    }
    final dogBreed = breedElements[0].text.trim();
    
    return Dog(name: dogName, breed: dogBreed);
  } catch (e) {
    // 统一捕获异常,方便后续在UI上提示用户
    throw Exception('加载数据失败:$e');
  }
}

3. 用ListView.builder + FutureBuilder构建列表

接下来在你的Widget中,用ListView.builder生成列表,每个列表项都用FutureBuilder包裹,处理异步加载的不同状态:

class AsyncDogList extends StatefulWidget {
  // 假设你有一个包含所有要请求的URL的列表
  final List<String> dogUrls;

  const AsyncDogList({super.key, required this.dogUrls});

  @override
  State<AsyncDogList> createState() => _AsyncDogListState();
}

class _AsyncDogListState extends State<AsyncDogList> {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: widget.dogUrls.length,
      itemBuilder: (context, index) {
        final url = widget.dogUrls[index];
        // 给每个FutureBuilder加唯一key,避免滚动时复用错误的状态
        return FutureBuilder<Dog>(
          future: fetchBasicData(url),
          key: ValueKey(url),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              // 加载中显示加载指示器
              return const ListTile(
                title: Center(child: CircularProgressIndicator()),
              );
            } else if (snapshot.hasError) {
              // 加载失败显示错误信息,还可以加个重试按钮
              return ListTile(
                title: const Text('加载失败'),
                subtitle: Text('${snapshot.error}'),
                trailing: IconButton(
                  icon: const Icon(Icons.refresh),
                  onPressed: () => setState(() {}),
                ),
              );
            } else if (snapshot.hasData) {
              // 加载成功显示狗狗信息
              final dog = snapshot.data!;
              return ListTile(
                title: Text(dog.name),
                subtitle: Text(dog.breed),
              );
            } else {
              // 兜底的异常情况
              return const ListTile(title: Text('无数据'));
            }
          },
        );
      },
    );
  }
}

4. 实用优化建议

  • 缓存数据:如果同一URL会被多次请求,建议用Map<String, Dog>缓存已加载的数据,避免重复发起网络请求,提升列表滚动流畅度。
  • 防抖处理:如果列表滚动速度快,可能触发大量请求,可以给请求加防抖逻辑,或者用Future.microtask优化请求时机。
  • 全局加载状态:如果需要等待所有列表项加载完成后再显示,可以在父Widget中统一处理整体的加载状态。

内容的提问来源于stack exchange,提问作者Zoltán Györkei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:49:02