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

Flutter从API获取多卡片数据显示异常排查求助

问题分析与解决方案

核心问题总结

你的代码存在几个关键问题导致显示异常:

  • 全局列表未清空,重复请求时数据堆积
  • 异步方法内错误调用setState()引发UI提前重建
  • ListView依赖全局列表长度而非FutureBuilder返回的快照数据
  • 所有列表项复用同一个ScreenshotController,导致截图逻辑混乱
  • ListView未正确处理父容器约束,shrinkWrap设置不当引发布局空白

1. 修复数据获取逻辑

修改getData()方法,使用局部列表避免全局数据污染,移除不必要的setState():

List<DataDetails> multiCardData = [];

Future<List<DataDetails>> getData() async {
  final sp_savedId = await SharedPreferences.getInstance();
  final get_savedId = sp_savedId.getString("Key_1") ?? "";
  final sp_savedPhone = await SharedPreferences.getInstance();
  final get_savedPhone = sp_savedPhone.getString("Key_2") ?? "";

  final response = await http.get(Uri.parse('你的API地址'));
  if (response.statusCode == 200) {
    final temp = jsonDecode(response.body.toString());
    // 每次请求前清空全局列表,避免数据堆积
    multiCardData.clear();
    for (var userData in temp['data']) {
      final cardData = DataDetails()
        ..cardName = userData['cardName'].toString()
        ..id = userData['$get_savedId']
        ..name = userData['name'].toString()
        ..designation = userData['designation'].toString()
        ..company = userData['company'].toString()
        ..address = userData['address'].toString()
        ..email = userData['email'].toString()
        ..phone = userData['phone'].toString()
        ..profilePhotoPath = userData['profilePhotoPath'].toString()
        ..qrCode = 'BEGIN:VCARD\nVERSION:3.0;\nN:${cardData.name};\nORG:${cardData.company}\nTITLE:${cardData.designation}\nADR:${cardData.address};;;;;;\nTEL;CELL:${cardData.phone}\nEMAIL:${cardData.email}\nEND:VCARD';
      
      multiCardData.add(cardData);
    }
    return multiCardData;
  } else {
    multiCardData.clear();
    return multiCardData;
  }
}

2. 修复FutureBuilder与ListView逻辑

  • 使用snapshot.data的长度作为itemCount,而非全局列表
  • 每个列表项创建独立的ScreenshotController
  • 用Expanded包裹ListView,解决shrinkWrap: false时的布局空白问题:
FutureBuilder(
  future: _futureBuilderData,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(child: CircularProgressIndicator());
    }
    if (!snapshot.hasData || snapshot.data!.isEmpty) {
      return const Center(child: Text("暂无数据"));
    }
    return Expanded(
      child: ListView.builder(
        shrinkWrap: false,
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          final ssController = ScreenshotController();
          final itemData = snapshot.data![index];
          
          return Screenshot(
            controller: ssController,
            child: GestureDetector(
              onTap: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) => OntapView()));
              },
              child: Container(
                margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                padding: const EdgeInsets.all(10),
                width: double.infinity,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(15),
                  color: const Color(0xffccf7ff)
                ),
                child: Column(
                  children: [
                    Container(
                      padding: const EdgeInsets.all(10),
                      child: Row(
                        children: [
                          SizedBox(
                            width: 200,
                            child: Text(itemData.cardName, style: const TextStyle(fontWeight: FontWeight.bold)),
                          ),
                          const SizedBox(width: 20),
                          SizedBox(
                            height: 20,
                            width: 90,
                            child: Image.asset('asset/image.png'),
                          )
                        ],
                      ),
                    ),
                    const SizedBox(height: 8),
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        CircleAvatar(
                          radius: 45,
                          backgroundColor: Colors.grey,
                          backgroundImage: NetworkImage(itemData.profilePhotoPath),
                        ),
                        const SizedBox(width: 10),
                        SizedBox(
                          width: 130,
                          height: 100,
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.start,
                            children: [
                              Text(itemData.name, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
                              const SizedBox(height: 5),
                              Text(itemData.designation, style: const TextStyle(fontSize: 12)),
                            ],
                          ),
                        ),
                        const SizedBox(width: 20),
                        Container(
                          width: MediaQuery.of(context).size.width * 0.08,
                          height: MediaQuery.of(context).size.height * 0.08,
                          decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            color: Colors.blue[600]
                          ),
                          child: IconButton(
                            onPressed: () async {
                              final list = await ssController.capture();
                              if (list != null) {
                                final directory = (await getTemporaryDirectory()).path;
                                final imgFile = File('$directory/${DateTime.now().millisecondsSinceEpoch}.png');
                                imgFile.writeAsBytesSync(list);
                                await Share.shareFiles([imgFile.path], text: 'Hi this is from qr code').catchError((e) {
                                  debugPrint('Error: $e');
                                });
                              }
                            },
                            icon: const Icon(Icons.share, color: Colors.white, size: 18),
                          ),
                        )
                      ],
                    ),
                    Container(
                      padding: const EdgeInsets.all(10),
                      child: Row(
                        children: [
                          SizedBox(
                            height: 60,
                            width: 200,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Text('Mob   : +91 -${itemData.phone}'),
                                Text('Email : ${itemData.email}'),
                                Text('Work  : ${itemData.company}')
                              ],
                            ),
                          ),
                          const SizedBox(width: 20),
                          ClipRRect(
                            borderRadius: BorderRadius.circular(10),
                            child: SizedBox(
                              height: 90,
                              width: 90,
                              child: QrImage(
                                data: itemData.qrCode,
                                backgroundColor: Colors.white,
                              ),
                            ),
                          )
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  },
)

3. 额外优化建议

  • 确保_futureBuilderData只初始化一次,避免重复请求:
late Future<List<DataDetails>> _futureBuilderData;

@override
void initState() {
  super.initState();
  _futureBuilderData = getData();
}
  • 处理API返回的空值,使用??运算符设置默认值,避免空指针异常
  • 检查userData['$get_savedId']的字段名是否与API返回一致,确保数据正确赋值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 16:54:57